Verify that GCD(m,n) != 1 when n is an RSA modulus
[oweals/gnunet.git] / src / dns / dns_api.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2012 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, 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   struct GNUNET_SCHEDULER_Task * 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 {
167   struct GNUNET_DNS_Handle *dh = cls;
168   struct ReplyQueueEntry *qe;
169   struct GNUNET_DNS_Register *msg;
170
171   dh->reconnect_task = NULL;
172   dh->dns_connection = GNUNET_CLIENT_connect ("dns", dh->cfg);
173   if (NULL == dh->dns_connection)
174     return;
175   dh->generation++;
176   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
177                       sizeof (struct GNUNET_DNS_Register));
178   msg = (struct GNUNET_DNS_Register*) &qe[1];
179   qe->msg = &msg->header;
180   msg->header.size = htons (sizeof (struct GNUNET_DNS_Register));
181   msg->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
182   msg->flags = htonl (dh->flags);
183   queue_reply (dh, qe);
184 }
185
186
187 /**
188  * Disconnect from the DNS service.
189  *
190  * @param dh handle with the connection to disconnect
191  */
192 static void
193 disconnect (struct GNUNET_DNS_Handle *dh)
194 {
195   struct ReplyQueueEntry *qe;
196
197   if (NULL != dh->dns_transmit_handle)
198   {
199     GNUNET_CLIENT_notify_transmit_ready_cancel (dh->dns_transmit_handle);
200     dh->dns_transmit_handle = NULL;
201   }
202   if (NULL != dh->dns_connection)
203   {
204     GNUNET_CLIENT_disconnect (dh->dns_connection);
205     dh->dns_connection = NULL;
206   }
207   while (NULL != (qe = dh->rq_head))
208   {
209     GNUNET_CONTAINER_DLL_remove (dh->rq_head,
210                                  dh->rq_tail,
211                                  qe);
212     GNUNET_free (qe);
213   }
214   dh->in_receive = GNUNET_NO;
215 }
216
217
218 /**
219  * This receives packets from the DNS service and calls the application to
220  * handle it.
221  *
222  * @param cls the struct GNUNET_DNS_Handle
223  * @param msg message from the service (request)
224  */
225 static void
226 request_handler (void *cls,
227                  const struct GNUNET_MessageHeader *msg)
228 {
229   struct GNUNET_DNS_Handle *dh = cls;
230   const struct GNUNET_DNS_Request *req;
231   struct GNUNET_DNS_RequestHandle *rh;
232   size_t payload_length;
233
234   /* the service disconnected, reconnect after short wait */
235   if (msg == NULL)
236   {
237     disconnect (dh);
238     dh->reconnect_task =
239         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
240                                       &reconnect, dh);
241     return;
242   }
243   if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST) ||
244        (ntohs (msg->size) < sizeof (struct GNUNET_DNS_Request)) )
245   {
246     /* the service did something strange, reconnect immediately */
247     GNUNET_break (0);
248     disconnect (dh);
249     dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
250     return;
251   }
252   req = (const struct GNUNET_DNS_Request *) msg;
253   GNUNET_break (ntohl (req->reserved) == 0);
254   payload_length = ntohs (req->header.size) - sizeof (struct GNUNET_DNS_Request);
255   GNUNET_CLIENT_receive (dh->dns_connection,
256                          &request_handler, dh,
257                          GNUNET_TIME_UNIT_FOREVER_REL);
258
259   /* finally, pass request to callback for answers */
260   rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
261   rh->dh =dh;
262   rh->request_id = req->request_id;
263   rh->generation = dh->generation;
264   dh->pending_requests++;
265   dh->rh (dh->rh_cls,
266           rh,
267           payload_length,
268           (const char*) &req[1]);
269 }
270
271
272 /**
273  * Callback called by notify_transmit_ready; sends DNS replies
274  * to the DNS service.
275  *
276  * @param cls the struct GNUNET_DNS_Handle
277  * @param size number of bytes available in buf
278  * @param buf where to copy the message for transmission
279  * @return number of bytes copied to buf
280  */
281 static size_t
282 send_response (void *cls, size_t size, void *buf)
283 {
284   struct GNUNET_DNS_Handle *dh = cls;
285   struct ReplyQueueEntry *qe;
286   size_t len;
287
288   dh->dns_transmit_handle = NULL;
289   if (NULL == buf)
290   {
291     disconnect (dh);
292     dh->reconnect_task =
293       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
294                                     &reconnect, dh);
295     return 0;
296   }
297   qe = dh->rq_head;
298   if (NULL == qe)
299     return 0;
300   len = ntohs (qe->msg->size);
301   if (len > size)
302   {
303     dh->dns_transmit_handle =
304       GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
305                                            len,
306                                            GNUNET_TIME_UNIT_FOREVER_REL,
307                                            GNUNET_NO,
308                                            &send_response, dh);
309     return 0;
310   }
311   memcpy (buf, qe->msg, len);
312   GNUNET_CONTAINER_DLL_remove (dh->rq_head,
313                                dh->rq_tail,
314                                qe);
315   GNUNET_free (qe);
316   if (GNUNET_NO == dh->in_receive)
317   {
318     dh->in_receive = GNUNET_YES;
319     GNUNET_CLIENT_receive (dh->dns_connection,
320                            &request_handler, dh,
321                            GNUNET_TIME_UNIT_FOREVER_REL);
322   }
323   if (NULL != (qe = dh->rq_head))
324   {
325     dh->dns_transmit_handle =
326       GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
327                                            ntohs (qe->msg->size),
328                                            GNUNET_TIME_UNIT_FOREVER_REL,
329                                            GNUNET_NO,
330                                            &send_response, dh);
331   }
332   return len;
333 }
334
335
336 /**
337  * Add the given reply to our transmission queue and trigger sending if needed.
338  *
339  * @param dh handle with the connection
340  * @param qe reply to queue
341  */
342 static void
343 queue_reply (struct GNUNET_DNS_Handle *dh,
344              struct ReplyQueueEntry *qe)
345 {
346   if (NULL == dh->dns_connection)
347   {
348     GNUNET_free (qe);
349     return;
350   }
351   GNUNET_CONTAINER_DLL_insert_tail (dh->rq_head,
352                                     dh->rq_tail,
353                                     qe);
354   if (NULL != dh->dns_transmit_handle)
355     return;
356   /* trigger sending */
357   dh->dns_transmit_handle =
358     GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
359                                          ntohs (dh->rq_head->msg->size),
360                                          GNUNET_TIME_UNIT_FOREVER_REL,
361                                          GNUNET_NO,
362                                          &send_response, dh);
363 }
364
365
366 /**
367  * If a GNUNET_DNS_RequestHandler calls this function, the request is
368  * given to other clients or the global DNS for resolution.  Once a
369  * global response has been obtained, the request handler is AGAIN
370  * called to give it a chance to observe and modify the response after
371  * the "normal" resolution.  It is not legal for the request handler
372  * to call this function if a response is already present.
373  *
374  * @param rh request that should now be forwarded
375  */
376 void
377 GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
378 {
379   struct ReplyQueueEntry *qe;
380   struct GNUNET_DNS_Response *resp;
381
382   GNUNET_assert (0 < rh->dh->pending_requests--);
383   if (rh->generation != rh->dh->generation)
384   {
385     GNUNET_free (rh);
386     return;
387   }
388   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
389                       sizeof (struct GNUNET_DNS_Response));
390   resp = (struct GNUNET_DNS_Response*) &qe[1];
391   qe->msg = &resp->header;
392   resp->header.size = htons (sizeof (struct GNUNET_DNS_Response));
393   resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
394   resp->drop_flag = htonl (1);
395   resp->request_id = rh->request_id;
396   queue_reply (rh->dh, qe);
397   GNUNET_free (rh);
398 }
399
400
401 /**
402  * If a GNUNET_DNS_RequestHandler calls this function, the request is
403  * to be dropped and no response should be generated.
404  *
405  * @param rh request that should now be dropped
406  */
407 void
408 GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
409 {
410   struct ReplyQueueEntry *qe;
411   struct GNUNET_DNS_Response *resp;
412
413   GNUNET_assert (0 < rh->dh->pending_requests--);
414   if (rh->generation != rh->dh->generation)
415   {
416       GNUNET_free (rh);
417       return;
418   }
419   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
420                       sizeof (struct GNUNET_DNS_Response));
421   resp = (struct GNUNET_DNS_Response*) &qe[1];
422   qe->msg = &resp->header;
423   resp->header.size = htons (sizeof (struct GNUNET_DNS_Response));
424   resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
425   resp->request_id = rh->request_id;
426   resp->drop_flag = htonl (0);
427   queue_reply (rh->dh, qe);
428   GNUNET_free (rh);
429 }
430
431
432 /**
433  * If a GNUNET_DNS_RequestHandler calls this function, the request is
434  * supposed to be answered with the data provided to this call (with
435  * the modifications the function might have made).
436  *
437  * @param rh request that should now be answered
438  * @param reply_length size of reply (uint16_t to force sane size)
439  * @param reply reply data
440  */
441 void
442 GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
443                            uint16_t reply_length,
444                            const char *reply)
445 {
446   struct ReplyQueueEntry *qe;
447   struct GNUNET_DNS_Response *resp;
448
449   GNUNET_assert (0 < rh->dh->pending_requests--);
450   if (rh->generation != rh->dh->generation)
451   {
452       GNUNET_free (rh);
453       return;
454   }
455   if (reply_length + sizeof (struct GNUNET_DNS_Response) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
456   {
457     GNUNET_break (0);
458     GNUNET_free (rh);
459     return;
460   }
461   qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
462                       sizeof (struct GNUNET_DNS_Response) + reply_length);
463   resp = (struct GNUNET_DNS_Response*) &qe[1];
464   qe->msg = &resp->header;
465   resp->header.size = htons (sizeof (struct GNUNET_DNS_Response) + reply_length);
466   resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
467   resp->drop_flag = htonl (2);
468   resp->request_id = rh->request_id;
469   memcpy (&resp[1], reply, reply_length);
470   queue_reply (rh->dh, qe);
471   GNUNET_free (rh);
472 }
473
474
475 /**
476  * Connect to the service-dns
477  *
478  * @param cfg configuration to use
479  * @param flags when to call rh
480  * @param rh function to call with DNS requests
481  * @param rh_cls closure to pass to rh
482  * @return DNS handle
483  */
484 struct GNUNET_DNS_Handle *
485 GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
486                     enum GNUNET_DNS_Flags flags,
487                     GNUNET_DNS_RequestHandler rh,
488                     void *rh_cls)
489 {
490   struct GNUNET_DNS_Handle *dh;
491
492   dh = GNUNET_new (struct GNUNET_DNS_Handle);
493   dh->cfg = cfg;
494   dh->flags = flags;
495   dh->rh = rh;
496   dh->rh_cls = rh_cls;
497   dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
498   return dh;
499 }
500
501
502 /**
503  * Disconnect from the DNS service.
504  *
505  * @param dh DNS handle
506  */
507 void
508 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
509 {
510   if (NULL != dh->reconnect_task)
511   {
512     GNUNET_SCHEDULER_cancel (dh->reconnect_task);
513     dh->reconnect_task = NULL;
514   }
515   disconnect (dh);
516   /* make sure client has no pending requests left over! */
517   GNUNET_assert (0 == dh->pending_requests);
518   GNUNET_free (dh);
519 }
520
521 /* end of dns_api_new.c */