2 This file is part of GNUnet
3 Copyright (C) 2012, 2016 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
23 * @brief API to access the DNS service.
24 * @author Christian Grothoff
27 #include "gnunet_dns_service.h"
32 * Handle to identify an individual DNS request.
34 struct GNUNET_DNS_RequestHandle
40 struct GNUNET_DNS_Handle *dh;
43 * Stored in network byte order (as for us, it is just a random number).
48 * Re-connect counter, to make sure we did not reconnect in the meantime.
58 struct GNUNET_DNS_Handle
62 * Connection to DNS service, or NULL.
64 struct GNUNET_MQ_Handle *mq;
67 * Configuration to use.
69 const struct GNUNET_CONFIGURATION_Handle *cfg;
72 * Function to call to get replies.
74 GNUNET_DNS_RequestHandler rh;
82 * Task to reconnect to the service.
84 struct GNUNET_SCHEDULER_Task *reconnect_task;
87 * Re-connect counter, to make sure we did not reconnect in the meantime.
92 * Flags for events we care about.
94 enum GNUNET_DNS_Flags flags;
97 * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
98 * we can be disconnected.
100 unsigned int pending_requests;
105 * Reconnect to the DNS service.
107 * @param cls handle with the connection to connect
108 * @param tc scheduler context (unused)
111 reconnect (void *cls);
115 * Drop the existing connection and reconnect to the DNS service.
117 * @param dh handle with the connection
120 force_reconnect (struct GNUNET_DNS_Handle *dh)
124 GNUNET_MQ_destroy (dh->mq);
128 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
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.
139 * @param cls closure with the `struct GNUNET_DNS_Handle *`
140 * @param error error code
143 mq_error_handler (void *cls,
144 enum GNUNET_MQ_Error error)
146 struct GNUNET_DNS_Handle *dh = cls;
148 force_reconnect (dh);
154 * This receives packets from the DNS service and calls the application to
155 * check that the request is well-formed
157 * @param cls the struct GNUNET_DNS_Handle
158 * @param req message from the service (request)
161 check_request (void *cls,
162 const struct GNUNET_DNS_Request *req)
164 if (0 != ntohl (req->reserved))
167 return GNUNET_SYSERR;
174 * This receives packets from the DNS service and calls the application to
177 * @param cls the `struct GNUNET_DNS_Handle *`
178 * @param msg message from the service (request)
181 handle_request (void *cls,
182 const struct GNUNET_DNS_Request *req)
184 struct GNUNET_DNS_Handle *dh = cls;
185 size_t payload_length = ntohs (req->header.size) - sizeof (*req);
186 struct GNUNET_DNS_RequestHandle *rh;
188 rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
190 rh->request_id = req->request_id;
191 rh->generation = dh->generation;
192 dh->pending_requests++;
196 (const char*) &req[1]);
201 * Reconnect to the DNS service.
203 * @param cls handle with the connection to connect
206 reconnect (void *cls)
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,
214 GNUNET_MQ_handler_end ()
216 struct GNUNET_MQ_Envelope *env;
217 struct GNUNET_DNS_Register *msg;
219 dh->reconnect_task = NULL;
220 dh->mq = GNUNET_CLIENT_connect (dh->cfg,
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,
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.
244 * @param rh request that should now be forwarded
247 GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
249 struct GNUNET_MQ_Envelope *env;
250 struct GNUNET_DNS_Response *resp;
252 GNUNET_assert (0 < rh->dh->pending_requests--);
253 if (rh->generation != rh->dh->generation)
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,
269 * If a GNUNET_DNS_RequestHandler calls this function, the request is
270 * to be dropped and no response should be generated.
272 * @param rh request that should now be dropped
275 GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
277 struct GNUNET_MQ_Envelope *env;
278 struct GNUNET_DNS_Response *resp;
280 GNUNET_assert (0 < rh->dh->pending_requests--);
281 if (rh->generation != rh->dh->generation)
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,
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).
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
306 GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
307 uint16_t reply_length,
310 struct GNUNET_MQ_Envelope *env;
311 struct GNUNET_DNS_Response *resp;
313 GNUNET_assert (0 < rh->dh->pending_requests--);
314 if (rh->generation != rh->dh->generation)
319 if (reply_length + sizeof (struct GNUNET_DNS_Response)
320 >= GNUNET_MAX_MESSAGE_SIZE)
326 env = GNUNET_MQ_msg_extra (resp,
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],
334 GNUNET_MQ_send (rh->dh->mq,
341 * Connect to the service-dns
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
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,
355 struct GNUNET_DNS_Handle *dh;
357 dh = GNUNET_new (struct GNUNET_DNS_Handle);
362 dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
368 * Disconnect from the DNS service.
370 * @param dh DNS handle
373 GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
377 GNUNET_MQ_destroy (dh->mq);
380 if (NULL != dh->reconnect_task)
382 GNUNET_SCHEDULER_cancel (dh->reconnect_task);
383 dh->reconnect_task = NULL;
385 /* make sure client has no pending requests left over! */
386 GNUNET_break (0 == dh->pending_requests);
390 /* end of dns_api.c */