📄 sres.c
字号:
su_log_t sresolv_log[] = { SU_LOG_INIT("sresolv", "SRESOLV_DEBUG", 3) };/** 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 with old */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 * @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_desctructor(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) { read(fd, &res->res_id, (sizeof res->res_id)); 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;}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;}/**Send a DNS query. * * Sends a DNS query with specified @a type and @a domain to the DNS server. * The sres resolver takes care of retransmitting the query if * sres_resolver_timer() is called in regular intervals. It generates an * error record with nonzero status if no response is received. * * @sa sres_search(), sres_blocking_query(), sres_query_make() * * @ERRORS * @ERROR EFAULT @a res or @a domain point outside the address space * @ERROR ENAMETOOLONG @a domain is longer than SRES_MAXDNAME * @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, %p, %s, \"%s\") called\n", res, callback, 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); 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. * * Sends DNS queries with specified @a type and @a name to 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. * * The sres resolver takes care of retransmitting the queries 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 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 * @ERROR ENOMEM memory exhausted * * @sa sres_query(), sres_blocking_search() */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]; SU_DEBUG_9(("sres_search(%p, %p, %p, %s, \"%s\") called\n", res, callback, 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; } sres_resolver_update(res, 0); if (sres_has_search_domain(res)) for (dots = 0, dot = strchr(domain, '.'); dots < res->res_config->c_opt.ndots && dot; dots++, dot = strchr(dot + 1, '.')) ; else dots = 0; query = sres_query_alloc(res, callback, context, type, domain); if (query) { /* Create sub-query for each search domain */ if (dots < res->res_config->c_opt.ndots) { sres_query_t *sub; int i, subs, len; char const **domains = res->res_config->c_search; char search[SRES_MAXDNAME + 1]; 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]); 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); if (sres_send_dns_query(res, sub) == 0) { query->q_subqueries[i] = sub; } else { sres_free_query(res, sub), sub = NULL; } subs += sub != NULL; } } 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. * * 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 * retransmitting the query if sres_resolver_timer() is called in regular * intervals. It generates an error record with nonzero status if no * response is received. */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){ char name[80]; if (!res || !addr) return su_seterrno(EFAULT), (void *)NULL; if (!sres_sockaddr2string(res, name, sizeof(name), addr)) return NULL; return sres_query(res, callback, context, type, name);}/** Make a DNS query. * * Sends a DNS query with specified @a type and @a domain to the DNS server. * The sres resolver takes care of retransmitting the query if * sres_resolver_timer() is called in regular intervals. It generates an * error record with nonzero status if no response is received. * * This function just makes sure that we have the @a socket is valid, * otherwise it behaves exactly like sres_query(). */sres_query_t *sres_query_make(sres_resolver_t *res, sres_answer_f *callback, sres_context_t *context, int socket, uint16_t type,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -