aboutsummaryrefslogtreecommitdiff
path: root/src/dns
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-01-02 14:28:25 +0000
committerChristian Grothoff <christian@grothoff.org>2012-01-02 14:28:25 +0000
commit13fef3b77277dcc312c9d6ea02e18b5fbdd874b0 (patch)
treea7bc04dbfa5364c4919e3a5f281521ee4dbff05a /src/dns
parent8b453e998873b8b77ebef9d5282314ff3c5f7875 (diff)
downloadgnunet-13fef3b77277dcc312c9d6ea02e18b5fbdd874b0.tar.gz
gnunet-13fef3b77277dcc312c9d6ea02e18b5fbdd874b0.zip
-implementing new DNS client API
Diffstat (limited to 'src/dns')
-rw-r--r--src/dns/dns_api_new.c527
1 files changed, 527 insertions, 0 deletions
diff --git a/src/dns/dns_api_new.c b/src/dns/dns_api_new.c
new file mode 100644
index 000000000..0b654e139
--- /dev/null
+++ b/src/dns/dns_api_new.c
@@ -0,0 +1,527 @@
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 2, 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_new.c
23 * @brief API to access the DNS service.
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include "gnunet_dns_service-new.h"
28#include "dns_new.h"
29
30
31/**
32 * Reply to send to service.
33 */
34struct 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 */
57struct 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 */
81struct 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 * Did we start the receive loop yet?
131 */
132 int in_receive;
133
134 /**
135 * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
136 * we can be disconnected.
137 */
138 unsigned int pending_requests;
139};
140
141
142/**
143 * Add the given reply to our transmission queue and trigger sending if needed.
144 *
145 * @param dh handle with the connection
146 * @param qe reply to queue
147 */
148static void
149queue_reply (struct GNUNET_DNS_Handle *dh,
150 struct ReplyQueueEntry *qe);
151
152
153/**
154 * Reconnect to the DNS service.
155 *
156 * @param cls handle with the connection to connect
157 * @param tc scheduler context (unused)
158 */
159static void
160reconnect (void *cls,
161 const struct GNUNET_SCHEDULER_TaskContext *tc)
162{
163 struct GNUNET_DNS_Handle *dh = cls;
164 struct ReplyQueueEntry *qe;
165 struct GNUNET_MessageHeader *msg;
166
167 dh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
168 dh->dns_connection = GNUNET_CLIENT_connect ("dns", dh->cfg);
169 if (NULL == dh->dns_connection)
170 return;
171 dh->generation++;
172 qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
173 sizeof (struct GNUNET_MessageHeader));
174 msg = (struct GNUNET_MessageHeader*) &qe[1];
175 qe->msg = msg;
176 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
177 msg->type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
178 queue_reply (dh, qe);
179}
180
181
182/**
183 * Disconnect from the DNS service.
184 *
185 * @param dh handle with the connection to disconnect
186 */
187static void
188disconnect (struct GNUNET_DNS_Handle *dh)
189{
190 struct ReplyQueueEntry *qe;
191
192 if (NULL != dh->dns_transmit_handle)
193 {
194 GNUNET_CLIENT_notify_transmit_ready_cancel (dh->dns_transmit_handle);
195 dh->dns_transmit_handle = NULL;
196 }
197 if (NULL != dh->dns_connection)
198 {
199 GNUNET_CLIENT_disconnect (dh->dns_connection, GNUNET_NO);
200 dh->dns_connection = NULL;
201 }
202 while (NULL != (qe = dh->rq_head))
203 {
204 GNUNET_CONTAINER_DLL_remove (dh->rq_head,
205 dh->rq_tail,
206 qe);
207 GNUNET_free (qe);
208 }
209 dh->in_receive = GNUNET_NO;
210}
211
212
213/**
214 * This receives packets from the DNS service and calls the application to
215 * handle it.
216 *
217 * @param cls the struct GNUNET_DNS_Handle
218 * @param msg message from the service (request)
219 */
220static void
221request_handler (void *cls,
222 const struct GNUNET_MessageHeader *msg)
223{
224 struct GNUNET_DNS_Handle *dh = cls;
225 const struct GNUNET_DNS_Request *req;
226 struct GNUNET_DNS_RequestHandle *rh;
227 const char *name;
228 uint16_t name_len;
229
230 /* the service disconnected, reconnect after short wait */
231 if (msg == NULL)
232 {
233 disconnect (dh);
234 dh->reconnect_task =
235 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
236 &reconnect, dh);
237 return;
238 }
239 /* the service did something strange, reconnect immediately */
240 req = (const struct GNUNET_DNS_Request *) msg;
241 name = (const char*) &req[1];
242
243 if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST) ||
244 (ntohs (msg->size) < sizeof (struct GNUNET_DNS_Request)) ||
245 (ntohs (msg->size) != sizeof (struct GNUNET_DNS_Request) + ntohs (req->rdata_length) + (name_len = ntohs (req->name_length))) ||
246 (name[name_len-1] != '\0') )
247 {
248 GNUNET_break (0);
249 disconnect (dh);
250 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
251 return;
252 }
253 GNUNET_CLIENT_receive (dh->dns_connection,
254 &request_handler, dh,
255 GNUNET_TIME_UNIT_FOREVER_REL);
256
257 /* finally, pass request to callback for answers */
258 rh = GNUNET_malloc (sizeof (struct GNUNET_DNS_RequestHandle));
259 rh->dh =dh;
260 rh->request_id = req->request_id;
261 rh->generation = dh->generation;
262 dh->pending_requests++;
263 dh->rh (dh->rh_cls,
264 rh,
265 name,
266 ntohs (req->dns_type),
267 ntohs (req->dns_class),
268 ntohl (req->dns_ttl),
269 ntohs (req->rdata_length),
270 &name[name_len]);
271}
272
273
274/**
275 * Callback called by notify_transmit_ready; sends DNS replies
276 * to the DNS service.
277 *
278 * @param cls the struct GNUNET_DNS_Handle
279 * @param size number of bytes available in buf
280 * @param buf where to copy the message for transmission
281 * @return number of bytes copied to buf
282 */
283static size_t
284send_response (void *cls, size_t size, void *buf)
285{
286 struct GNUNET_DNS_Handle *dh = cls;
287 struct ReplyQueueEntry *qe;
288 size_t len;
289
290 dh->dns_transmit_handle = NULL;
291 if (NULL == buf)
292 {
293 disconnect (dh);
294 dh->reconnect_task =
295 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
296 &reconnect, dh);
297 return 0;
298 }
299 qe = dh->rq_head;
300 if (NULL == qe)
301 return 0;
302 len = ntohs (qe->msg->size);
303 if (len > size)
304 {
305 dh->dns_transmit_handle =
306 GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
307 len,
308 GNUNET_TIME_UNIT_FOREVER_REL,
309 GNUNET_NO,
310 &send_response, dh);
311 return 0;
312 }
313 memcpy (buf, qe->msg, len);
314 GNUNET_CONTAINER_DLL_remove (dh->rq_head,
315 dh->rq_tail,
316 qe);
317 GNUNET_free (qe);
318 if (GNUNET_NO == dh->in_receive)
319 {
320 dh->in_receive = GNUNET_YES;
321 GNUNET_CLIENT_receive (dh->dns_connection,
322 &request_handler, dh,
323 GNUNET_TIME_UNIT_FOREVER_REL);
324 }
325 if (NULL != (qe = dh->rq_head))
326 {
327 dh->dns_transmit_handle =
328 GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
329 ntohs (qe->msg->size),
330 GNUNET_TIME_UNIT_FOREVER_REL,
331 GNUNET_NO,
332 &send_response, dh);
333 }
334 return len;
335}
336
337
338/**
339 * Add the given reply to our transmission queue and trigger sending if needed.
340 *
341 * @param dh handle with the connection
342 * @param qe reply to queue
343 */
344static void
345queue_reply (struct GNUNET_DNS_Handle *dh,
346 struct ReplyQueueEntry *qe)
347{
348 if (NULL == dh->dns_connection)
349 {
350 GNUNET_free (qe);
351 return;
352 }
353 GNUNET_CONTAINER_DLL_insert_tail (dh->rq_head,
354 dh->rq_tail,
355 qe);
356 if (NULL != dh->dns_transmit_handle)
357 return;
358 /* trigger sending */
359 dh->dns_transmit_handle =
360 GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
361 ntohs (dh->rq_head->msg->size),
362 GNUNET_TIME_UNIT_FOREVER_REL,
363 GNUNET_NO,
364 &send_response, dh);
365}
366
367
368/**
369 * If a GNUNET_DNS_RequestHandler calls this function, the request is
370 * given to other clients or the global DNS for resolution. Once a
371 * global response has been obtained, the request handler is AGAIN
372 * called to give it a chance to observe and modify the response after
373 * the "normal" resolution. It is not legal for the request handler
374 * to call this function if a response is already present.
375 *
376 * @param rh request that should now be forwarded
377 */
378void
379GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
380{
381 struct ReplyQueueEntry *qe;
382 struct GNUNET_DNS_Response *resp;
383
384 GNUNET_assert (0 < rh->dh->pending_requests--);
385 if (rh->generation != rh->dh->generation)
386 {
387 GNUNET_free (rh);
388 return;
389 }
390 qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
391 sizeof (struct GNUNET_DNS_Response));
392 resp = (struct GNUNET_DNS_Response*) &qe[1];
393 qe->msg = &resp->header;
394 resp->header.size = htons (sizeof (struct GNUNET_DNS_Response));
395 resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
396 resp->dns_ttl = htonl (0);
397 resp->request_id = rh->request_id;
398 resp->drop_flag = htons (0);
399 resp->rdata_length = htons (0);
400 queue_reply (rh->dh, qe);
401 GNUNET_free (rh);
402}
403
404
405/**
406 * If a GNUNET_DNS_RequestHandler calls this function, the request is
407 * to be dropped and no response should be generated.
408 *
409 * @param rh request that should now be dropped
410 */
411void
412GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
413{
414 struct ReplyQueueEntry *qe;
415 struct GNUNET_DNS_Response *resp;
416
417 GNUNET_assert (0 < rh->dh->pending_requests--);
418 if (rh->generation != rh->dh->generation)
419 {
420 GNUNET_free (rh);
421 return;
422 }
423 qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
424 sizeof (struct GNUNET_DNS_Response));
425 resp = (struct GNUNET_DNS_Response*) &qe[1];
426 qe->msg = &resp->header;
427 resp->header.size = htons (sizeof (struct GNUNET_DNS_Response));
428 resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
429 resp->dns_ttl = htonl (0);
430 resp->request_id = rh->request_id;
431 resp->drop_flag = htons (1);
432 resp->rdata_length = htons (0);
433 queue_reply (rh->dh, qe);
434 GNUNET_free (rh);
435}
436
437
438/**
439 * If a GNUNET_DNS_RequestHandler calls this function, the request is supposed to
440 * be answered with the data provided to this call (with the modifications the function might have made).
441 *
442 * @param rh request that should now be answered
443 * @param dns_ttl seconds that the RR stays valid, can be updated
444 * @param rdata_length size of rdata, can be updated
445 * @param rdata record data, can be updated
446 */
447void
448GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
449 uint32_t dns_ttl,
450 uint16_t rdata_length,
451 char *rdata)
452{
453 struct ReplyQueueEntry *qe;
454 struct GNUNET_DNS_Response *resp;
455
456 GNUNET_assert (0 < rh->dh->pending_requests--);
457 if (rh->generation != rh->dh->generation)
458 {
459 GNUNET_free (rh);
460 return;
461 }
462 if (rdata_length + sizeof (struct GNUNET_DNS_Response) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
463 {
464 GNUNET_break (0);
465 GNUNET_free (rh);
466 return;
467 }
468 qe = GNUNET_malloc (sizeof (struct ReplyQueueEntry) +
469 sizeof (struct GNUNET_DNS_Response) + rdata_length);
470 resp = (struct GNUNET_DNS_Response*) &qe[1];
471 qe->msg = &resp->header;
472 resp->header.size = htons (sizeof (struct GNUNET_DNS_Response) + rdata_length);
473 resp->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
474 resp->dns_ttl = htonl (dns_ttl);
475 resp->request_id = rh->request_id;
476 resp->drop_flag = htons (0);
477 resp->rdata_length = htons (rdata_length);
478 memcpy (&resp[1], rdata, rdata_length);
479 queue_reply (rh->dh, qe);
480 GNUNET_free (rh);
481}
482
483
484/**
485 * Connect to the service-dns
486 *
487 * @param cfg configuration to use
488 * @param rh function to call with DNS requests
489 * @param rh_cls closure to pass to rh
490 * @return DNS handle
491 */
492struct GNUNET_DNS_Handle *
493GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
494 GNUNET_DNS_RequestHandler rh,
495 void *rh_cls)
496{
497 struct GNUNET_DNS_Handle *dh;
498
499 dh = GNUNET_malloc (sizeof (struct GNUNET_DNS_Handle));
500 dh->cfg = cfg;
501 dh->rh = rh;
502 dh->rh_cls = rh_cls;
503 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
504 return dh;
505}
506
507
508/**
509 * Disconnect from the DNS service.
510 *
511 * @param dh DNS handle
512 */
513void
514GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
515{
516 if (GNUNET_SCHEDULER_NO_TASK != dh->reconnect_task)
517 {
518 GNUNET_SCHEDULER_cancel (dh->reconnect_task);
519 dh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
520 }
521 disconnect (dh);
522 /* make sure client has no pending requests left over! */
523 GNUNET_assert (0 == dh->pending_requests);
524 GNUNET_free (dh);
525}
526
527/* end of dns_api_new.c */