diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 3063439..8cd51bd 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -76,12 +76,15 @@ log-level info # audit-num 2 # remote udp dns server list -# server [IP]:[PORT] [-blacklist-ip] [-check-edns] +# server [IP]:[PORT] [-blacklist-ip] [-check-edns] [-check-ttl=[ttl]] # default port is 53 +# -blacklist-ip: filter result with blacklist ip +# -check-edns: result must exist edns RR, or discard result. +# -check-ttl: Check whether TTL is the corresponding value, or discard result, -check-ttl=0 for auto check TTL. # server 8.8.8.8 -blacklist-ip -check-edns # remote tcp dns server list -# server-tcp [IP]:[PORT] [-blacklist-ip] [-check-edns] +# server-tcp [IP]:[PORT] [-blacklist-ip] [-check-edns] # default port is 53 # server-tcp 8.8.8.8 diff --git a/src/dns_client.c b/src/dns_client.c index 1ccd266..3ca2be2 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -109,6 +109,7 @@ struct dns_server_info { /* client socket */ int fd; + int ttl; SSL *ssl; SSL_CTX *ssl_ctx; dns_server_status status; @@ -225,8 +226,21 @@ int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t server_type return -1; } +void _dns_client_server_update_ttl(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, + int ttl, struct timeval *tv, void *userptr) +{ + struct dns_server_info *server_info = userptr; + if (result != PING_RESULT_RESPONSE || server_info == NULL) { + return; + } + + double rtt = tv->tv_sec * 1000.0 + tv->tv_usec / 1000.0; + tlog(TLOG_INFO, "from %15s: seq=%d ttl=%d time=%.3f\n", host, seqno, ttl, rtt); + server_info->ttl = ttl; +} + /* add dns server information */ -int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, int result_flag) +int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, unsigned int result_flag, int ttl) { struct dns_server_info *server_info = NULL; @@ -238,6 +252,11 @@ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_typ if (server_info == NULL) { goto errout; } + + if (server_type != DNS_SERVER_UDP) { + result_flag &= (~DNSSERVER_FLAG_CHECK_TTL); + } + memset(server_info, 0, sizeof(*server_info)); server_info->ai_family = gai->ai_family; server_info->ai_addrlen = gai->ai_addrlen; @@ -245,6 +264,7 @@ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_typ server_info->fd = 0; server_info->status = DNS_SERVER_STATUS_INIT; server_info->result_flag = result_flag; + server_info->ttl = ttl; if (gai->ai_addrlen > sizeof(server_info->in6)) { tlog(TLOG_ERROR, "addr len invalid, %d, %zd, %d", gai->ai_addrlen, sizeof(server_info->addr), server_info->ai_family); @@ -253,13 +273,13 @@ int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_typ memcpy(&server_info->addr, gai->ai_addr, gai->ai_addrlen); /* start ping task */ -#if 0 - server_info->ping_host = fast_ping_start(server_ip, 0, 60000, 1000, NULL, server_info); - if (server_info->ping_host == NULL) { - tlog(TLOG_ERROR, "start ping failed."); - goto errout; + if (ttl == 0 && (result_flag & DNSSERVER_FLAG_CHECK_TTL)) { + server_info->ping_host = fast_ping_start(server_ip, 0, 60000, 1000, _dns_client_server_update_ttl, server_info); + if (server_info->ping_host == NULL) { + tlog(TLOG_ERROR, "start ping failed."); + goto errout; + } } -#endif /* add to list */ pthread_mutex_lock(&client.server_list_lock); @@ -352,7 +372,7 @@ int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_ return -1; } -int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int operate) +int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int ttl, int operate) { char port_s[8]; int sock_type; @@ -386,7 +406,7 @@ int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t serv } if (operate == 0) { - ret = _dns_client_server_add(server_ip, gai, server_type, result_flag); + ret = _dns_client_server_add(server_ip, gai, server_type, result_flag, ttl); if (ret != 0) { goto errout; } @@ -405,14 +425,14 @@ errout: return -1; } -int dns_add_server(char *server_ip, int port, dns_server_type_t server_type, int result_flag) +int dns_add_server(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int ttl) { - return _dns_client_server_operate(server_ip, port, server_type, result_flag, 0); + return _dns_client_server_operate(server_ip, port, server_type, result_flag, ttl, 0); } int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type) { - return _dns_client_server_operate(server_ip, port, server_type, 0, 1); + return _dns_client_server_operate(server_ip, port, server_type, 0, 0, 1); } int dns_server_num(void) @@ -684,6 +704,8 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info) { int fd = 0; struct epoll_event event; + const int on = 1; + const int val=255; fd = socket(server_info->ai_family, SOCK_DGRAM, 0); if (fd < 0) { @@ -701,6 +723,8 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info) server_info->fd = fd; server_info->status = DNS_SERVER_STATUS_CONNECTIONLESS; + setsockopt(server_info->fd, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); + setsockopt(server_info->fd, SOL_IP, IP_TTL, &val, sizeof(val)); return 0; errout: @@ -857,15 +881,45 @@ static int _dns_client_process_udp(struct dns_server_info *server_info, struct e struct sockaddr_storage from; socklen_t from_len = sizeof(from); char from_host[DNS_MAX_CNAME_LEN]; + struct msghdr msg; + struct iovec iov; + char ans_data[4096]; + int ttl = 0; + struct cmsghdr *cmsg; - /* receive from udp */ - len = recvfrom(server_info->fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len); + memset(&msg, 0, sizeof(msg)); + iov.iov_base = (char *)inpacket; + iov.iov_len = sizeof(inpacket); + msg.msg_name = &from; + msg.msg_namelen = sizeof(from); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ans_data; + msg.msg_controllen = sizeof(ans_data); + + len = recvmsg(server_info->fd, &msg, MSG_DONTWAIT); if (len < 0) { tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno)); return -1; } + from_len = msg.msg_namelen; - tlog(TLOG_DEBUG, "recv udp, from %s", gethost_by_addr(from_host, (struct sockaddr *)&from, from_len)); + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_IP + && cmsg->cmsg_type == IP_TTL + ) { + uint8_t * ttlPtr = (uint8_t *)CMSG_DATA(cmsg); + ttl = *ttlPtr; + break; + } + } + + tlog(TLOG_DEBUG, "recv udp, from %s, ttl: %d", gethost_by_addr(from_host, (struct sockaddr *)&from, from_len), ttl); + + if ((ttl != server_info->ttl) && (server_info->result_flag & DNSSERVER_FLAG_CHECK_TTL)) { + tlog(TLOG_DEBUG, "TTL mismatch, from:%d, local %d, discard result", ttl, server_info->ttl); + return 0; + } time(&server_info->last_recv); if (_dns_client_recv(server_info, inpacket, len, (struct sockaddr *)&from, from_len) != 0) { diff --git a/src/dns_client.h b/src/dns_client.h index 651a11d..313bfe5 100644 --- a/src/dns_client.h +++ b/src/dns_client.h @@ -18,6 +18,7 @@ typedef enum dns_result_type { #define DNSSERVER_FLAG_BLACKLIST_IP (0x1 << 0) #define DNSSERVER_FLAG_CHECK_EDNS (0x1 << 1) +#define DNSSERVER_FLAG_CHECK_TTL (0x1 << 2) int dns_client_init(void); @@ -32,7 +33,7 @@ int dns_client_query(char *domain, int qtype, dns_client_callback callback, void void dns_client_exit(void); /* add remote dns server */ -int dns_add_server(char *server_ip, int port, dns_server_type_t server_type, int result_flag); +int dns_add_server(char *server_ip, int port, dns_server_type_t server_type, int result_flag, int ttl); /* remove remote dns server */ int dns_remove_server(char *server_ip, int port, dns_server_type_t server_type); diff --git a/src/dns_conf.c b/src/dns_conf.c index 207ded0..2217d81 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -55,10 +55,12 @@ int config_server(int argc, char *argv[], dns_server_type_t type, int default_po char *ip = NULL; int opt = 0; int result_flag = 0; + int ttl = 0; /* clang-format off */ static struct option long_options[] = { {"blacklist-ip", 0, 0, 'b'}, {"check-edns", 0, 0, 'e'}, + {"check-ttl", required_argument, 0, 't'}, {0, 0, 0, 0} }; /* clang-format on */ @@ -84,6 +86,18 @@ int config_server(int argc, char *argv[], dns_server_type_t type, int default_po result_flag |= DNSSERVER_FLAG_CHECK_EDNS; break; } + case 't': { + if (DNS_SERVER_UDP != type) { + break; + } + + ttl = atoi(optarg); + if (ttl < 0 || ttl > 255) { + tlog(TLOG_ERROR, "ttl value is invalid."); + return -1; + } + result_flag |= DNSSERVER_FLAG_CHECK_TTL; + } } } @@ -106,8 +120,9 @@ int config_server(int argc, char *argv[], dns_server_type_t type, int default_po server->type = type; server->port = port; server->result_flag = result_flag; + server->ttl = ttl; dns_conf_server_num++; - tlog(TLOG_DEBUG, "add server %s, flag: %X", ip, result_flag); + tlog(TLOG_DEBUG, "add server %s, flag: %X, ttl: %d", ip, result_flag, ttl); return 0; } diff --git a/src/dns_conf.h b/src/dns_conf.h index 740d9d2..1798c3f 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -53,6 +53,7 @@ struct dns_servers { char server[DNS_MAX_IPLEN]; unsigned short port; unsigned int result_flag; + int ttl; dns_server_type_t type; }; diff --git a/src/dns_server.c b/src/dns_server.c index 9f9fb98..42ce90b 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -650,7 +650,7 @@ void _dns_server_request_get(struct dns_request *request) } void _dns_server_ping_result(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, - int seqno, struct timeval *tv, void *userptr) + int seqno, int ttl, struct timeval *tv, void *userptr) { struct dns_request *request = userptr; int may_complete = 0; diff --git a/src/fast_ping.c b/src/fast_ping.c index d9562c7..30fbbab 100644 --- a/src/fast_ping.c +++ b/src/fast_ping.c @@ -56,6 +56,7 @@ struct fast_ping_packet { struct icmp icmp; struct icmp6_hdr icmp6; }; + unsigned int ttl; struct fast_ping_packet_msg msg; }; @@ -72,6 +73,7 @@ struct ping_host_struct { int fd; unsigned int seq; + int ttl; struct timeval last; int interval; int timeout; @@ -267,7 +269,7 @@ static void _fast_ping_host_put(struct ping_host_struct *ping_host) tv.tv_sec = 0; tv.tv_usec = 0; - ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_END, &ping_host->addr, ping_host->addr_len, ping_host->seq, &tv, ping_host->userptr); + ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_END, &ping_host->addr, ping_host->addr_len, ping_host->seq, ping_host->ttl, &tv, ping_host->userptr); } tlog(TLOG_DEBUG, "ping %p end", ping_host); @@ -294,7 +296,7 @@ static void _fast_ping_host_remove(struct ping_host_struct *ping_host) tv.tv_sec = 0; tv.tv_usec = 0; - ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_END, &ping_host->addr, ping_host->addr_len, ping_host->seq, &tv, ping_host->userptr); + ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_END, &ping_host->addr, ping_host->addr_len, ping_host->seq, ping_host->ttl, &tv, ping_host->userptr); } _fast_ping_host_put(ping_host); @@ -469,6 +471,8 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type) struct epoll_event event; int buffsize = 64 * 1024; socklen_t optlen = sizeof(buffsize); + const int val=255; + const int on = 1; switch (type) { case FAST_PING_ICMP: @@ -487,6 +491,9 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type) goto errout; } _fast_ping_install_filter_v6(fd); + setsockopt(fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); + setsockopt(fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)); + setsockopt(fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); icmp_host = &ping.icmp6_host; break; default: @@ -495,6 +502,7 @@ static int _fast_ping_create_icmp_sock(FAST_PING_TYPE type) setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (const char *)&buffsize, optlen); setsockopt(fd, SOL_SOCKET, SO_RCVBUF, (const char *)&buffsize, optlen); + setsockopt(fd, SOL_IP, IP_TTL, &val, sizeof(val)); memset(&event, 0, sizeof(event)); event.events = EPOLLIN; @@ -552,11 +560,11 @@ errout: } void fast_ping_print_result(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, - struct timeval *tv, void *userptr) + int ttl, struct timeval *tv, void *userptr) { if (result == PING_RESULT_RESPONSE) { double rtt = tv->tv_sec * 1000.0 + tv->tv_usec / 1000.0; - tlog(TLOG_INFO, "from %15s: seq=%d time=%.3f\n", host, seqno, rtt); + tlog(TLOG_INFO, "from %15s: seq=%d ttl=%d time=%.3f\n", host, seqno, ttl, rtt); } else if (result == PING_RESULT_TIMEOUT) { tlog(TLOG_INFO, "from %15s: seq=%d timeout\n", host, seqno); } else if (result == PING_RESULT_END) { @@ -694,6 +702,10 @@ errout_remove: int fast_ping_stop(struct ping_host_struct *ping_host) { + if (ping_host == NULL) { + return 0; + } + atomic_inc_return(&ping_host->notified); _fast_ping_host_remove(ping_host); _fast_ping_host_put(ping_host); @@ -709,12 +721,29 @@ static void tv_sub(struct timeval *out, struct timeval *in) out->tv_sec -= in->tv_sec; } -static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len) +static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct *ping_host, struct msghdr *msg, u_char *packet_data, int data_len) { int icmp_len; struct fast_ping_packet *packet = (struct fast_ping_packet *)packet_data; struct icmp6_hdr *icmp6 = &packet->icmp6; + struct cmsghdr *c; + int hops = 0; + for (c = CMSG_FIRSTHDR(msg); c; c = CMSG_NXTHDR(msg, c)) { + if (c->cmsg_level != IPPROTO_IPV6) + continue; + switch(c->cmsg_type) { + case IPV6_HOPLIMIT: +#ifdef IPV6_2292HOPLIMIT + case IPV6_2292HOPLIMIT: +#endif + if (c->cmsg_len < CMSG_LEN(sizeof(int))) + continue; + memcpy(&hops, CMSG_DATA(c), sizeof(hops)); + } + } + + packet->ttl = hops; if (icmp6->icmp6_type != ICMP6_ECHO_REPLY) { tlog(TLOG_DEBUG, "icmp6 type faild, %d:%d", icmp6->icmp6_type, ICMP6_ECHO_REPLY); return NULL; @@ -734,7 +763,7 @@ static struct fast_ping_packet *_fast_ping_icmp6_packet(struct ping_host_struct return packet; } -static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct *ping_host, u_char *packet_data, int data_len) +static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct *ping_host, struct msghdr *msg, u_char *packet_data, int data_len) { struct ip *ip = (struct ip *)packet_data; struct fast_ping_packet *packet; @@ -751,6 +780,7 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct * packet = (struct fast_ping_packet *)(packet_data + hlen); icmp = &packet->icmp; icmp_len = data_len - hlen; + packet->ttl = ip->ip_ttl; if (icmp_len < 16) { tlog(TLOG_ERROR, "length is invalid, %d", icmp_len); @@ -770,17 +800,17 @@ static struct fast_ping_packet *_fast_ping_icmp_packet(struct ping_host_struct * return packet; } -struct fast_ping_packet *_fast_ping_recv_packet(struct ping_host_struct *ping_host, u_char *inpacket, int len, struct timeval *tvrecv) +struct fast_ping_packet *_fast_ping_recv_packet(struct ping_host_struct *ping_host, struct msghdr *msg, u_char *inpacket, int len, struct timeval *tvrecv) { struct fast_ping_packet *packet = NULL; if (ping_host->type == FAST_PING_ICMP6) { - packet = _fast_ping_icmp6_packet(ping_host, inpacket, len); + packet = _fast_ping_icmp6_packet(ping_host, msg, inpacket, len); if (packet == NULL) { goto errout; } } else if (ping_host->type == FAST_PING_ICMP) { - packet = _fast_ping_icmp_packet(ping_host, inpacket, len); + packet = _fast_ping_icmp_packet(ping_host, msg, inpacket, len); if (packet == NULL) { goto errout; } @@ -808,14 +838,28 @@ static int _fast_ping_process_icmp(struct ping_host_struct *ping_host, struct ti unsigned int sid; unsigned int seq; unsigned int cookie; + struct msghdr msg; + struct iovec iov; + char ans_data[4096]; - len = recvfrom(ping_host->fd, inpacket, sizeof(inpacket), 0, (struct sockaddr *)&from, (socklen_t *)&from_len); + memset(&msg, 0, sizeof(msg)); + iov.iov_base = (char *)inpacket; + iov.iov_len = sizeof(inpacket); + msg.msg_name = &from; + msg.msg_namelen = sizeof(from); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = ans_data; + msg.msg_controllen = sizeof(ans_data); + + len = recvmsg(ping_host->fd, &msg, MSG_DONTWAIT); if (len < 0) { tlog(TLOG_ERROR, "recvfrom failed, %s\n", strerror(errno)); goto errout; } - packet = _fast_ping_recv_packet(ping_host, inpacket, len, now); + from_len = msg.msg_namelen; + packet = _fast_ping_recv_packet(ping_host, &msg, inpacket, len, now); if (packet == NULL) { char name[PING_MAX_HOSTLEN]; tlog(TLOG_DEBUG, "recv ping packet from %s failed.", gethost_by_addr(name, (struct sockaddr *)&from, from_len)); @@ -848,10 +892,11 @@ static int _fast_ping_process_icmp(struct ping_host_struct *ping_host, struct ti return -1; } + recv_ping_host->ttl = packet->ttl; tv_sub(&tvresult, tvsend); if (recv_ping_host->ping_callback) { recv_ping_host->ping_callback(recv_ping_host, recv_ping_host->host, PING_RESULT_RESPONSE, &recv_ping_host->addr, recv_ping_host->addr_len, - recv_ping_host->seq, &tvresult, recv_ping_host->userptr); + recv_ping_host->seq, recv_ping_host->ttl, &tvresult, recv_ping_host->userptr); } recv_ping_host->send = 0; @@ -882,8 +927,8 @@ static int _fast_ping_process_tcp(struct ping_host_struct *ping_host, struct epo } tv_sub(&tvresult, tvsend); if (ping_host->ping_callback) { - ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_RESPONSE, &ping_host->addr, ping_host->addr_len, ping_host->seq, &tvresult, - ping_host->userptr); + ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_RESPONSE, &ping_host->addr, ping_host->addr_len, ping_host->seq, + ping_host->ttl, &tvresult, ping_host->userptr); } ping_host->send = 0; @@ -984,8 +1029,8 @@ static void _fast_ping_period_run(void) tv_sub(&interval, &ping_host->last); millisecond = interval.tv_sec * 1000 + interval.tv_usec / 1000; if (millisecond >= ping_host->timeout && ping_host->send == 1) { - ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_TIMEOUT, &ping_host->addr, ping_host->addr_len, ping_host->seq, &interval, - ping_host->userptr); + ping_host->ping_callback(ping_host, ping_host->host, PING_RESULT_TIMEOUT, &ping_host->addr, ping_host->addr_len, ping_host->seq, + ping_host->ttl, &interval, ping_host->userptr); ping_host->send = 0; } diff --git a/src/fast_ping.h b/src/fast_ping.h index 5edfde2..62b221c 100644 --- a/src/fast_ping.h +++ b/src/fast_ping.h @@ -22,7 +22,7 @@ typedef enum { } FAST_PING_RESULT; struct ping_host_struct; -typedef void (*fast_ping_result)(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, struct timeval *tv, void *userptr); +typedef void (*fast_ping_result)(struct ping_host_struct *ping_host, const char *host, FAST_PING_RESULT result, struct sockaddr *addr, socklen_t addr_len, int seqno, int ttl, struct timeval *tv, void *userptr); /* start ping */ struct ping_host_struct *fast_ping_start(const char *host, int count, int interval, int timeout, fast_ping_result ping_callback, void *userptr); diff --git a/src/smartdns.c b/src/smartdns.c index d3da0b6..430eb1e 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -124,7 +124,8 @@ int smartdns_add_servers(void) int i = 0; int ret = 0; for (i = 0; i < dns_conf_server_num; i++) { - ret = dns_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, dns_conf_servers[i].result_flag); + ret = dns_add_server(dns_conf_servers[i].server, dns_conf_servers[i].port, dns_conf_servers[i].type, dns_conf_servers[i].result_flag, + dns_conf_servers[i].ttl); if (ret != 0) { tlog(TLOG_ERROR, "add server failed, %s:%d", dns_conf_servers[i].server, dns_conf_servers[i].port); return -1;