123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387 |
- /*
- This file is part of GNUnet
- Copyright (C) 2012, 2016 GNUnet e.V.
- GNUnet is free software: you can redistribute it and/or modify it
- under the terms of the GNU Affero General Public License as published
- by the Free Software Foundation, either version 3 of the License,
- or (at your option) any later version.
- GNUnet is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Affero General Public License for more details.
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- SPDX-License-Identifier: AGPL3.0-or-later
- */
- /**
- * @file dns/dns_api.c
- * @brief API to access the DNS service.
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_dns_service.h"
- #include "dns.h"
- /**
- * Handle to identify an individual DNS request.
- */
- struct GNUNET_DNS_RequestHandle
- {
- /**
- * Handle to DNS API.
- */
- struct GNUNET_DNS_Handle *dh;
- /**
- * Stored in network byte order (as for us, it is just a random number).
- */
- uint64_t request_id;
- /**
- * Re-connect counter, to make sure we did not reconnect in the meantime.
- */
- uint32_t generation;
- };
- /**
- * DNS handle
- */
- struct GNUNET_DNS_Handle
- {
- /**
- * Connection to DNS service, or NULL.
- */
- struct GNUNET_MQ_Handle *mq;
- /**
- * Configuration to use.
- */
- const struct GNUNET_CONFIGURATION_Handle *cfg;
- /**
- * Function to call to get replies.
- */
- GNUNET_DNS_RequestHandler rh;
- /**
- * Closure for @e rh.
- */
- void *rh_cls;
- /**
- * Task to reconnect to the service.
- */
- struct GNUNET_SCHEDULER_Task *reconnect_task;
- /**
- * Re-connect counter, to make sure we did not reconnect in the meantime.
- */
- uint32_t generation;
- /**
- * Flags for events we care about.
- */
- enum GNUNET_DNS_Flags flags;
- /**
- * Number of GNUNET_DNS_RequestHandles we have outstanding. Must be 0 before
- * we can be disconnected.
- */
- unsigned int pending_requests;
- };
- /**
- * Reconnect to the DNS service.
- *
- * @param cls handle with the connection to connect
- * @param tc scheduler context (unused)
- */
- static void
- reconnect (void *cls);
- /**
- * Drop the existing connection and reconnect to the DNS service.
- *
- * @param dh handle with the connection
- */
- static void
- force_reconnect (struct GNUNET_DNS_Handle *dh)
- {
- if (NULL != dh->mq)
- {
- GNUNET_MQ_destroy (dh->mq);
- dh->mq = NULL;
- }
- dh->reconnect_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
- &reconnect,
- dh);
- }
- /**
- * Generic error handler, called with the appropriate error code and
- * the same closure specified at the creation of the message queue.
- * Not every message queue implementation supports an error handler.
- *
- * @param cls closure with the `struct GNUNET_DNS_Handle *`
- * @param error error code
- */
- static void
- mq_error_handler (void *cls,
- enum GNUNET_MQ_Error error)
- {
- struct GNUNET_DNS_Handle *dh = cls;
- force_reconnect (dh);
- }
- /**
- * This receives packets from the DNS service and calls the application to
- * check that the request is well-formed
- *
- * @param cls the struct GNUNET_DNS_Handle
- * @param req message from the service (request)
- */
- static int
- check_request (void *cls,
- const struct GNUNET_DNS_Request *req)
- {
- if (0 != ntohl (req->reserved))
- {
- GNUNET_break (0);
- return GNUNET_SYSERR;
- }
- return GNUNET_OK;
- }
- /**
- * This receives packets from the DNS service and calls the application to
- * handle it.
- *
- * @param cls the `struct GNUNET_DNS_Handle *`
- * @param msg message from the service (request)
- */
- static void
- handle_request (void *cls,
- const struct GNUNET_DNS_Request *req)
- {
- struct GNUNET_DNS_Handle *dh = cls;
- size_t payload_length = ntohs (req->header.size) - sizeof(*req);
- struct GNUNET_DNS_RequestHandle *rh;
- rh = GNUNET_new (struct GNUNET_DNS_RequestHandle);
- rh->dh = dh;
- rh->request_id = req->request_id;
- rh->generation = dh->generation;
- dh->pending_requests++;
- dh->rh (dh->rh_cls,
- rh,
- payload_length,
- (const char *) &req[1]);
- }
- /**
- * Reconnect to the DNS service.
- *
- * @param cls handle with the connection to connect
- */
- static void
- reconnect (void *cls)
- {
- struct GNUNET_DNS_Handle *dh = cls;
- struct GNUNET_MQ_MessageHandler handlers[] = {
- GNUNET_MQ_hd_var_size (request,
- GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST,
- struct GNUNET_DNS_Request,
- dh),
- GNUNET_MQ_handler_end ()
- };
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_DNS_Register *msg;
- dh->reconnect_task = NULL;
- dh->mq = GNUNET_CLIENT_connect (dh->cfg,
- "dns",
- handlers,
- &mq_error_handler,
- dh);
- if (NULL == dh->mq)
- return;
- dh->generation++;
- env = GNUNET_MQ_msg (msg,
- GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT);
- msg->flags = htonl (dh->flags);
- GNUNET_MQ_send (dh->mq,
- env);
- }
- /**
- * If a GNUNET_DNS_RequestHandler calls this function, the request is
- * given to other clients or the global DNS for resolution. Once a
- * global response has been obtained, the request handler is AGAIN
- * called to give it a chance to observe and modify the response after
- * the "normal" resolution. It is not legal for the request handler
- * to call this function if a response is already present.
- *
- * @param rh request that should now be forwarded
- */
- void
- GNUNET_DNS_request_forward (struct GNUNET_DNS_RequestHandle *rh)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_DNS_Response *resp;
- GNUNET_assert (0 < rh->dh->pending_requests--);
- if (rh->generation != rh->dh->generation)
- {
- GNUNET_free (rh);
- return;
- }
- env = GNUNET_MQ_msg (resp,
- GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
- resp->drop_flag = htonl (1);
- resp->request_id = rh->request_id;
- GNUNET_MQ_send (rh->dh->mq,
- env);
- GNUNET_free (rh);
- }
- /**
- * If a GNUNET_DNS_RequestHandler calls this function, the request is
- * to be dropped and no response should be generated.
- *
- * @param rh request that should now be dropped
- */
- void
- GNUNET_DNS_request_drop (struct GNUNET_DNS_RequestHandle *rh)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_DNS_Response *resp;
- GNUNET_assert (0 < rh->dh->pending_requests--);
- if (rh->generation != rh->dh->generation)
- {
- GNUNET_free (rh);
- return;
- }
- env = GNUNET_MQ_msg (resp,
- GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
- resp->request_id = rh->request_id;
- resp->drop_flag = htonl (0);
- GNUNET_MQ_send (rh->dh->mq,
- env);
- GNUNET_free (rh);
- }
- /**
- * If a GNUNET_DNS_RequestHandler calls this function, the request is
- * supposed to be answered with the data provided to this call (with
- * the modifications the function might have made).
- *
- * @param rh request that should now be answered
- * @param reply_length size of @a reply (uint16_t to force sane size)
- * @param reply reply data
- */
- void
- GNUNET_DNS_request_answer (struct GNUNET_DNS_RequestHandle *rh,
- uint16_t reply_length,
- const char *reply)
- {
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_DNS_Response *resp;
- GNUNET_assert (0 < rh->dh->pending_requests--);
- if (rh->generation != rh->dh->generation)
- {
- GNUNET_free (rh);
- return;
- }
- if (reply_length + sizeof(struct GNUNET_DNS_Response)
- >= GNUNET_MAX_MESSAGE_SIZE)
- {
- GNUNET_break (0);
- GNUNET_free (rh);
- return;
- }
- env = GNUNET_MQ_msg_extra (resp,
- reply_length,
- GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE);
- resp->drop_flag = htonl (2);
- resp->request_id = rh->request_id;
- GNUNET_memcpy (&resp[1],
- reply,
- reply_length);
- GNUNET_MQ_send (rh->dh->mq,
- env);
- GNUNET_free (rh);
- }
- /**
- * Connect to the service-dns
- *
- * @param cfg configuration to use
- * @param flags when to call @a rh
- * @param rh function to call with DNS requests
- * @param rh_cls closure to pass to @a rh
- * @return DNS handle
- */
- struct GNUNET_DNS_Handle *
- GNUNET_DNS_connect (const struct GNUNET_CONFIGURATION_Handle *cfg,
- enum GNUNET_DNS_Flags flags,
- GNUNET_DNS_RequestHandler rh,
- void *rh_cls)
- {
- struct GNUNET_DNS_Handle *dh;
- dh = GNUNET_new (struct GNUNET_DNS_Handle);
- dh->cfg = cfg;
- dh->flags = flags;
- dh->rh = rh;
- dh->rh_cls = rh_cls;
- dh->reconnect_task = GNUNET_SCHEDULER_add_now (&reconnect, dh);
- return dh;
- }
- /**
- * Disconnect from the DNS service.
- *
- * @param dh DNS handle
- */
- void
- GNUNET_DNS_disconnect (struct GNUNET_DNS_Handle *dh)
- {
- if (NULL != dh->mq)
- {
- GNUNET_MQ_destroy (dh->mq);
- dh->mq = NULL;
- }
- if (NULL != dh->reconnect_task)
- {
- GNUNET_SCHEDULER_cancel (dh->reconnect_task);
- dh->reconnect_task = NULL;
- }
- /* make sure client has no pending requests left over! */
- GNUNET_break (0 == dh->pending_requests);
- GNUNET_free (dh);
- }
- /* end of dns_api.c */
|