use NULL value in load_path_suffix to NOT load any files
[oweals/gnunet.git] / src / dns / dns_api.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2012, 2016 GNUnet e.V.
4
5       GNUnet is free software: you can redistribute it and/or modify it
6       under the terms of the GNU Affero General Public License as published
7       by the Free Software Foundation, either version 3 of the License,
8       or (at your option) any later version.
9
10       GNUnet is distributed in the hope that it will be useful, but
11       WITHOUT ANY WARRANTY; without even the implied warranty of
12       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13       Affero General Public License for more details.
14
15       You should have received a copy of the GNU Affero General Public License
16       along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20
21 /**
22  * @file dns/dns_api.c
23  * @brief API to access the DNS service.
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_dns_service.h"
28 #include "dns.h"
29
30
31 /**
32  * Handle to identify an individual DNS request.
33  */
34 struct GNUNET_DNS_RequestHandle
35 {
36   /**
37    * Handle to DNS API.
38    */
39   struct GNUNET_DNS_Handle *dh;
40
41   /**
42    * Stored in network byte order (as for us, it is just a random number).
43    */
44   uint64_t request_id;
45
46   /**
47    * Re-connect counter, to make sure we did not reconnect in the meantime.
48    */
49   uint32_t generation;
50 };
51
52
53 /**
54  * DNS handle
55  */
56 struct GNUNET_DNS_Handle
57 {
58   /**
59    * Connection to DNS service, or NULL.
60    */
61   struct GNUNET_MQ_Handle *mq;
62
63   /**
64    * Configuration to use.
65    */
66   const struct GNUNET_CONFIGURATION_Handle *cfg;
67
68   /**
69    * Function to call to get replies.
70    */
71   GNUNET_DNS_RequestHandler rh;
72
73   /**
74    * Closure for @e rh.
75    */
76   void *rh_cls;
77
78   /**
79    * Task to reconnect to the service.
80    */
81   struct GNUNET_SCHEDULER_Task *reconnect_task;
82
83   /**
84    * Re-connect counter, to make sure we did not reconnect in the meantime.
85    */
86   uint32_t generation;
87
88   /**
89    * Flags for events we care about.
90    */
91   enum GNUNET_DNS_Flags flags;
92
93   /**
94    * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
95    * we can be disconnected.
96    */
97   unsigned int pending_requests;
98 };
99
100
101 /**
102  * Reconnect to the DNS service.
103  *
104  * @param cls handle with the connection to connect
105  * @param tc scheduler context (unused)
106  */
107 static void
108 reconnect (void *cls);
109
110
111 /**
112  * Drop the existing connection and reconnect to the DNS service.
113  *
114  * @param dh handle with the connection
115  */
116 static void
117 force_reconnect (struct GNUNET_DNS_Handle *dh)
118 {
119   if (NULL != dh->mq)
120   {
121     GNUNET_MQ_destroy (dh->mq);
122     dh->mq = NULL;
123   }
124   dh->reconnect_task =
125     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
126                                   &reconnect,
127                                   dh);
128 }
129
130
131 /**
132  * Generic error handler, called with the appropriate error code and
133  * the same closure specified at the creation of the message queue.
134  * Not every message queue implementation supports an error handler.
135  *
136  * @param cls closure with the `struct GNUNET_DNS_Handle *`
137  * @param error error code
138  */
139 static void
140 mq_error_handler (void *cls,
141                   enum GNUNET_MQ_Error error)
142 {
143   struct GNUNET_DNS_Handle *dh = cls;
144
145   force_reconnect (dh);
146 }
147
148
149 /**
150  * This receives packets from the DNS service and calls the application to
151  * check that the request is well-formed
152  *
153  * @param cls the struct GNUNET_DNS_Handle
154  * @param req message from the service (request)
155  */
156 static int
157 check_request (void *cls,
158                const struct GNUNET_DNS_Request *req)
159 {
160   if (0 != ntohl (req->reserved))
161   {
162     GNUNET_break (0);
163     return GNUNET_SYSERR;
164   }
165   return GNUNET_OK;
166 }
167
168
169 /**
170  * This receives packets from the DNS service and calls the application to
171  * handle it.
172  *
173  * @param cls the `struct GNUNET_DNS_Handle *`
174  * @param msg message from the service (request)
175  */
176 static void
177 handle_request (void *cls,
178                 const struct GNUNET_DNS_Request *req)
179 {
180   struct GNUNET_DNS_Handle *dh = cls;
181   size_t payload_length = ntohs (req->header.size) - sizeof(*req);
182   struct GNUNET_DNS_RequestHandle *rh;
183
184   rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
185   rh->dh = dh;
186   rh->request_id = req->request_id;
187   rh->generation = dh->generation;
188   dh->pending_requests++;
189   dh->rh (dh->rh_cls,
190           rh,
191           payload_length,
192           (const char *) &req[1]);
193 }
194
195
196 /**
197  * Reconnect to the DNS service.
198  *
199  * @param cls handle with the connection to connect
200  */
201 static void
202 reconnect (void *cls)
203 {
204   struct GNUNET_DNS_Handle *dh = cls;
205   struct GNUNET_MQ_MessageHandler handlers[] = {
206     GNUNET_MQ_hd_var_size (request,
207                            GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST,
208                            struct GNUNET_DNS_Request,
209                            dh),
210     GNUNET_MQ_handler_end ()
211   };
212   struct GNUNET_MQ_Envelope *env;
213   struct GNUNET_DNS_Register *msg;
214
215   dh->reconnect_task = NULL;
216   dh->mq = GNUNET_CLIENT_connect (dh->cfg,
217                                   "dns",
218                                   handlers,
219                                   &mq_error_handler,
220                                   dh);
221   if (NULL == dh->mq)
222     return;
223   dh->generation++;
224   env = GNUNET_MQ_msg (msg,
225                        GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
226   msg->flags = htonl (dh->flags);
227   GNUNET_MQ_send (dh->mq,
228                   env);
229 }
230
231
232 /**
233  * If a GNUNET_DNS_RequestHandler calls this function, the request is
234  * given to other clients or the global DNS for resolution.  Once a
235  * global response has been obtained, the request handler is AGAIN
236  * called to give it a chance to observe and modify the response after
237  * the "normal" resolution.  It is not legal for the request handler
238  * to call this function if a response is already present.
239  *
240  * @param rh request that should now be forwarded
241  */
242 void
243 GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
244 {
245   struct GNUNET_MQ_Envelope *env;
246   struct GNUNET_DNS_Response *resp;
247
248   GNUNET_assert (0 < rh->dh->pending_requests--);
249   if (rh->generation != rh->dh->generation)
250   {
251     GNUNET_free (rh);
252     return;
253   }
254   env = GNUNET_MQ_msg (resp,
255                        GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
256   resp->drop_flag = htonl (1);
257   resp->request_id = rh->request_id;
258   GNUNET_MQ_send (rh->dh->mq,
259                   env);
260   GNUNET_free (rh);
261 }
262
263
264 /**
265  * If a GNUNET_DNS_RequestHandler calls this function, the request is
266  * to be dropped and no response should be generated.
267  *
268  * @param rh request that should now be dropped
269  */
270 void
271 GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
272 {
273   struct GNUNET_MQ_Envelope *env;
274   struct GNUNET_DNS_Response *resp;
275
276   GNUNET_assert (0 < rh->dh->pending_requests--);
277   if (rh->generation != rh->dh->generation)
278   {
279     GNUNET_free (rh);
280     return;
281   }
282   env = GNUNET_MQ_msg (resp,
283                        GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
284   resp->request_id = rh->request_id;
285   resp->drop_flag = htonl (0);
286   GNUNET_MQ_send (rh->dh->mq,
287                   env);
288   GNUNET_free (rh);
289 }
290
291
292 /**
293  * If a GNUNET_DNS_RequestHandler calls this function, the request is
294  * supposed to be answered with the data provided to this call (with
295  * the modifications the function might have made).
296  *
297  * @param rh request that should now be answered
298  * @param reply_length size of @a reply (uint16_t to force sane size)
299  * @param reply reply data
300  */
301 void
302 GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
303                            uint16_t reply_length,
304                            const char *reply)
305 {
306   struct GNUNET_MQ_Envelope *env;
307   struct GNUNET_DNS_Response *resp;
308
309   GNUNET_assert (0 < rh->dh->pending_requests--);
310   if (rh->generation != rh->dh->generation)
311   {
312     GNUNET_free (rh);
313     return;
314   }
315   if (reply_length + sizeof(struct GNUNET_DNS_Response)
316       >= GNUNET_MAX_MESSAGE_SIZE)
317   {
318     GNUNET_break (0);
319     GNUNET_free (rh);
320     return;
321   }
322   env = GNUNET_MQ_msg_extra (resp,
323                              reply_length,
324                              GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
325   resp->drop_flag = htonl (2);
326   resp->request_id = rh->request_id;
327   GNUNET_memcpy (&resp[1],
328                  reply,
329                  reply_length);
330   GNUNET_MQ_send (rh->dh->mq,
331                   env);
332   GNUNET_free (rh);
333 }
334
335
336 /**
337  * Connect to the service-dns
338  *
339  * @param cfg configuration to use
340  * @param flags when to call @a rh
341  * @param rh function to call with DNS requests
342  * @param rh_cls closure to pass to @a rh
343  * @return DNS handle
344  */
345 struct GNUNET_DNS_Handle *
346 GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
347                     enum GNUNET_DNS_Flags flags,
348                     GNUNET_DNS_RequestHandler rh,
349                     void *rh_cls)
350 {
351   struct GNUNET_DNS_Handle *dh;
352
353   dh = GNUNET_new (struct GNUNET_DNS_Handle);
354   dh->cfg = cfg;
355   dh->flags = flags;
356   dh->rh = rh;
357   dh->rh_cls = rh_cls;
358   dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
359   return dh;
360 }
361
362
363 /**
364  * Disconnect from the DNS service.
365  *
366  * @param dh DNS handle
367  */
368 void
369 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
370 {
371   if (NULL != dh->mq)
372   {
373     GNUNET_MQ_destroy (dh->mq);
374     dh->mq = NULL;
375   }
376   if (NULL != dh->reconnect_task)
377   {
378     GNUNET_SCHEDULER_cancel (dh->reconnect_task);
379     dh->reconnect_task = NULL;
380   }
381   /* make sure client has no pending requests left over! */
382   GNUNET_break (0 == dh->pending_requests);
383   GNUNET_free (dh);
384 }
385
386
387 /* end of dns_api.c */