diff --git a/ReadMe.md b/ReadMe.md index 5b8d58c7df..89ab865431 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -75,6 +75,8 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms 从对比看出,SmartDNS 找到了访问 www.baidu.com 最快的 IP 地址,比阿里 DNS 速度快了 5 倍。 ## 特性 +1. **多虚拟DNS服务器** + 支持多个虚拟DNS服务器,不同虚拟DNS服务器不同的端口,规则,客户端。 1. **多 DNS 上游服务器** 支持配置多个上游 DNS 服务器,并同时进行查询,即使其中有 DNS 服务器异常,也不会影响查询。 diff --git a/ReadMe_en.md b/ReadMe_en.md index 6f99c69793..fb540e201b 100644 --- a/ReadMe_en.md +++ b/ReadMe_en.md @@ -75,6 +75,9 @@ From the comparison, smartdns found the fastest IP address to visit www.baidu.co ## Features +1. **Multiple Virtual DNS server** + Support multiple virtual DNS servers with different ports, rules, and clients. + 1. **Multiple upstream DNS servers** Support configuring multiple upstream DNS servers and query at the same time.the query will not be affected, Even if there is a DNS server exception. @@ -82,7 +85,7 @@ From the comparison, smartdns found the fastest IP address to visit www.baidu.co Support controlling clients using different query rules based on MAC and IP addresses, enabling features such as parental control. 1. **Return the fastest IP address** - Supports finding the fastest access IP address from the IP address list of the domain name and returning it to the client to avoid DNS pollution and improve network access speed. + Support finding the fastest access IP address from the IP address list of the domain name and returning it to the client to avoid DNS pollution and improve network access speed. 1. **Support for multiple query protocols** Support UDP, TCP, DOT(DNS over TLS), DOH(DNS over HTTPS) queries and service, and non-53 port queries, effectively avoiding DNS pollution and protect privacy, and support query DNS over socks5, http proxy. diff --git a/src/Makefile b/src/Makefile index 536060b7aa..df0687e0a7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -35,16 +35,22 @@ override CFLAGS += $(EXTRA_CFLAGS) ifdef VER override CFLAGS += -DSMARTDNS_VERION='"$(VER)"' -else - HAS_GIT := $(shell command -v git 2> /dev/null) +endif + +HAS_GIT := $(shell command -v git 2> /dev/null) +ifndef NO_GIT_VER ifdef HAS_GIT IS_GIT_REPO := $(shell git rev-parse --is-inside-work-tree 2>/dev/null) ifdef IS_GIT_REPO - override CFLAGS += -DSMARTDNS_VERION='"$(shell git describe --tags --always --dirty)"' + override COMMIT_VERION = $(shell git describe --tags --always --dirty) endif endif endif +ifdef COMMIT_VERION + override CFLAGS += -DCOMMIT_VERION='"$(shell git describe --tags --always --dirty)"' +endif + CXXFLAGS=-O2 -g -Wall -std=c++11 override CXXFLAGS +=-Iinclude diff --git a/src/dns.c b/src/dns.c index 3c41aa0cc9..519a3db9ca 100644 --- a/src/dns.c +++ b/src/dns.c @@ -2777,6 +2777,36 @@ int dns_packet_init(struct dns_packet *packet, int size, struct dns_head *head) return 0; } +int dns_decode_head_only(struct dns_packet *packet, int maxsize, unsigned char *data, int size) +{ + struct dns_head *head = &packet->head; + struct dns_context context; + int ret = 0; + + memset(&context, 0, sizeof(context)); + memset(packet, 0, sizeof(*packet)); + + context.data = data; + context.packet = packet; + context.ptr = data; + context.maxsize = size; + context.namedict = &packet->namedict; + + ret = dns_packet_init(packet, maxsize, head); + if (ret != 0) { + return -1; + } + + ret = _dns_decode_head(&context); + if (ret < 0) { + return -1; + } + + packet->size = context.ptr - context.data + sizeof(*packet); + + return 0; +} + int dns_decode(struct dns_packet *packet, int maxsize, unsigned char *data, int size) { struct dns_head *head = &packet->head; diff --git a/src/dns.h b/src/dns.h index eb7741135f..1676c62879 100644 --- a/src/dns.h +++ b/src/dns.h @@ -325,6 +325,7 @@ struct dns_https_param *dns_get_HTTPS_svcparm_next(struct dns_rrs *rrs, struct d /* * Packet operation */ +int dns_decode_head_only(struct dns_packet *packet, int maxsize, unsigned char *data, int size); int dns_decode(struct dns_packet *packet, int maxsize, unsigned char *data, int size); int dns_encode(unsigned char *data, int size, struct dns_packet *packet); diff --git a/src/dns_client.c b/src/dns_client.c index 4c95483b4c..48c4ea1575 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -2483,7 +2483,16 @@ static int _dns_client_process_udp_proxy(struct dns_server_info *server_info, st len = proxy_conn_recvfrom(server_info->proxy, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, &from_len); if (len < 0) { - tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno)); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return 0; + } + + if (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EHOSTUNREACH) { + tlog(TLOG_DEBUG, "recvfrom %s failed, %s\n", server_info->ip, strerror(errno)); + goto errout; + } + + tlog(TLOG_ERROR, "recvfrom %s failed, %s\n", server_info->ip, strerror(errno)); goto errout; } else if (len == 0) { pthread_mutex_lock(&client.server_list_lock); @@ -2560,6 +2569,7 @@ static int _dns_client_process_udp(struct dns_server_info *server_info, struct e return 0; } + server_info->prohibit = 1; if (errno == ECONNREFUSED || errno == ENETUNREACH || errno == EHOSTUNREACH) { tlog(TLOG_DEBUG, "recvfrom %s failed, %s\n", server_info->ip, strerror(errno)); goto errout; @@ -2649,7 +2659,8 @@ static int _dns_client_socket_ssl_send(struct dns_server_info *server, const voi errno = EAGAIN; ret = -SSL_ERROR_WANT_WRITE; break; - case SSL_ERROR_SSL: + case SSL_ERROR_SSL: { + char buff[256]; ssl_err = ERR_get_error(); int ssl_reason = ERR_GET_REASON(ssl_err); if (ssl_reason == SSL_R_UNINITIALIZED || ssl_reason == SSL_R_PROTOCOL_IS_SHUTDOWN || @@ -2659,10 +2670,10 @@ static int _dns_client_socket_ssl_send(struct dns_server_info *server, const voi return -1; } - tlog(TLOG_ERROR, "SSL write fail error no: %s(%d)\n", ERR_reason_error_string(ssl_err), ssl_reason); + tlog(TLOG_ERROR, "server %s SSL write fail error: %s", server->ip, ERR_error_string(ssl_err, buff)); errno = EFAULT; ret = -1; - break; + } break; case SSL_ERROR_SYSCALL: tlog(TLOG_DEBUG, "SSL syscall failed, %s", strerror(errno)); return ret; @@ -2705,7 +2716,9 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf errno = EAGAIN; ret = -SSL_ERROR_WANT_WRITE; break; - case SSL_ERROR_SSL: + case SSL_ERROR_SSL: { + char buff[256]; + ssl_err = ERR_get_error(); int ssl_reason = ERR_GET_REASON(ssl_err); if (ssl_reason == SSL_R_UNINITIALIZED) { @@ -2723,11 +2736,10 @@ static int _dns_client_socket_ssl_recv(struct dns_server_info *server, void *buf } #endif - tlog(TLOG_WARN, "SSL read fail error no: %s(%lx), reason: %d\n", ERR_reason_error_string(ssl_err), ssl_err, - ssl_reason); + tlog(TLOG_ERROR, "server %s SSL read fail error: %s", server->ip, ERR_error_string(ssl_err, buff)); errno = EFAULT; ret = -1; - break; + } break; case SSL_ERROR_SYSCALL: if (errno == 0) { return 0; diff --git a/src/dns_server.c b/src/dns_server.c index ba35fc3afd..13a46d5df0 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -1168,6 +1168,10 @@ static int _dns_setup_dns_packet(struct dns_server_post_context *context) return -1; } + if (request->domain[0] == '\0') { + return 0; + } + /* add request domain */ ret = dns_add_domain(context->packet, request->domain, context->qtype, request->qclass); if (ret != 0) { @@ -6624,6 +6628,48 @@ static int _dns_server_parser_request(struct dns_request *request, struct dns_pa return -1; } +static int _dns_server_reply_format_error(struct dns_request *request, struct dns_server_conn_head *conn, + unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *local, + socklen_t local_len, struct sockaddr_storage *from, socklen_t from_len) +{ + unsigned char packet_buff[DNS_PACKSIZE]; + struct dns_packet *packet = (struct dns_packet *)packet_buff; + int decode_len = 0; + int need_release = 0; + int ret = -1; + + if (request == NULL) { + decode_len = dns_decode_head_only(packet, DNS_PACKSIZE, inpacket, inpacket_len); + if (decode_len < 0) { + ret = -1; + goto out; + } + + request = _dns_server_new_request(); + if (request == NULL) { + ret = -1; + goto out; + } + + need_release = 1; + memcpy(&request->localaddr, local, local_len); + _dns_server_request_set_client(request, conn); + _dns_server_request_set_client_addr(request, from, from_len); + _dns_server_request_set_id(request, packet->head.id); + } + + request->rcode = DNS_RC_FORMERR; + request->no_cache = 1; + request->send_tick = get_tick_count(); + ret = 0; +out: + if (request && need_release) { + _dns_server_request_release(request); + } + + return ret; +} + static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *local, socklen_t local_len, struct sockaddr_storage *from, socklen_t from_len) @@ -6687,9 +6733,7 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in last_log_time = now; tlog(TLOG_WARN, "maximum number of dns queries reached, max: %d", dns_conf_max_query_limit); } - request->send_tick = get_tick_count(); request->rcode = DNS_RC_REFUSED; - request->no_cache = 1; ret = 0; goto errout; } @@ -6708,7 +6752,16 @@ static int _dns_server_recv(struct dns_server_conn_head *conn, unsigned char *in _dns_server_request_release_complete(request, 0); return ret; errout: + if (ret == RECV_ERROR_INVALID_PACKET) { + if (_dns_server_reply_format_error(request, conn, inpacket, inpacket_len, local, local_len, from, from_len) == + 0) { + ret = 0; + } + } + if (request) { + request->send_tick = get_tick_count(); + request->no_cache = 1; _dns_server_forward_request(inpacket, inpacket_len); _dns_server_request_release(request); } @@ -7405,7 +7458,8 @@ static int _dns_server_tcp_process_one_request(struct dns_server_conn_tcp_client free(http_decode_data); } - if (ret == RECV_ERROR_FAIL && tcpclient->head.type == DNS_CONN_TYPE_HTTPS_CLIENT) { + if ((ret == RECV_ERROR_FAIL || ret == RECV_ERROR_INVALID_PACKET) && + tcpclient->head.type == DNS_CONN_TYPE_HTTPS_CLIENT) { _dns_server_reply_http_error(tcpclient, 400, "Bad Request", "Bad Request"); } @@ -8797,6 +8851,7 @@ int dns_server_init(void) if (_dns_server_local_addr_cache_init() != 0) { tlog(TLOG_WARN, "init local addr cache failed, disable local ptr."); + dns_conf_local_ptr_enable = 0; } if (_dns_server_neighbor_cache_init() != 0) { diff --git a/src/smartdns.c b/src/smartdns.c index 108436b17b..9a973f26ba 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -177,18 +177,28 @@ static void _help(void) printf("%s", help); } -static void _show_version(void) +static void _smartdns_get_version(char *str_ver, int str_ver_len) { - char str_ver[256] = {0}; + char commit_ver[TMP_BUFF_LEN_32] = {0}; +#ifdef COMMIT_VERION + snprintf(commit_ver, sizeof(commit_ver), " (%s)", COMMIT_VERION); +#endif + #ifdef SMARTDNS_VERION const char *ver = SMARTDNS_VERION; - snprintf(str_ver, sizeof(str_ver), "%s", ver); + snprintf(str_ver, str_ver_len, "%s%s", ver, commit_ver); #else struct tm tm; get_compiled_time(&tm); - snprintf(str_ver, sizeof(str_ver), "1.%.4d%.2d%.2d-%.2d%.2d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min); + snprintf(str_ver, str_ver_len, "1.%.4d%.2d%.2d-%.2d%.2d%s", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, commit_ver); #endif +} + +static void _show_version(void) +{ + char str_ver[256] = {0}; + _smartdns_get_version(str_ver, sizeof(str_ver)); printf("smartdns %s\n", str_ver); } @@ -618,14 +628,16 @@ static int _smartdns_init_load_from_resolv(void) static int _smartdns_init(void) { int ret = 0; + char str_ver[256] = {0}; if (_smartdns_init_log() != 0) { tlog(TLOG_ERROR, "init log failed."); goto errout; } - tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng , build: %s %s)", __DATE__, - __TIME__); + _smartdns_get_version(str_ver, sizeof(str_ver)); + + tlog(TLOG_NOTICE, "smartdns starting...(Copyright (C) Nick Peng , build: %s)", str_ver); if (dns_timer_init() != 0) { tlog(TLOG_ERROR, "init timer failed.");