📄 sres.c
字号:
char const *info);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[], size_t bsize, char const *domain);static sres_record_t *sres_create_record(sres_resolver_t *, sres_message_t *m);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, 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);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, uint16_t offset);static int m_get_domain(char *d, int n, sres_message_t *m, uint16_t offset);/* ---------------------------------------------------------------------- */#define SU_LOG sresolv_log#include <sofia-sip/su_debug.h>#ifdef HAVE_WIN32#include <winreg.h>#endif/**@ingroup sresolv_env * * 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 <sofia-sip/su_debug.h>, sresolv_log, SOFIA_DEBUG */#ifdef DOXYGENextern char const SRESOLV_DEBUG[]; /* dummy declaration for Doxygen */#endif#ifndef SU_DEBUG#define SU_DEBUG 3#endif/**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", SU_DEBUG) };/** Internal errors */enum { SRES_EDNS0_ERR = 255 /**< Server did not support EDNS. */};/* ---------------------------------------------------------------------- *//**Create a resolver. * * 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. * * @param conf_file_path name of the resolv.conf configuration file * * @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){ return sres_resolver_new_internal(NULL, NULL, conf_file_path, NULL);}/** Copy a resolver. * * Make a copy of resolver sharing the configuration and cache with old * resolver. */sres_resolver_t *sres_resolver_copy(sres_resolver_t *res){ char const *cnffile; sres_config_t *config; sres_cache_t *cache; char const **options; if (!res) return NULL; cnffile = res->res_cnffile; config = su_home_ref(res->res_config->c_home); cache = res->res_cache; options = res->res_options; return sres_resolver_new_internal(cache, config, cnffile, options);}/**New resolver object. * * 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. * * @param conf_file_path name of the resolv.conf configuration file * @param cache optional pointer to a resolver cache (may be NULL) * @param option, ... list of resolv.conf options directives * (overriding options in conf_file) * * @par Environment Variables * - #LOCALDOMAIN overrides @c domain or @c search directives * - #RES_OPTIONS overrides values of @a options in resolv.conf * - #SRES_OPTIONS overrides values of @a options in resolv.conf, #RES_OPTIONS, * 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, char const *option, ...){ sres_resolver_t *retval; va_list va; va_start(va, option); retval = sres_resolver_new_with_cache_va(conf_file_path, cache, option, va); va_end(va); return retval;}/**Create a resolver. * * Allocate and initialize a new sres resolver object. * * This is a stdarg version of sres_resolver_new_with_cache(). */sres_resolver_t *sres_resolver_new_with_cache_va(char const *conf_file_path, sres_cache_t *cache, char const *option, va_list va){ va_list va0; size_t i; char const *o, *oarray[16], **olist = oarray; sres_resolver_t *res; va_copy(va0, va); 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; res = sres_resolver_new_internal(cache, NULL, conf_file_path, olist); if (olist != oarray) free(olist); return res;}sres_resolver_t *sres_resolver_new_internal(sres_cache_t *cache, sres_config_t const *config, char const *conf_file_path, char const **options){ sres_resolver_t *res; size_t i, n, len; char **array, *o, *end; 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); if (res == NULL) return NULL; 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); su_home_destructor(res->res_home, sres_resolver_destructor); while (res->res_id == 0) {#if HAVE_DEV_URANDOM int fd; if ((fd = open("/dev/urandom", O_RDONLY, 0)) != -1) { size_t len = read(fd, &res->res_id, (sizeof res->res_id)); (void)len; close(fd); } else#endif res->res_id = time(NULL); } time(&res->res_now); if (cache) res->res_cache = sres_cache_ref(cache); else res->res_cache = sres_cache_new(0); res->res_config = config; if (conf_file_path && conf_file_path != sres_conf_file_path) res->res_cnffile = su_strdup(res->res_home, conf_file_path); else res->res_cnffile = conf_file_path = sres_conf_file_path; 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"); } else if (sres_resolver_update(res, config == NULL) < 0) { perror("sres: sres_resolver_update"); } else { return res; } sres_resolver_unref(res); return NULL;}/** Increase reference count on a resolver object. */sres_resolver_t *sres_resolver_ref(sres_resolver_t *res){ return su_home_ref(res->res_home);} /** Decrease the reference count on a resolver object. */voidsres_resolver_unref(sres_resolver_t *res){ su_home_unref(res->res_home);}/** Set userdata pointer. * * @return New userdata pointer. * * @ERRORS * @ERROR EFAULT @a res points outside the address space */void *sres_resolver_set_userdata(sres_resolver_t *res, void *userdata){ void *old; if (!res) return su_seterrno(EFAULT), (void *)NULL; old = res->res_userdata, res->res_userdata = userdata; return old;}/**Get userdata pointer. * * @return Userdata pointer. * * @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. * * @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, sres_async_t *async, 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; res->res_async = async; res->res_updcb = callback; res->res_update_all = callback && update_all != 0; return async;}/** Get async object */sres_async_t *sres_resolver_get_async(sres_resolver_t const *res, sres_update_f *callback){ if (res == NULL) return su_seterrno(EFAULT), (void *)NULL; else if (callback == NULL) return res->res_async ? (sres_async_t *)-1 : 0; else if (res->res_updcb != callback) return NULL; else return res->res_async;}/** 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;}/**Send a DNS query. * * 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 * #sres_type_a, * #sres_type_aaaa, * #sres_type_cname, * #sres_type_ptr * #sres_type_soa, * #sres_type_aaaa, * #sres_type_srv, and * #sres_type_naptr. * * Deprecated query type #sres_type_a6 is also decoded. * * @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 * in search path. * * @sa sres_search(), sres_blocking_query(), sres_cached_answers(), * sres_query_sockaddr() * * @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 */sres_query_t *sres_query(sres_resolver_t *res, sres_answer_f *callback, sres_context_t *context, uint16_t type, char const *domain){ sres_query_t *query = NULL; size_t dlen; char b[8]; SU_DEBUG_9(("sres_query(%p, %p, %s, \"%s\") called\n", (void *)res, (void *)context, sres_record_type(type, b), domain)); 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; } /* Reread resolv.conf if needed */ sres_resolver_update(res, 0); if (res->res_n_servers == 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -