Add TCP DNS server
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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服务器"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
25
src/conf.c
25
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},
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
538
src/dns_server.c
538
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
38
src/tlog.c
38
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 */
|
||||
|
||||
Reference in New Issue
Block a user