📄 sres.c
字号:
/* * This file is part of the Sofia-SIP package * * Copyright (C) 2006 Nokia Corporation. * Copyright (C) 2006 Dimitri E. Prado. * * 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.c * @brief Sofia DNS Resolver implementation. * * @author Pekka Pessi <Pekka.Pessi@nokia.com> * @author Teemu Jalava <Teemu.Jalava@nokia.com> * @author Mikko Haataja * @author Kai Vehmanen <kai.vehmanen@nokia.com> * (work on the win32 nameserver discovery) * @author Dimitri E. Prado * (initial version of win32 nameserver discovery) * * @todo The resolver should allow handling arbitrary records, too. */#include "config.h"#if HAVE_STDINT_H#include <stdint.h>#elif HAVE_INTTYPES_H#include <inttypes.h>#else#if defined(HAVE_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_ARPA_INET_H#include <arpa/inet.h>#endif#if HAVE_WINSOCK2_H#include <winsock2.h>#include <ws2tcpip.h>#ifndef IPPROTO_IPV6 /* socklen_t is used with @RFC2133 API */typedef int socklen_t;#endif#endif#if HAVE_IPHLPAPI_H#include <iphlpapi.h>#endif#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"#include <sofia-sip/su_alloc.h>#include <sofia-sip/su_strlst.h>#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/* Posix send() */su_inline ssize_t sres_send(sres_socket_t s, void *b, size_t length, int flags){ if (length > INT_MAX) length = INT_MAX; return (ssize_t)send(s, b, (int)length, flags);}/* Posix recvfrom() */su_inline ssize_t sres_recvfrom(sres_socket_t s, void *buffer, size_t length, int flags, struct sockaddr *from, socklen_t *fromlen){ int retval, ilen; if (fromlen) ilen = *fromlen; if (length > INT_MAX) length = INT_MAX; retval = recvfrom(s, buffer, (int)length, flags, (void *)from, fromlen ? &ilen : NULL); if (fromlen) *fromlen = ilen; return (ssize_t)retval;}su_inlineint sres_close(sres_socket_t s){ return closesocket(s);}#if !defined(IPPROTO_IPV6) && (_WIN32_WINNT < 0x0600)#if HAVE_SIN6#include <tpipv6.h>#else#if !defined(__MINGW32__)struct sockaddr_storage { short ss_family; char ss_pad[126];};#endif#endif#endif#else#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))#define sres_close(s) close((s))#define SOCKET_ERROR (-1)#define INVALID_SOCKET ((sres_socket_t)-1)#endif#define SRES_TIME_MAX ((time_t)LONG_MAX)#if !HAVE_INET_PTONint su_inet_pton(int af, char const *src, void *dst);#else#define su_inet_pton inet_pton#endif#if !HAVE_INET_NTOPconst char *su_inet_ntop(int af, void const *src, char *dst, size_t size);#else#define su_inet_ntop inet_ntop#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/** * How often to recheck nameserver information (seconds). */#ifndef HAVE_WIN32#define SRES_UPDATE_INTERVAL_SECS 5#else#define SRES_UPDATE_INTERVAL_SECS 180#endifvoid 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;/** Default path to resolv.conf */static char const sres_conf_file_path[] = "/etc/resolv.conf";/** EDNS0 support. @internal */enum edns { edns_not_tried = -1, edns_not_supported = 0, edns0_configured = 1, edns0_supported = 2,};struct sres_server { sres_socket_t dns_socket; 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; /** Persistent error, zero when successful or timeout. * * Never selected if dns_error is SRES_TIME_MAX. */ time_t dns_error;};HTABLE_DECLARE_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t);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 */ char const *res_cnffile; /**< Configuration file name */ char const **res_options; /**< Option strings */ sres_config_t const *res_config; time_t res_checked; unsigned long res_updated; sres_update_f *res_updcb; sres_async_t *res_async; sres_schedule_f *res_schedulecb; short res_update_all; uint16_t res_id; short res_i_server; /**< Current server to try (when doing round-robin) */ short res_n_servers; /**< Number of servers */ sres_server_t **res_servers;};/* Parsed configuration. @internal */struct sres_config { su_home_t c_home[1]; time_t c_modified; char const *c_filename; /* domain and search */ char const *c_search[SRES_MAX_SEARCH + 1]; /* nameserver */ struct sres_nameserver { struct sockaddr_storage ns_addr[1]; ssize_t ns_addrlen; } *c_nameservers[SRES_MAX_NAMESERVERS + 1]; /* sortlist */ 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 */ /* options */ struct sres_options { uint16_t timeout; uint16_t attempts; uint16_t ndots; enum edns edns; 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 { unsigned q_hash; 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; 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#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_parsed sr_record->r_parsed#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_WITH(sres_qtable, qt, sres_query_t, unsigned, size_t);#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)#endifstatic int sres_config_changed_servers(sres_config_t const *new_c, sres_config_t const *old_c);static sres_server_t **sres_servers_new(sres_resolver_t *res, sres_config_t const *c);/** Generate new 16-bit identifier for DNS query. */static uint16_tsres_new_id(sres_resolver_t *res){ return res->res_id ? res->res_id++ : (res->res_id = 2, 1);}/** 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_config->c_search[0] != NULL;}static void sres_resolver_destructor(void *);sres_resolver_t *sres_resolver_new_with_cache_va(char const *conf_file_path, sres_cache_t *cache, char const *options, va_list va);staticsres_resolver_t *sres_resolver_new_internal(sres_cache_t *cache, sres_config_t const *config, char const *conf_file_path, char const **options);static void sres_servers_close(sres_resolver_t *res, sres_server_t **servers);static int sres_servers_count(sres_server_t * const *servers);static sres_socket_t sres_server_socket(sres_resolver_t *res, sres_server_t *dns);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);static int sres_sockaddr2string(sres_resolver_t *, char name[], size_t namelen, struct sockaddr const *);static sres_config_t *sres_parse_resolv_conf(sres_resolver_t *res, char const **options);staticsres_server_t *sres_next_server(sres_resolver_t *res, uint8_t *in_out_i, int always);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);staticsres_record_t **sres_combine_results(sres_resolver_t *res, sres_record_t **search_results[SRES_MAX_SEARCH + 1]);staticvoid sres_query_report_error(sres_query_t *q, sres_record_t **answers);static voidsres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout);static sres_server_t *sres_server_by_socket(sres_resolver_t const *ts, sres_socket_t socket);staticint sres_resolver_report_error(sres_resolver_t *res, sres_socket_t socket, int errcode, struct sockaddr_storage *remote, socklen_t remotelen,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -