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