2 This file is part of GNUnet
3 (C) 2012 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, 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 GNUNET_SCHEDULER_TaskIdentifier 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,
166 const struct GNUNET_SCHEDULER_TaskContext *tc)
168 struct GNUNET_DNS_Handle *dh = cls;
169 struct ReplyQueueEntry *qe;
170 struct GNUNET_DNS_Register *msg;
172 dh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
173 dh->dns_connection = GNUNET_CLIENT_connect ("dns", dh->cfg);
174 if (NULL == dh->dns_connection)
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);
189 * Disconnect from the DNS service.
191 * @param dh handle with the connection to disconnect
194 disconnect (struct GNUNET_DNS_Handle *dh)
196 struct ReplyQueueEntry *qe;
198 if (NULL != dh->dns_transmit_handle)
200 GNUNET_CLIENT_notify_transmit_ready_cancel (dh->dns_transmit_handle);
201 dh->dns_transmit_handle = NULL;
203 if (NULL != dh->dns_connection)
205 GNUNET_CLIENT_disconnect (dh->dns_connection);
206 dh->dns_connection = NULL;
208 while (NULL != (qe = dh->rq_head))
210 GNUNET_CONTAINER_DLL_remove (dh->rq_head,
215 dh->in_receive = GNUNET_NO;
220 * This receives packets from the DNS service and calls the application to
223 * @param cls the struct GNUNET_DNS_Handle
224 * @param msg message from the service (request)
227 request_handler (void *cls,
228 const struct GNUNET_MessageHeader *msg)
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;
235 /* the service disconnected, reconnect after short wait */
240 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
244 if ( (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST) ||
245 (ntohs (msg->size) < sizeof (struct GNUNET_DNS_Request)) )
247 /* the service did something strange, reconnect immediately */
250 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
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);
260 /* finally, pass request to callback for answers */
261 rh = GNUNET_malloc (sizeof (struct GNUNET_DNS_RequestHandle));
263 rh->request_id = req->request_id;
264 rh->generation = dh->generation;
265 dh->pending_requests++;
269 (const char*) &req[1]);
274 * Callback called by notify_transmit_ready; sends DNS replies
275 * to the DNS service.
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
283 send_response (void *cls, size_t size, void *buf)
285 struct GNUNET_DNS_Handle *dh = cls;
286 struct ReplyQueueEntry *qe;
289 dh->dns_transmit_handle = NULL;
294 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
301 len = ntohs (qe->msg->size);
304 dh->dns_transmit_handle =
305 GNUNET_CLIENT_notify_transmit_ready (dh->dns_connection,
307 GNUNET_TIME_UNIT_FOREVER_REL,
312 memcpy (buf, qe->msg, len);
313 GNUNET_CONTAINER_DLL_remove (dh->rq_head,
317 if (GNUNET_NO == dh->in_receive)
319 dh->in_receive = GNUNET_YES;
320 GNUNET_CLIENT_receive (dh->dns_connection,
321 &request_handler, dh,
322 GNUNET_TIME_UNIT_FOREVER_REL);
324 if (NULL != (qe = dh->rq_head))
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,
338 * Add the given reply to our transmission queue and trigger sending if needed.
340 * @param dh handle with the connection
341 * @param qe reply to queue
344 queue_reply (struct GNUNET_DNS_Handle *dh,
345 struct ReplyQueueEntry *qe)
347 if (NULL == dh->dns_connection)
352 GNUNET_CONTAINER_DLL_insert_tail (dh->rq_head,
355 if (NULL != dh->dns_transmit_handle)
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,
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.
375 * @param rh request that should now be forwarded
378 GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
380 struct ReplyQueueEntry *qe;
381 struct GNUNET_DNS_Response *resp;
383 GNUNET_assert (0 < rh->dh->pending_requests--);
384 if (rh->generation != rh->dh->generation)
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);
403 * If a GNUNET_DNS_RequestHandler calls this function, the request is
404 * to be dropped and no response should be generated.
406 * @param rh request that should now be dropped
409 GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
411 struct ReplyQueueEntry *qe;
412 struct GNUNET_DNS_Response *resp;
414 GNUNET_assert (0 < rh->dh->pending_requests--);
415 if (rh->generation != rh->dh->generation)
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);
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).
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
443 GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
444 uint16_t reply_length,
447 struct ReplyQueueEntry *qe;
448 struct GNUNET_DNS_Response *resp;
450 GNUNET_assert (0 < rh->dh->pending_requests--);
451 if (rh->generation != rh->dh->generation)
456 if (reply_length + sizeof (struct GNUNET_DNS_Response) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
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);
477 * Connect to the service-dns
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
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,
491 struct GNUNET_DNS_Handle *dh;
493 dh = GNUNET_malloc (sizeof (struct GNUNET_DNS_Handle));
498 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
504 * Disconnect from the DNS service.
506 * @param dh DNS handle
509 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
511 if (GNUNET_SCHEDULER_NO_TASK != dh->reconnect_task)
513 GNUNET_SCHEDULER_cancel (dh->reconnect_task);
514 dh->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
517 /* make sure client has no pending requests left over! */
518 GNUNET_assert (0 == dh->pending_requests);
522 /* end of dns_api_new.c */