2 This file is part of GNUnet
3 Copyright (C) 2012 GNUnet e.V.
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.
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.
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.
23 * @brief API to access the DNS service.
24 * @author Christian Grothoff
27 #include "gnunet_dns_service.h"
32 * Reply to send to service.
34 struct ReplyQueueEntry
39 struct ReplyQueueEntry *next;
44 struct ReplyQueueEntry *prev;
47 * Message to transmit, allocated at the end of this struct.
49 const struct GNUNET_MessageHeader *msg;
55 * Handle to identify an individual DNS request.
57 struct GNUNET_DNS_RequestHandle
63 struct GNUNET_DNS_Handle *dh;
66 * Stored in network byte order (as for us, it is just a random number).
71 * Re-connect counter, to make sure we did not reconnect in the meantime.
81 struct GNUNET_DNS_Handle
85 * Connection to DNS service, or NULL.
87 struct GNUNET_CLIENT_Connection *dns_connection;
90 * Handle to active transmission request, or NULL.
92 struct GNUNET_CLIENT_TransmitHandle *dns_transmit_handle;
95 * Configuration to use.
97 const struct GNUNET_CONFIGURATION_Handle *cfg;
100 * Function to call to get replies.
102 GNUNET_DNS_RequestHandler rh;
110 * Head of replies to transmit.
112 struct ReplyQueueEntry *rq_head;
115 * Tail of replies to transmit.
117 struct ReplyQueueEntry *rq_tail;
120 * Task to reconnect to the service.
122 struct GNUNET_SCHEDULER_Task * reconnect_task;
125 * Re-connect counter, to make sure we did not reconnect in the meantime.
130 * Flags for events we care about.
132 enum GNUNET_DNS_Flags flags;
135 * Did we start the receive loop yet?
140 * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
141 * we can be disconnected.
143 unsigned int pending_requests;
148 * Add the given reply to our transmission queue and trigger sending if needed.
150 * @param dh handle with the connection
151 * @param qe reply to queue
154 queue_reply (struct GNUNET_DNS_Handle *dh,
155 struct ReplyQueueEntry *qe);
159 * Reconnect to the DNS service.
161 * @param cls handle with the connection to connect
162 * @param tc scheduler context (unused)
165 reconnect (void *cls)
167 struct GNUNET_DNS_Handle *dh = cls;
168 struct ReplyQueueEntry *qe;
169 struct GNUNET_DNS_Register *msg;
171 dh->reconnect_task = NULL;
172 dh->dns_connection = GNUNET_CLIENT_connect ("dns", dh->cfg);
173 if (NULL == dh->dns_connection)
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);
188 * Disconnect from the DNS service.
190 * @param dh handle with the connection to disconnect
193 disconnect (struct GNUNET_DNS_Handle *dh)
195 struct ReplyQueueEntry *qe;
197 if (NULL != dh->dns_transmit_handle)
199 GNUNET_CLIENT_notify_transmit_ready_cancel (dh->dns_transmit_handle);
200 dh->dns_transmit_handle = NULL;
202 if (NULL != dh->dns_connection)
204 GNUNET_CLIENT_disconnect (dh->dns_connection);
205 dh->dns_connection = NULL;
207 while (NULL != (qe = dh->rq_head))
209 GNUNET_CONTAINER_DLL_remove (dh->rq_head,
214 dh->in_receive = GNUNET_NO;
219 * This receives packets from the DNS service and calls the application to
222 * @param cls the struct GNUNET_DNS_Handle
223 * @param msg message from the service (request)
226 request_handler (void *cls,
227 const struct GNUNET_MessageHeader *msg)
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;
234 /* the service disconnected, reconnect after short wait */
239 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
243 if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST) ||
244 (ntohs (msg->size) < sizeof (struct GNUNET_DNS_Request)) )
246 /* the service did something strange, reconnect immediately */
249 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
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);
259 /* finally, pass request to callback for answers */
260 rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
262 rh->request_id = req->request_id;
263 rh->generation = dh->generation;
264 dh->pending_requests++;
268 (const char*) &req[1]);
273 * Callback called by notify_transmit_ready; sends DNS replies
274 * to the DNS service.
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
282 send_response (void *cls, size_t size, void *buf)
284 struct GNUNET_DNS_Handle *dh = cls;
285 struct ReplyQueueEntry *qe;
288 dh->dns_transmit_handle = NULL;
293 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
300 len = ntohs (qe->msg->size);
303 dh->dns_transmit_handle =
304 GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
306 GNUNET_TIME_UNIT_FOREVER_REL,
311 memcpy (buf, qe->msg, len);
312 GNUNET_CONTAINER_DLL_remove (dh->rq_head,
316 if (GNUNET_NO == dh->in_receive)
318 dh->in_receive = GNUNET_YES;
319 GNUNET_CLIENT_receive (dh->dns_connection,
320 &request_handler, dh,
321 GNUNET_TIME_UNIT_FOREVER_REL);
323 if (NULL != (qe = dh->rq_head))
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,
337 * Add the given reply to our transmission queue and trigger sending if needed.
339 * @param dh handle with the connection
340 * @param qe reply to queue
343 queue_reply (struct GNUNET_DNS_Handle *dh,
344 struct ReplyQueueEntry *qe)
346 if (NULL == dh->dns_connection)
351 GNUNET_CONTAINER_DLL_insert_tail (dh->rq_head,
354 if (NULL != dh->dns_transmit_handle)
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,
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.
374 * @param rh request that should now be forwarded
377 GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
379 struct ReplyQueueEntry *qe;
380 struct GNUNET_DNS_Response *resp;
382 GNUNET_assert (0 < rh->dh->pending_requests--);
383 if (rh->generation != rh->dh->generation)
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);
402 * If a GNUNET_DNS_RequestHandler calls this function, the request is
403 * to be dropped and no response should be generated.
405 * @param rh request that should now be dropped
408 GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
410 struct ReplyQueueEntry *qe;
411 struct GNUNET_DNS_Response *resp;
413 GNUNET_assert (0 < rh->dh->pending_requests--);
414 if (rh->generation != rh->dh->generation)
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);
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).
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
442 GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
443 uint16_t reply_length,
446 struct ReplyQueueEntry *qe;
447 struct GNUNET_DNS_Response *resp;
449 GNUNET_assert (0 < rh->dh->pending_requests--);
450 if (rh->generation != rh->dh->generation)
455 if (reply_length + sizeof (struct GNUNET_DNS_Response) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
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);
476 * Connect to the service-dns
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
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,
490 struct GNUNET_DNS_Handle *dh;
492 dh = GNUNET_new (struct GNUNET_DNS_Handle);
497 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
503 * Disconnect from the DNS service.
505 * @param dh DNS handle
508 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
510 if (NULL != dh->reconnect_task)
512 GNUNET_SCHEDULER_cancel (dh->reconnect_task);
513 dh->reconnect_task = NULL;
516 /* make sure client has no pending requests left over! */
517 GNUNET_assert (0 == dh->pending_requests);
521 /* end of dns_api_new.c */