📄 sresolv.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 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 sresolv.c * @brief Sofia Asynchronous DNS Resolver implementation. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * @author Teemu Jalava <Teemu.Jalava@nokia.com> * @author Mikko Haataja <ext-Mikko.A.Haataja@nokia.com> * * @todo The resolver should allow handling arbitrary records. */#include "config.h"#include <stdlib.h>#include <stdarg.h>#include <stddef.h>#include <string.h>#include <stdio.h>#include <unistd.h>#include <errno.h>#include <fcntl.h>#include <time.h>#include <limits.h>#include <assert.h>#if 0 && !HAVE_SOFIA_SU#include <sys/socket.h>#include <sys/time.h>#include <sys/types.h>#include <netinet/in.h>#include <arpa/inet.h>#include <netdb.h>#endif#include <sofia-sip/su_alloc.h>#include <sofia-sip/su_strlst.h>#include <sofia-sip/su.h>#if HAVE_SU_WAIT_H#define SU_TIMER_ARG_T struct sres_sofia_s#define SU_WAKEUP_ARG_T struct sres_sofia_s#include <sofia-sip/su_wait.h>#include <sofia-sip/su_types.h>#include <sofia-sip/su_time.h>#define SU_LOG sresolv_log#include <sofia-sip/su_debug.h>/**@var SRESOLV_DEBUG * * 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. * * @sa <su_debug.h>, sresolv_log, SOFIA_DEBUG */extern char const SRESOLV_DEBUG[];/**Debug log for @b sresolv module. * * The sresolv_log is the log object used by @b sresolv module. The level of * #sresolv_log is set using #SRESOLV_DEBUG environment variable. */su_log_t sresolv_log[] = { SU_LOG_INIT("sresolv", "SRESOLV_DEBUG", 3) };#else#define su_close(s) close(s)#define su_strerror(s) strerror(s)#define su_errno() errno#define su_seterrno(x) ((errno = (x)), -1)#define SU_DEBUG_0(x) printf x#define SU_DEBUG_1(x) printf x#define SU_DEBUG_3(x) printf x#define SU_DEBUG_5(x) printf x#define SU_DEBUG_7(x) printf x#define SU_DEBUG_9(x) printf x#endif#define sres_resolver_create public_sres_resolver_create#include "sofia-sip/sresolv.h"#undef sres_resolver_create#include "sofia-sip/htable.h"/** Cache cleanup interval in seconds. */#define SRES_CACHE_TIMER_INTERVAL (30)/** Sofia timer interval in milliseconds. */#define SRES_RETRANSMIT_INTERVAL (500)/** Maximum number of retries sent. */#define SRES_MAX_RETRY_COUNT (6)/** Maximum number of search domains. */#define SRES_MAX_SEARCH (6)/** Maximum length of domain name. */#define SRES_MAXDNAME (1025)/** Internal errors */enum { SRES_EDNS0_ERR = 255 /**< Server did not support EDNS. */};typedef struct sres_rr_hash_entry_s sres_rr_hash_entry_t;typedef struct sres_message_s sres_message_t;#define SRES_HENTRY_HASH(e) ((e)->rr_hash_key)HTABLE_DECLARE(sres_htable, ht, sres_rr_hash_entry_t);HTABLE_DECLARE(sres_qtable, qt, sres_query_t);typedef struct sres_server_s { char const *dns_name; /**< Server name */ struct sockaddr_storage dns_addr[1]; /**< Server node address */ ssize_t dns_addrlen; /**< Size of addres */ unsigned dns_edns; /**< Server supports edns */ /** ICMP error received, zero when successful. */ time_t dns_icmp_error; } sres_server_t;struct sres_resolver_s { su_home_t res_home[1]; unsigned res_refcount; void *res_userdata; int (*res_lock)(void *mutex); int (*res_unlock)(void *mutex); void *res_mutex; time_t res_now; uint16_t res_id; sres_qtable_t res_queries[1]; /**< Table of active queries */ time_t res_cache_cleaned; sres_htable_t res_cache[1]; char const *res_config; /**< Configuration file name */ uint16_t res_port; /**< Server port to use */ short res_n_servers; /**< Number of servers */ short res_i_server; /**< Current server to try (when doing round-robin) */ sres_server_t *res_servers; char const *res_search[1 + SRES_MAX_SEARCH];};struct sres_rr_hash_entry_s { unsigned int rr_hash_key; time_t rr_received; sres_record_t *rr;};struct sres_query_s { hash_value_t q_hash; sres_resolver_t*q_res; sres_answer_f *q_callback; sres_context_t *q_context; char *q_name; time_t q_timestamp; int q_socket; uint16_t q_type; uint16_t q_class; uint16_t q_id; /**< If nonzero, not answered */ uint16_t q_retry_count; short q_i_server; uint8_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_s { 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#define m_arcount m_packet.mp_header.mh_arcount #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#define sr_rdata sr_generic->g_dataenum { 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 */};HTABLE_PROTOS(sres_htable, ht, sres_rr_hash_entry_t);HTABLE_PROTOS(sres_qtable, qt, sres_query_t);/** Get address from sockaddr storage. */#if SU_HAVE_IN6#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/** Generate new 16-bit identifier for DNS query. */uint16_tsres_new_id(sres_resolver_t *res){ return res->res_id ? res->res_id++ : (res->res_id += 2);}/** Return true if we have a search list or a local domain name. */static int sres_has_search_domain(sres_resolver_t *res) { return res->res_search[0] || res->res_search[1];}static sres_query_t * sres_query_alloc(sres_resolver_t *res, sres_answer_f *callback, sres_context_t *context, int socket, uint16_t type, char const * domain);static void sres_free_query(sres_resolver_t *res, sres_query_t *q);static void sres_store(sres_resolver_t *res, sres_record_t *rr);static unsigned int sres_hash_key(const char *string);static int sres_record_compare(sres_record_t const *a, sres_record_t const *b);static int sres_sockaddr2string(char name[], size_t namelen, struct sockaddr const *);static int sres_parse_resolv_conf(sres_resolver_t *, const char *filename);staticint sres_send_dns_query(sres_resolver_t *res, sres_query_t *q);static void sres_answer_subquery(sres_context_t *context, sres_query_t *query, sres_record_t **answers);staticvoid sres_query_report_error(sres_resolver_t *res, sres_query_t *q, sres_record_t **answers);voidsres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout);static sres_server_t *sres_server_by_sockaddr(sres_resolver_t const *res, void const *from, int fromlen);staticint sres_resolver_report_error(sres_resolver_t *res, int socket, int errcode, struct sockaddr_storage *remote, socklen_t remotelen, char const *info);static inline void _sres_free_answer(sres_resolver_t *, sres_record_t *);static inline void _sres_free_answers(sres_resolver_t *, sres_record_t **);staticvoid sres_log_response(sres_resolver_t const *res, sres_message_t const *m, struct sockaddr_storage const *from, sres_query_t const *query, sres_record_t * const *reply);static int sres_decode_msg(sres_resolver_t *res, sres_message_t *m, sres_query_t **, sres_record_t ***aanswers);static char const *sres_toplevel(char buf[SRES_MAXDNAME], char const *domain);static sres_record_t *sres_create_record(sres_resolver_t *, sres_message_t *m);static void sres_init_rr_soa(sres_resolver_t *res, sres_soa_record_t *rr, sres_message_t *m);static void sres_init_rr_a(sres_resolver_t *res, sres_a_record_t *rr, sres_message_t *m);#if SU_HAVE_IN6static void sres_init_rr_a6(sres_resolver_t *res, sres_a6_record_t *rr, sres_message_t *m);static void sres_init_rr_aaaa(sres_resolver_t *res, sres_aaaa_record_t *rr, sres_message_t *m);#endifstatic void sres_init_rr_cname(sres_resolver_t *res, sres_cname_record_t *rr, sres_message_t *m);static void sres_init_rr_ptr(sres_resolver_t *res, sres_ptr_record_t *rr, sres_message_t *m);static void sres_init_rr_srv(sres_resolver_t *res, sres_srv_record_t *rr, sres_message_t *m);static void sres_init_rr_naptr(sres_resolver_t *res, sres_naptr_record_t *rr, sres_message_t *m);static sres_record_t *sres_create_error_rr(sres_resolver_t *res, sres_query_t const *q, uint16_t errcode);static int sres_get_domain(sres_resolver_t *res, char **buf, sres_message_t *m);static int sres_get_string(sres_resolver_t *res, char **buf, sres_message_t *m);static void m_put_uint16(sres_message_t *m, uint16_t h);static void m_put_uint32(sres_message_t *m, uint32_t w);static uint16_t m_put_domain(sres_message_t *m, char const *domain, uint16_t top, 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);static int m_get_string(char *d, int n, sres_message_t *m);static int m_get_domain(char *d, int n, sres_message_t *m, int indirected);/**Create a resolver. * * The function sres_resolver_new() is used to allocate and initialize a new * sres resolver object. The resolver object contains the parsed resolv.conf * file, cached 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. * * @param conf_file_path name of the resolv.conf configuration file * * @return The function sres_resolver_new() returns 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){ sres_resolver_t *res; res = su_home_new(sizeof(*res)); if (res == NULL) return NULL; while (res->res_id == 0) {#if HAVE_SU_WAIT_H su_ntp_t ntp; ntp = su_ntp_now(); res->res_id = su_ntp_lo(ntp) + su_ntp_hi(ntp) + su_random();#else res->res_id = time(NULL);#endif } res->res_port = 53; /* Domain */ if (sres_htable_resize(res->res_home, res->res_cache, 0) < 0) { perror("sres: res_htable_resize"); } else if (sres_qtable_resize(res->res_home, res->res_queries, 0) < 0) { perror("sres: res_qtable_resize"); } else if (sres_parse_resolv_conf(res, conf_file_path)) { res->res_refcount = 1; return res; } sres_resolver_unref(res); return NULL;}/** Add a lock to resolver object. * * The function sres_resolver_add_mutex() is used to pass a mutex along with * the functions used obtaining and releasing the mutex to the resolver. The * mutex is needed when resolver is used in multithreaded environment. The * @a lock function is used to obtain the lock. The @a unlock function is * used to release the lock. Both are called with @a mutex as their only * argument. Both @a lock() and @a unlock() should return 0 when successful, * -1 upon an error. * * @note * Please note that the resolver gives away its mutex lock while it * calls the query-specific callback functions. * * @param res pointer to resolver object * @param mutex pointer to a mutex object (may be NULL) * @param lock function used to obtain a lock on @a mutex * @param unlock function used to release a lock on @a mutex * * @retval 0 when successful * @retval -1 upon an error * * @ERRORS * @ERROR EFAULT @a res, @a lock or @a unlock point outside the address space */int sres_resolver_add_mutex(sres_resolver_t *res, void *mutex, int (*lock)(void *mutex), int (*unlock)(void *mutex)){ if (res && lock && unlock) { res->res_mutex = mutex; res->res_lock = lock; res->res_unlock = unlock; return 0; } else { return su_seterrno(EFAULT); }}/** Obtain lock */#define LOCK(res) \ ((res ? 1 : (errno = EFAULT, 0)) && \ ((res)->res_lock ? (res)->res_lock((res)->res_mutex) : 0) == 0)#define UNLOCK(res) \ (((res)->res_unlock ? (res)->res_unlock((res)->res_mutex) : 0) == 0)/** Create a new reference to resolver. */sres_resolver_t *sres_resolver_ref(sres_resolver_t *res){ if (LOCK(res)) { if (res->res_refcount != UINT_MAX) res->res_refcount++; UNLOCK(res); return res; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -