- improve handling of duplicate connection_create
[oweals/gnunet.git] / src / dns / dns_api.c
1 /*
2       This file is part of GNUnet
3       (C) 2012 Christian Grothoff (and other contributing authors)
4
5       GNUnet is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published
7       by the Free Software Foundation; either version 3, or (at your
8       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       General Public License for more details.
14
15       You should have received a copy of the GNU General Public License
16       along with GNUnet; see the file COPYING.  If not, write to the
17       Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18       Boston, MA 02111-1307, USA.
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  * Reply to send to service.
33  */
34 struct ReplyQueueEntry
35 {
36   /**
37    * Kept in DLL.
38    */
39   struct ReplyQueueEntry *next;
40
41   /**
42    * Kept in DLL.
43    */
44   struct ReplyQueueEntry *prev;
45
46   /**
47    * Message to transmit, allocated at the end of this struct.
48    */
49   const struct GNUNET_MessageHeader *msg;
50
51 };
52
53
54 /**
55  * Handle to identify an individual DNS request.
56  */
57 struct GNUNET_DNS_RequestHandle
58 {
59
60   /**
61    * Handle to DNS API.
62    */
63   struct GNUNET_DNS_Handle *dh;
64
65   /**
66    * Stored in network byte order (as for us, it is just a random number).
67    */
68   uint64_t request_id;
69
70   /**
71    * Re-connect counter, to make sure we did not reconnect in the meantime.
72    */
73   uint32_t generation;
74
75 };
76
77
78 /**
79  * DNS handle
80  */
81 struct GNUNET_DNS_Handle
82 {
83
84   /**
85    * Connection to DNS service, or NULL.
86    */
87   struct GNUNET_CLIENT_Connection *dns_connection;
88
89   /**
90    * Handle to active transmission request, or NULL.
91    */
92   struct GNUNET_CLIENT_TransmitHandle *dns_transmit_handle;
93
94   /**
95    * Configuration to use.
96    */
97   const struct GNUNET_CONFIGURATION_Handle *cfg;
98
99   /**
100    * Function to call to get replies.
101    */
102   GNUNET_DNS_RequestHandler rh;
103
104   /**
105    * Closure for 'rh'.
106    */
107   void *rh_cls;
108
109   /**
110    * Head of replies to transmit.
111    */
112   struct ReplyQueueEntry *rq_head;
113
114   /**
115    * Tail of replies to transmit.
116    */
117   struct ReplyQueueEntry *rq_tail;
118
119   /**
120    * Task to reconnect to the service.
121    */
122   GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
123
124   /**
125    * Re-connect counter, to make sure we did not reconnect in the meantime.
126    */
127   uint32_t generation;
128
129   /**
130    * Flags for events we care about.
131    */
132   enum GNUNET_DNS_Flags flags;
133
134   /**
135    * Did we start the receive loop yet?
136    */
137   int in_receive;
138
139   /**
140    * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
141    * we can be disconnected.
142    */
143   unsigned int pending_requests;
144 };
145
146
147 /**
148  * Add the given reply to our transmission queue and trigger sending if needed.
149  *
150  * @param dh handle with the connection
151  * @param qe reply to queue
152  */
153 static void
154 queue_reply (struct GNUNET_DNS_Handle *dh,
155              struct ReplyQueueEntry *qe);
156
157
158 /**
159  * Reconnect to the DNS service.
160  *
161  * @param cls handle with the connection to connect
162  * @param tc scheduler context (unused)
163  */
164 static void
165 reconnect (void *cls,
166            const struct GNUNET_SCHEDULER_TaskContext *tc)
167 {
168   struct GNUNET_DNS_Handle *dh = cls;
169   struct ReplyQueueEntry *qe;
170   struct GNUNET_DNS_Register *msg;
171
172   dh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
173   dh->dns_connection = GNUNET_CLIENT_connect ("dns", dh->cfg);
174   if (NULL == dh->dns_connection)
175     return;
176   dh->generation++;
177   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
178                       sizeof (struct GNUNET_DNS_Register));
179   msg = (struct GNUNET_DNS_Register*) &qe[1];
180   qe->msg = &msg->header;
181   msg->header.size = htons (sizeof (struct GNUNET_DNS_Register));
182   msg->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
183   msg->flags = htonl (dh->flags);
184   queue_reply (dh, qe);
185 }
186
187
188 /**
189  * Disconnect from the DNS service.
190  *
191  * @param dh handle with the connection to disconnect
192  */
193 static void
194 disconnect (struct GNUNET_DNS_Handle *dh)
195 {
196   struct ReplyQueueEntry *qe;
197
198   if (NULL != dh->dns_transmit_handle)
199   {
200     GNUNET_CLIENT_notify_transmit_ready_cancel (dh->dns_transmit_handle);
201     dh->dns_transmit_handle = NULL;
202   }
203   if (NULL != dh->dns_connection)
204   {
205     GNUNET_CLIENT_disconnect (dh->dns_connection);
206     dh->dns_connection = NULL;
207   }
208   while (NULL != (qe = dh->rq_head))
209   {
210     GNUNET_CONTAINER_DLL_remove (dh->rq_head,
211                                  dh->rq_tail,
212                                  qe);
213     GNUNET_free (qe);
214   }
215   dh->in_receive = GNUNET_NO;
216 }
217
218
219 /**
220  * This receives packets from the DNS service and calls the application to
221  * handle it.
222  *
223  * @param cls the struct GNUNET_DNS_Handle
224  * @param msg message from the service (request)
225  */
226 static void
227 request_handler (void *cls,
228                  const struct GNUNET_MessageHeader *msg)
229 {
230   struct GNUNET_DNS_Handle *dh = cls;
231   const struct GNUNET_DNS_Request *req;
232   struct GNUNET_DNS_RequestHandle *rh;
233   size_t payload_length;
234
235   /* the service disconnected, reconnect after short wait */
236   if (msg == NULL)
237   {
238     disconnect (dh);
239     dh->reconnect_task =
240         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
241                                       &reconnect, dh);
242     return;
243   }
244   if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST) ||
245        (ntohs (msg->size) < sizeof (struct GNUNET_DNS_Request)) )
246   {
247     /* the service did something strange, reconnect immediately */
248     GNUNET_break (0);
249     disconnect (dh);
250     dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
251     return;
252   }
253   req = (const struct GNUNET_DNS_Request *) msg;
254   GNUNET_break (ntohl (req->reserved) == 0);
255   payload_length = ntohs (req->header.size) - sizeof (struct GNUNET_DNS_Request);
256   GNUNET_CLIENT_receive (dh->dns_connection,
257                          &request_handler, dh,
258                          GNUNET_TIME_UNIT_FOREVER_REL);
259
260   /* finally, pass request to callback for answers */
261   rh = GNUNET_malloc (sizeof (struct GNUNET_DNS_RequestHandle));
262   rh->dh =dh;
263   rh->request_id = req->request_id;
264   rh->generation = dh->generation;
265   dh->pending_requests++;
266   dh->rh (dh->rh_cls,
267           rh,
268           payload_length,
269           (const char*) &req[1]);
270 }
271
272
273 /**
274  * Callback called by notify_transmit_ready; sends DNS replies
275  * to the DNS service.
276  *
277  * @param cls the struct GNUNET_DNS_Handle
278  * @param size number of bytes available in buf
279  * @param buf where to copy the message for transmission
280  * @return number of bytes copied to buf
281  */
282 static size_t
283 send_response (void *cls, size_t size, void *buf)
284 {
285   struct GNUNET_DNS_Handle *dh = cls;
286   struct ReplyQueueEntry *qe;
287   size_t len;
288
289   dh->dns_transmit_handle = NULL;
290   if (NULL == buf)
291   {
292     disconnect (dh);
293     dh->reconnect_task =
294       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
295                                     &reconnect, dh);
296     return 0;
297   }
298   qe = dh->rq_head;
299   if (NULL == qe)
300     return 0;
301   len = ntohs (qe->msg->size);
302   if (len > size)
303   {
304     dh->dns_transmit_handle =
305       GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
306                                            len,
307                                            GNUNET_TIME_UNIT_FOREVER_REL,
308                                            GNUNET_NO,
309                                            &send_response, dh);
310     return 0;
311   }
312   memcpy (buf, qe->msg, len);
313   GNUNET_CONTAINER_DLL_remove (dh->rq_head,
314                                dh->rq_tail,
315                                qe);
316   GNUNET_free (qe);
317   if (GNUNET_NO == dh->in_receive)
318   {
319     dh->in_receive = GNUNET_YES;
320     GNUNET_CLIENT_receive (dh->dns_connection,
321                            &request_handler, dh,
322                            GNUNET_TIME_UNIT_FOREVER_REL);
323   }
324   if (NULL != (qe = dh->rq_head))
325   {
326     dh->dns_transmit_handle =
327       GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
328                                            ntohs (qe->msg->size),
329                                            GNUNET_TIME_UNIT_FOREVER_REL,
330                                            GNUNET_NO,
331                                            &send_response, dh);
332   }
333   return len;
334 }
335
336
337 /**
338  * Add the given reply to our transmission queue and trigger sending if needed.
339  *
340  * @param dh handle with the connection
341  * @param qe reply to queue
342  */
343 static void
344 queue_reply (struct GNUNET_DNS_Handle *dh,
345              struct ReplyQueueEntry *qe)
346 {
347   if (NULL == dh->dns_connection)
348   {
349     GNUNET_free (qe);
350     return;
351   }
352   GNUNET_CONTAINER_DLL_insert_tail (dh->rq_head,
353                                     dh->rq_tail,
354                                     qe);
355   if (NULL != dh->dns_transmit_handle)
356     return;
357   /* trigger sending */
358   dh->dns_transmit_handle =
359     GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
360                                          ntohs (dh->rq_head->msg->size),
361                                          GNUNET_TIME_UNIT_FOREVER_REL,
362                                          GNUNET_NO,
363                                          &send_response, dh);
364 }
365
366
367 /**
368  * If a GNUNET_DNS_RequestHandler calls this function, the request is
369  * given to other clients or the global DNS for resolution.  Once a
370  * global response has been obtained, the request handler is AGAIN
371  * called to give it a chance to observe and modify the response after
372  * the "normal" resolution.  It is not legal for the request handler
373  * to call this function if a response is already present.
374  *
375  * @param rh request that should now be forwarded
376  */
377 void
378 GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
379 {
380   struct ReplyQueueEntry *qe;
381   struct GNUNET_DNS_Response *resp;
382
383   GNUNET_assert (0 < rh->dh->pending_requests--);
384   if (rh->generation != rh->dh->generation)
385   {
386     GNUNET_free (rh);
387     return;
388   }
389   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
390                       sizeof (struct GNUNET_DNS_Response));
391   resp = (struct GNUNET_DNS_Response*) &qe[1];
392   qe->msg = &resp->header;
393   resp->header.size = htons (sizeof (struct GNUNET_DNS_Response));
394   resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
395   resp->drop_flag = htonl (1);
396   resp->request_id = rh->request_id;
397   queue_reply (rh->dh, qe);
398   GNUNET_free (rh);
399 }
400
401
402 /**
403  * If a GNUNET_DNS_RequestHandler calls this function, the request is
404  * to be dropped and no response should be generated.
405  *
406  * @param rh request that should now be dropped
407  */
408 void
409 GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
410 {
411   struct ReplyQueueEntry *qe;
412   struct GNUNET_DNS_Response *resp;
413
414   GNUNET_assert (0 < rh->dh->pending_requests--);
415   if (rh->generation != rh->dh->generation)
416   {
417       GNUNET_free (rh);
418       return;
419   }
420   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
421                       sizeof (struct GNUNET_DNS_Response));
422   resp = (struct GNUNET_DNS_Response*) &qe[1];
423   qe->msg = &resp->header;
424   resp->header.size = htons (sizeof (struct GNUNET_DNS_Response));
425   resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
426   resp->request_id = rh->request_id;
427   resp->drop_flag = htonl (0);
428   queue_reply (rh->dh, qe);
429   GNUNET_free (rh);
430 }
431
432
433 /**
434  * If a GNUNET_DNS_RequestHandler calls this function, the request is
435  * supposed to be answered with the data provided to this call (with
436  * the modifications the function might have made).
437  *
438  * @param rh request that should now be answered
439  * @param reply_length size of reply (uint16_t to force sane size)
440  * @param reply reply data
441  */
442 void
443 GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh, 
444                            uint16_t reply_length,
445                            const char *reply)
446 {
447   struct ReplyQueueEntry *qe;
448   struct GNUNET_DNS_Response *resp;
449
450   GNUNET_assert (0 < rh->dh->pending_requests--);
451   if (rh->generation != rh->dh->generation)
452   {
453       GNUNET_free (rh);
454       return;
455   }
456   if (reply_length + sizeof (struct GNUNET_DNS_Response) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
457   {
458     GNUNET_break (0);
459     GNUNET_free (rh);
460     return;
461   }
462   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
463                       sizeof (struct GNUNET_DNS_Response) + reply_length);
464   resp = (struct GNUNET_DNS_Response*) &qe[1];
465   qe->msg = &resp->header;
466   resp->header.size = htons (sizeof (struct GNUNET_DNS_Response) + reply_length);
467   resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
468   resp->drop_flag = htonl (2);
469   resp->request_id = rh->request_id;
470   memcpy (&resp[1], reply, reply_length);
471   queue_reply (rh->dh, qe);
472   GNUNET_free (rh);
473 }
474
475
476 /**
477  * Connect to the service-dns
478  *
479  * @param cfg configuration to use
480  * @param flags when to call rh
481  * @param rh function to call with DNS requests
482  * @param rh_cls closure to pass to rh
483  * @return DNS handle
484  */
485 struct GNUNET_DNS_Handle *
486 GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
487                     enum GNUNET_DNS_Flags flags,
488                     GNUNET_DNS_RequestHandler rh,
489                     void *rh_cls)
490 {
491   struct GNUNET_DNS_Handle *dh;
492
493   dh = GNUNET_malloc (sizeof (struct GNUNET_DNS_Handle));
494   dh->cfg = cfg;
495   dh->flags = flags;
496   dh->rh = rh;
497   dh->rh_cls = rh_cls;
498   dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
499   return dh;
500 }
501
502
503 /**
504  * Disconnect from the DNS service.
505  *
506  * @param dh DNS handle
507  */
508 void
509 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
510 {
511   if (GNUNET_SCHEDULER_NO_TASK != dh->reconnect_task)
512   {
513     GNUNET_SCHEDULER_cancel (dh->reconnect_task);
514     dh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
515   }
516   disconnect (dh);
517   /* make sure client has no pending requests left over! */
518   GNUNET_assert (0 == dh->pending_requests);
519   GNUNET_free (dh);
520 }
521
522 /* end of dns_api_new.c */