📄 sresolv.c
字号:
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; switch (a->r_type) { case sres_type_soa: { sres_soa_record_t const *A = aa->sr_soa, *B = bb->sr_soa; D = A->soa_serial - B->soa_serial; if (D) return D; D = strcasecmp(A->soa_mname, B->soa_mname); if (D) return D; D = strcasecmp(A->soa_rname, B->soa_rname); if (D) return D; 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); }#if SU_HAVE_IN6 case sres_type_a6: { sres_a6_record_t const *A = aa->sr_a6, *B = bb->sr_a6; D = A->a6_prelen - B->a6_prelen; if (D) return D; D = !A->a6_prename - !B->a6_prename; if (D == 0 && A->a6_prename && B->a6_prename) D = strcasecmp(A->a6_prename, B->a6_prename); if (D) return D; return memcmp(&A->a6_suffix, &B->a6_suffix, sizeof A->a6_suffix); } case sres_type_aaaa: { sres_aaaa_record_t const *A = aa->sr_aaaa, *B = bb->sr_aaaa; return memcmp(&A->aaaa_addr, &B->aaaa_addr, sizeof A->aaaa_addr); }#endif case sres_type_cname: { sres_cname_record_t const *A = aa->sr_cname, *B = bb->sr_cname; return strcmp(A->cn_cname, B->cn_cname); } case sres_type_ptr: { sres_ptr_record_t const *A = aa->sr_ptr, *B = bb->sr_ptr; return strcmp(A->ptr_domain, B->ptr_domain); } case sres_type_srv: { sres_srv_record_t const *A = aa->sr_srv, *B = bb->sr_srv; D = A->srv_priority - B->srv_priority; if (D) return D; /* Record with larger weight first */ D = B->srv_weight - A->srv_weight; if (D) return D; D = strcmp(A->srv_target, B->srv_target); if (D) return D; return A->srv_port - B->srv_port; } case sres_type_naptr: { sres_naptr_record_t const *A = aa->sr_naptr, *B = bb->sr_naptr; D = A->na_order - B->na_order; if (D) return D; D = A->na_prefer - B->na_prefer; if (D) return D; D = strcmp(A->na_flags, B->na_flags); if (D) return D; D = strcmp(A->na_services, B->na_services); if (D) return D; D = strcmp(A->na_regexp, B->na_regexp); if (D) return D; return strcmp(A->na_replace, B->na_replace); } default: return 0; }}static intsres_sockaddr2string(char name[], size_t namelen, struct sockaddr const *addr){ name[0] = '\0'; if (addr->sa_family == AF_INET) { struct sockaddr_in const *sin = (struct sockaddr_in *)addr; uint8_t const *in_addr = (uint8_t*)&sin->sin_addr; return snprintf(name, namelen, "%u.%u.%u.%u.in-addr.arpa.", in_addr[3], in_addr[2], in_addr[1], in_addr[0]); }#if HAVE_SIN6 else if (addr->sa_family == AF_INET6) { struct sockaddr_in6 const *sin6 = (struct sockaddr_in6 *)addr; int addrsize = sizeof(sin6->sin6_addr.s6_addr); int required = addrsize * 4 + strlen("ip6.int."); int i; if (namelen <= required) return required; for (i = 0; i < addrsize; i++) { uint8_t byte = sin6->sin6_addr.s6_addr[addrsize - i - 1]; uint8_t hex; hex = byte & 0xf; name[4 * i] = hex > 9 ? hex + 'a' - 10 : hex + '0'; name[4 * i + 1] = '.'; hex = (byte >> 4) & 0xf; name[4 * i + 2] = hex > 9 ? hex + 'a' - 10 : hex + '0'; name[4 * i + 3] = '.'; } strcpy(name + 4 * i, "ip6.int"); return required; }#endif else { su_seterrno(EAFNOSUPPORT); SU_DEBUG_3(("%s: %s\n", "sres_sockaddr2string", su_strerror(EAFNOSUPPORT))); return 0; }}/** Parse /etc/resolv.conf file. * * @retval 0 when successful * @retval -1 upon an error * * @todo The resolv.conf directives @b sortlist and options * are currently ignored. */static intsres_parse_resolv_conf(sres_resolver_t *res, const char *filename){ char buf[PATH_MAX]; FILE *f; int line; su_home_t *home = res->res_home; su_strlst_t *dns_list; sres_server_t *dns; int num_servers, i, search = 0; char const *localdomain = getenv("LOCALDOMAIN"); res->res_search[0] = localdomain; dns_list = su_strlst_create(home); if (filename) res->res_config = su_strdup(res->res_home, filename); else res->res_config = filename = "/etc/resolv.conf"; f = fopen(filename, "r"); if (f != NULL) { for (line = 1; fgets(buf, sizeof(buf), f); line++) { int n; char *value, *b; /* Skip whitespace at the beginning ...*/ b = buf + strspn(buf, " \t"); /* ... and at the end of line */ for (n = strlen(b); n > 0 && strchr(" \t\r\n", b[n - 1]); n--) ; if (n == 0 || b[0] == '#') /* Empty line or comment */ continue; b[n] = '\0'; n = strcspn(b, " \t"); value = b + n; value += strspn(value, " \t");#define MATCH(token) (n == strlen(token) && strncasecmp(token, b, n) == 0) if (MATCH("nameserver")) { su_strlst_dup_append(dns_list, value); } else if (MATCH("domain")) { if (localdomain) continue; memset(res->res_search, 0, sizeof res->res_search); res->res_search[0] = su_strdup(home, value); } else if (MATCH("search")) { if (localdomain) continue; memset(res->res_search, 0, sizeof res->res_search); while (value[0] && search < SRES_MAX_SEARCH) { n = strcspn(value, " \t\r\n"); res->res_search[search++ + 1] = su_strndup(home, value, n); value += n + strspn(value + n, " \t\r\n"); } } else if (MATCH("port")) { unsigned long port = strtoul(value, NULL, 10); res->res_port = port; } } fclose(f); } num_servers = su_strlst_len(dns_list); if (num_servers == 0) { su_strlst_dup_append(dns_list, "127.0.0.1"); num_servers++; } res->res_n_servers = num_servers; dns = su_zalloc(res->res_home, num_servers * sizeof(*res->res_servers)); res->res_servers = dns; if (!dns) return -1; for (i = 0; i < num_servers; i++) { const char* server = su_strlst_item(dns_list, i); struct sockaddr *sa = (struct sockaddr *)dns->dns_addr; int err;#if HAVE_SIN6 && 0 /* Disabled for now */ if (strchr(server, ':')) { struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; memset(sa, 0, dns->dns_addrlen = sizeof *sin6); err = inet_pton(sa->sa_family = AF_INET6, server, &sin6->sin6_addr); sin6->sin6_port = htons(res->res_port); } else #endif { struct sockaddr_in *sin = (struct sockaddr_in *)sa; memset(sa, 0, dns->dns_addrlen = sizeof *sin); err = inet_pton(sa->sa_family = AF_INET, server, &sin->sin_addr); sin->sin_port = htons(res->res_port); } if (err <= 0) { res->res_n_servers--; SU_DEBUG_3(("sres: nameserver %s: invalid address\n", server)); continue; }#if HAVE_SA_LEN sa->sa_len = dns->dns_addrlen;#endif dns->dns_name = su_strdup(res->res_home, server); dns->dns_edns = 1; dns++; } su_strlst_destroy(dns_list); return res->res_n_servers;}/** Send a query packet */static int sres_send_dns_query(sres_resolver_t *res, sres_query_t *q){ sres_message_t m[1]; int i, i0, N = res->res_n_servers; int transient, error; unsigned size, no_edns_size, edns_size; uint16_t id = q->q_id; uint16_t type = q->q_type; char const *domain = q->q_name; sres_server_t *dns; SU_DEBUG_9(("sres_send_dns_query(%p, %p) called\n", res, q)); if (domain == NULL) return -1; memset(m, 0, offsetof(sres_message_t, m_data[sizeof m->m_packet.mp_header])); /* Create a DNS message */ m->m_size = sizeof(m->m_data); m->m_offset = size = sizeof(m->m_packet.mp_header); m->m_id = id; m->m_flags = htons(SRES_HDR_QUERY | SRES_HDR_RD); /* Query record */ m->m_qdcount = htons(1); m_put_domain(m, domain, 0, NULL); m_put_uint16(m, type); m_put_uint16(m, sres_class_in); no_edns_size = m->m_offset; /* EDNS0 record (optional) */ m_put_domain(m, ".", 0, NULL); m_put_uint16(m, sres_type_opt); m_put_uint16(m, sizeof(m->m_packet)); /* Class: our UDP payload size */ m_put_uint32(m, 0); /* TTL: extended RCODE & flags */ m_put_uint16(m, 0); edns_size = m->m_offset; if (m->m_error) { SU_DEBUG_3(("%s(): encoding: %s\n", "sres_send_dns_query", m->m_error)); su_seterrno(EIO); return -1; } i = i0 = q->q_i_server; assert(i0 < N); transient = 0; for (;;) { dns = res->res_servers + i; /* If server supports EDNS, include EDNS0 record */ q->q_edns = dns->dns_edns != 0; /* 0 (no EDNS) or 1 (EDNS supported) additional data records */ m->m_arcount = htons(q->q_edns); /* Size with or without EDNS record */ size = q->q_edns ? edns_size : no_edns_size; /* Send the DNS message to the UDP socket */ if (sendto(q->q_socket, m->m_data, size, 0, (struct sockaddr *)dns->dns_addr, dns->dns_addrlen) == size) break; error = su_errno(); /* EINVAL is returned if destination address is bad */ if (transient++ < 3 && error != EINVAL) continue; transient = 0; dns->dns_icmp_error = res->res_now; /* Mark as a bad destination */ /* Retry using another server */ for (i = (i + 1) % N; res->res_servers[i].dns_icmp_error; i = (i + 1) % N) { if (i == i0) { /* All servers have reported errors */ SU_DEBUG_5(("%s(): sendto: %s\n", "sres_send_dns_query", su_strerror(error))); return su_seterrno(error); } } } q->q_i_server = i; SU_DEBUG_5(("%s(%p, %p) id=%u %u? %s (to [%s]:%u)\n", "sres_send_dns_query", res, q, id, type, domain, dns->dns_name, res->res_port)); return 0;}/** * Callback function for subqueries */staticvoid sres_answer_subquery(sres_context_t *context, sres_query_t *query, sres_record_t **answers){ sres_resolver_t *res; sres_query_t *top = (sres_query_t *)context; int i; assert(top); assert(top->q_n_subs > 0); assert(query); res = query->q_res; LOCK(query->q_res); for (i = 0; i <= SRES_MAX_SEARCH; i++) { if (top->q_subqueries[i] == query) break; } assert(i <= SRES_MAX_SEARCH); if (i > SRES_MAX_SEARCH || top->q_n_subs == 0) { _sres_free_answers(res, answers); UNLOCK(res); return; } if (answers) { int j, k; for (j = 0, k = 0; answers[j]; j++) { if (answers[j]->sr_status) _sres_free_answer(query->q_res, answers[j]); else answers[k++] = answers[j]; } answers[k] = NULL; if (!answers[0]) _sres_free_answers(query->q_res, answers), answers = NULL; } top->q_subqueries[i] = NULL; top->q_subanswers[i] = answers; if (--top->q_n_subs == 0 && top->q_id == 0) { sres_query_report_error(top->q_res, top, NULL); }; UNLOCK(res);}/** Report sres error */static voidsres_query_report_error(sres_resolver_t *res, sres_query_t *q, sres_record_t **answers){ int i; if (q->q_callback) { for (i = 0; i <= SRES_MAX_SEARCH; i++) { if (q->q_subqueries[i]) /* a pending query... */ return; if (q->q_subanswers[i]) { answers = q->q_subanswers[i]; q->q_subanswers[i] = NULL; break; } } SU_DEBUG_5(("sres(%p): reporting errors for %u %s\n", res, q->q_type, q->q_name)); sres_remove_query(res, q, 1); UNLOCK(res); (q->q_callback)(q->q_context, q, answers); LOCK(res); } sres_free_query(res, q);}/** Resolver timer function. * * The function sresolver_timer() should be called in regular intervals. We * recommend calling it in 500 ms intervals. * */void sres_resolver_timer(sres_resolver_t *res, int socket){ int i; sres_query_t *q; time_t now, retry_time; if (res == NULL) return; if (!LOCK(res)) return; now = time(&res->res_now); if (res->res_queries->qt_used) { /** Every time it is called it goes through all query structures, and * retransmits all the query messages, which have not been answered yet. */ for (i = 0; i < res->res_queries->qt_size; i++) { q = res->res_queries->qt_table[i]; if (!q || q->q_socket != socket) continue; /* Exponential backoff */ retry_time = q->q_timestamp + (1 << q->q_retry_count); if (now < retry_time) continue; sres_resend_dns_query(res, q, 1); if (q != res->res_queries->qt_table[i]) i--; } } /** Every 30 seconds it goes through the cache and removes outdated entries. */ if (res->res_now > res->res_cache_cleaned + SRES_CACHE_TIMER_INTERVAL) { /* Clean cache from old entries */ res->res_cache_cleaned = res->res_now; for (i = 0; i < res->res_cache->ht_size; i++) { sres_rr_hash_entry_t *e; while ((e = res->res_cache->ht_table[i]) != NULL) { if (res->res_now - e->rr_received <= e->rr->sr_ttl) break; sres_htable_remove(res->res_cache, e); _sres_free_answer(res, e->rr); } } } UNLOCK(res);}/** Resend DNS query, report error cannot resend any more. */voidsres_resend_dns_query(sres_resolver_t *res, sres_query_t *q, int timeout){ int i, N; SU_DEBUG_9(("sres_resend_dns_query(%p, %p, %u) called\n",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -