sres.c 109 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
 * Copyright (C) 2006 Dimitri E. Prado.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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
26 27
/**@CFILE sres.c
 * @brief Sofia DNS Resolver implementation.
28
 *
29 30
 * @author Pekka Pessi <Pekka.Pessi@nokia.com>
 * @author Teemu Jalava <Teemu.Jalava@nokia.com>
Pekka Pessi's avatar
Pekka Pessi committed
31
 * @author Mikko Haataja
32 33 34
 * @author Kai Vehmanen <kai.vehmanen@nokia.com>
 *         (work on the win32 nameserver discovery)
 * @author Dimitri E. Prado
35
 *         (initial version of win32 nameserver discovery)
36
 *
Pekka Pessi's avatar
Pekka Pessi committed
37
 * @todo The resolver should allow handling arbitrary records, too.
38 39 40 41 42 43 44 45
 */

#include "config.h"

#if HAVE_STDINT_H
#include <stdint.h>
#elif HAVE_INTTYPES_H
#include <inttypes.h>
46
#else
47
#if defined(HAVE_WIN32)
48 49 50 51 52
typedef _int8 int8_t;
typedef unsigned _int8 uint8_t;
typedef unsigned _int16 uint16_t;
typedef unsigned _int32 uint32_t;
#endif
53
#endif
54

55
#if HAVE_NETINET_IN_H
Pekka Pessi's avatar
Pekka Pessi committed
56 57
#include <sys/types.h>
#include <sys/socket.h>
58 59
#include <netinet/in.h>
#endif
60

61 62 63 64
#if HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif

Pekka Pessi's avatar
Pekka Pessi committed
65 66 67
#if HAVE_WINSOCK2_H
#include <winsock2.h>
#include <ws2tcpip.h>
Pekka Pessi's avatar
Pekka Pessi committed
68 69 70
#ifndef IPPROTO_IPV6		/* socklen_t is used with @RFC2133 API */
typedef int socklen_t;
#endif
Michael Jerris's avatar
Michael Jerris committed
71 72
#endif

73 74 75 76
#if HAVE_IPHLPAPI_H
#include <iphlpapi.h>
#endif

77 78 79 80 81 82
#if HAVE_IP_RECVERR || HAVE_IPV6_RECVERR
#include <linux/types.h>
#include <linux/errqueue.h>
#include <sys/uio.h>
#endif

Michael Jerris's avatar
Michael Jerris committed
83 84 85 86 87 88
#include <time.h>

#include "sofia-resolv/sres.h"
#include "sofia-resolv/sres_cache.h"
#include "sofia-resolv/sres_record.h"
#include "sofia-resolv/sres_async.h"
89

Michael Jerris's avatar
Michael Jerris committed
90 91
#include <sofia-sip/su_alloc.h>
#include <sofia-sip/su_strlst.h>
92
#include <sofia-sip/su_string.h>
Michael Jerris's avatar
Michael Jerris committed
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
#include <sofia-sip/su_errno.h>

#include "sofia-sip/htable.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

#include <limits.h>

#include <assert.h>

#if HAVE_WINSOCK2_H
114
/* Posix send() */
115
su_inline
116
ssize_t sres_send(sres_socket_t s, void *b, size_t length, int flags)
117
{
118 119 120
  if (length > INT_MAX)
    length = INT_MAX;
  return (ssize_t)send(s, b, (int)length, flags);
121 122 123
}

/* Posix recvfrom() */
124
su_inline
125
ssize_t sres_recvfrom(sres_socket_t s, void *buffer, size_t length, int flags,
126 127
		      struct sockaddr *from, socklen_t *fromlen)
{
128 129
  int retval, ilen;

130 131
  if (fromlen)
    ilen = *fromlen;
132 133 134 135

  if (length > INT_MAX)
    length = INT_MAX;

136
  retval = recvfrom(s, buffer, (int)length, flags,
137 138
		    (void *)from, fromlen ? &ilen : NULL);

139 140
  if (fromlen)
    *fromlen = ilen;
141

142 143 144
  return (ssize_t)retval;
}

145
su_inline
146 147 148 149 150
int sres_close(sres_socket_t s)
{
  return closesocket(s);
}

Michael Jerris's avatar
Michael Jerris committed
151
#if !defined(IPPROTO_IPV6) && (_WIN32_WINNT < 0x0600)
152 153 154
#if HAVE_SIN6
#include <tpipv6.h>
#else
155
#if !defined(__MINGW32__)
156 157 158 159
struct sockaddr_storage {
    short ss_family;
    char ss_pad[126];
};
Pekka Pessi's avatar
Pekka Pessi committed
160
#endif
161
#endif
162
#endif
163
#else
164 165 166 167

#define sres_send(s,b,len,flags) send((s),(b),(len),(flags))
#define sres_recvfrom(s,b,len,flags,a,alen) \
  recvfrom((s),(b),(len),(flags),(a),(alen))
168
#define sres_close(s) close((s))
169 170 171 172
#define SOCKET_ERROR   (-1)
#define INVALID_SOCKET ((sres_socket_t)-1)
#endif

173
#define SRES_TIME_MAX ((time_t)LONG_MAX)
174 175

#if !HAVE_INET_PTON
Michael Jerris's avatar
Michael Jerris committed
176 177 178
int su_inet_pton(int af, char const *src, void *dst);
#else
#define su_inet_pton inet_pton
179 180
#endif
#if !HAVE_INET_NTOP
Michael Jerris's avatar
Michael Jerris committed
181 182 183
const char *su_inet_ntop(int af, void const *src, char *dst, size_t size);
#else
#define su_inet_ntop inet_ntop
184 185 186 187 188 189 190 191 192
#endif

#if defined(va_copy)
#elif defined(__va_copy)
#define va_copy(dst, src) __va_copy((dst), (src))
#else
#define va_copy(dst, src) (memcpy(&(dst), &(src), sizeof (va_list)))
#endif

193 194 195 196 197 198 199
/*
 * 3571 is a prime =>
 * we hash successive id values to different parts of hash tables
 */
#define Q_PRIME 3571
#define SRES_QUERY_HASH(q) ((q)->q_hash)

200 201 202
/**
 * How often to recheck nameserver information (seconds).
 */
203
#ifndef HAVE_WIN32
204 205
#define SRES_UPDATE_INTERVAL_SECS        5
#else
206
#define SRES_UPDATE_INTERVAL_SECS        180
207
#endif
208 209 210 211 212 213 214
void sres_cache_clean(sres_cache_t *cache, time_t now);

typedef struct sres_message    sres_message_t;
typedef struct sres_config     sres_config_t;
typedef struct sres_server     sres_server_t;
typedef struct sres_nameserver sres_nameserver_t;

215 216 217
/** Default path to resolv.conf */
static char const sres_conf_file_path[] = "/etc/resolv.conf";

218
/** EDNS0 support. @internal */
219
enum edns {
220 221
  edns_not_tried = -1,
  edns_not_supported = 0,
222 223
  edns0_configured = 1,
  edns0_supported = 2,
224 225 226
};

struct sres_server {
227
  sres_socket_t           dns_socket;
228 229 230 231 232 233 234 235 236

  char                    dns_name[48];     /**< Server name */
  struct sockaddr_storage dns_addr[1];  /**< Server node address */
  ssize_t                 dns_addrlen;  /**< Size of address */

  enum edns               dns_edns;	/**< Server supports edns. */

  /** ICMP/temporary error received, zero when successful. */
  time_t                  dns_icmp;
237
  /** Persistent error, zero when successful or timeout.
238 239 240
   *
   * Never selected if dns_error is SRES_TIME_MAX.
   */
241 242 243
  time_t                  dns_error;
};

244
HTABLE_DECLARE_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t);
245 246 247 248 249 250 251 252 253 254

struct sres_resolver_s {
  su_home_t           res_home[1];

  void               *res_userdata;
  sres_cache_t       *res_cache;

  time_t              res_now;
  sres_qtable_t       res_queries[1];   /**< Table of active queries */

255
  char const         *res_cnffile;      /**< Configuration file name */
256
  char const        **res_options;      /**< Option strings */
257

258
  sres_config_t const *res_config;
259 260 261 262 263
  time_t              res_checked;

  unsigned long       res_updated;
  sres_update_f      *res_updcb;
  sres_async_t       *res_async;
264
  sres_schedule_f    *res_schedulecb;
265 266 267
  short               res_update_all;

  uint16_t            res_id;
268
  short               res_i_server;  /**< Current server to try
269 270 271 272 273
					(when doing round-robin) */
  short               res_n_servers; /**< Number of servers */
  sres_server_t     **res_servers;
};

274
/* Parsed configuration. @internal */
275 276 277 278 279 280
struct sres_config {
  su_home_t c_home[1];

  time_t c_modified;
  char const *c_filename;

281
  /* domain and search */
Pekka Pessi's avatar
Pekka Pessi committed
282
  char const *c_search[SRES_MAX_SEARCH + 1];
283

284
  /* nameserver */
285 286 287 288 289
  struct sres_nameserver {
    struct sockaddr_storage ns_addr[1];
    ssize_t ns_addrlen;
  } *c_nameservers[SRES_MAX_NAMESERVERS + 1];

290
  /* sortlist */
291 292 293 294 295 296 297 298
  struct sres_sortlist {
    struct sockaddr_storage addr[1];
    ssize_t addrlen;
    char const *name;
  } *c_sortlist[SRES_MAX_SORTLIST + 1];

  uint16_t    c_port;	     /**< Server port to use */

299
  /* options */
300 301 302 303
  struct sres_options {
    uint16_t timeout;
    uint16_t attempts;
    uint16_t ndots;
304
    enum edns edns;
305 306 307 308 309 310 311 312 313 314
    unsigned debug:1;
    unsigned rotate:1;
    unsigned check_names:1;
    unsigned inet6:1;
    unsigned ip6int:1;
    unsigned ip6bytestring:1;
  } c_opt;
};

struct sres_query_s {
315
  unsigned        q_hash;
316 317 318 319 320 321 322 323 324 325 326
  sres_resolver_t*q_res;
  sres_answer_f  *q_callback;
  sres_context_t *q_context;
  char           *q_name;
  time_t          q_timestamp;
  uint16_t        q_type;
  uint16_t        q_class;
  uint16_t        q_id;			/**< If nonzero, not answered */
  uint16_t        q_retry_count;
  uint8_t         q_n_servers;
  uint8_t         q_i_server;
327
  int8_t          q_aliased;
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
  int8_t          q_edns;
  uint8_t         q_n_subs;
  sres_query_t   *q_subqueries[1 + SRES_MAX_SEARCH];
  sres_record_t **q_subanswers[1 + SRES_MAX_SEARCH];
};


struct sres_message {
  uint16_t m_offset;
  uint16_t m_size;
  char const *m_error;
  union {
    struct {
      /* Header defined in RFC 1035 section 4.1.1 (page 26) */
      uint16_t mh_id;		/* Query ID */
      uint16_t mh_flags;	/* Flags */
      uint16_t mh_qdcount;	/* Question record count */
      uint16_t mh_ancount;	/* Answer record count */
      uint16_t mh_nscount;	/* Authority records count */
      uint16_t mh_arcount;	/* Additional records count */
    } mp_header;
    uint8_t mp_data[1500 - 40];	/**< IPv6 datagram */
  } m_packet;
#define m_id      m_packet.mp_header.mh_id
#define m_flags   m_packet.mp_header.mh_flags
#define m_qdcount m_packet.mp_header.mh_qdcount
#define m_ancount m_packet.mp_header.mh_ancount
#define m_nscount m_packet.mp_header.mh_nscount
356
#define m_arcount m_packet.mp_header.mh_arcount
357 358 359 360 361 362 363 364 365 366 367
#define m_data    m_packet.mp_data
};

#define sr_refcount sr_record->r_refcount
#define sr_name     sr_record->r_name
#define sr_status   sr_record->r_status
#define sr_size     sr_record->r_size
#define sr_type     sr_record->r_type
#define sr_class    sr_record->r_class
#define sr_ttl      sr_record->r_ttl
#define sr_rdlen    sr_record->r_rdlen
368
#define sr_parsed   sr_record->r_parsed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
#define sr_rdata    sr_generic->g_data

enum {
  SRES_HDR_QR = (1 << 15),
  SRES_HDR_QUERY = (0 << 11),
  SRES_HDR_IQUERY = (1 << 11),
  SRES_HDR_STATUS = (2 << 11),
  SRES_HDR_OPCODE = (15 << 11),	/* mask */
  SRES_HDR_AA = (1 << 10),
  SRES_HDR_TC = (1 << 9),
  SRES_HDR_RD = (1 << 8),
  SRES_HDR_RA = (1 << 7),
  SRES_HDR_RCODE = (15 << 0)	/* mask of return code */
};

384
HTABLE_PROTOS_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t);
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402

#define CHOME(cache) ((su_home_t *)(cache))

/** Get address from sockaddr storage. */
#if HAVE_SIN6
#define SS_ADDR(ss) \
  ((ss)->ss_family == AF_INET ? \
   (void *)&((struct sockaddr_in *)ss)->sin_addr : \
  ((ss)->ss_family == AF_INET6 ? \
   (void *)&((struct sockaddr_in6 *)ss)->sin6_addr : \
   (void *)&((struct sockaddr *)ss)->sa_data))
#else
#define SS_ADDR(ss) \
  ((ss)->ss_family == AF_INET ? \
   (void *)&((struct sockaddr_in *)ss)->sin_addr : \
   (void *)&((struct sockaddr *)ss)->sa_data)
#endif

403
static int sres_config_changed_servers(sres_config_t const *new_c,
404
				       sres_config_t const *old_c);
405
static sres_server_t **sres_servers_new(sres_resolver_t *res,
406
					sres_config_t const *c);
407

408
/** Generate new 16-bit identifier for DNS query. */
409 410
static void
sres_gen_id(sres_resolver_t *res, sres_query_t *query)
411
{
412 413 414 415 416
  if (res->res_id == 0) {
    res->res_id = 1;
  }
  query->q_id = res->res_id++;
  query->q_hash = query->q_id * Q_PRIME;
417 418 419
}

/** Return true if we have a search list or a local domain name. */
420 421
static int
sres_has_search_domain(sres_resolver_t *res)
422 423 424 425 426 427
{
  return res->res_config->c_search[0] != NULL;
}

static void sres_resolver_destructor(void *);

428 429 430
sres_resolver_t *
sres_resolver_new_with_cache_va(char const *conf_file_path,
				sres_cache_t *cache,
431
				char const *options,
432 433 434
				va_list va);
static
sres_resolver_t *
435 436 437
sres_resolver_new_internal(sres_cache_t *cache,
			   sres_config_t const *config,
			   char const *conf_file_path,
438
			   char const **options);
439

440
static void sres_servers_close(sres_resolver_t *res,
441 442 443 444
			       sres_server_t **servers);

static int sres_servers_count(sres_server_t * const *servers);

445 446
static sres_socket_t sres_server_socket(sres_resolver_t *res,
					sres_server_t *dns);
447 448 449 450 451 452 453 454 455

static sres_query_t * sres_query_alloc(sres_resolver_t *res,
				       sres_answer_f *callback,
				       sres_context_t *context,
				       uint16_t type,
				       char const * domain);

static void sres_free_query(sres_resolver_t *res, sres_query_t *q);

456 457 458
static
int sres_sockaddr2string(sres_resolver_t *,
			 char name[], size_t namelen,
459 460
			 struct sockaddr const *);

461
static
462 463
sres_config_t *sres_parse_resolv_conf(sres_resolver_t *res,
				      char const **options);
464 465

static
466
sres_server_t *sres_next_server(sres_resolver_t *res,
Pekka Pessi's avatar
Pekka Pessi committed
467
				uint8_t *in_out_i,
468
				int always);
469 470 471 472

static
int sres_send_dns_query(sres_resolver_t *res, sres_query_t *q);

473 474
static
void sres_answer_subquery(sres_context_t *context,
475 476 477
			  sres_query_t *query,
			  sres_record_t **answers);

Pekka Pessi's avatar
Pekka Pessi committed
478 479 480 481 482
static
sres_record_t **
sres_combine_results(sres_resolver_t *res,
		     sres_record_t **search_results[SRES_MAX_SEARCH + 1]);

483 484 485 486 487 488 489
static
void sres_query_report_error(sres_query_t *q,
			     sres_record_t **answers);

static void
sres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout);

490
static
491 492
sres_server_t *sres_server_by_socket(sres_resolver_t const *ts,
				     sres_socket_t socket);
493 494

static
495
int sres_resolver_report_error(sres_resolver_t *res,
496
			       sres_socket_t socket,
497 498
			       int errcode,
			       struct sockaddr_storage *remote,
499
			       socklen_t remotelen,
500 501 502
			       char const *info);

static
503
void sres_log_response(sres_resolver_t const *res,
504 505 506 507 508
		       sres_message_t const *m,
		       struct sockaddr_storage const *from,
		       sres_query_t const *query,
		       sres_record_t * const *reply);

509
static int sres_decode_msg(sres_resolver_t *res,
510 511 512 513 514 515
			   sres_message_t *m,
			   sres_query_t **,
			   sres_record_t ***aanswers);

static char const *sres_toplevel(char buf[], size_t bsize, char const *domain);

516 517 518
static sres_record_t *sres_create_record(sres_resolver_t *,
					 sres_message_t *m,
					 int nth);
519

520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
static sres_record_t *sres_init_rr_soa(sres_cache_t *cache,
				       sres_soa_record_t *,
				       sres_message_t *m);
static sres_record_t *sres_init_rr_a(sres_cache_t *cache,
				     sres_a_record_t *,
				     sres_message_t *m);
static sres_record_t *sres_init_rr_a6(sres_cache_t *cache,
				      sres_a6_record_t *,
				      sres_message_t *m);
static sres_record_t *sres_init_rr_aaaa(sres_cache_t *cache,
					sres_aaaa_record_t *,
					sres_message_t *m);
static sres_record_t *sres_init_rr_cname(sres_cache_t *cache,
					 sres_cname_record_t *,
					 sres_message_t *m);
static sres_record_t *sres_init_rr_ptr(sres_cache_t *cache,
				       sres_ptr_record_t *,
				       sres_message_t *m);
static sres_record_t *sres_init_rr_srv(sres_cache_t *cache,
				       sres_srv_record_t *,
				       sres_message_t *m);
static sres_record_t *sres_init_rr_naptr(sres_cache_t *cache,
					 sres_naptr_record_t *,
					 sres_message_t *m);
static sres_record_t *sres_init_rr_unknown(sres_cache_t *cache,
					   sres_common_t *r,
					   sres_message_t *m);

static sres_record_t *sres_create_error_rr(sres_cache_t *cache,
549 550 551 552 553 554
                                           sres_query_t const *q,
                                           uint16_t errcode);

static void m_put_uint16(sres_message_t *m, uint16_t h);
static void m_put_uint32(sres_message_t *m, uint32_t w);

555 556 557
static uint16_t m_put_domain(sres_message_t *m,
                             char const *domain,
                             uint16_t top,
558 559 560 561 562
                             char const *topdomain);

static uint32_t m_get_uint32(sres_message_t *m);
static uint16_t m_get_uint16(sres_message_t *m);
static uint8_t m_get_uint8(sres_message_t *m);
563

564 565
static unsigned m_get_string(char *d, unsigned n, sres_message_t *m, uint16_t offset);
static unsigned m_get_domain(char *d, unsigned n, sres_message_t *m, uint16_t offset);
566 567 568 569 570 571 572

/* ---------------------------------------------------------------------- */

#define SU_LOG sresolv_log

#include <sofia-sip/su_debug.h>

573
#ifdef HAVE_WIN32
574 575 576
#include <winreg.h>
#endif

577
/**@ingroup sresolv_env
578 579 580 581 582 583
 *
 * Environment variable determining the debug log level for @b sresolv
 * module.
 *
 * The SRESOLV_DEBUG environment variable is used to determine the debug
 * logging level for @b sresolv module. The default level is 3.
584
 *
585
 * @sa <sofia-sip/su_debug.h>, sresolv_log, SOFIA_DEBUG
586
 */
Michael Jerris's avatar
Michael Jerris committed
587 588
#ifdef DOXYGEN
extern char const SRESOLV_DEBUG[]; /* dummy declaration for Doxygen */
589
#endif
590

591 592 593 594
#ifndef SU_DEBUG
#define SU_DEBUG 3
#endif

595 596
/**Debug log for @b sresolv module.
 *
597 598 599
 * The sresolv_log is the log object used by @b sresolv module. The level of
 * #sresolv_log is set using #SRESOLV_DEBUG environment variable.
 */
600
su_log_t sresolv_log[] = { SU_LOG_INIT("sresolv", "SRESOLV_DEBUG", SU_DEBUG) };
601 602 603 604 605 606 607 608 609 610

/** Internal errors */
enum {
  SRES_EDNS0_ERR = 255		/**< Server did not support EDNS. */
};

/* ---------------------------------------------------------------------- */

/**Create a resolver.
 *
611 612 613 614 615
 * Allocate and initialize a new sres resolver object. The resolver object
 * contains the parsed resolv.conf file, a cache object containing past
 * answers from DNS, and a list of active queries. The default resolv.conf
 * file can be overriden by giving the name of the configuration file as @a
 * conf_file_path.
616
 *
617
 * @param conf_file_path name of the resolv.conf configuration file
618
 *
619 620 621 622 623 624
 * @return A pointer to a newly created sres resolver object, or NULL upon
 * an error.
 */
sres_resolver_t *
sres_resolver_new(char const *conf_file_path)
{
625
  return sres_resolver_new_internal(NULL, NULL, conf_file_path, NULL);
626 627 628 629
}

/** Copy a resolver.
 *
630 631
 * Make a copy of resolver sharing the configuration and cache with old
 * resolver.
632 633 634 635
 */
sres_resolver_t *sres_resolver_copy(sres_resolver_t *res)
{
  char const *cnffile;
636
  sres_config_t *config;
637 638 639 640 641 642 643
  sres_cache_t *cache;
  char const **options;

  if (!res)
    return NULL;

  cnffile = res->res_cnffile;
644
  config = su_home_ref(res->res_config->c_home);
645 646 647
  cache = res->res_cache;
  options = res->res_options;

648
  return sres_resolver_new_internal(cache, config, cnffile, options);
649 650
}

651
/**New resolver object.
652 653 654 655 656 657 658 659 660 661
 *
 * Allocate and initialize a new sres resolver object. The resolver object
 * contains the parsed resolv.conf file, a cache object containing past
 * answers from DNS, and a list of active queries. The default resolv.conf
 * file can be overriden by giving the name of the configuration file as @a
 * conf_file_path.
 *
 * It is also possible to override the values in the resolv.conf and
 * RES_OPTIONS by giving the directives in the NULL-terminated list.
 *
662
 * @param conf_file_path name of the resolv.conf configuration file
663
 * @param cache          optional pointer to a resolver cache (may be NULL)
664
 * @param option, ...    list of resolv.conf options directives
665
 *                       (overriding options in conf_file)
666 667
 *
 * @par Environment Variables
668
 * - #LOCALDOMAIN overrides @c domain or @c search directives
669 670
 * - #RES_OPTIONS overrides values of @a options in resolv.conf
 * - #SRES_OPTIONS overrides values of @a options in resolv.conf, #RES_OPTIONS,
671 672 673 674 675 676 677 678
 *   and @a options, ... list given as argument for this function
 *
 * @return A pointer to a newly created sres resolver object, or NULL upon
 * an error.
 */
sres_resolver_t *
sres_resolver_new_with_cache(char const *conf_file_path,
			     sres_cache_t *cache,
679
			     char const *option, ...)
680 681 682
{
  sres_resolver_t *retval;
  va_list va;
683 684
  va_start(va, option);
  retval = sres_resolver_new_with_cache_va(conf_file_path, cache, option, va);
685 686 687 688 689 690
  va_end(va);
  return retval;
}

/**Create a resolver.
 *
691
 * Allocate and initialize a new sres resolver object.
692 693
 *
 * This is a stdarg version of sres_resolver_new_with_cache().
694 695
 */
sres_resolver_t *
696 697
sres_resolver_new_with_cache_va(char const *conf_file_path,
				sres_cache_t *cache,
698
				char const *option,
699
				va_list va)
700
{
701 702 703
  va_list va0;
  size_t i;
  char const *o, *oarray[16], **olist = oarray;
704 705
  sres_resolver_t *res;

706
  va_copy(va0, va);
707

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
  for (i = 0, o = option; o; o = va_arg(va0, char const *)) {
    if (i < 16)
      olist[i] = o;
    i++;
  }

  if (i >= 16) {
    olist = malloc((i + 1) * sizeof *olist);
    if (!olist)
      return NULL;
    for (i = 0, o = option; o; o = va_arg(va, char const *)) {
      olist[i++] = o;
      i++;
    }
  }
  olist[i] = NULL;
724
  res = sres_resolver_new_internal(cache, NULL, conf_file_path, olist);
725 726 727
  if (olist != oarray)
    free(olist);

728 729
  va_end(va0);

730 731 732 733
  return res;
}

sres_resolver_t *
734 735 736
sres_resolver_new_internal(sres_cache_t *cache,
			   sres_config_t const *config,
			   char const *conf_file_path,
737 738 739 740 741
			   char const **options)
{
  sres_resolver_t *res;
  size_t i, n, len;
  char **array, *o, *end;
742

743 744 745 746
  for (n = 0, len = 0; options && options[n]; n++)
    len += strlen(options[n]) + 1;

  res = su_home_new(sizeof(*res) + (n + 1) * (sizeof *options) + len);
747 748 749 750

  if (res == NULL)
    return NULL;

751 752 753 754 755 756 757 758
  array = (void *)(res + 1);
  o = (void *)(array + n + 1);
  end = o + len;

  for (i = 0; options && options[i]; i++)
    o = memccpy(array[i] = o, options[i], '\0', len - (end - o));
  assert(o == end);

759
  su_home_destructor(res->res_home, sres_resolver_destructor);
760 761 762

  while (res->res_id == 0) {
#if HAVE_DEV_URANDOM
763
    int fd;
764
    if ((fd = open("/dev/urandom", O_RDONLY, 0)) != -1) {
765
      size_t len = read(fd, &res->res_id, (sizeof res->res_id)); (void)len;
766 767 768 769 770 771 772 773 774
      close(fd);
    }
    else
#endif
    res->res_id = time(NULL);
  }

  time(&res->res_now);

775 776 777 778
  if (cache)
    res->res_cache = sres_cache_ref(cache);
  else
    res->res_cache = sres_cache_new(0);
779

780 781 782
  res->res_config = config;

  if (conf_file_path && conf_file_path != sres_conf_file_path)
783 784
    res->res_cnffile = su_strdup(res->res_home, conf_file_path);
  else
785
    res->res_cnffile = conf_file_path = sres_conf_file_path;
786 787 788 789 790 791 792

  if (!res->res_cache || !res->res_cnffile) {
    perror("sres: malloc");
  }
  else if (sres_qtable_resize(res->res_home, res->res_queries, 0) < 0) {
    perror("sres: res_qtable_resize");
  }
793
  else if (sres_resolver_update(res, config == NULL) < 0) {
794
    perror("sres: sres_resolver_update");
795 796 797 798 799 800 801 802 803 804
  }
  else {
    return res;
  }

  sres_resolver_unref(res);

  return NULL;
}

805
/** Increase reference count on a resolver object. */
806 807 808 809 810
sres_resolver_t *
sres_resolver_ref(sres_resolver_t *res)
{
  return su_home_ref(res->res_home);
}
811

812 813 814 815 816 817 818 819 820 821
/** Decrease the reference count on a resolver object.  */
void
sres_resolver_unref(sres_resolver_t *res)
{
  su_home_unref(res->res_home);
}

/** Set userdata pointer.
 *
 * @return New userdata pointer.
822
 *
823 824 825 826
 * @ERRORS
 * @ERROR EFAULT @a res points outside the address space
 */
void *
827
sres_resolver_set_userdata(sres_resolver_t *res,
828 829
			   void *userdata)
{
830 831
  void *old;

832 833 834
  if (!res)
    return su_seterrno(EFAULT), (void *)NULL;

835 836 837
  old = res->res_userdata, res->res_userdata = userdata;

  return old;
838 839 840 841 842
}

/**Get userdata pointer.
 *
 * @return Userdata pointer.
843
 *
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
 * @ERRORS
 * @ERROR EFAULT @a res points outside the address space
 */
void *
sres_resolver_get_userdata(sres_resolver_t const *res)
{
  if (res == NULL)
    return su_seterrno(EFAULT), (void *)NULL;
  else
    return res->res_userdata;
}

/** Set async object.
 *
 * @return Set async object.
859
 *
860 861 862 863 864 865 866
 * @ERRORS
 * @ERROR EFAULT @a res points outside the address space
 * @ERROR EALREADY different async callback already set
 */
sres_async_t *
sres_resolver_set_async(sres_resolver_t *res,
			sres_update_f *callback,
867
			sres_async_t *async,
868 869 870 871 872 873 874
			int update_all)
{
  if (!res)
    return su_seterrno(EFAULT), (void *)NULL;

  if (res->res_updcb && res->res_updcb != callback)
    return su_seterrno(EALREADY), (void *)NULL;
875

876 877
  res->res_async = async;
  res->res_updcb = callback;
878
  res->res_update_all = callback && update_all != 0;
879 880 881 882

  return async;
}

883
/** Get async object */
884 885 886 887
sres_async_t *
sres_resolver_get_async(sres_resolver_t const *res,
			sres_update_f *callback)
{
888
  if (res == NULL)
889
    return su_seterrno(EFAULT), (void *)NULL;
890 891 892 893
  else if (callback == NULL)
    return res->res_async ? (sres_async_t *)-1 : 0;
  else if (res->res_updcb != callback)
    return NULL;
894 895 896
  else
    return res->res_async;
}
897

898 899 900 901 902 903 904 905 906 907 908 909 910 911
/** Register resolver timer callback. */
int sres_resolver_set_timer_cb(sres_resolver_t *res,
			       sres_schedule_f *callback,
			       sres_async_t *async)
{
  if (res == NULL)
    return su_seterrno(EFAULT);
  if (res->res_async != async)
    return su_seterrno(EALREADY);

  res->res_schedulecb = callback;
  return 0;
}

Pekka Pessi's avatar
Pekka Pessi committed
912
/**Send a DNS query.
913
 *
914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931
 * Sends a DNS query with specified @a type and @a domain to the DNS server.
 * When an answer is received, the @a callback function is called with
 * @a context and returned records as arguments.
 *
 * The sres resolver takes care of retransmitting the query if a root object
 * is associate with the resolver or if sres_resolver_timer() is called in
 * regular intervals. It generates an error record with nonzero status if no
 * response is received.
 *
 * @param res pointer to resolver
 * @param callback function called when query is answered or times out
 * @param context pointer given as an extra argument to @a callback function
 * @param type record type to query (see #sres_qtypes)
 * @param domain name to query
 *
 * Query types also indicate the record type of the result.
 * Any record can be queried with #sres_qtype_any.
 * Well-known query types understood and decoded by @b sres include
932
 * #sres_type_a,
933 934
 * #sres_type_aaaa,
 * #sres_type_cname,
935 936 937
 * #sres_type_ptr
 * #sres_type_soa,
 * #sres_type_aaaa,
938 939 940
 * #sres_type_srv, and
 * #sres_type_naptr.
 *
941
 * Deprecated query type #sres_type_a6 is also decoded.
942
 *
943 944
 * @note The domain name is @b not concatenated with the domains from seach
 * path or with the local domain. Use sres_search() in order to try domains
945 946
 * in search path.
 *
947 948
 * @sa sres_search(), sres_blocking_query(), sres_cached_answers(),
 * sres_query_sockaddr()
Pekka Pessi's avatar
Pekka Pessi committed
949 950 951 952
 *
 * @ERRORS
 * @ERROR EFAULT @a res or @a domain point outside the address space
 * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME
953
 * @ERROR ENETDOWN no DNS servers configured
Pekka Pessi's avatar
Pekka Pessi committed
954
 * @ERROR ENOMEM memory exhausted
955 956
 */
sres_query_t *
957 958 959 960 961
sres_query(sres_resolver_t *res,
	   sres_answer_f *callback,
	   sres_context_t *context,
	   uint16_t type,
	   char const *domain)
962 963 964
{
  sres_query_t *query = NULL;
  size_t dlen;
965

Pekka Pessi's avatar
Pekka Pessi committed
966
  char b[8];
967 968
  SU_DEBUG_9(("sres_query(%p, %p, %s, \"%s\") called\n",
			  (void *)res, (void *)context, sres_record_type(type, b), domain));
969 970 971 972 973 974 975 976 977 978 979

  if (res == NULL || domain == NULL)
    return su_seterrno(EFAULT), (void *)NULL;

  dlen = strlen(domain);
  if (dlen > SRES_MAXDNAME ||
      (dlen == SRES_MAXDNAME && domain[dlen - 1] != '.')) {
    su_seterrno(ENAMETOOLONG);
    return NULL;
  }

Pekka Pessi's avatar
Pekka Pessi committed
980 981 982
  /* Reread resolv.conf if needed */
  sres_resolver_update(res, 0);

983 984 985
  if (res->res_n_servers == 0)
    return (void)su_seterrno(ENETDOWN), (sres_query_t *)NULL;

Pekka Pessi's avatar
Pekka Pessi committed
986 987 988 989 990 991 992 993 994 995
  query = sres_query_alloc(res, callback, context, type, domain);

  if (query && sres_send_dns_query(res, query) != 0)
    sres_free_query(res, query), query = NULL;

  return query;
}

/**Search DNS.
 *
996
 * Sends DNS queries with specified @a type and @a name to the DNS server.
Pekka Pessi's avatar
Pekka Pessi committed
997
 * If the @a name does not contain enought dots, the search domains are
998 999 1000
 * appended to the name and resulting domain name are also queried. When
 * answer to all the search domains is received, the @a callback function
 * is called with @a context and combined records from answers as arguments.
1001
 *
1002 1003 1004 1005
 * The sres resolver takes care of retransmitting the queries if a root
 * object is associate with the resolver or if sres_resolver_timer() is
 * called in regular intervals. It generates an error record with nonzero
 * status if no response is received.
Pekka Pessi's avatar
Pekka Pessi committed
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015
 *
 * @param res pointer to resolver object
 * @param callback pointer to completion function
 * @param context argument given to the completion function
 * @param type record type to search (or sres_qtype_any for any record)
 * @param name host or domain name to search from DNS
 *
 * @ERRORS
 * @ERROR EFAULT @a res or @a domain point outside the address space
 * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME
1016
 * @ERROR ENETDOWN no DNS servers configured
Pekka Pessi's avatar
Pekka Pessi committed
1017 1018
 * @ERROR ENOMEM memory exhausted
 *
1019
 * @sa sres_query(), sres_blocking_search(), sres_search_cached_answers().
Pekka Pessi's avatar
Pekka Pessi committed
1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
 */
sres_query_t *
sres_search(sres_resolver_t *res,
	    sres_answer_f *callback,
	    sres_context_t *context,
	    uint16_t type,
	    char const *name)
{
  char const *domain = name;
  sres_query_t *query = NULL;
  size_t dlen;
  unsigned dots; char const *dot;
  char b[8];

1034 1035
  SU_DEBUG_9(("sres_search(%p, %p, %s, \"%s\") called\n",
			  (void *)res, (void *)context, sres_record_type(type, b), domain));
Pekka Pessi's avatar
Pekka Pessi committed
1036 1037 1038 1039 1040 1041 1042 1043 1044 1045

  if (res == NULL || domain == NULL)
    return su_seterrno(EFAULT), (void *)NULL;

  dlen = strlen(domain);
  if (dlen > SRES_MAXDNAME ||
      (dlen == SRES_MAXDNAME && domain[dlen - 1] != '.')) {
    su_seterrno(ENAMETOOLONG);
    return NULL;
  }
1046

1047
  sres_resolver_update(res, 0);
1048

1049 1050 1051
  if (res->res_n_servers == 0)
    return (void)su_seterrno(ENETDOWN), (sres_query_t *)NULL;

1052 1053 1054 1055
  if (domain[dlen - 1] == '.')
    /* Domain ends with dot - do not search */
    dots = res->res_config->c_opt.ndots;
  else if (sres_has_search_domain(res))
Pekka Pessi's avatar
Pekka Pessi committed
1056
    for (dots = 0, dot = strchr(domain, '.');
1057
	 dots < res->res_config->c_opt.ndots && dot;
Pekka Pessi's avatar
Pekka Pessi committed
1058 1059 1060 1061 1062
	 dots++, dot = strchr(dot + 1, '.'))
      ;
  else
    dots = 0;

1063
  query = sres_query_alloc(res, callback, context, type, domain);
1064 1065 1066

  if (query) {
    /* Create sub-query for each search domain */
Pekka Pessi's avatar
Pekka Pessi committed
1067
    if (dots < res->res_config->c_opt.ndots) {
1068
      sres_query_t *sub;
Pekka Pessi's avatar
Pekka Pessi committed
1069 1070
      int i, subs;
      size_t len;
1071
      char const *const *domains = res->res_config->c_search;
1072 1073
      char search[SRES_MAXDNAME + 1];

1074 1075
      assert(dlen < SRES_MAXDNAME);

1076 1077 1078 1079 1080 1081 1082
      memcpy(search, domain, dlen);
      search[dlen++] = '.';
      search[dlen] = '\0';

      for (i = 0, subs = 0; i <= SRES_MAX_SEARCH; i++) {
	if (domains[i]) {
	  len = strlen(domains[i]);
1083

1084 1085 1086 1087 1088 1089 1090 1091 1092
	  if (dlen + len + 1 > SRES_MAXDNAME)
	    continue;

	  memcpy(search + dlen, domains[i], len);
	  search[dlen + len] = '.';
	  search[dlen + len + 1] = '\0';
	  sub = sres_query_alloc(res, sres_answer_subquery, (void *)query,
				 type, search);

1093 1094 1095
	  if (sub == NULL) {
	  }
	  else if (sres_send_dns_query(res, sub) == 0) {
1096 1097 1098 1099 1100 1101 1102 1103
	    query->q_subqueries[i] = sub;
	  }
	  else {
	    sres_free_query(res, sub), sub = NULL;
	  }
	  subs += sub != NULL;
	}
      }
Pekka Pessi's avatar
Pekka Pessi committed
1104

1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120
      query->q_n_subs = subs;
    }

    if (sres_send_dns_query(res, query) != 0) {
      if (!query->q_n_subs)
	sres_free_query(res, query), query = NULL;
      else
	query->q_id = 0;
    }
  }

  return query;
}

/** Make a reverse DNS query.
 *
1121 1122
 * Send a query to DNS server with specified @a type and domain name formed
 * from the socket address @a addr. The sres resolver takes care of
1123 1124 1125 1126 1127 1128 1129 1130 1131
 * retransmitting the query if a root object is associate with the resolver or
 * if sres_resolver_timer() is called in regular intervals. It generates an
 * error record with nonzero status if no response is received.
 *
 * @param res pointer to resolver
 * @param callback function called when query is answered or times out
 * @param context pointer given as an extra argument to @a callback function
 * @param type record type to query (or sres_qtype_any for any record)
 * @param addr socket address structure
1132
 *
1133 1134 1135
 * The @a type should be #sres_type_ptr. The @a addr should contain either
 * IPv4 (AF_INET) or IPv6 (AF_INET6) address.
 *
1136
 * If the #SRES_OPTIONS environment variable, #RES_OPTIONS environment
1137
 * variable, or an "options" entry in resolv.conf file contains an option
1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149
 * "ip6-dotint", the IPv6 addresses are resolved using suffix ".ip6.int"
 * instead of the standard ".ip6.arpa" suffix.
 *
 * @ERRORS
 * @ERROR EAFNOSUPPORT address family specified in @a addr is not supported
 * @ERROR ENETDOWN no DNS servers configured
 * @ERROR EFAULT @a res or @a addr point outside the address space
 * @ERROR ENOMEM memory exhausted
 *
 * @sa sres_query(), sres_blocking_query_sockaddr(),
 * sres_cached_answers_sockaddr()
 *
1150 1151 1152 1153 1154 1155 1156 1157
 */
sres_query_t *
sres_query_sockaddr(sres_resolver_t *res,
		    sres_answer_f *callback,
		    sres_context_t *context,
		    uint16_t type,
		    struct sockaddr const *addr)
{
1158
  char name[80];
1159

1160 1161 1162
  if (!res || !addr)
    return su_seterrno(EFAULT), (void *)NULL;

1163 1164 1165 1166 1167 1168 1169 1170 1171
  if (!sres_sockaddr2string(res, name, sizeof(name), addr))
    return NULL;

  return sres_query(res, callback, context, type, name);
}


/** Make a DNS query.
 *
1172
 * @deprecated Use sres_query() instead.
1173 1174 1175 1176 1177
 */
sres_query_t *
sres_query_make(sres_resolver_t *res,
		sres_answer_f *callback,
		sres_context_t *context,
1178
		int dummy,
1179 1180 1181 1182 1183 1184 1185 1186
		uint16_t type,
		char const *domain)
{
  return sres_query(res, callback, context, type, domain);
}

/** Make a reverse DNS query.
 *
1187
 * @deprecated Use sres_query_sockaddr() instead.
1188 1189 1190 1191 1192
 */
sres_query_t *
sres_query_make_sockaddr(sres_resolver_t *res,
			 sres_answer_f *callback,
			 sres_context_t *context,
1193
			 int dummy,
1194 1195 1196
			 uint16_t type,
			 struct sockaddr const *addr)
{
1197
  char name[80];
1198

1199 1200 1201
  if (!res || !addr)
    return su_seterrno(EFAULT), (void *)NULL;

1202 1203 1204
  if (!sres_sockaddr2string(res, name, sizeof(name), addr))
    return NULL;

1205
  return sres_query_make(res, callback, context, dummy, type, name);
1206 1207 1208
}


1209 1210 1211 1212 1213 1214 1215
/** Bind a query with another callback and context pointer.
 *
 * @param query pointer to a query object to bind
 * @param callback pointer to new callback function (may be NULL)
 * @param context pointer to callback context (may be NULL)
*/
void sres_query_bind(sres_query_t *query,
1216 1217 1218
                     sres_answer_f *callback,
                     sres_context_t *context)
{
1219 1220 1221 1222
  if (query) {
    query->q_callback = callback;
    query->q_context = context;
  }
1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
}

/**Get a list of matching (type/domain) records from cache.
 *
 * @return
 * pointer to an array of pointers to cached records, or
 * NULL if no entry was found.
 *
 * @ERRORS
 * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME
 * @ERROR ENOENT no cached records were found
 * @ERROR EFAULT @a res or @a domain point outside the address space
 * @ERROR ENOMEM memory exhausted
 */
sres_record_t **
sres_cached_answers(sres_resolver_t *res,
		    uint16_t type,
		    char const *domain)
{
  sres_record_t **result;
  char rooted_domain[SRES_MAXDNAME];

  if (!res)
    return su_seterrno(EFAULT), (void *)NULL;

  domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain);

  if (!domain)
    return NULL;
1252

1253
  if (!sres_cache_get(res->res_cache, type, domain, &result))
Pekka Pessi's avatar
Pekka Pessi committed
1254
    return su_seterrno(ENOENT), (void *)NULL;
1255 1256 1257 1258

  return result;
}

Pekka Pessi's avatar
Pekka Pessi committed
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
/**Search for a list of matching (type/name) records from cache.
 *
 * @return
 * pointer to an array of pointers to cached records, or
 * NULL if no entry was found.
 *
 * @ERRORS
 * @ERROR ENAMETOOLONG @a name or resulting domain is longer than SRES_MAXDNAME
 * @ERROR ENOENT no cached records were found
 * @ERROR EFAULT @a res or @a domain point outside the address space
 * @ERROR ENOMEM memory exhausted
 *
 * @sa sres_search(), sres_cached_answers()
 */
sres_record_t **
sres_search_cached_answers(sres_resolver_t *res,
			   uint16_t type,
			   char const *name)
{
  char const *domain = name;
  sres_record_t **search_results[SRES_MAX_SEARCH + 1] = { NULL };
  char rooted_domain[SRES_MAXDNAME];
  unsigned dots; char const *dot;
  size_t found = 0;
  int i;

  SU_DEBUG_9(("sres_search_cached_answers(%p, %s, \"%s\") called\n",
1286
	      (void *)res, sres_record_type(type, rooted_domain), domain));
Pekka Pessi's avatar
Pekka Pessi committed
1287 1288 1289 1290 1291 1292

  if (!res || !name)
    return su_seterrno(EFAULT), (void *)NULL;

  if (sres_has_search_domain(res))
    for (dots = 0, dot = strchr(domain, '.');
1293
	 dots < res->res_config->c_opt.ndots && dot;
Pekka Pessi's avatar
Pekka Pessi committed
1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
	 dots++, dot = strchr(dot + 1, '.'))
      ;
  else
    dots = 0;

  domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain);

  if (!domain)
    return NULL;

  if (sres_cache_get(res->res_cache, type, domain, &search_results[0]))
    found = 1;

  if (dots < res->res_config->c_opt.ndots) {
1308
    char const *const *domains = res->res_config->c_search;
Pekka Pessi's avatar
Pekka Pessi committed
1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336
    size_t dlen = strlen(domain);

    for (i = 0; domains[i] && i < SRES_MAX_SEARCH; i++) {
      size_t len = strlen(domains[i]);
      if (dlen + len + 1 >= SRES_MAXDNAME)
	continue;
      if (domain != rooted_domain)
	domain = memcpy(rooted_domain, domain, dlen);
      memcpy(rooted_domain + dlen, domains[i], len);
      strcpy(rooted_domain + dlen + len, ".");
      if (sres_cache_get(res->res_cache, type, domain, search_results + i + 1))
	found++;
    }
  }

  if (found == 0)
    return su_seterrno(ENOENT), (void *)NULL;

  if (found == 1) {
    for (i = 0; i <= SRES_MAX_SEARCH; i++)
      if (search_results[i])
	return search_results[i];
  }

  return sres_combine_results(res, search_results);
}

/**Get a list of matching (type/domain) reverse records from cache.
1337
 *
1338 1339 1340
 * @param res pointer to resolver
 * @param type record type to query (or sres_qtype_any for any record)
 * @param addr socket address structure
1341
 *
1342 1343 1344 1345 1346 1347 1348
 * The @a type should be #sres_type_ptr. The @a addr should contain either
 * IPv4 (AF_INET) or IPv6 (AF_INET6) address.
 *
 * If the #SRES_OPTIONS environment variable, #RES_OPTIONS environment
 * variable or an "options" entry in resolv.conf file contains an option
 * "ip6-dotint", the IPv6 addresses are resolved using suffix ".ip6.int"
 * instead of default ".ip6.arpa".
1349
 *
1350
 * @retval
1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
 * pointer to an array of pointers to cached records, or
 * NULL if no entry was found.
 *
 * @ERRORS
 * @ERROR EAFNOSUPPORT address family specified in @a addr is not supported
 * @ERROR ENOENT no cached records were found
 * @ERROR EFAULT @a res or @a addr point outside the address space
 * @ERROR ENOMEM memory exhausted
 */
sres_record_t **
sres_cached_answers_sockaddr(sres_resolver_t *res,
			     uint16_t type,
			     struct sockaddr const *addr)
{
  sres_record_t **result;
  char name[80];

  if (!res || !addr)
    return su_seterrno(EFAULT), (void *)NULL;

  if (!sres_sockaddr2string(res, name, sizeof name, addr))
    return NULL;

  if (!sres_cache_get(res->res_cache, type, name, &result))
    su_seterrno(ENOENT), (void *)NULL;

  return result;
}

1380 1381 1382 1383 1384 1385 1386 1387 1388
/** Set the priority of the matching cached SRV record.
 *
 * The SRV records with the domain name, target and port are matched and
 * their priority value is adjusted. This function is used to implement
 * greylisting of SIP servers.
 *
 * @param res      pointer to resolver
 * @param domain   domain name of the SRV record(s) to modify
 * @param target   SRV target of the SRV record(s) to modify
1389 1390
 * @param port     port number of SRV record(s) to modify
 *                 (in host byte order)
1391
 * @param ttl      new ttl for SRV records of the domain
1392 1393 1394
 * @param priority new priority value (0=highest, 65535=lowest)
 *
 * @sa sres_cache_set_srv_priority()
1395
 *
1396 1397
 * @NEW_1_12_8
 */
1398 1399 1400 1401
int sres_set_cached_srv_priority(sres_resolver_t *res,
				 char const *domain,
				 char const *target,
				 uint16_t port,
1402
				 uint32_t ttl,
1403
				 uint16_t priority)
1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414
{
  char rooted_domain[SRES_MAXDNAME];

  if (res == NULL || res->res_cache == NULL)
    return su_seterrno(EFAULT);

  domain = sres_toplevel(rooted_domain, sizeof rooted_domain, domain);

  if (!domain)
    return -1;

1415 1416
  return sres_cache_set_srv_priority(res->res_cache,
				     domain, target, port,
1417
				     ttl, priority);
1418 1419 1420
}


1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
/** Sort answers. */
int
sres_sort_answers(sres_resolver_t *res, sres_record_t **answers)
{
  int i, j;

  if (res == NULL || answers == NULL)
    return su_seterrno(EFAULT);

  if (answers[0] == NULL || answers[1] == NULL)
    return 0;

  /* Simple insertion sorting */
  /*
1435
   * We do not use qsort because we want later extend this to sort
1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456
   * local A records first etc.
   */
  for (i = 1; answers[i]; i++) {
    for (j = 0; j < i; j++) {
      if (sres_record_compare(answers[i], answers[j]) < 0)
	break;
    }
    if (j < i) {
      sres_record_t *r = answers[i];
      for (; j < i; i--) {
	answers[i] = answers[i - 1];
      }
      answers[j] = r;
    }
  }

  return 0;
}

/** Sort and filter query results */
int
1457 1458
sres_filter_answers(sres_resolver_t *res,
		    sres_record_t **answers,
1459
		    uint16_t type)
1460
{
1461 1462
  int i, n;

1463 1464 1465 1466
  if (res == NULL || answers == NULL)
    return su_seterrno(EFAULT);

  for (n = 0, i = 0; answers[i]; i++) {
1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
    if (answers[i]->sr_record->r_status ||
	answers[i]->sr_record->r_class != sres_class_in ||
	(type != 0 && answers[i]->sr_record->r_type != type)) {
      sres_free_answer(res, answers[i]);
      continue;
    }
    answers[n++] = answers[i];
  }
  answers[n] = NULL;

  sres_sort_answers(res, answers);

  return n;
}


/** Free and zero one record. */
void sres_free_answer(sres_resolver_t *res, sres_record_t *answer)
{
  if (res && answer)
    sres_cache_free_one(res->res_cache, answer);
}

1490
/** Free and zero an array of records.
1491 1492
 *
 * The array of records can be returned by sres_cached_answers() or
1493 1494
 * given by callback function.
 */
1495
void
1496 1497 1498 1499 1500 1501 1502
sres_free_answers(sres_resolver_t *res,
		  sres_record_t **answers)
{
  if (res && answers)
    sres_cache_free_answers(res->res_cache, answers);
}

Pekka Pessi's avatar
Pekka Pessi committed
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553
/** Convert type to its name. */
char const *sres_record_type(int type, char buffer[8])
{
  switch (type) {
  case sres_type_a: return "A";
  case sres_type_ns: return "NS";
  case sres_type_mf: return "MF";
  case sres_type_cname: return "CNAME";
  case sres_type_soa: return "SOA";
  case sres_type_mb: return "MB";
  case sres_type_mg: return "MG";
  case sres_type_mr: return "MR";
  case sres_type_null: return "NULL";
  case sres_type_wks: return "WKS";
  case sres_type_ptr: return "PTR";
  case sres_type_hinfo: return "HINFO";
  case sres_type_minfo: return "MINFO";
  case sres_type_mx: return "MX";
  case sres_type_txt: return "TXT";
  case sres_type_rp: return "RP";
  case sres_type_afsdb: return "AFSDB";
  case sres_type_x25: return "X25";
  case sres_type_isdn: return "ISDN";
  case sres_type_rt: return "RT";
  case sres_type_nsap: return "NSAP";
  case sres_type_nsap_ptr: return "NSAP_PTR";
  case sres_type_sig: return "SIG";
  case sres_type_key: return "KEY";
  case sres_type_px: return "PX";
  case sres_type_gpos: return "GPOS";
  case sres_type_aaaa: return "AAAA";
  case sres_type_loc: return "LOC";
  case sres_type_nxt: return "NXT";
  case sres_type_eid: return "EID";
  case sres_type_nimloc: return "NIMLOC";
  case sres_type_srv: return "SRV";
  case sres_type_atma: return "ATMA";
  case sres_type_naptr: return "NAPTR";
  case sres_type_kx: return "KX";
  case sres_type_cert: return "CERT";
  case sres_type_a6: return "A6";
  case sres_type_dname: return "DNAME";
  case sres_type_sink: return "SINK";
  case sres_type_opt: return "OPT";

  case sres_qtype_tsig: return "TSIG";
  case sres_qtype_ixfr: return "IXFR";
  case sres_qtype_axfr: return "AXFR";
  case sres_qtype_mailb: return "MAILB";
  case sres_qtype_maila: return "MAILA";
  case sres_qtype_any: return "ANY";
1554

Pekka Pessi's avatar
Pekka Pessi committed
1555
  default:
1556 1557
    if (buffer)
      sprintf(buffer, "%u?", type & 65535);
Pekka Pessi's avatar
Pekka Pessi committed
1558 1559 1560 1561
    return buffer;
  }
}

1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576
/** Convert record status to its name */
char const *sres_record_status(int status, char buffer[8])
{
  switch (status) {
  case SRES_OK: return "OK";
  case SRES_FORMAT_ERR: return "FORMAT_ERR";
  case SRES_SERVER_ERR: return "SERVER_ERR";
  case SRES_NAME_ERR: return "NAME_ERR";
  case SRES_UNIMPL_ERR: return "UNIMPL_ERR";
  case SRES_AUTH_ERR: return "AUTH_ERR";

  /* Errors generated by sresolv */
  case SRES_TIMEOUT_ERR: return "TIMEOUT_ERR";
  case SRES_RECORD_ERR: return "RECORD_ERR";
  case SRES_INTERNAL_ERR: return "INTERNAL_ERR";
1577
  case SRES_NETWORK_ERR: return "NETWORK_ERR";
1578 1579 1580 1581 1582 1583 1584 1585 1586

  default:
    if (buffer)
      sprintf(buffer, "%u?", status & 255);
    return buffer;
  }
}


Pekka Pessi's avatar
Pekka Pessi committed
1587
/** Convert class to its name. */
1588 1589
static char const *
sres_record_class(int rclass, char buffer[8])
Pekka Pessi's avatar
Pekka Pessi committed
1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605
{
  switch (rclass) {
  case 1: return "IN";
  case 2: return "2?";
  case 3: return "CHAOS";
  case 4: return "HS";
  case 254: return "NONE";
  case 255: return "ANY";

  default:
    sprintf(buffer, "%u?", rclass & 65535);
    return buffer;
  }
}

/** Compare two records. */
1606
int
Pekka Pessi's avatar
Pekka Pessi committed
1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617
sres_record_compare(sres_record_t const *aa, sres_record_t const *bb)
{
  int D;
  sres_common_t const *a = aa->sr_record, *b = bb->sr_record;

  D = a->r_status - b->r_status; if (D) return D;
  D = a->r_class - b->r_class; if (D) return D;
  D = a->r_type - b->r_type; if (D) return D;

  if (a->r_status)
    return 0;
1618

Pekka Pessi's avatar
Pekka Pessi committed
1619
  switch (a->r_type) {
1620
  case sres_type_soa:
Pekka Pessi's avatar
Pekka Pessi committed
1621 1622 1623
    {
      sres_soa_record_t const *A = aa->sr_soa, *B = bb->sr_soa;
      D = A->soa_serial - B->soa_serial; if (D) return D;
1624 1625
      D = su_strcasecmp(A->soa_mname, B->soa_mname); if (D) return D;
      D = su_strcasecmp(A->soa_rname, B->soa_rname); if (D) return D;
Pekka Pessi's avatar
Pekka Pessi committed
1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640
      D = A->soa_refresh - B->soa_refresh; if (D) return D;
      D = A->soa_retry - B->soa_retry; if (D) return D;
      D = A->soa_expire - B->soa_expire; if (D) return D;
      D = A->soa_minimum - B->soa_minimum; if (D) return D;
      return 0;
    }
  case sres_type_a:
    {
      sres_a_record_t const *A = aa->sr_a, *B = bb->sr_a;
      return memcmp(&A->a_addr, &B->a_addr, sizeof A->a_addr);
    }
  case sres_type_a6:
    {