diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index a281197..f11c3b0 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -109,7 +109,7 @@ cache-size 16384 # rr-ttl: ttl for all record # rr-ttl-min: minimum ttl for resource record # rr-ttl-max: maximum ttl for resource record -# tr-ttl-reply-max: maximum reply ttl for resource record +# rr-ttl-reply-max: maximum reply ttl for resource record # example: # rr-ttl 300 # rr-ttl-min 60 @@ -120,6 +120,10 @@ cache-size 16384 # example: # max-reply-ip-num 1 +# response mode +# Experimental feature +# response-mode [first-ping|fastest-ip|fastest-response] + # set log level # log-level: [level], level=fatal, error, warn, notice, info, debug # log-file: file path of log file. diff --git a/src/dns_cache.h b/src/dns_cache.h index d26fdf4..76f21bd 100644 --- a/src/dns_cache.h +++ b/src/dns_cache.h @@ -31,7 +31,7 @@ extern "C" { #endif -#define DNS_CACHE_TTL_MIN 30 +#define DNS_CACHE_TTL_MIN 1 #define DNS_CACHE_VERSION_LEN 32 #define MAGIC_NUMBER 0x6548634163536e44 #define MAGIC_CACHE_DATA 0x44615461 diff --git a/src/dns_client.c b/src/dns_client.c index e439cd1..8d40436 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -1988,6 +1988,11 @@ static int _dns_client_socket_ssl_send(struct dns_server_info *server, const voi return -1; } + if (num < 0) { + errno = EINVAL; + return -1; + } + ret = _ssl_write(server, buf, num); if (ret > 0) { return ret; @@ -2075,7 +2080,7 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf return 0; } - tlog(TLOG_ERROR, "SSL read fail error no: %s(%lx)\n", ERR_reason_error_string(ssl_err), ssl_err); + tlog(TLOG_INFO, "SSL read fail error no: %s(%lx), len: %d\n", ERR_reason_error_string(ssl_err), ssl_err, num); errno = EFAULT; ret = -1; break; diff --git a/src/dns_conf.c b/src/dns_conf.c index 6a445a3..c54a056 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -59,6 +59,14 @@ int dns_conf_tcp_idle_time = 120; int dns_conf_max_reply_ip_num = DNS_MAX_REPLY_IP_NUM; +static struct config_enum_list dns_conf_response_mode_enum[] = { + {"first-ping", DNS_RESPONSE_MODE_FIRST_PING_IP}, + {"fastest-ip", DNS_RESPONSE_MODE_FASTEST_IP}, + {"fastest-response", DNS_RESPONSE_MODE_FASTEST_RESPONSE}, + {0, 0}}; + +enum response_mode_type dns_conf_response_mode; + /* cache */ int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE; int dns_conf_prefetch = 0; @@ -1022,7 +1030,7 @@ static int _config_bind_ip(int argc, char *argv[], DNS_BIND_TYPE type) bind_ip->flags = server_flag; bind_ip->group = group; dns_conf_bind_ip_num++; - tlog(TLOG_DEBUG, "bind ip %s, type:%d, flag: %X", ip, type, server_flag); + tlog(TLOG_DEBUG, "bind ip %s, type: %d, flag: %X", ip, type, server_flag); return 0; @@ -1897,6 +1905,7 @@ static struct config_item _config_item[] = { CONF_INT("rr-ttl-max", &dns_conf_rr_ttl_max, 0, CONF_INT_MAX), CONF_INT("rr-ttl-reply-max", &dns_conf_rr_ttl_reply_max, 0, CONF_INT_MAX), CONF_INT("max-reply-ip-num", &dns_conf_max_reply_ip_num, 1, CONF_INT_MAX), + CONF_ENUM("response-mode", &dns_conf_response_mode, &dns_conf_response_mode_enum), CONF_YESNO("force-AAAA-SOA", &dns_conf_force_AAAA_SOA), CONF_YESNO("force-no-CNAME", &dns_conf_force_no_cname), CONF_CUSTOM("force-qtype-SOA", _config_qtype_soa, NULL), @@ -2069,6 +2078,12 @@ static int _dns_conf_load_post(void) { _dns_conf_speed_check_mode_verify(); + if (dns_conf_cachesize == 0 && dns_conf_response_mode == DNS_RESPONSE_MODE_FASTEST_RESPONSE) { + dns_conf_response_mode = DNS_RESPONSE_MODE_FASTEST_IP; + tlog(TLOG_WARN, "force set response to %s as cache size is 0", + dns_conf_response_mode_enum[dns_conf_response_mode].name); + } + return 0; } diff --git a/src/dns_conf.h b/src/dns_conf.h index 2af259c..8e62b99 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -298,6 +298,12 @@ extern int dns_conf_dualstack_ip_allow_force_AAAA; extern int dns_conf_dualstack_ip_selection_threshold; extern int dns_conf_max_reply_ip_num; +enum response_mode_type { + DNS_RESPONSE_MODE_FIRST_PING_IP = 0, + DNS_RESPONSE_MODE_FASTEST_IP, + DNS_RESPONSE_MODE_FASTEST_RESPONSE, +}; +extern enum response_mode_type dns_conf_response_mode; extern int dns_conf_rr_ttl; extern int dns_conf_rr_ttl_reply_max; diff --git a/src/dns_server.c b/src/dns_server.c index a16c98c..84e15db 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -110,6 +110,8 @@ struct dns_server_post_context { int do_ipset; int do_log_result; int reply_ttl; + int cache_ttl; + int no_check_add_ip; int do_audit; int do_force_soa; int skip_notify_count; @@ -477,6 +479,11 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) continue; } + if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + const char *fmt = "%d.%d.%d.%d"; if (ip_num > 0) { fmt = ", %d.%d.%d.%d"; @@ -492,6 +499,12 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) if (dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, ipv6_addr) != 0) { continue; } + + if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + const char *fmt = "%s"; if (ip_num > 0) { fmt = ", %s"; @@ -543,8 +556,9 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) snprintf(req_time, sizeof(req_time), "[%.4d-%.2d-%.2d %.2d:%.2d:%.2d,%.3d]", tm.year, tm.mon, tm.mday, tm.hour, tm.min, tm.sec, tm.usec / 1000); - tlog_printf(dns_audit, "%s %s query %s, time %lums, type %d, result %s\n", req_time, req_host, request->domain, - get_tick_count() - request->send_tick, request->qtype, req_result); + tlog_printf(dns_audit, "%s %s query %s, type %d, time %lums, speed: %.1fms, result %s\n", req_time, req_host, + request->domain, request->qtype, get_tick_count() - request->send_tick, ((float)request->ping_time) / 10, + req_result); } static void _dns_rrs_result_log(struct dns_server_post_context *context, struct dns_ip_address *addr_map) @@ -909,7 +923,7 @@ static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpac } static int _dns_server_request_update_cache(struct dns_request *request, dns_type_t qtype, - struct dns_cache_data *cache_data, int has_soa) + struct dns_cache_data *cache_data, int has_soa, int cache_ttl) { int ttl = 0; int speed = 0; @@ -918,7 +932,11 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ goto errout; } - ttl = _dns_server_get_conf_ttl(request->ip_ttl); + if (cache_ttl > 0) { + ttl = cache_ttl; + } else { + ttl = _dns_server_get_conf_ttl(request->ip_ttl); + } speed = request->ping_time; if (has_soa) { @@ -926,11 +944,14 @@ static int _dns_server_request_update_cache(struct dns_request *request, dns_typ ttl = _dns_server_get_conf_ttl(request->ip_ttl); } else { ttl = dns_conf_rr_ttl; + if (ttl == 0) { + ttl = DNS_SERVER_TMOUT_TTL; + } } dns_cache_set_data_soa(cache_data, request->server_flags, request->cname, request->ttl_cname); } - tlog(TLOG_DEBUG, "cache %s qtype:%d ttl: %d\n", request->domain, qtype, ttl); + tlog(TLOG_DEBUG, "cache %s qtype: %d ttl: %d\n", request->domain, qtype, ttl); /* if doing prefetch, update cache only */ if (request->prefetch) { @@ -1006,6 +1027,10 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context) continue; } + if (strncmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + ret = dns_add_A(cname_packet, DNS_RRS_AN, request->cname, ttl, ipv4_addr); if (ret != 0) { return -1; @@ -1018,6 +1043,10 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context) continue; } + if (strncmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + ret = dns_add_AAAA(cname_packet, DNS_RRS_AN, request->cname, ttl, ipv6_addr); if (ret != 0) { return -1; @@ -1234,7 +1263,7 @@ static int _dns_cache_reply_packet(struct dns_server_post_context *context) has_soa = 0; } - if (_dns_server_request_update_cache(request, context->qtype, cache_packet, has_soa) != 0) { + if (_dns_server_request_update_cache(request, context->qtype, cache_packet, has_soa, context->cache_ttl) != 0) { tlog(TLOG_WARN, "update packet cache failed."); } @@ -1937,6 +1966,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch int may_complete = 0; int threshold = 100; struct dns_ip_address *addr_map = NULL; + int last_rtt = request->ping_time; if (request == NULL) { return; @@ -1948,6 +1978,7 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch return; } else if (result == PING_RESULT_TIMEOUT) { tlog(TLOG_DEBUG, "ping %s timeout", host); + goto out; return; } else if (result == PING_RESULT_ERROR) { if (addr->sa_family != AF_INET6) { @@ -1965,7 +1996,6 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch } int rtt = tv->tv_sec * 10000 + tv->tv_usec / 100; - int last_rtt = request->ping_time; if (result == PING_RESULT_RESPONSE) { tlog(TLOG_DEBUG, "from %s: seq=%d time=%d, lasttime=%d id=%d", host, seqno, rtt, last_rtt, request->id); @@ -2058,10 +2088,18 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch break; } +out: /* If the ping delay is less than the threshold, the result is returned */ - if (rtt < threshold) { - may_complete = 1; - } else if (rtt < (int)(get_tick_count() - request->send_tick) * 8) { + if (request->ping_time > 0) { + if (request->ping_time < threshold) { + may_complete = 1; + } else if (request->ping_time < (int)(get_tick_count() - request->send_tick) * 8) { + may_complete = 1; + } + } + + /* Get first ping result */ + if (dns_conf_response_mode == DNS_RESPONSE_MODE_FIRST_PING_IP && last_rtt == -1 && request->ping_time > 0) { may_complete = 1; } @@ -2204,8 +2242,8 @@ static int _dns_server_is_adblock_ipv6(const unsigned char addr[16]) return -1; } -static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request *request, const char *domain, char *cname, - unsigned int result_flag) +static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request *request, const char *domain, + char *cname, unsigned int result_flag) { int ttl = 0; int ip_check_result = 0; @@ -2282,8 +2320,8 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request return 0; } -static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_request *request, const char *domain, char *cname, - unsigned int result_flag) +static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_request *request, const char *domain, + char *cname, unsigned int result_flag) { unsigned char addr[16]; char name[DNS_MAX_CNAME_LEN] = {0}; @@ -2408,10 +2446,17 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d case DNS_T_NS: { char nsname[DNS_MAX_CNAME_LEN]; dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, nsname, DNS_MAX_CNAME_LEN); - tlog(TLOG_DEBUG, "NS: %s ttl:%d nsname: %s\n", name, ttl, nsname); + tlog(TLOG_DEBUG, "NS: %s ttl: %d nsname: %s\n", name, ttl, nsname); } break; case DNS_T_CNAME: { - dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); + char domain_name[DNS_MAX_CNAME_LEN] = {0}; + char domain_cname[DNS_MAX_CNAME_LEN] = {0}; + dns_get_CNAME(rrs, domain_name, DNS_MAX_CNAME_LEN, &ttl, domain_cname, DNS_MAX_CNAME_LEN); + if (strncmp(domain_name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncmp(domain_name, cname, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + safe_strncpy(cname, domain_cname, DNS_MAX_CNAME_LEN); tlog(TLOG_DEBUG, "name: %s ttl: %d cname: %s\n", name, ttl, cname); } break; case DNS_T_SOA: { @@ -2440,8 +2485,8 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d return 0; } -static int _dns_server_passthrough_rule_check(struct dns_request *request, const char *domain, struct dns_packet *packet, - unsigned int result_flag, int *pttl) +static int _dns_server_passthrough_rule_check(struct dns_request *request, const char *domain, + struct dns_packet *packet, unsigned int result_flag, int *pttl) { int ttl = 0; char name[DNS_MAX_CNAME_LEN] = {0}; @@ -2486,7 +2531,7 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const continue; } - tlog(TLOG_DEBUG, "domain: %s TTL:%d IP: %d.%d.%d.%d", name, ttl_tmp, addr[0], addr[1], addr[2], + tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %d.%d.%d.%d", name, ttl_tmp, addr[0], addr[1], addr[2], addr[3]); /* ip rule check */ @@ -2575,7 +2620,13 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); - if (_dns_ip_address_check_add(request, name, addr, DNS_T_A) != 0) { + + if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + + if (context->no_check_add_ip == 0 && _dns_ip_address_check_add(request, name, addr, DNS_T_A) != 0) { continue; } @@ -2599,7 +2650,13 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) continue; } dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); - if (_dns_ip_address_check_add(request, name, addr, DNS_T_AAAA) != 0) { + + if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + + if (context->no_check_add_ip == 0 && _dns_ip_address_check_add(request, name, addr, DNS_T_AAAA) != 0) { continue; } @@ -2617,7 +2674,7 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) char cname[DNS_MAX_CNAME_LEN]; char name[DNS_MAX_CNAME_LEN] = {0}; dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); - tlog(TLOG_DEBUG, "NS: %s ttl:%d cname: %s\n", name, ttl, cname); + tlog(TLOG_DEBUG, "NS: %s ttl: %d cname: %s\n", name, ttl, cname); } break; case DNS_T_CNAME: { char cname[DNS_MAX_CNAME_LEN]; @@ -2627,7 +2684,12 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) } dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); - tlog(TLOG_DEBUG, "name:%s ttl: %d cname: %s\n", name, ttl, cname); + tlog(TLOG_DEBUG, "name: %s ttl: %d cname: %s\n", name, ttl, cname); + if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + continue; + } + safe_strncpy(request->cname, cname, DNS_MAX_CNAME_LEN); request->ttl_cname = _dns_server_get_conf_ttl(ttl); request->has_cname = 1; @@ -2644,9 +2706,6 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) "%d, minimum: %d", request->domain, request->qtype, request->soa.mname, request->soa.rname, request->soa.serial, request->soa.refresh, request->soa.retry, request->soa.expire, request->soa.minimum); - if (atomic_inc_return(&request->soa_num) >= (dns_server_num() / 2)) { - _dns_server_request_complete(request); - } } break; default: break; @@ -2754,10 +2813,10 @@ static int dns_server_resolve_callback(const char *domain, dns_result_type rtype } if (rtype == DNS_QUERY_RESULT) { - tlog(TLOG_DEBUG, "query result from server %s:%d, type: %d", dns_client_get_server_ip(server_info), + tlog(TLOG_DEBUG, "query result from server %s: %d, type: %d", dns_client_get_server_ip(server_info), dns_client_get_server_port(server_info), dns_client_get_server_type(server_info)); - if (request->passthrough) { + if (request->passthrough && atomic_read(&request->notified) == 0) { struct dns_server_post_context context; int ttl = 0; ret = _dns_server_passthrough_rule_check(request, domain, packet, result_flag, &ttl); @@ -2778,6 +2837,33 @@ static int dns_server_resolve_callback(const char *domain, dns_result_type rtype context.reply_ttl = ttl; return _dns_server_reply_passthrouth(&context); } + + if (request->prefetch == 0 && dns_conf_response_mode == DNS_RESPONSE_MODE_FASTEST_RESPONSE && + atomic_read(&request->notified) == 0) { + struct dns_server_post_context context; + int ttl = 0; + ret = _dns_server_passthrough_rule_check(request, domain, packet, result_flag, &ttl); + if (ret != 0) { + _dns_server_post_context_init_from(&context, request, packet, inpacket, inpacket_len); + context.do_cache = 1; + context.do_audit = 1; + context.do_reply = 1; + context.do_ipset = 1; + context.reply_ttl = 2; + context.cache_ttl = 2; + context.no_check_add_ip = 1; + _dns_server_reply_passthrouth(&context); + request->cname[0] = 0; + request->has_ip = 0; + request->has_cname = 0; + request->has_ping_result = 0; + request->has_soa = 0; + request->has_ptr = 0; + request->ping_time = -1; + request->ip_ttl = 0; + } + } + _dns_server_process_answer(request, domain, packet, result_flag); return 0; } else if (rtype == DNS_QUERY_ERR) { diff --git a/src/include/conf.h b/src/include/conf.h index 4050bb5..f1863de 100644 --- a/src/include/conf.h +++ b/src/include/conf.h @@ -64,6 +64,16 @@ struct config_item_size { size_t max; }; +struct config_enum_list { + char *name; + int id; +}; + +struct config_enum { + int *data; + struct config_enum_list *list; +}; + #define CONF_INT(key, value, min_value, max_value) \ { \ key, conf_int, &(struct config_item_int) \ @@ -92,6 +102,15 @@ struct config_item_size { .data = value, .min = min_value, .max = max_value \ } \ } + +#define CONF_ENUM(key, value, enum) \ + { \ + key, conf_enum, &(struct config_enum) \ + { \ + .data = (int *)value, .list = (struct config_enum_list *)enum \ + } \ + } + /* * func: int (*func)(void *data, int argc, char *argv[]); */ @@ -118,6 +137,8 @@ extern int conf_yesno(const char *item, void *data, int argc, char *argv[]); extern int conf_size(const char *item, void *data, int argc, char *argv[]); +extern int conf_enum(const char *item, void *data, int argc, char *argv[]); + /* * Example: * int num = 0; diff --git a/src/lib/conf.c b/src/lib/conf.c index 308fe3a..63713b2 100644 --- a/src/lib/conf.c +++ b/src/lib/conf.c @@ -97,7 +97,6 @@ int conf_yesno(const char *item, void *data, int argc, char *argv[]) int conf_size(const char *item, void *data, int argc, char *argv[]) { - /* read dns cache size */ int base = 1; size_t size = 0; int num = 0; @@ -129,6 +128,31 @@ int conf_size(const char *item, void *data, int argc, char *argv[]) return 0; } +int conf_enum(const char *item, void *data, int argc, char *argv[]) +{ + struct config_enum *item_enum = data; + char *enum_name = argv[1]; + int i = 0; + + if (argc <= 0) { + return -1; + } + + for (i = 0; item_enum->list[i].name != NULL; i++) { + if (strcmp(enum_name, item_enum->list[i].name) == 0) { + *(item_enum->data) = item_enum->list[i].id; + return 0; + } + } + + printf("Not found config value '%s', valid value is:\n", enum_name); + for (i = 0; item_enum->list[i].name != NULL; i++) { + printf(" %s\n", item_enum->list[i].name); + } + + return -1; +} + static void conf_getopt_reset(void) { static struct option long_options[] = {{"-", 0, 0, 0}, {0, 0, 0, 0}}; diff --git a/src/smartdns.c b/src/smartdns.c index 0a170c4..bfbdbb3 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -348,7 +348,7 @@ static int _smartdns_init(void) tlog_setlogscreen(verbose_screen); tlog_setlevel(dns_conf_log_level); - tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng , build:%s %s)", __DATE__, + tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng , build: %s %s)", __DATE__, __TIME__); if (_smartdns_init_ssl() != 0) {