/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * * Contact: Pekka Pessi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /**@CFILE sres_blocking.c * @brief Blocking interface for Sofia DNS Resolver implementation. * * @author Pekka Pessi * @date Created: Fri Mar 24 15:23:08 EET 2006 ppessi */ #include "config.h" #if HAVE_STDINT_H #include #elif HAVE_INTTYPES_H #include #else #if defined(_WIN32) typedef _int8 int8_t; typedef unsigned _int8 uint8_t; typedef unsigned _int16 uint16_t; typedef unsigned _int32 uint32_t; #endif #endif #if HAVE_NETINET_IN_H #include #include #include #endif #if HAVE_WINSOCK2_H #include #include #define HAVE_SELECT 1 #else #define SOCKET_ERROR (-1) #define INVALID_SOCKET ((sres_socket_t)-1) #endif typedef struct sres_blocking_s sres_blocking_t; typedef struct sres_blocking_context_s sres_blocking_context_t; #define SRES_CONTEXT_T struct sres_blocking_context_s #define SRES_ASYNC_T struct sres_blocking_s #include "sofia-resolv/sres.h" #include "sofia-resolv/sres_async.h" #include #if HAVE_POLL #include #elif HAVE_SYS_SELECT_H #include #endif #include #include struct sres_blocking_s { int n_sockets; #if HAVE_POLL struct pollfd fds[SRES_MAX_NAMESERVERS]; #elif HAVE_SELECT struct { sres_socket_t fd; } fds[SRES_MAX_NAMESERVERS]; #else #warning No guaranteed wait mechanism! /* typedef struct os_specific su_wait_t; */ struct _pollfd { sres_socket_t fd; /* file descriptor */ short events; /* requested events */ short revents; /* returned events */ } fds[SRES_MAX_NAMESERVERS]; #endif sres_record_t ***return_records; }; struct sres_blocking_context_s { int ready; sres_resolver_t *resolver; sres_blocking_t *block; sres_query_t *query; sres_record_t ***return_records; }; static int sres_blocking_update(sres_blocking_t *b, sres_socket_t new_socket, sres_socket_t old_socket) { int i, N = b->n_sockets; if (old_socket == new_socket) { if (old_socket == INVALID_SOCKET) { free(b); /* Destroy us */ } return 0; } if (old_socket != INVALID_SOCKET) { for (i = 0; i < N; i++) { if (b->fds[i].fd == old_socket) break; } if (i == N) return -1; N--; b->fds[i].fd = b->fds[N].fd; b->fds[N].fd = INVALID_SOCKET; #if HAVE_POLL b->fds[i].events = b->fds[N].events; b->fds[N].events = 0; #endif b->n_sockets = N; } if (new_socket != INVALID_SOCKET) { if (N == SRES_MAX_NAMESERVERS) return -1; b->fds[N].fd = new_socket; #if HAVE_POLL b->fds[N].events = POLLIN; #endif b->n_sockets = N + 1; } return 0; } static int sres_blocking_complete(sres_blocking_context_t *c) { while (!c->ready) { int n, i; #if HAVE_POLL n = poll(c->block->fds, c->block->n_sockets, 500); if (n < 0) { c->ready = n; } else if (n == 0) { sres_resolver_timer(c->resolver, -1); } else for (i = 0; i < c->block->n_sockets; i++) { if (c->block->fds[i].revents | POLLERR) sres_resolver_error(c->resolver, c->block->fds[i].fd); if (c->block->fds[i].revents | POLLIN) sres_resolver_receive(c->resolver, c->block->fds[i].fd); } #elif HAVE_SELECT fd_set readfds[1], errorfds[1]; struct timeval timeval[1]; FD_ZERO(readfds); FD_ZERO(errorfds); timeval->tv_sec = 0; timeval->tv_usec = 500000; for (i = 0, n = 0; i < c->block->n_sockets; i++) { FD_SET(c->block->fds[i].fd, readfds); FD_SET(c->block->fds[i].fd, errorfds); if (c->block->fds[i].fd >= n) n = c->block->fds[i].fd + 1; } n = select(n, readfds, NULL, errorfds, timeval); if (n <= 0) sres_resolver_timer(c->resolver, -1); else for (i = 0; n > 0 && i < c->block->n_sockets; i++) { if (FD_ISSET(c->block->fds[i].fd, errorfds)) sres_resolver_error(c->resolver, c->block->fds[i].fd); else if (FD_ISSET(c->block->fds[i].fd, readfds)) sres_resolver_receive(c->resolver, c->block->fds[i].fd); else continue; n--; } #endif } return c->ready; } static void sres_blocking_callback(sres_blocking_context_t *c, sres_query_t *query, sres_record_t **answers) { c->ready = 1; *c->return_records = answers; } static sres_blocking_t *sres_set_blocking(sres_resolver_t *res) { sres_blocking_t *b; int i; b = sres_resolver_get_async(res, sres_blocking_update); if (b) return b; /* Check if resolver is already in asynchronous mode */ if (sres_resolver_get_async(res, NULL)) return NULL; /* Create a synchronous (blocking) interface towards resolver */ b = calloc(1, sizeof *b); for (i = 0; i < SRES_MAX_NAMESERVERS; i++) b->fds[i].fd = INVALID_SOCKET; if (!sres_resolver_set_async(res, sres_blocking_update, b, 0)) { free(b), b = NULL; } return b; } /** Return true (and set resolver in blocking mode) if resolver can block. */ int sres_is_blocking(sres_resolver_t *res) { if (res == NULL) return 0; return sres_set_blocking(res) != NULL; } /**Send a DNS query, wait for response, return results. * * Sends a DNS query with specified @a type and @a domain to the DNS server, * if @a ignore_cache is not given or no records are found from cache. * Function returns an error record with nonzero status if no response is * received from DNS server. * * @param res pointer to resolver object * @param type record type to search (or sres_qtype_any for any record) * @param domain domain name to query * @param ignore_cache ignore cached answers if nonzero * @param return_records return-value parameter for dns records * * @retval >0 if query was responded * @retval 0 if result was found from cache * @retval -1 upon error * * @ERRORS * @ERROR EFAULT @a res or @a domain point outside the address space * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME * @ERROR ENETDOWN no DNS servers configured * @ERROR ENOMEM memory exhausted * @ERROR EOPNOTSUPP resolver @a res is in asynchronous mode * * @sa sres_query(), sres_blocking_search() * * @note A blocking query converts a resolver object permanently into * blocking mode. If you need to make blocking and non-blocking queries, use * sres_resolver_copy() to make a separate resolver object for blocking * queries. */ int sres_blocking_query(sres_resolver_t *res, uint16_t type, char const *domain, int ignore_cache, sres_record_t ***return_records) { sres_blocking_context_t c[1]; sres_record_t **cached; if (return_records == NULL) return su_seterrno(EFAULT); *return_records = NULL; c->block = sres_set_blocking(res); if (c->block == NULL) return su_seterrno(EOPNOTSUPP); /* Resolver in asynchronous mode */ if (!ignore_cache) { cached = sres_cached_answers(res, type, domain); if (cached) { *return_records = cached; return 0; } } c->ready = 0; c->resolver = res; c->return_records = return_records; c->query = sres_query(res, sres_blocking_callback, c, type, domain); return sres_blocking_complete(c); } /** Search DNS, return results. * * Search for @a name with specified @a type and @a name from the DNS server. * If the @a name does not contain enought dots, the search domains are * appended to the name and resulting domain name are also queried. * * @param res pointer to resolver object * @param type record type to search (or sres_qtype_any for any record) * @param name host or domain name to search from DNS * @param ignore_cache ignore cached answers if nonzero * @param return_records return-value parameter for dns records * * @retval >0 if query was responded * @retval 0 if result was found from cache * @retval -1 upon error * * @ERRORS * @ERROR EFAULT @a res or @a domain point outside the address space * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME * @ERROR ENETDOWN no DNS servers configured * @ERROR ENOMEM memory exhausted * @ERROR EOPNOTSUPP resolver @a res is in asynchronous mode * * @sa sres_blocking_query(), sres_search() * * @note A blocking query converts a resolver object permanently into * blocking mode. If you need to make blocking and non-blocking queries, use * sres_resolver_copy() to make a separate resolver object for blocking * queries. */ int sres_blocking_search(sres_resolver_t *res, uint16_t type, char const *name, int ignore_cache, sres_record_t ***return_records) { sres_blocking_context_t c[1]; sres_record_t **cached; if (return_records == NULL) return su_seterrno(EFAULT); *return_records = NULL; c->block = sres_set_blocking(res); if (c->block == NULL) return su_seterrno(EOPNOTSUPP); /* Resolver in asynchronous mode */ if (!ignore_cache) { cached = sres_search_cached_answers(res, type, name); if (cached) { *return_records = cached; return 0; } } c->ready = 0; c->resolver = res; c->return_records = return_records; c->query = sres_search(res, sres_blocking_callback, c, type, name); return sres_blocking_complete(c); } /** Send a a reverse DNS query, return results. * * Sends a reverse DNS query with specified @a type and @a domain to the DNS * server if @a ignore_cache is not given or no cached records are found from * the cache. Function returns an error record with nonzero status if no * response is received from DNS server. * * @retval >0 if query was responded * @retval 0 if result was found from cache * @retval -1 upon error * * @ERRORS * @ERROR EFAULT @a res or @a addr point outside the address space * @ERROR ENOMEM memory exhausted * @ERROR ENETDOWN no DNS servers configured * @ERROR EOPNOTSUPP resolver @a res is in asynchronous mode * * @sa sres_blocking_query(), sres_query_sockaddr(), sres_cached_answers_sockaddr() * * @note A blocking query converts a resolver object permanently into * blocking mode. If you need to make blocking and non-blocking queries, use * sres_resolver_copy() to make a separate resolver object for blocking * queries. */ int sres_blocking_query_sockaddr(sres_resolver_t *res, uint16_t type, struct sockaddr const *addr, int ignore_cache, sres_record_t ***return_records) { sres_blocking_context_t c[1]; sres_record_t **cached; if (return_records == NULL) return errno = EFAULT, -1; *return_records = NULL; c->block = sres_set_blocking(res); if (c->block == NULL) return su_seterrno(EOPNOTSUPP); /* Resolver in asynchronous mode */ if (!ignore_cache) { cached = sres_cached_answers_sockaddr(res, type, addr); if (cached) { *return_records = cached; return 0; } } c->ready = 0; c->resolver = res; c->return_records = return_records; c->query = sres_query_sockaddr(res, sres_blocking_callback, c, type, addr); return sres_blocking_complete(c); }