diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 9d8d53e..c86d8e4 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -36,9 +36,14 @@ cache-size 512 # List of IPs that will be filtered when nameserver is configured -blacklist-ip parameter # blacklist-ip [ip/subnet] -# force AAAA query return SO +# force AAAA query return SOA # force-AAAA-SOA [yes|no] +# enabel ipv4 and ipv6 preference +# dualstack-preference-threshold [num] (100~10000) +# dualstack-preference [yes|no] +# dualstack-preference yes + # ttl for all resource record # rr-ttl: ttl for all record # rr-ttl-min: minimum ttl for resource record diff --git a/src/dns_conf.c b/src/dns_conf.c index 9e82d47..28d7c40 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -36,6 +36,9 @@ int dns_conf_audit_num = 2; art_tree dns_conf_domain_rule; radix_tree_t *dns_conf_address_rule; +int dns_conf_dualstack_preference; +int dns_conf_dualstack_threshold = 1000; // cent usecond + int dns_conf_rr_ttl; int dns_conf_rr_ttl_min; int dns_conf_rr_ttl_max; @@ -514,6 +517,8 @@ struct config_item config_item[] = { CONF_INT("tcp-idle-time", &dns_conf_tcp_idle_time, 0, 3600), CONF_INT("cache-size", &dns_conf_cachesize, 0, CONF_INT_MAX), CONF_YESNO("prefetch-domain", &dns_conf_prefetch), + CONF_YESNO("dualstack-preference", &dns_conf_dualstack_preference), + CONF_INT("dualstack-preference-threshold", &dns_conf_dualstack_threshold, 100, 10000), CONF_CUSTOM("log-level", config_log_level, NULL), CONF_STRING("log-file", (char *)dns_conf_log_file, DNS_MAX_PATH), CONF_SIZE("log-size", &dns_conf_log_size, 0, 1024 * 1024 * 1024), diff --git a/src/dns_conf.h b/src/dns_conf.h index f949e06..a8fc1bd 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -99,6 +99,9 @@ extern char dns_conf_server_name[DNS_MAX_CONF_CNAME_LEN]; extern art_tree dns_conf_domain_rule; extern radix_tree_t *dns_conf_address_rule; +extern int dns_conf_dualstack_preference; +extern int dns_conf_dualstack_threshold; + extern int dns_conf_rr_ttl; extern int dns_conf_rr_ttl_min; extern int dns_conf_rr_ttl_max; diff --git a/src/dns_server.c b/src/dns_server.c index b2e5742..f4356f4 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -148,7 +148,7 @@ struct dns_request { /* send original raw packet to server/client like proxy */ int passthrough; - + int request_wait; int prefetch; pthread_mutex_t ip_map_lock; @@ -419,6 +419,30 @@ static int _dns_reply(struct dns_request *request) return _dns_reply_inpacket(request, inpacket, encode_len); } +static int _dns_server_reply_SOA(int rcode, struct dns_request *request, struct dns_packet *packet) +{ + struct dns_soa *soa; + + request->rcode = rcode; + request->has_soa = 1; + request->has_ipv4 = 0; + request->has_ipv6 = 0; + request->has_ptr = 0; + + soa = &request->soa; + + strcpy(soa->mname, "a.gtld-servers.net"); + strcpy(soa->rname, "nstld.verisign-grs.com"); + soa->serial = 1800; + soa->refresh = 1800; + soa->retry = 900; + soa->expire = 604800; + soa->minimum = 86400; + _dns_reply(request); + + return 0; +} + static int _dns_setup_ipset(struct dns_request *request) { struct dns_ipset_rule *ipset_rule = NULL; @@ -489,6 +513,15 @@ int _dns_server_request_complete(struct dns_request *request) request->ipv6_addr[6], request->ipv6_addr[7], request->ipv6_addr[8], request->ipv6_addr[9], request->ipv6_addr[10], request->ipv6_addr[11], request->ipv6_addr[12], request->ipv6_addr[13], request->ipv6_addr[14], request->ipv6_addr[15]); + if (request->has_ipv4) { + dns_cache_insert(request->domain, cname, cname_ttl, request->ttl_v4, DNS_T_AAAA, request->ipv4_addr, DNS_RR_A_LEN); + + if ((request->ping_ttl_v4 - dns_conf_dualstack_threshold < request->ping_ttl_v6 ) && (request->ping_ttl_v4 > 0)) { + tlog(TLOG_DEBUG, "Force IPV4 perfered."); + return _dns_server_reply_SOA(DNS_RC_NOERROR, request, NULL); + } + } + if (request->has_ipv6) { if (request->has_ping_result == 0 && request->ttl_v6 > DNS_SERVER_TMOUT_TTL) { request->ttl_v6 = DNS_SERVER_TMOUT_TTL; @@ -566,6 +599,7 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos { struct dns_request *request = userptr; int may_complete = 0; + int threshold = 100; if (request == NULL) { return; @@ -579,6 +613,7 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos unsigned int rtt = tv->tv_sec * 10000 + tv->tv_usec / 100; + switch (addr->sa_family) { case AF_INET: { struct sockaddr_in *addr_in; @@ -588,6 +623,10 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos request->has_ipv4 = 1; memcpy(request->ipv4_addr, &addr_in->sin_addr.s_addr, 4); } + + if (dns_conf_dualstack_preference == 1 && request->qtype == DNS_T_AAAA) { + threshold = dns_conf_dualstack_threshold; + } } break; case AF_INET6: { struct sockaddr_in6 *addr_in6; @@ -616,7 +655,7 @@ void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *hos tlog(TLOG_DEBUG, "from %15s: seq=%d timeout\n", host, seqno); } - if (rtt < 100) { + if (rtt < threshold) { may_complete = 1; } else if (rtt < (get_tick_count() - request->send_tick) * 10) { may_complete = 1; @@ -763,7 +802,9 @@ static int _dns_server_process_answer(struct dns_request *request, char *domain, unsigned char addr[4]; if (request->qtype != DNS_T_A) { /* ignore non-matched query type */ - break; + if (dns_conf_dualstack_preference == 0) { + break; + } } _dns_server_request_get(request); dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); @@ -905,6 +946,7 @@ static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsi { struct dns_request *request = user_ptr; int ip_num = 0; + int request_wait = 0; if (request == NULL) { return -1; @@ -925,10 +967,12 @@ static int dns_server_resolve_callback(char *domain, dns_result_type rtype, unsi } else { pthread_mutex_lock(&request->ip_map_lock); ip_num = request->ip_map_num; + request_wait = request->request_wait; + request->request_wait--; pthread_mutex_unlock(&request->ip_map_lock); /* Not need to wait check result if only has one ip address */ - if (ip_num == 1) { + if (ip_num == 1 && request_wait == 1) { _dns_server_request_complete(request); } @@ -1014,27 +1058,6 @@ errout: return -1; } -static int _dns_server_reply_SOA(int rcode, struct dns_request *request, struct dns_packet *packet) -{ - struct dns_soa *soa; - - request->rcode = rcode; - request->has_soa = 1; - - soa = &request->soa; - - strcpy(soa->mname, "a.gtld-servers.net"); - strcpy(soa->rname, "nstld.verisign-grs.com"); - soa->serial = 1800; - soa->refresh = 1800; - soa->retry = 900; - soa->expire = 604800; - soa->minimum = 86400; - _dns_reply(request); - - return 0; -} - static void _dns_server_log_rule(char *domain, unsigned char *rule_key, int rule_key_len) { char rule_name[DNS_MAX_CNAME_LEN]; @@ -1278,7 +1301,14 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac _dns_server_request_get(request); request->send_tick = get_tick_count(); + dns_client_query(request->domain, qtype, dns_server_resolve_callback, request); + request->request_wait++; + if (qtype == DNS_T_AAAA && dns_conf_dualstack_preference) { + _dns_server_request_get(request); + dns_client_query(request->domain, DNS_T_A, dns_server_resolve_callback, request); + request->request_wait++; + } return 0; clean_exit: diff --git a/src/smartdns.c b/src/smartdns.c index 7fcdbb3..cdaf66d 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -285,7 +285,7 @@ void sig_error_exit(int signo, siginfo_t *siginfo, void *context) _exit(0); } -int sig_list[] = {SIGSEGV, SIGABRT, SIGPIPE, SIGBUS, SIGILL, SIGFPE}; +int sig_list[] = {SIGSEGV, SIGABRT, SIGBUS, SIGILL, SIGFPE}; int sig_num = sizeof(sig_list) / sizeof(int); @@ -362,11 +362,12 @@ int main(int argc, char *argv[]) } signal(SIGINT, sig_exit); + signal(SIGPIPE, SIG_IGN); atexit(smartdns_exit); return smartdns_run(); - + errout: return 1; -} \ No newline at end of file +}