From 9eb8aa15716cb1ecead14d64133cc97229485176 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Sat, 1 Dec 2018 01:22:16 +0800 Subject: [PATCH] Add TCP DNS server --- ReadMe.md | 7 +- ReadMe_zh-CN.md | 7 +- .../luci/files/luci/i18n/smartdns.zh-cn.po | 6 + .../luci/files/luci/model/cbi/smartdns.lua | 12 +- package/openwrt/files/etc/init.d/smartdns | 11 +- package/openwrt/make.sh | 2 +- src/conf.c | 25 + src/conf.h | 2 + src/dns.c | 2 - src/dns_server.c | 538 ++++++++++++++++-- src/fast_ping.c | 5 +- src/tlog.c | 38 +- 12 files changed, 599 insertions(+), 56 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index bbd7836..ab7cb0b 100755 --- a/ReadMe.md +++ b/ReadMe.md @@ -260,10 +260,10 @@ Download the matching version of the SmartDNS installation package. The correspo Log in to the router, click `Network`->`DHCP and DNS`, and modify `DNS forwardings` to: ```shell - /#/127.0.0.1#5353 + /#/127.0.0.1#5053 ``` - Where `#5353` is the service port number of smartdns. If it is not modified, the default is 5353. + Where `#5053` is the service port number of smartdns. If it is not modified, the default is 5053. * **Check if the service is configured successfully** @@ -371,7 +371,9 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |--|--|--|--|--| |server-name|DNS name|host name/smartdns|any string like hosname|server-name smartdns |bind|DNS bind port|[::]:53|IP:PORT|bind 192.168.1.1:53 +|bind-tcp|TCP mode DNS bind port|[::]:53|IP:PORT|bind-tcp 192.168.1.1:53 |cache-size|Domain name result cache number|512|integer|cache-size 512 +|tcp-idle-time|TCP connection idle timeout|120|integer|tcp-idle-time 120 |rr-ttl|Domain name TTL|Remote query result|number greater than 0|rr-ttl 600 |rr-ttl-min|Domain name Minimum TTL|Remote query result|number greater than 0|rr-ttl-min 60 |rr-ttl-max|Domain name Maximum TTL|Remote query result|number greater than 0|rr-ttl-max 600 @@ -382,6 +384,7 @@ Note: Merlin firmware is derived from ASUS firmware and can theoretically be use |conf-file|additional conf file|None|File path|conf-file /etc/smartdns/smartdns.more.conf |server|Upstream UDP DNS server|None|[ip][:port], Repeatable| server 8.8.8.8:53 |server-tcp|Upstream TCP DNS server|None|[IP][:port], Repeatable| server-tcp 8.8.8.8:53 +|server-tls|Upstream TLS DNS server|None|[IP][:port], Repeatable| server-tls 8.8.8.8:853 |address|Domain IP address|None|address /domain/ip| address /www.example.com/1.2.3.4 |bogus-nxdomain|bogus IP address|None|[IP],Repeatable| bogus-nxdomain 1.2.3.4 |force-AAAA-SOA|force AAAA query return SOA|no|[yes\|no]|force-AAAA-SOA yes diff --git a/ReadMe_zh-CN.md b/ReadMe_zh-CN.md index 9610b3e..a8f3a23 100644 --- a/ReadMe_zh-CN.md +++ b/ReadMe_zh-CN.md @@ -260,10 +260,10 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms 登录路由器,点击`Network`->`DHCP and DNS`,修改`DNS forwardings(DNS转发)`为: ```shell - /#/127.0.0.1#5353 + /#/127.0.0.1#5053 ``` - 其中`#5353`为smartdns的服务端口号,未修改的情况下,默认为5353。 + 其中`#5053`为smartdns的服务端口号,未修改的情况下,默认为5053。 * **检测上游服务是否配置成功** @@ -371,7 +371,9 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms |--|--|--|--|--| |server-name|DNS服务器名称|操作系统主机名/smartdns|符合主机名规格的字符串|server-name smartdns |bind|DNS监听端口号|[::]:53|IP:PORT|bind 192.168.1.1:53 +|bind-tcp|TCP模式DNS监听端口号|[::]:53|IP:PORT|bind-tcp 192.168.1.1:53 |cache-size|域名结果缓存个数|512|数字|cache-size 512 +|tcp-idle-time|TCP链接空闲超时时间|120|数字|tcp-idle-time 120 |rr-ttl|域名结果TTL|远程查询结果|大于0的数字|rr-ttl 600 |rr-ttl-min|允许的最小TTL值|远程查询结果|大于0的数字|rr-ttl-min 60 |rr-ttl-max|允许的最大TTL值|远程查询结果|大于0的数字|rr-ttl-max 600 @@ -382,6 +384,7 @@ rtt min/avg/max/mdev = 5.954/6.133/6.313/0.195 ms |conf-file|附加配置文件|无|文件路径|conf-file /etc/smartdns/smartdns.more.conf |server|上游UDP DNS|无|[ip][:port],可重复| server 8.8.8.8:53 |server-tcp|上游TCP DNS|无|[IP][:port],可重复| server-tcp 8.8.8.8:53 +|server-tls|上游TLS DNS|无|[IP][:port],可重复| server-tls 8.8.8.8:853 |address|指定域名IP地址|无|address /domain/ip| address /www.example.com/1.2.3.4 |bogus-nxdomain|假冒IP地址过滤|无|[ip],可重复| bogus-nxdomain 1.2.3.4 |force-AAAA-SOA|强制AAAA地址返回SOA|no|[yes\|no]|force-AAAA-SOA yes diff --git a/package/luci/files/luci/i18n/smartdns.zh-cn.po b/package/luci/files/luci/i18n/smartdns.zh-cn.po index 6fd77e9..1b6fe3f 100644 --- a/package/luci/files/luci/i18n/smartdns.zh-cn.po +++ b/package/luci/files/luci/i18n/smartdns.zh-cn.po @@ -40,6 +40,12 @@ msgstr "IPV4 53端口重定向失败" msgid "IPV6 53 Port Redirect Failure" msgstr "IPV6 53端口重定向失败" +msgid "TCP Server" +msgstr "TCP服务器" + +msgid "Enable TCP DNS Server" +msgstr "启用TCP服务器" + msgid "IPV6 Server" msgstr "IPV6服务器" diff --git a/package/luci/files/luci/model/cbi/smartdns.lua b/package/luci/files/luci/model/cbi/smartdns.lua index 43a0a96..cb21f16 100644 --- a/package/luci/files/luci/model/cbi/smartdns.lua +++ b/package/luci/files/luci/model/cbi/smartdns.lua @@ -31,11 +31,19 @@ o.rempty = false ---- Port o = s:taboption("settings", Value, "port", translate("Local Port"), translate("Smartdns local server port")) -o.placeholder = 5353 -o.default = 5353 +o.placeholder = 5053 +o.default = 5053 o.datatype = "port" o.rempty = false +---- Enable TCP server +o = s:taboption("settings", Flag, "tcp_server", translate("TCP Server"), translate("Enable TCP DNS Server")) +o.rmempty = false +o.default = o.enabled +o.cfgvalue = function(...) + return Flag.cfgvalue(...) or "1" +end + ---- Support IPV6 o = s:taboption("settings", Flag, "ipv6_server", translate("IPV6 Server"), translate("Enable IPV6 DNS Server")) o.rmempty = false diff --git a/package/openwrt/files/etc/init.d/smartdns b/package/openwrt/files/etc/init.d/smartdns index 097a9d9..02cce70 100644 --- a/package/openwrt/files/etc/init.d/smartdns +++ b/package/openwrt/files/etc/init.d/smartdns @@ -100,13 +100,22 @@ start_service() { conf_append "server-name" "$server_name" fi - config_get "port" "$section" "port" "5353" + config_get "port" "$section" "port" "5053" config_get "ipv6_server" "$section" "ipv6_server" "1" + config_get "tcp_server" "$section" "tcp_server" "1" if [ "$ipv6_server" = "1" ]; then conf_append "bind" "[::]:$port" else conf_append "bind" ":$port" fi + + if [ "$tcp_server" = "1" ]; then + if [ "$ipv6_server" = "1" ]; then + conf_append "bind-tcp" "[::]:$port" + else + conf_append "bind-tcp" ":$port" + fi + fi SMARTDNS_PORT="$port" mkdir -p $(dirname $SMARTDNS_CONF) diff --git a/package/openwrt/make.sh b/package/openwrt/make.sh index 485a69f..83200e4 100644 --- a/package/openwrt/make.sh +++ b/package/openwrt/make.sh @@ -42,7 +42,7 @@ build() sed -i "s/^Architecture.*/Architecture: $ARCH/g" $ROOT/control/control sed -i "s/Version:.*/Version: $VER/" $ROOT/control/control - sed -i "s/^\(bind .*\):53/\1:5353/g" $ROOT/root/etc/smartdns/smartdns.conf + sed -i "s/^\(bind .*\):53/\1:5053/g" $ROOT/root/etc/smartdns/smartdns.conf if [ ! -z "$INST_SIZE" ]; then echo "Installed-Size: $INST_SIZE" >> $ROOT/control/control fi diff --git a/src/conf.c b/src/conf.c index d0714c8..58fa0d4 100644 --- a/src/conf.c +++ b/src/conf.c @@ -14,6 +14,8 @@ #define DEFAULT_DNS_CACHE_SIZE 512 char dns_conf_server_ip[DNS_MAX_IPLEN]; +char dns_conf_server_tcp_ip[DNS_MAX_IPLEN]; +int dns_conf_tcp_idle_time = 120; int dns_conf_cachesize = DEFAULT_DNS_CACHE_SIZE; int dns_conf_prefetch = 0; struct dns_servers dns_conf_servers[DNS_MAX_SERVERS]; @@ -45,6 +47,14 @@ int config_bind(char *value) return 0; } +int config_bind_tcp(char *value) +{ + /* server bind address */ + strncpy(dns_conf_server_tcp_ip, value, DNS_MAX_IPLEN); + + return 0; +} + int config_server_name(char *value) { strncpy(dns_conf_server_name, value, DNS_MAX_CONF_CNAME_LEN); @@ -199,6 +209,19 @@ int config_server_tcp(char *value) return config_server(value, DNS_SERVER_TCP, DEFAULT_DNS_PORT); } +int config_tcp_idle_time(char *value) +{ + /* read dns cache size */ + int idle_time = atoi(value); + if (idle_time < 0) { + return -1; + } + + dns_conf_tcp_idle_time = idle_time; + + return 0; +} + int config_server_tls(char *value) { return config_server(value, DNS_SERVER_TLS, DEFAULT_DNS_TLS_PORT); @@ -527,9 +550,11 @@ struct config_item { struct config_item config_item[] = { {"server-name", config_server_name}, {"bind", config_bind}, + {"bind-tcp", config_bind_tcp}, {"server", config_server_udp}, {"address", config_address}, {"server-tcp", config_server_tcp}, + {"tcp-idle-time", config_tcp_idle_time}, {"server-tls", config_server_tls}, {"cache-size", config_cache_size}, {"prefetch-domain", config_cache_prefetch_domain}, diff --git a/src/conf.h b/src/conf.h index 39cfe87..1ff5383 100644 --- a/src/conf.h +++ b/src/conf.h @@ -49,6 +49,8 @@ struct dns_bogus_nxdomain { }; extern char dns_conf_server_ip[DNS_MAX_IPLEN]; +extern char dns_conf_server_tcp_ip[DNS_MAX_IPLEN]; +extern int dns_conf_tcp_idle_time; extern int dns_conf_cachesize; extern int dns_conf_prefetch; extern struct dns_servers dns_conf_servers[DNS_MAX_SERVERS]; diff --git a/src/dns.c b/src/dns.c index 327bf5f..70172d3 100644 --- a/src/dns.c +++ b/src/dns.c @@ -1290,8 +1290,6 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign return -1; } - - tlog(TLOG_DEBUG, "decode opt."); while (context->ptr - start < rr_len) { opt_code = dns_read_short(&context->ptr); opt_len = dns_read_short(&context->ptr); diff --git a/src/dns_server.c b/src/dns_server.c index 17ad7fb..0814a0b 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ - +#define _GNU_SOURCE #include "dns_server.h" #include "atomic.h" #include "conf.h" @@ -50,18 +50,42 @@ #define DNS_MAX_EVENTS 256 #define DNS_SERVER_TMOUT_TTL (3 * 60) +#define DNS_CONN_BUFF_SIZE 4096 + +struct dns_conn_buf { + char buf[DNS_CONN_BUFF_SIZE]; + int buffsize; + int size; +}; + +struct dns_server_conn { + struct list_head list; + atomic_t refcnt; + dns_server_type_t type; + int fd; + struct dns_conn_buf recvbuff; + struct dns_conn_buf sndbuff; + + socklen_t addr_len; + struct sockaddr_storage addr; + + time_t last_request_time; +}; /* dns server data */ struct dns_server { int run; int epoll_fd; - int fd; + struct dns_server_conn udp_server; + struct dns_server_conn tcp_server; /* dns request list */ pthread_mutex_t request_list_lock; struct list_head request_list; + struct list_head client_list; }; + /* ip address lists of domain */ struct dns_ip_address { struct hlist_node node; @@ -75,6 +99,8 @@ struct dns_ip_address { struct dns_request { atomic_t refcnt; + + struct dns_server_conn *client; /* dns request list */ struct list_head list; @@ -127,6 +153,7 @@ struct dns_request { int prefetch; pthread_mutex_t ip_map_lock; + int ip_map_num; DECLARE_HASHTABLE(ip_map, 4); }; @@ -242,14 +269,86 @@ static int _dns_add_rrs(struct dns_packet *packet, struct dns_request *request) return ret; } -static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpacket, int inpacket_len) +static void _dns_server_client_release(struct dns_server_conn *client) +{ + int refcnt = atomic_dec_return(&client->refcnt); + + if (refcnt) { + if (refcnt < 0) { + tlog(TLOG_ERROR, "BUG: refcnt is %d", refcnt); + abort(); + } + return; + } + + if (client->fd > 0) { + close(client->fd); + client->fd = -1; + } + + list_del_init(&client->list); + free(client); +} + +static void _dns_server_client_get(struct dns_server_conn *client) +{ + atomic_inc(&client->refcnt); +} + +static int _dns_server_reply_tcp_to_buffer(struct dns_server_conn *client, void *packet, int len) +{ + struct epoll_event event; + + if (sizeof(client->sndbuff.buf) - client->sndbuff.size < len) { + return -1; + } + + memcpy(client->sndbuff.buf + client->sndbuff.size, packet, len); + client->sndbuff.size += len; + + memset(&event, 0, sizeof(event)); + event.events = EPOLLIN | EPOLLOUT; + event.data.ptr = client; + if (epoll_ctl(server.epoll_fd, EPOLL_CTL_MOD, client->fd, &event) != 0) { + tlog(TLOG_ERROR, "epoll ctl failed."); + return -1; + } + + return 0; +} + +static int _dns_server_reply_tcp(struct dns_server_conn *client, void *packet, unsigned short len) { int send_len = 0; - unsigned short *id = (unsigned short *)inpacket; + unsigned char inpacket_data[DNS_IN_PACKSIZE]; + unsigned char *inpacket = inpacket_data; - *id = htons(request->id); + /* TCP query format + * | len (short) | dns query data | + */ + *((unsigned short *)(inpacket)) = htons(len); + memcpy(inpacket + 2, packet, len); + len += 2; - send_len = sendto(server.fd, inpacket, inpacket_len, 0, &request->addr, request->addr_len); + send_len = send(client->fd, inpacket, len, MSG_NOSIGNAL); + if (send_len < 0) { + if (errno == EAGAIN) { + /* save data to buffer, and retry when EPOLLOUT is available */ + return _dns_server_reply_tcp_to_buffer(client, inpacket, len); + } + return -1; + } else if (send_len < len) { + /* save remain data to buffer, and retry when EPOLLOUT is available */ + return _dns_server_reply_tcp_to_buffer(client, inpacket + send_len, len - send_len); + } + + return 0; +} + +static int _dns_server_reply_udp(struct dns_request *request, struct dns_server_conn *client, unsigned char *inpacket, int inpacket_len) +{ + int send_len = 0; + send_len = sendto(client->fd, inpacket, inpacket_len, 0, &request->addr, request->addr_len); if (send_len != inpacket_len) { tlog(TLOG_ERROR, "send failed."); return -1; @@ -258,6 +357,26 @@ static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpac return 0; } +static int _dns_reply_inpacket(struct dns_request *request, unsigned char *inpacket, int inpacket_len) +{ + struct dns_server_conn *client = request->client; + int ret = 0; + + if (client->type == DNS_SERVER_UDP) { + ret = _dns_server_reply_udp(request, client, inpacket, inpacket_len); + } else if (client->type == DNS_SERVER_TCP) { + ret = _dns_server_reply_tcp(client, inpacket, inpacket_len); + } else if (client->type == DNS_SERVER_TLS) { + ret = -1; + } else { + ret = -1; + } + + _dns_server_client_release(client); + + return ret; +} + static int _dns_reply(struct dns_request *request) { unsigned char inpacket[DNS_IN_PACKSIZE]; @@ -980,7 +1099,7 @@ errout: return -1; } -static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *from, socklen_t from_len) +static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpacket, int inpacket_len, struct sockaddr_storage *from, socklen_t from_len) { int decode_len; int ret = -1; @@ -994,6 +1113,8 @@ static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct so int qclass; int qtype; + _dns_server_client_get(client); + decode_len = dns_decode(packet, DNS_PACKSIZE, inpacket, inpacket_len); if (decode_len < 0) { tlog(TLOG_ERROR, "decode failed.\n"); @@ -1016,6 +1137,7 @@ static int _dns_server_recv(unsigned char *inpacket, int inpacket_len, struct so request->ping_ttl_v6 = -1; request->prefetch = 0; request->rcode = DNS_RC_SERVFAIL; + request->client = client; if (_dns_recv_addr(request, from, from_len) != 0) { goto errout; @@ -1091,6 +1213,8 @@ errout: ret = _dns_server_forward_request(inpacket, inpacket_len); free(request); } + + _dns_server_client_release(client); return ret; } @@ -1133,20 +1257,252 @@ errout: return ret; } -static int _dns_server_process(unsigned long now) +static int _dns_server_process_udp(struct dns_server_conn *dnsserver, struct epoll_event *event, unsigned long now) { int len; unsigned char inpacket[DNS_IN_PACKSIZE]; struct sockaddr_storage from; socklen_t from_len = sizeof(from); - len = recvfrom(server.fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len); + len = recvfrom(dnsserver->fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len); if (len < 0) { tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno)); return -1; } - return _dns_server_recv(inpacket, len, &from, from_len); + return _dns_server_recv(dnsserver, inpacket, len, &from, from_len); +} + +static void _dns_server_client_touch(struct dns_server_conn *client) +{ + time(&client->last_request_time); +} + +static int _dns_server_client_close(struct dns_server_conn *client) +{ + if (client->fd > 0) { + close(client->fd); + client->fd = -1; + } + + list_del_init(&client->list); + + _dns_server_client_release(client); + + return 0; +} + +static int _dns_server_accept(struct dns_server_conn *dnsserver, struct epoll_event *event, unsigned long now) +{ + struct sockaddr_storage addr; + struct dns_server_conn *client = NULL; + socklen_t addr_len = sizeof(addr); + int fd = -1; + + fd = accept4(dnsserver->fd, (struct sockaddr *)&addr, &addr_len, SOCK_NONBLOCK | SOCK_CLOEXEC); + if (fd < 0) { + return -1; + } + + client = malloc(sizeof(*client)); + if (client == NULL) { + goto errout; + } + + memset(client, 0, sizeof(*client)); + struct epoll_event event_client; + memset(&event_client, 0, sizeof(event_client)); + event_client.data.ptr = client; + event_client.events = EPOLLIN; + if (epoll_ctl(server.epoll_fd, EPOLL_CTL_ADD, fd, &event_client) != 0) { + tlog(TLOG_ERROR, "epoll add failed, %s", strerror(errno)); + goto errout; + } + + client->fd = fd; + client->type = DNS_SERVER_TCP; + atomic_set(&client->refcnt, 0); + memcpy(&client->addr, &addr, addr_len); + client->addr_len = addr_len; + + _dns_server_client_touch(client); + + list_add(&client->list, &server.client_list); + _dns_server_client_get(client); + + return 0; +errout: + if (fd > 0) { + close(fd); + } + if (client) { + free(client); + } + return -1; +} + +int _dns_server_tcp_recv(struct dns_server_conn *dnsserver) +{ + int len = 0; + + while (dnsserver->recvbuff.size < sizeof(dnsserver->recvbuff.buf)) { + if (dnsserver->recvbuff.size == sizeof(dnsserver->recvbuff.buf)) { + return 0; + } + + len = recv(dnsserver->fd, dnsserver->recvbuff.buf + dnsserver->recvbuff.size, + sizeof(dnsserver->recvbuff.buf) - dnsserver->recvbuff.size, 0); + if (len < 0) { + if (errno == EAGAIN) { + return 1; + } + + return -1; + } else if (len == 0) { + return -1; + } + + dnsserver->recvbuff.size += len; + } + + return 0; +} + +int _dns_server_tcp_process_one_request(struct dns_server_conn *dnsserver) +{ + int request_len = 0; + int total_len = dnsserver->recvbuff.size; + int proceed_len = 0; + unsigned char *request_data = NULL; + + for (;;) { + if ((total_len - proceed_len) <= sizeof(unsigned short)) { + return 1; + } + + request_len = ntohs(*((unsigned short *)(dnsserver->recvbuff.buf + proceed_len))); + if (request_len > (total_len - proceed_len)) { + return 1; + } + + if (request_len > 4096) { + tlog(TLOG_ERROR, "request length is invalid."); + return -1; + } + + request_data = (unsigned char *)(dnsserver->recvbuff.buf + proceed_len + sizeof(unsigned short)); + + if (_dns_server_recv(dnsserver, request_data, request_len, &dnsserver->addr, dnsserver->addr_len) != 0) { + tlog(TLOG_ERROR, "process tcp request failed."); + return -1; + } + + proceed_len += sizeof(unsigned short) + request_len; + } + + if (total_len > proceed_len && proceed_len > 0) { + memmove(dnsserver->recvbuff.buf, dnsserver->recvbuff.buf + proceed_len, total_len - proceed_len); + } + + dnsserver->recvbuff.size -= proceed_len; + + return 0; +} + +int _dns_server_tcp_process_requests(struct dns_server_conn *client) +{ + int recv_ret = 0; + int request_ret = 0; + int is_eof = 0; + + for (;;) { + recv_ret = _dns_server_tcp_recv(client); + if (recv_ret < 0) { + if (client->recvbuff.size > 0) { + is_eof = 1; + } else { + return -1; + } + } + + request_ret = _dns_server_tcp_process_one_request(client); + if (request_ret < 0) { + return -1; + } + + if (request_ret == 1 && is_eof == 1) { + return -1; + } + + if (recv_ret == 1 && request_ret == 1) { + return 0; + } + } + + return 0; +} + +int _dns_server_tcp_send(struct dns_server_conn *client) +{ + int len; + while (client->sndbuff.size > 0) { + len = send(client->fd, client->sndbuff.buf, client->sndbuff.size, MSG_NOSIGNAL); + if (len < 0) { + if (errno == EAGAIN) { + return 1; + } + return -1; + } else if (len == 0 ) { + break; + } + + client->sndbuff.size -= len; + } + + struct epoll_event event_client; + event_client.data.ptr = client; + event_client.events = EPOLLIN; + if (epoll_ctl(server.epoll_fd, EPOLL_CTL_MOD, client->fd, &event_client) != 0) { + tlog(TLOG_ERROR, "epoll add failed, %s", strerror(errno)); + return -1; + } + + return 0; +} + +static int _dns_server_process_tcp(struct dns_server_conn *dnsserver, struct epoll_event *event, unsigned long now) +{ + if (dnsserver == &server.tcp_server) { + return _dns_server_accept(dnsserver, event, now); + } + + if (event->events & EPOLLIN) { + if (_dns_server_tcp_process_requests(dnsserver) != 0) { + _dns_server_client_close(dnsserver); + } + } + + if (event->events & EPOLLOUT) { + if (_dns_server_tcp_send(dnsserver) != 0) { + _dns_server_client_close(dnsserver); + } + } + + return 0; +} + +static int _dns_server_process(struct dns_server_conn *dnsserver, struct epoll_event *event, unsigned long now) +{ + _dns_server_client_touch(dnsserver); + if (dnsserver->type == DNS_SERVER_UDP) { + return _dns_server_process_udp(dnsserver, event, now); + } else if (dnsserver->type == DNS_SERVER_TCP) { + return _dns_server_process_tcp(dnsserver, event, now); + } else if (dnsserver->type == DNS_SERVER_TLS) { + return -1; + } else { + return -1; + } } void _dns_server_tcp_ping_check(struct dns_request *request) @@ -1203,6 +1559,27 @@ void _dns_server_prefetch_domain(struct dns_cache *dns_cache) } } +void _dns_server_tcp_idle_check(void) +{ + struct dns_server_conn *client, *tmp; + time_t now; + + if (dns_conf_tcp_idle_time <= 0) { + return; + } + + time(&now); + list_for_each_entry_safe(client, tmp, &server.client_list, list) + { + if (client->last_request_time > now - dns_conf_tcp_idle_time) { + continue; + } + + _dns_server_client_close(client); + } +} + + void _dns_server_period_run_second(void) { static unsigned int sec = 0; @@ -1215,6 +1592,8 @@ void _dns_server_period_run_second(void) dns_cache_invalidate(NULL, 0); } } + + _dns_server_tcp_idle_check(); } void _dns_server_period_run(void) @@ -1269,6 +1648,7 @@ int dns_server_run(void) sleep_time = sleep - (now - expect_time); if (sleep_time < 0) { sleep_time = 0; + expect_time = now; } expect_time += sleep; } @@ -1284,12 +1664,13 @@ int dns_server_run(void) } for (i = 0; i < num; i++) { struct epoll_event *event = &events[i]; - if (event->data.fd != server.fd) { + struct dns_server_conn *dnsserver = event->data.ptr; + if (dnsserver == NULL) { tlog(TLOG_ERROR, "invalid fd\n"); continue; } - if (_dns_server_process(now) != 0) { + if (_dns_server_process(dnsserver, event, now) != 0) { tlog(TLOG_ERROR, "dns server process failed."); } } @@ -1324,13 +1705,18 @@ errout: return NULL; } -int dns_server_start(void) +int _dns_server_start_udp(void) { struct epoll_event event; + + if (server.udp_server.fd <= 0) { + return 0; + } + memset(&event, 0, sizeof(event)); event.events = EPOLLIN; - event.data.fd = server.fd; - if (epoll_ctl(server.epoll_fd, EPOLL_CTL_ADD, server.fd, &event) != 0) { + event.data.ptr = &server.udp_server; + if (epoll_ctl(server.epoll_fd, EPOLL_CTL_ADD, server.udp_server.fd, &event) != 0) { tlog(TLOG_ERROR, "epoll ctl failed."); return -1; } @@ -1338,7 +1724,41 @@ int dns_server_start(void) return 0; } -int dns_server_socket(void) +int _dns_server_start_tcp(void) +{ + struct epoll_event event; + + if (server.tcp_server.fd <= 0) { + return 0; + } + + memset(&event, 0, sizeof(event)); + event.events = EPOLLIN; + event.data.ptr = &server.tcp_server; + if (epoll_ctl(server.epoll_fd, EPOLL_CTL_ADD, server.tcp_server.fd, &event) != 0) { + tlog(TLOG_ERROR, "epoll ctl failed."); + return -1; + } + + return 0; +} + +int dns_server_start(void) +{ + if (_dns_server_start_udp() != 0) { + tlog(TLOG_ERROR, "start udp server failed."); + return -1; + } + + if (_dns_server_start_tcp() != 0) { + tlog(TLOG_ERROR, "start tcp server failed."); + return -1; + } + return 0; +} + + +int _dns_create_socket(const char *host_ip, int type) { int fd = -1; struct addrinfo *gai = NULL; @@ -1348,7 +1768,7 @@ int dns_server_socket(void) char *host = NULL; int optval = 1; - if (parse_ip(dns_conf_server_ip, ip, &port) == 0) { + if (parse_ip(host_ip, ip, &port) == 0) { host = ip; } @@ -1357,7 +1777,7 @@ int dns_server_socket(void) } snprintf(port_str, sizeof(port_str), "%d", port); - gai = _dns_server_getaddr(host, port_str, SOCK_DGRAM, 0); + gai = _dns_server_getaddr(host, port_str, type, 0); if (gai == NULL) { tlog(TLOG_ERROR, "get address failed.\n"); goto errout; @@ -1379,7 +1799,13 @@ int dns_server_socket(void) goto errout; } - server.fd = fd; + if (type == SOCK_STREAM) { + if (listen(fd, 16) != 0) { + tlog(TLOG_ERROR, "listen failed.\n"); + goto errout; + } + } + freeaddrinfo(gai); return fd; @@ -1394,6 +1820,60 @@ errout: return -1; } +int _dns_server_socket(void) +{ + int fd_udp = -1; + int fd_tcp = -1; + + if (dns_conf_server_ip[0] != 0) { + fd_udp = _dns_create_socket(dns_conf_server_ip, SOCK_DGRAM); + if (fd_udp < 0) { + goto errout; + } + } + + if (dns_conf_server_tcp_ip[0] != 0) { + fd_tcp = _dns_create_socket(dns_conf_server_tcp_ip, SOCK_STREAM); + if (fd_tcp < 0) { + goto errout; + } + + } + + server.udp_server.fd = fd_udp; + server.udp_server.type = DNS_SERVER_UDP; + _dns_server_client_get(&server.udp_server); + INIT_LIST_HEAD(&server.udp_server.list); + server.tcp_server.fd = fd_tcp; + server.tcp_server.type = DNS_SERVER_TCP; + INIT_LIST_HEAD(&server.tcp_server.list); + _dns_server_client_get(&server.tcp_server); + return 0; +errout: + if (fd_udp > 0) { + close(fd_udp); + } + + if (fd_tcp > 0) { + close(fd_tcp); + } + + return -1; +} + +void _dns_server_close_socket(void) +{ + if (server.udp_server.fd > 0) { + close(server.udp_server.fd); + server.udp_server.fd = 0; + } + + if (server.tcp_server.fd > 0) { + close(server.tcp_server.fd); + server.tcp_server.fd = 0; + } +} + int _dns_server_audit_init(void) { char *audit_file = SMARTDNS_AUDIT_FILE; @@ -1417,7 +1897,7 @@ int dns_server_init(void) { pthread_attr_t attr; int epollfd = -1; - int fd = -1; + int ret = -1; if (server.epoll_fd > 0) { return -1; @@ -1435,6 +1915,7 @@ int dns_server_init(void) memset(&server, 0, sizeof(server)); pthread_attr_init(&attr); + INIT_LIST_HEAD(&server.client_list); epollfd = epoll_create1(EPOLL_CLOEXEC); if (epollfd < 0) { @@ -1442,8 +1923,8 @@ int dns_server_init(void) goto errout; } - fd = dns_server_socket(); - if (fd < 0) { + ret = _dns_server_socket(); + if (ret != 0) { tlog(TLOG_ERROR, "create server socket failed.\n"); goto errout; } @@ -1451,7 +1932,6 @@ int dns_server_init(void) pthread_mutex_init(&server.request_list_lock, 0); INIT_LIST_HEAD(&server.request_list); server.epoll_fd = epollfd; - server.fd = fd; server.run = 1; if (dns_server_start() != 0) { @@ -1463,14 +1943,11 @@ int dns_server_init(void) errout: server.run = 0; - if (fd > 0) { - close(fd); - } - if (epollfd) { close(epollfd); } + _dns_server_close_socket(); pthread_mutex_destroy(&server.request_list_lock); dns_cache_destroy(); @@ -1489,12 +1966,7 @@ void dns_server_exit(void) LIST_HEAD(remove_list); server.run = 0; - - if (server.fd > 0) { - close(server.fd); - server.fd = -1; - } - + _dns_server_close_socket(); pthread_mutex_lock(&server.request_list_lock); list_for_each_entry_safe(request, tmp, &server.request_list, list) { diff --git a/src/fast_ping.c b/src/fast_ping.c index 2bd1437..d9562c7 100644 --- a/src/fast_ping.c +++ b/src/fast_ping.c @@ -474,7 +474,7 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type) case FAST_PING_ICMP: fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (fd < 0) { - tlog(TLOG_ERROR, "create icmp socket failed.\n"); + tlog(TLOG_ERROR, "create icmp socket failed, %s\n", strerror(errno)); goto errout; } _fast_ping_install_filter_v4(fd); @@ -483,7 +483,7 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type) case FAST_PING_ICMP6: fd = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (fd < 0) { - tlog(TLOG_ERROR, "create icmp socket failed.\n"); + tlog(TLOG_ERROR, "create icmp socket failed, %s\n", strerror(errno)); goto errout; } _fast_ping_install_filter_v6(fd); @@ -1032,6 +1032,7 @@ static void *_fast_ping_work(void *arg) sleep_time = sleep - (now - expect_time); if (sleep_time < 0) { sleep_time = 0; + expect_time = now; } expect_time += sleep; } diff --git a/src/tlog.c b/src/tlog.c index d3602b8..21f5e5c 100644 --- a/src/tlog.c +++ b/src/tlog.c @@ -835,6 +835,11 @@ static int _tlog_write_log(struct tlog_log *log, char *buff, int bufflen) return 0; } + /* output log to screen */ + if (log->logscreen) { + write(STDOUT_FILENO, buff, bufflen); + } + /* if log file size exceeds threshold, start to compress */ if (log->multi_log) { log->filesize = lseek(log->fd, 0, SEEK_END); @@ -854,8 +859,18 @@ static int _tlog_write_log(struct tlog_log *log, char *buff, int bufflen) if (log->fd <= 0) { /* open a new log file to write */ - char logfile[PATH_MAX * 2]; - if (_tlog_mkdir(log->logdir) != 0) { + static time_t last_try = 0; + static int print_errmsg = 1; + time_t now; + + time(&now); + if (now == last_try) { + return -1; + } + last_try = now; + + char logfile[PATH_MAX * 2]; + if (_tlog_mkdir(log->logdir) != 0) { fprintf(stderr, "create log dir %s failed.\n", log->logdir); return -1; } @@ -863,17 +878,18 @@ static int _tlog_write_log(struct tlog_log *log, char *buff, int bufflen) log->filesize = 0; log->fd = open(logfile, O_APPEND | O_CREAT | O_WRONLY | O_CLOEXEC, 0640); if (log->fd < 0) { - fprintf(stderr, "open log file %s failed, %s\n", logfile, strerror(errno)); - return -1; - } + if (print_errmsg == 0) { + return -1; + } - /* get log file size */ - log->filesize = lseek(log->fd, 0, SEEK_END); - } + fprintf(stderr, "open log file %s failed, %s\n", logfile, strerror(errno)); + print_errmsg = 0; + return -1; + } - /* output log to screen */ - if (log->logscreen) { - write(STDOUT_FILENO, buff, bufflen); + print_errmsg = 1; + /* get log file size */ + log->filesize = lseek(log->fd, 0, SEEK_END); } /* write log to file */