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