forked from Mirrors/freeswitch
d8c4d22d40
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@10802 d0543943-73ff-0310-b7d9-9358b9ac24b2
447 lines
12 KiB
C
447 lines
12 KiB
C
/*
|
|
* This file is part of the Sofia-SIP package
|
|
*
|
|
* Copyright (C) 2006 Nokia Corporation.
|
|
*
|
|
* Contact: Pekka Pessi <pekka.pessi@nokia.com>
|
|
*
|
|
* 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 <Pekka.Pessi@nokia.com>
|
|
* @date Created: Fri Mar 24 15:23:08 EET 2006 ppessi
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#if HAVE_STDINT_H
|
|
#include <stdint.h>
|
|
#elif HAVE_INTTYPES_H
|
|
#include <inttypes.h>
|
|
#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 <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#endif
|
|
|
|
#if HAVE_WINSOCK2_H
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#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 <sofia-sip/su_errno.h>
|
|
|
|
#if HAVE_POLL
|
|
#include <poll.h>
|
|
#elif HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
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;
|
|
|
|
if (b == NULL)
|
|
return -1;
|
|
|
|
if (old_socket == new_socket) {
|
|
if (old_socket == INVALID_SOCKET) {
|
|
free(b); /* Destroy us */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
N = b->n_sockets;
|
|
|
|
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);
|
|
|
|
if (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);
|
|
}
|