sres_blocking.c 10.3 KB
Newer Older
1 2 3
/*
 * This file is part of the Sofia-SIP package
 *
Pekka Pessi's avatar
Pekka Pessi committed
4
 * Copyright (C) 2006 Nokia Corporation.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * 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
 *
 */

Pekka Pessi's avatar
Pekka Pessi committed
25 26
/**@CFILE sres_blocking.c
 * @brief Blocking interface for Sofia DNS Resolver implementation.
27 28
 * 
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
29
 * @date Created: Fri Mar 24 15:23:08 EET 2006 ppessi
30 31 32 33 34 35 36 37
 */

#include "config.h"

#if HAVE_STDINT_H
#include <stdint.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
38 39 40 41 42 43 44
#else
#if defined(_WIN32)
typedef _int8 int8_t;
typedef unsigned _int8 uint8_t;
typedef unsigned _int16 uint16_t;
typedef unsigned _int32 uint32_t;
#endif
45
#endif
46

47
#if HAVE_NETINET_IN_H
Pekka Pessi's avatar
Pekka Pessi committed
48 49
#include <sys/types.h>
#include <sys/socket.h>
50 51
#include <netinet/in.h>
#endif
52

Pekka Pessi's avatar
Pekka Pessi committed
53 54 55
#if HAVE_WINSOCK2_H
#include <winsock2.h>
#include <ws2tcpip.h>
56
#define HAVE_SELECT 1
Pekka Pessi's avatar
Pekka Pessi committed
57
#endif
58 59 60 61 62 63 64 65

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"
66
#include "sofia-resolv/sres_async.h"
67 68 69

#if HAVE_POLL
#include <poll.h>
Pekka Pessi's avatar
Pekka Pessi committed
70
#elif HAVE_SYS_SELECT_H
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
#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 { int fd; } fds[SRES_MAX_NAMESERVERS];
#else
#error No wait mechanism!
#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,
			 int new_socket,
			 int old_socket)
{
  int i, N = b->n_sockets;

  if (old_socket == new_socket) {
    if (old_socket == -1) {
      free(b);      /* Destroy us */
    }
    return 0;
  }

  if (old_socket != -1) {
    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 = -1;
124 125
#if HAVE_POLL
    b->fds[i].events = b->fds[N].events;
126
    b->fds[N].events = 0;
127
#endif
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147

    b->n_sockets = N;
  }
  
  if (new_socket != -1) {
    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)
{
148
  while (!c->ready) {
149 150 151 152 153 154 155 156 157 158
    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++) {
159 160 161 162
      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);
163 164 165 166 167 168 169 170 171 172 173 174
    }
#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++) {
175 176 177 178
      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;
179 180
    }

181 182 183 184
    n = select(n, readfds, NULL, errorfds, timeval);
  
    if (n <= 0)
      sres_resolver_timer(c->resolver, -1);
185
    else for (i = 0; n > 0 && i < c->block->n_sockets; i++) {
186 187 188 189 190 191
      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;
192
      n--;
193
    }
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
#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;

219 220 221 222
  /* Check if resolver is already in asynchronous mode */
  if (sres_resolver_get_async(res, NULL))
    return NULL;

223 224 225 226 227 228 229 230 231 232 233 234 235
  /* Create a synchronous (blocking) interface towards resolver */
  b = calloc(1, sizeof *b);

  for (i = 0; i < SRES_MAX_NAMESERVERS; i++)
    b->fds[i].fd = -1;
  
  if (!sres_resolver_set_async(res, sres_blocking_update, b, 0)) {
    free(b), b = NULL;
  }

  return b;
}

Pekka Pessi's avatar
Pekka Pessi committed
236
/** Return true (and set resolver in blocking mode) if resolver can block. */
237 238 239 240 241 242 243
int sres_is_blocking(sres_resolver_t *res)
{
  if (res == NULL)
    return 0;
  return sres_set_blocking(res) != NULL;
}

Pekka Pessi's avatar
Pekka Pessi committed
244 245 246 247 248 249 250 251 252 253 254 255
/**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
256 257 258 259
 *
 * @retval >0 if query was responded
 * @retval 0 if result was found from cache
 * @retval -1 upon error
Pekka Pessi's avatar
Pekka Pessi committed
260 261 262 263 264 265 266
 *
 * @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.
267
 */
268 269 270
int sres_blocking_query(sres_resolver_t *res,
			uint16_t type,
			char const *domain,
Pekka Pessi's avatar
Pekka Pessi committed
271
			int ignore_cache,
272 273 274 275 276 277 278 279
			sres_record_t ***return_records)
{
  sres_blocking_context_t c[1];
  sres_record_t **cached;

  if (return_records == NULL)
    return errno = EFAULT, -1;

Pekka Pessi's avatar
Pekka Pessi committed
280 281
  *return_records = NULL;

282 283
  c->block = sres_set_blocking(res);
  if (c->block == NULL)
Pekka Pessi's avatar
Pekka Pessi committed
284
    return -1;			/* Resolver in asynchronous mode */ 
285

Pekka Pessi's avatar
Pekka Pessi committed
286 287 288 289 290 291
  if (!ignore_cache) {
    cached = sres_cached_answers(res, type, domain);
    if (cached) {
      *return_records = cached;
      return 0;
    }
292 293 294 295 296 297 298 299 300 301
  }

  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);
}

Pekka Pessi's avatar
Pekka Pessi committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
/** 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
 *
 * @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 errno = EFAULT, -1;

  *return_records = NULL;

  c->block = sres_set_blocking(res);
  if (c->block == NULL)
    return -1;			/* 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);
}

359
/** Send a a reverse DNS query, return results.
Pekka Pessi's avatar
Pekka Pessi committed
360 361 362 363 364
 *
 * 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.
365 366 367 368
 *
 * @retval >0 if query was responded
 * @retval 0 if result was found from cache
 * @retval -1 upon error
Pekka Pessi's avatar
Pekka Pessi committed
369 370 371 372 373 374 375
 *
 * @sa sres_blocking_query(), sres_query_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.
376
 */
377 378 379
int sres_blocking_query_sockaddr(sres_resolver_t *res,
				 uint16_t type,
				 struct sockaddr const *addr,
Pekka Pessi's avatar
Pekka Pessi committed
380
				 int ignore_cache,
381 382 383 384 385 386 387
				 sres_record_t ***return_records)
{
  sres_blocking_context_t c[1];
  sres_record_t **cached;

  if (return_records == NULL)
    return errno = EFAULT, -1;
Pekka Pessi's avatar
Pekka Pessi committed
388 389 390

  *return_records = NULL;

391 392
  c->block = sres_set_blocking(res);
  if (c->block == NULL)
Pekka Pessi's avatar
Pekka Pessi committed
393
    return -1;			/* Resolver in asynchronous mode */ 
394

Pekka Pessi's avatar
Pekka Pessi committed
395 396 397 398 399 400
  if (!ignore_cache) {
    cached = sres_cached_answers_sockaddr(res, type, addr);
    if (cached) {
      *return_records = cached;
      return 0;
    }
401 402 403 404 405 406 407 408 409
  }

  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);
}