1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294 |
- /*
- This file is part of GNUnet.
- Copyright (C) 2009-2018 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 util/resolver_api.c
- * @brief resolver for writing a tool
- * @author Christian Grothoff
- */
- #include "platform.h"
- #include "gnunet_util_lib.h"
- #include "gnunet_protocols.h"
- #include "gnunet_resolver_service.h"
- #include "resolver.h"
- #define LOG(kind, ...) GNUNET_log_from (kind, "util-resolver-api", __VA_ARGS__)
- #define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror (kind, \
- "util-resolver-api", \
- syscall)
- /**
- * Maximum supported length for a hostname
- */
- #define MAX_HOSTNAME 1024
- /**
- * Possible hostnames for "loopback".
- */
- static const char *loopback[] = {
- "localhost",
- "ip6-localnet",
- NULL
- };
- /**
- * Configuration.
- */
- static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
- /**
- * Our connection to the resolver service, created on-demand, but then
- * persists until error or shutdown.
- */
- static struct GNUNET_MQ_Handle *mq;
- /**
- * Head of DLL of requests.
- */
- static struct GNUNET_RESOLVER_RequestHandle *req_head;
- /**
- * Tail of DLL of requests.
- */
- static struct GNUNET_RESOLVER_RequestHandle *req_tail;
- /**
- * ID of the last request we sent to the service
- */
- static uint32_t last_request_id;
- /**
- * How long should we wait to reconnect?
- */
- static struct GNUNET_TIME_Relative backoff;
- /**
- * Task for reconnecting.
- */
- static struct GNUNET_SCHEDULER_Task *r_task;
- /**
- * Task ID of shutdown task; only present while we have a
- * connection to the resolver service.
- */
- static struct GNUNET_SCHEDULER_Task *s_task;
- /**
- * Handle to a request given to the resolver. Can be used to cancel
- * the request prior to the timeout or successful execution. Also
- * used to track our internal state for the request.
- */
- struct GNUNET_RESOLVER_RequestHandle
- {
- /**
- * Next entry in DLL of requests.
- */
- struct GNUNET_RESOLVER_RequestHandle *next;
- /**
- * Previous entry in DLL of requests.
- */
- struct GNUNET_RESOLVER_RequestHandle *prev;
- /**
- * Callback if this is an name resolution request,
- * otherwise NULL.
- */
- GNUNET_RESOLVER_AddressCallback addr_callback;
- /**
- * Callback if this is a reverse lookup request,
- * otherwise NULL.
- */
- GNUNET_RESOLVER_HostnameCallback name_callback;
- /**
- * Closure for the callbacks.
- */
- void *cls;
- /**
- * When should this request time out?
- */
- struct GNUNET_TIME_Absolute timeout;
- /**
- * Task handle for making reply callbacks in numeric lookups
- * asynchronous, and for timeout handling.
- */
- struct GNUNET_SCHEDULER_Task *task;
- /**
- * Desired address family.
- */
- int af;
- /**
- * Identifies the request. The response will contain this id.
- */
- uint32_t id;
- /**
- * Has this request been transmitted to the service?
- * #GNUNET_YES if transmitted
- * #GNUNET_NO if not transmitted
- * #GNUNET_SYSERR when request was canceled
- */
- int was_transmitted;
- /**
- * Did we add this request to the queue?
- */
- int was_queued;
- /**
- * Desired direction (IP to name or name to IP)
- */
- int direction;
- /**
- * #GNUNET_YES if a response was received
- */
- int received_response;
- /**
- * Length of the data that follows this struct.
- */
- size_t data_len;
- };
- /**
- * Check that the resolver service runs on localhost
- * (or equivalent).
- *
- * @return #GNUNET_OK if the resolver is properly configured,
- * #GNUNET_SYSERR otherwise.
- */
- static int
- check_config ()
- {
- char *hostname;
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- if (GNUNET_OK ==
- GNUNET_CONFIGURATION_have_value (resolver_cfg,
- "resolver",
- "UNIXPATH"))
- return GNUNET_OK;
- memset (&v4, 0, sizeof(v4));
- v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- v4.sin_family = AF_INET;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v4.sin_len = sizeof(v4);
- #endif
- memset (&v6, 0, sizeof(v6));
- v6.sin6_family = AF_INET6;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v6.sin6_len = sizeof(v6);
- #endif
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (resolver_cfg,
- "resolver",
- "HOSTNAME",
- &hostname))
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- _ (
- "Missing `%s' for `%s' in configuration, DNS resolution will be unavailable.\n"),
- "HOSTNAME",
- "resolver");
- return GNUNET_SYSERR;
- }
- if ((1 == inet_pton (AF_INET, hostname, &v4)) ||
- (1 == inet_pton (AF_INET6, hostname, &v6)))
- {
- GNUNET_free (hostname);
- return GNUNET_OK;
- }
- for (unsigned int i = 0;
- NULL != loopback[i];
- i++)
- if (0 == strcasecmp (loopback[i],
- hostname))
- {
- GNUNET_free (hostname);
- return GNUNET_OK;
- }
- LOG (GNUNET_ERROR_TYPE_INFO,
- _ (
- "Missing `%s' or numeric IP address for `%s' of `%s' in configuration, DNS resolution will be unavailable.\n"),
- "localhost",
- "HOSTNAME",
- "resolver");
- GNUNET_free (hostname);
- return GNUNET_SYSERR;
- }
- /**
- * Create the connection to the resolver service.
- *
- * @param cfg configuration to use
- */
- void
- GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
- {
- GNUNET_assert (NULL != cfg);
- backoff = GNUNET_TIME_UNIT_MILLISECONDS;
- resolver_cfg = cfg;
- }
- /**
- * Destroy the connection to the resolver service.
- */
- void
- GNUNET_RESOLVER_disconnect ()
- {
- struct GNUNET_RESOLVER_RequestHandle *rh;
- while (NULL != (rh = req_head))
- {
- GNUNET_assert (GNUNET_SYSERR == rh->was_transmitted);
- GNUNET_CONTAINER_DLL_remove (req_head,
- req_tail,
- rh);
- GNUNET_free (rh);
- }
- if (NULL != mq)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Disconnecting from DNS service\n");
- GNUNET_MQ_destroy (mq);
- mq = NULL;
- }
- if (NULL != r_task)
- {
- GNUNET_SCHEDULER_cancel (r_task);
- r_task = NULL;
- }
- if (NULL != s_task)
- {
- GNUNET_SCHEDULER_cancel (s_task);
- s_task = NULL;
- }
- }
- /**
- * Task executed on system shutdown.
- */
- static void
- shutdown_task (void *cls)
- {
- (void) cls;
- s_task = NULL;
- GNUNET_RESOLVER_disconnect ();
- backoff = GNUNET_TIME_UNIT_MILLISECONDS;
- }
- /**
- * Consider disconnecting if we have no further requests pending.
- */
- static void
- check_disconnect ()
- {
- for (struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
- NULL != rh;
- rh = rh->next)
- if (GNUNET_SYSERR != rh->was_transmitted)
- return;
- if (NULL != r_task)
- {
- GNUNET_SCHEDULER_cancel (r_task);
- r_task = NULL;
- }
- if (NULL != s_task)
- return;
- s_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
- &shutdown_task,
- NULL);
- }
- /**
- * Convert IP address to string without DNS resolution.
- *
- * @param af address family
- * @param ip the address
- * @param ip_len number of bytes in @a ip
- * @return address as a string, NULL on error
- */
- static char *
- no_resolve (int af,
- const void *ip,
- socklen_t ip_len)
- {
- char buf[INET6_ADDRSTRLEN];
- switch (af)
- {
- case AF_INET:
- if (ip_len != sizeof(struct in_addr))
- return NULL;
- if (NULL ==
- inet_ntop (AF_INET,
- ip,
- buf,
- sizeof(buf)))
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
- "inet_ntop");
- return NULL;
- }
- break;
- case AF_INET6:
- if (ip_len != sizeof(struct in6_addr))
- return NULL;
- if (NULL ==
- inet_ntop (AF_INET6,
- ip,
- buf,
- sizeof(buf)))
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
- "inet_ntop");
- return NULL;
- }
- break;
- default:
- GNUNET_break (0);
- return NULL;
- }
- return GNUNET_strdup (buf);
- }
- /**
- * Adjust exponential back-off and reconnect to the service.
- */
- static void
- reconnect (void);
- /**
- * 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 NULL
- * @param error error code
- */
- static void
- mq_error_handler (void *cls,
- enum GNUNET_MQ_Error error)
- {
- (void) cls;
- GNUNET_MQ_destroy (mq);
- mq = NULL;
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "MQ error %d, reconnecting\n",
- error);
- reconnect ();
- }
- /**
- * Process pending requests to the resolver.
- */
- static void
- process_requests ()
- {
- struct GNUNET_RESOLVER_GetMessage *msg;
- struct GNUNET_MQ_Envelope *env;
- struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
- if (NULL == mq)
- {
- reconnect ();
- return;
- }
- if (NULL == rh)
- {
- /* nothing to do, release socket really soon if there is nothing
- * else happening... */
- if (NULL == s_task)
- s_task =
- GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
- &shutdown_task,
- NULL);
- return;
- }
- if (GNUNET_NO != rh->was_transmitted)
- return; /* waiting for reply */
- env = GNUNET_MQ_msg_extra (msg,
- rh->data_len,
- GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
- msg->direction = htonl (rh->direction);
- msg->af = htonl (rh->af);
- msg->client_id = rh->id;
- GNUNET_memcpy (&msg[1],
- &rh[1],
- rh->data_len);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Transmitting DNS resolution request (ID %u) to DNS service\n",
- rh->id);
- GNUNET_MQ_send (mq,
- env);
- rh->was_transmitted = GNUNET_YES;
- }
- /**
- * Check validity of response with a hostname for a DNS lookup.
- *
- * @param cls NULL
- * @param msg message with the hostname
- */
- static int
- check_response (void *cls,
- const struct GNUNET_RESOLVER_ResponseMessage *msg)
- {
- (void) cls;
- (void) msg;
- /* implemented in #handle_response() for now */
- return GNUNET_OK;
- }
- /**
- * Check validity of response with a hostname for a DNS lookup.
- * NOTE: right now rather messy, might want to use different
- * message types for different response formats in the future.
- *
- * @param cls NULL
- * @param msg message with the response
- */
- static void
- handle_response (void *cls,
- const struct GNUNET_RESOLVER_ResponseMessage *msg)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
- uint16_t size;
- char *nret;
- uint32_t client_request_id = msg->client_id;
- for (; rh != NULL; rh = rh->next)
- {
- if (rh->id == client_request_id)
- break;
- }
- (void) cls;
- if (NULL == rh)
- {
- /* Resolver service sent extra replies to query (after terminator)? Bad! */
- GNUNET_break (0);
- GNUNET_MQ_destroy (mq);
- mq = NULL;
- reconnect ();
- return;
- }
- size = ntohs (msg->header.size);
- if (size == sizeof(struct GNUNET_RESOLVER_ResponseMessage))
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received empty response from DNS service\n");
- /* message contains not data, just header; end of replies */
- /* check if request was canceled */
- if (GNUNET_SYSERR != rh->was_transmitted)
- {
- /* no reverse lookup was successful, return IP as string */
- if (NULL != rh->name_callback)
- {
- if (GNUNET_NO == rh->received_response)
- {
- nret = no_resolve (rh->af,
- &rh[1],
- rh->data_len);
- rh->name_callback (rh->cls, nret);
- GNUNET_free (nret);
- }
- /* finally, make termination call */
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->name_callback (rh->cls,
- NULL);
- }
- if ((NULL != rh->addr_callback) &&
- (GNUNET_SYSERR != rh->was_transmitted))
- rh->addr_callback (rh->cls,
- NULL,
- 0);
- }
- rh->was_transmitted = GNUNET_NO;
- GNUNET_RESOLVER_request_cancel (rh);
- process_requests ();
- return;
- }
- /* return reverse lookup results to caller */
- if (NULL != rh->name_callback)
- {
- const char *hostname;
- hostname = (const char *) &msg[1];
- if (hostname[size - sizeof(struct GNUNET_RESOLVER_ResponseMessage) - 1] !=
- '\0')
- {
- GNUNET_break (0);
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->name_callback (rh->cls,
- NULL);
- rh->was_transmitted = GNUNET_NO;
- GNUNET_RESOLVER_request_cancel (rh);
- GNUNET_MQ_destroy (mq);
- mq = NULL;
- reconnect ();
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Resolver returns `%s' for IP `%s'.\n",
- hostname,
- GNUNET_a2s ((const void *) &rh[1],
- rh->data_len));
- if (rh->was_transmitted != GNUNET_SYSERR)
- rh->name_callback (rh->cls,
- hostname);
- rh->received_response = GNUNET_YES;
- }
- /* return lookup results to caller */
- if (NULL != rh->addr_callback)
- {
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- const struct sockaddr *sa;
- socklen_t salen;
- const void *ip;
- size_t ip_len;
- ip = &msg[1];
- ip_len = size - sizeof(struct GNUNET_RESOLVER_ResponseMessage);
- if (ip_len == sizeof(struct in_addr))
- {
- memset (&v4, 0, sizeof(v4));
- v4.sin_family = AF_INET;
- v4.sin_addr = *(struct in_addr*) ip;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v4.sin_len = sizeof(v4);
- #endif
- salen = sizeof(v4);
- sa = (const struct sockaddr *) &v4;
- }
- else if (ip_len == sizeof(struct in6_addr))
- {
- memset (&v6, 0, sizeof(v6));
- v6.sin6_family = AF_INET6;
- v6.sin6_addr = *(struct in6_addr*) ip;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v6.sin6_len = sizeof(v6);
- #endif
- salen = sizeof(v6);
- sa = (const struct sockaddr *) &v6;
- }
- else
- {
- GNUNET_break (0);
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->addr_callback (rh->cls,
- NULL,
- 0);
- rh->was_transmitted = GNUNET_NO;
- GNUNET_RESOLVER_request_cancel (rh);
- GNUNET_MQ_destroy (mq);
- mq = NULL;
- reconnect ();
- return;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Received IP from DNS service\n");
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->addr_callback (rh->cls,
- sa,
- salen);
- }
- }
- /**
- * We've been asked to lookup the address for a hostname and were
- * given a valid numeric string. Perform the callbacks for the
- * numeric addresses.
- *
- * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
- */
- static void
- numeric_resolution (void *cls)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh = cls;
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- const char *hostname;
- rh->task = NULL;
- memset (&v4, 0, sizeof(v4));
- v4.sin_family = AF_INET;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v4.sin_len = sizeof(v4);
- #endif
- memset (&v6, 0, sizeof(v6));
- v6.sin6_family = AF_INET6;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v6.sin6_len = sizeof(v6);
- #endif
- hostname = (const char *) &rh[1];
- if (((rh->af == AF_UNSPEC) ||
- (rh->af == AF_INET)) &&
- (1 == inet_pton (AF_INET,
- hostname,
- &v4.sin_addr)))
- {
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v4,
- sizeof(v4));
- if ((rh->af == AF_UNSPEC) &&
- (GNUNET_SYSERR != rh->was_transmitted) &&
- (1 == inet_pton (AF_INET6,
- hostname,
- &v6.sin6_addr)))
- {
- /* this can happen on some systems IF "hostname" is "localhost" */
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v6,
- sizeof(v6));
- }
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->addr_callback (rh->cls,
- NULL,
- 0);
- GNUNET_free (rh);
- return;
- }
- if (((rh->af == AF_UNSPEC) ||
- (rh->af == AF_INET6)) &&
- (1 == inet_pton (AF_INET6,
- hostname,
- &v6.sin6_addr)))
- {
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v6,
- sizeof(v6));
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->addr_callback (rh->cls,
- NULL,
- 0);
- GNUNET_free (rh);
- return;
- }
- /* why are we here? this task should not have been scheduled! */
- GNUNET_assert (0);
- GNUNET_free (rh);
- }
- /**
- * We've been asked to lookup the address for a hostname and were
- * given a variant of "loopback". Perform the callbacks for the
- * respective loopback numeric addresses.
- *
- * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
- */
- static void
- loopback_resolution (void *cls)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh = cls;
- struct sockaddr_in v4;
- struct sockaddr_in6 v6;
- rh->task = NULL;
- memset (&v4, 0, sizeof(v4));
- v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
- v4.sin_family = AF_INET;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v4.sin_len = sizeof(v4);
- #endif
- memset (&v6, 0, sizeof(v6));
- v6.sin6_family = AF_INET6;
- #if HAVE_SOCKADDR_IN_SIN_LEN
- v6.sin6_len = sizeof(v6);
- #endif
- v6.sin6_addr = in6addr_loopback;
- switch (rh->af)
- {
- case AF_INET:
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v4,
- sizeof(v4));
- break;
- case AF_INET6:
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v6,
- sizeof(v6));
- break;
- case AF_UNSPEC:
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v6,
- sizeof(v6));
- rh->addr_callback (rh->cls,
- (const struct sockaddr *) &v4,
- sizeof(v4));
- break;
- default:
- GNUNET_break (0);
- break;
- }
- if (GNUNET_SYSERR != rh->was_transmitted)
- rh->addr_callback (rh->cls,
- NULL,
- 0);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Finished resolving hostname `%s'.\n",
- (const char *) &rh[1]);
- GNUNET_free (rh);
- }
- /**
- * Now try to reconnect to the resolver service.
- *
- * @param cls NULL
- */
- static void
- reconnect_task (void *cls)
- {
- struct GNUNET_MQ_MessageHandler handlers[] = {
- GNUNET_MQ_hd_var_size (response,
- GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE,
- struct GNUNET_RESOLVER_ResponseMessage,
- NULL),
- GNUNET_MQ_handler_end ()
- };
- (void) cls;
- r_task = NULL;
- if (NULL == req_head)
- return; /* no work pending */
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Trying to connect to DNS service\n");
- mq = GNUNET_CLIENT_connect (resolver_cfg,
- "resolver",
- handlers,
- &mq_error_handler,
- NULL);
- if (NULL == mq)
- {
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Failed to connect, will try again later\n");
- reconnect ();
- return;
- }
- process_requests ();
- }
- /**
- * Adjust exponential back-off and reconnect to the service.
- */
- static void
- reconnect ()
- {
- struct GNUNET_RESOLVER_RequestHandle *rh;
- if (NULL != r_task)
- return;
- GNUNET_assert (NULL == mq);
- if (NULL != (rh = req_head))
- {
- switch (rh->was_transmitted)
- {
- case GNUNET_NO:
- /* nothing more to do */
- break;
- case GNUNET_YES:
- /* disconnected, transmit again! */
- rh->was_transmitted = GNUNET_NO;
- break;
- case GNUNET_SYSERR:
- /* request was cancelled, remove entirely */
- GNUNET_CONTAINER_DLL_remove (req_head,
- req_tail,
- rh);
- GNUNET_free (rh);
- check_disconnect ();
- break;
- default:
- GNUNET_assert (0);
- break;
- }
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Will try to connect to DNS service in %s\n",
- GNUNET_STRINGS_relative_time_to_string (backoff,
- GNUNET_YES));
- GNUNET_assert (NULL != resolver_cfg);
- r_task = GNUNET_SCHEDULER_add_delayed (backoff,
- &reconnect_task,
- NULL);
- backoff = GNUNET_TIME_STD_BACKOFF (backoff);
- }
- /**
- * A DNS resolution timed out. Notify the application.
- *
- * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
- */
- static void
- handle_lookup_timeout (void *cls)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh = cls;
- rh->task = NULL;
- if (GNUNET_NO == rh->direction)
- {
- LOG (GNUNET_ERROR_TYPE_INFO,
- _ ("Timeout trying to resolve hostname `%s'.\n"),
- (const char *) &rh[1]);
- if (NULL != rh->addr_callback)
- rh->addr_callback (rh->cls,
- NULL,
- 0);
- }
- else
- {
- #if ! defined(GNUNET_CULL_LOGGING)
- char buf[INET6_ADDRSTRLEN];
- LOG (GNUNET_ERROR_TYPE_INFO,
- _ ("Timeout trying to resolve IP address `%s'.\n"),
- inet_ntop (rh->af,
- (const void *) &rh[1],
- buf,
- sizeof(buf)));
- #endif
- if (GNUNET_NO == rh->received_response)
- {
- char *nret;
- nret = no_resolve (rh->af,
- &rh[1],
- rh->data_len);
- if (NULL != rh->name_callback)
- rh->name_callback (rh->cls, nret);
- GNUNET_free (nret);
- }
- /* finally, make termination call */
- if (NULL != rh->name_callback)
- rh->name_callback (rh->cls,
- NULL);
- }
- rh->was_transmitted = GNUNET_NO;
- GNUNET_RESOLVER_request_cancel (rh);
- process_requests ();
- }
- /**
- * Convert a string to one or more IP addresses.
- *
- * @param hostname the hostname to resolve
- * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
- * @param callback function to call with addresses
- * @param callback_cls closure for @a callback
- * @param timeout how long to try resolving
- * @return handle that can be used to cancel the request, NULL on error
- */
- struct GNUNET_RESOLVER_RequestHandle *
- GNUNET_RESOLVER_ip_get (const char *hostname,
- int af,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_RESOLVER_AddressCallback callback,
- void *callback_cls)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh;
- size_t slen;
- struct in_addr v4;
- struct in6_addr v6;
- slen = strlen (hostname) + 1;
- if (slen + sizeof(struct GNUNET_RESOLVER_GetMessage) >=
- GNUNET_MAX_MESSAGE_SIZE)
- {
- GNUNET_break (0);
- return NULL;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Trying to resolve hostname `%s'.\n",
- hostname);
- rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + slen);
- rh->af = af;
- rh->id = ++last_request_id;
- rh->addr_callback = callback;
- rh->cls = callback_cls;
- GNUNET_memcpy (&rh[1],
- hostname,
- slen);
- rh->data_len = slen;
- rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
- rh->direction = GNUNET_NO;
- /* first, check if this is a numeric address */
- if (((1 == inet_pton (AF_INET,
- hostname,
- &v4)) &&
- ((af == AF_INET) ||
- (af == AF_UNSPEC))) ||
- ((1 == inet_pton (AF_INET6,
- hostname,
- &v6)) &&
- ((af == AF_INET6) ||
- (af == AF_UNSPEC))))
- {
- rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
- rh);
- return rh;
- }
- /* then, check if this is a loopback address */
- for (unsigned int i = 0;
- NULL != loopback[i];
- i++)
- if (0 == strcasecmp (loopback[i],
- hostname))
- {
- rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
- rh);
- return rh;
- }
- if (GNUNET_OK != check_config ())
- {
- GNUNET_free (rh);
- return NULL;
- }
- rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
- &handle_lookup_timeout,
- rh);
- GNUNET_CONTAINER_DLL_insert_tail (req_head,
- req_tail,
- rh);
- rh->was_queued = GNUNET_YES;
- if (NULL != s_task)
- {
- GNUNET_SCHEDULER_cancel (s_task);
- s_task = NULL;
- }
- process_requests ();
- return rh;
- }
- /**
- * We've been asked to convert an address to a string without
- * a reverse lookup, either because the client asked for it
- * or because the DNS lookup hit a timeout. Do the numeric
- * conversion and invoke the callback.
- *
- * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
- */
- static void
- numeric_reverse (void *cls)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh = cls;
- char *result;
- rh->task = NULL;
- result = no_resolve (rh->af,
- &rh[1],
- rh->data_len);
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Resolver returns `%s'.\n",
- result);
- if (NULL != result)
- {
- rh->name_callback (rh->cls,
- result);
- GNUNET_free (result);
- }
- rh->name_callback (rh->cls,
- NULL);
- if (NULL != rh->task)
- {
- GNUNET_SCHEDULER_cancel (rh->task);
- rh->task = NULL;
- }
- GNUNET_free (rh);
- }
- /**
- * Get an IP address as a string.
- *
- * @param sa host address
- * @param salen length of host address in @a sa
- * @param do_resolve use #GNUNET_NO to return numeric hostname
- * @param timeout how long to try resolving
- * @param callback function to call with hostnames
- * last callback is NULL when finished
- * @param cls closure for @a callback
- * @return handle that can be used to cancel the request
- */
- struct GNUNET_RESOLVER_RequestHandle *
- GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
- socklen_t salen,
- int do_resolve,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_RESOLVER_HostnameCallback callback,
- void *cls)
- {
- struct GNUNET_RESOLVER_RequestHandle *rh;
- size_t ip_len;
- const void *ip;
- if (GNUNET_OK != check_config ())
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _ ("Resolver not configured correctly.\n"));
- return NULL;
- }
- switch (sa->sa_family)
- {
- case AF_INET:
- GNUNET_assert (salen == sizeof(struct sockaddr_in));
- ip_len = sizeof(struct in_addr);
- ip = &((const struct sockaddr_in*) sa)->sin_addr;
- break;
- case AF_INET6:
- GNUNET_assert (salen == sizeof(struct sockaddr_in6));
- ip_len = sizeof(struct in6_addr);
- ip = &((const struct sockaddr_in6*) sa)->sin6_addr;
- break;
- default:
- GNUNET_break (0);
- return NULL;
- }
- rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + salen);
- rh->name_callback = callback;
- rh->cls = cls;
- rh->af = sa->sa_family;
- rh->id = ++last_request_id;
- rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
- GNUNET_memcpy (&rh[1],
- ip,
- ip_len);
- rh->data_len = ip_len;
- rh->direction = GNUNET_YES;
- rh->received_response = GNUNET_NO;
- if (GNUNET_NO == do_resolve)
- {
- rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
- rh);
- return rh;
- }
- rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
- &handle_lookup_timeout,
- rh);
- GNUNET_CONTAINER_DLL_insert_tail (req_head,
- req_tail,
- rh);
- rh->was_queued = GNUNET_YES;
- if (NULL != s_task)
- {
- GNUNET_SCHEDULER_cancel (s_task);
- s_task = NULL;
- }
- process_requests ();
- return rh;
- }
- /**
- * Get local fully qualified af name
- *
- * @return fqdn
- */
- char *
- GNUNET_RESOLVER_local_fqdn_get ()
- {
- char hostname[GNUNET_OS_get_hostname_max_length () + 1];
- if (0 != gethostname (hostname,
- sizeof(hostname) - 1))
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
- "gethostname");
- return NULL;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Resolving our FQDN `%s'\n",
- hostname);
- #if HAVE_GETADDRINFO
- {
- struct addrinfo *ai;
- int ret;
- char *rval;
- if (0 != (ret = getaddrinfo (hostname,
- NULL,
- NULL,
- &ai)))
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _ ("Could not resolve our FQDN: %s\n"),
- gai_strerror (ret));
- return NULL;
- }
- if (NULL != ai->ai_canonname)
- rval = GNUNET_strdup (ai->ai_canonname);
- else
- rval = GNUNET_strdup (hostname);
- freeaddrinfo (ai);
- return rval;
- }
- #elif HAVE_GETHOSTBYNAME2
- {
- struct hostent *host;
- host = gethostbyname2 (hostname,
- AF_INET);
- if (NULL == host)
- host = gethostbyname2 (hostname,
- AF_INET6);
- if (NULL == host)
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _ ("Could not resolve our FQDN: %s\n"),
- hstrerror (h_errno));
- return NULL;
- }
- return GNUNET_strdup (host->h_name);
- }
- #elif HAVE_GETHOSTBYNAME
- {
- struct hostent *host;
- host = gethostbyname (hostname);
- if (NULL == host)
- {
- LOG (GNUNET_ERROR_TYPE_ERROR,
- _ ("Could not resolve our FQDN: %s\n"),
- hstrerror (h_errno));
- return NULL;
- }
- return GNUNET_strdup (host->h_name);
- }
- #else
- /* fallback: just hope name is already FQDN */
- return GNUNET_strdup (hostname);
- #endif
- }
- /**
- * Looking our own hostname.
- *
- * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
- * @param timeout how long to try resolving
- * @param callback function to call with addresses
- * @param cls closure for @a callback
- * @return handle that can be used to cancel the request, NULL on error
- */
- struct GNUNET_RESOLVER_RequestHandle *
- GNUNET_RESOLVER_hostname_resolve (int af,
- struct GNUNET_TIME_Relative timeout,
- GNUNET_RESOLVER_AddressCallback callback,
- void *cls)
- {
- char hostname[GNUNET_OS_get_hostname_max_length () + 1];
- if (0 != gethostname (hostname, sizeof(hostname) - 1))
- {
- LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
- "gethostname");
- return NULL;
- }
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Resolving our hostname `%s'\n",
- hostname);
- return GNUNET_RESOLVER_ip_get (hostname,
- af,
- timeout,
- callback,
- cls);
- }
- /**
- * Cancel a request that is still pending with the resolver.
- * Note that a client MUST NOT cancel a request that has
- * been completed (i.e, the callback has been called to
- * signal timeout or the final result).
- *
- * @param rh handle of request to cancel
- */
- void
- GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
- {
- if (GNUNET_NO == rh->direction)
- LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Asked to cancel request to resolve hostname `%s'.\n",
- (const char *) &rh[1]);
- if (NULL != rh->task)
- {
- GNUNET_SCHEDULER_cancel (rh->task);
- rh->task = NULL;
- }
- if (GNUNET_NO == rh->was_transmitted)
- {
- if (GNUNET_YES == rh->was_queued)
- GNUNET_CONTAINER_DLL_remove (req_head,
- req_tail,
- rh);
- GNUNET_free (rh);
- check_disconnect ();
- return;
- }
- GNUNET_assert (GNUNET_YES == rh->was_transmitted);
- rh->was_transmitted = GNUNET_SYSERR; /* mark as cancelled */
- check_disconnect ();
- }
- /* end of resolver_api.c */
|