Support Delay DNS server bootstrap

This commit is contained in:
Nick Peng
2019-05-15 00:36:11 +08:00
parent 93d9ef4095
commit 1e88305df8
5 changed files with 285 additions and 102 deletions

View File

@@ -17,6 +17,7 @@
*/
#include "dns_client.h"
#include "dns_server.h"
#include "atomic.h"
#include "dns.h"
#include "dns_conf.h"
@@ -94,6 +95,7 @@ struct dns_server_info {
struct ping_host_struct *ping_host;
char ip[DNS_HOSTNAME_LEN];
int port;
/* server type */
dns_server_type_t type;
@@ -125,6 +127,26 @@ struct dns_server_info {
struct client_dns_server_flags flags;
};
struct dns_server_pending {
struct list_head list;
char host[DNS_HOSTNAME_LEN];
char ipv4[DNS_HOSTNAME_LEN];
char ipv6[DNS_HOSTNAME_LEN];
unsigned int ping_time_v6;
unsigned int ping_time_v4;
unsigned int has_v4;
unsigned int has_v6;
unsigned int query_v4;
unsigned int query_v6;
/* server type */
dns_server_type_t type;
int port;
struct client_dns_server_flags flags;
};
/* upstream server group member */
struct dns_server_group_member {
struct list_head list;
@@ -211,6 +233,9 @@ struct dns_query_struct {
static struct dns_client client;
static atomic_t dns_client_sid = ATOMIC_INIT(0);
static LIST_HEAD(pending_servers);
pthread_mutex_t pending_server_mutex = PTHREAD_MUTEX_INITIALIZER;
static int dns_client_has_bootstrap_dns = 0;
/* get addr info */
static struct addrinfo *_dns_client_getaddr(const char *host, char *port, int type, int protocol)
@@ -240,21 +265,17 @@ errout:
}
/* check whether server exists */
static int _dns_client_server_exist(struct addrinfo *gai, dns_server_type_t server_type)
static int _dns_client_server_exist(const char *server_ip, int port, dns_server_type_t server_type)
{
struct dns_server_info *server_info, *tmp;
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
if (server_info->ai_addrlen != gai->ai_addrlen || server_info->ai_family != gai->ai_family) {
if (server_info->port != port || server_info->type != server_type) {
continue;
}
if (server_info->type != server_type) {
continue;
}
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
if (strncmp(server_info->ip, server_ip, DNS_HOSTNAME_LEN)) {
continue;
}
@@ -284,49 +305,15 @@ static struct dns_server_info *_dns_client_get_server(char *server_ip, int port,
{
struct dns_server_info *server_info, *tmp;
struct dns_server_info *server_info_return = NULL;
char port_s[8];
int sock_type;
struct addrinfo *gai = NULL;
if (server_type >= DNS_SERVER_TYPE_END) {
tlog(TLOG_ERROR, "server type is invalid.");
return NULL;
}
switch (server_type) {
case DNS_SERVER_UDP:
sock_type = SOCK_DGRAM;
break;
case DNS_SERVER_TCP:
case DNS_SERVER_TLS:
case DNS_SERVER_HTTPS:
sock_type = SOCK_STREAM;
break;
default:
return NULL;
break;
}
/* get addr info */
snprintf(port_s, 8, "%d", port);
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
if (gai == NULL) {
tlog(TLOG_ERROR, "get address failed, %s:%d", server_ip, port);
goto errout;
}
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
if (server_info->ai_addrlen != gai->ai_addrlen || server_info->ai_family != gai->ai_family) {
if (server_info->port != port || server_info->type != server_type) {
continue;
}
if (server_info->type != server_type) {
continue;
}
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
if (strncmp(server_info->ip, server_ip, DNS_HOSTNAME_LEN)) {
continue;
}
@@ -337,13 +324,7 @@ static struct dns_server_info *_dns_client_get_server(char *server_ip, int port,
pthread_mutex_unlock(&client.server_list_lock);
freeaddrinfo(gai);
return server_info_return;
errout:
if (gai) {
freeaddrinfo(gai);
}
return NULL;
}
/* get server group by name */
@@ -618,12 +599,15 @@ static char *_dns_client_server_get_spki(struct dns_server_info *server_info, in
}
/* add dns server information */
static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type, struct client_dns_server_flags *flags)
static int _dns_client_server_add(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
{
struct dns_server_info *server_info = NULL;
struct addrinfo *gai = NULL;
unsigned char *spki_data = NULL;
int spki_data_len = 0;
int ttl = 0;
char port_s[8];
int sock_type;
switch (server_type) {
case DNS_SERVER_UDP: {
@@ -634,6 +618,8 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
} else if (ttl < -32) {
ttl = -32;
}
sock_type = SOCK_DGRAM;
} break;
case DNS_SERVER_HTTPS: {
struct client_dns_server_flag_https *flag_https = &flags->https;
@@ -641,13 +627,16 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
if (flag_https->httphost[0] == 0) {
strncpy(flag_https->httphost, server_ip, DNS_MAX_CNAME_LEN);
}
sock_type = SOCK_STREAM;
} break;
case DNS_SERVER_TLS: {
struct client_dns_server_flag_tls *flag_tls = &flags->tls;
spki_data_len = flag_tls->spi_len;
sock_type = SOCK_STREAM;
} break;
break;
case DNS_SERVER_TCP:
sock_type = SOCK_STREAM;
break;
default:
return -1;
@@ -660,10 +649,17 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
}
/* if server exist, return */
if (_dns_client_server_exist(gai, server_type) == 0) {
if (_dns_client_server_exist(server_ip, port, server_type) == 0) {
return 0;
}
snprintf(port_s, 8, "%d", port);
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
if (gai == NULL) {
tlog(TLOG_DEBUG, "get address failed, %s:%d", server_ip, port);
goto errout;
}
server_info = malloc(sizeof(*server_info));
if (server_info == NULL) {
goto errout;
@@ -675,6 +671,7 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
memset(server_info, 0, sizeof(*server_info));
strncpy(server_info->ip, server_ip, sizeof(server_info->ip));
server_info->port = port;
server_info->ai_family = gai->ai_family;
server_info->ai_addrlen = gai->ai_addrlen;
server_info->type = server_type;
@@ -729,6 +726,8 @@ static int _dns_client_server_add(char *server_ip, struct addrinfo *gai, dns_ser
pthread_mutex_unlock(&client.server_list_lock);
atomic_inc(&client.dns_server_num);
freeaddrinfo(gai);
return 0;
errout:
if (spki_data) {
@@ -747,6 +746,10 @@ errout:
free(server_info);
}
if (gai) {
freeaddrinfo(gai);
}
return -1;
}
@@ -812,7 +815,7 @@ static void _dns_client_server_remove_all(void)
}
/* remove single server */
static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_server_type_t server_type)
static int _dns_client_server_remove(char *server_ip, int port, dns_server_type_t server_type)
{
struct dns_server_info *server_info, *tmp;
@@ -820,11 +823,11 @@ static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_
pthread_mutex_lock(&client.server_list_lock);
list_for_each_entry_safe(server_info, tmp, &client.dns_server_list, list)
{
if (server_info->ai_addrlen != gai->ai_addrlen || server_info->ai_family != gai->ai_family) {
if (server_info->port != port || server_info->type != server_type) {
continue;
}
if (memcmp(&server_info->addr, gai->ai_addr, gai->ai_addrlen) != 0) {
if (strncmp(server_info->ip, server_ip, DNS_HOSTNAME_LEN)) {
continue;
}
@@ -840,70 +843,78 @@ static int _dns_client_server_remove(char *server_ip, struct addrinfo *gai, dns_
return -1;
}
static int _dns_client_server_operate(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags, int operate)
static int _dns_client_server_pending(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
{
char port_s[8];
int sock_type;
int ret;
struct addrinfo *gai = NULL;
struct dns_server_pending *pending = NULL;
pending = malloc(sizeof(*pending));
if (pending == NULL) {
tlog(TLOG_ERROR, "malloc failed");
goto errout;
}
memset(pending, 0, sizeof(*pending));
strncpy(pending->host, server_ip, DNS_HOSTNAME_LEN);
pending->port = port;
pending->type = server_type;
pending->ping_time_v4 = -1;
pending->ping_time_v6 = -1;
pending->ipv4[0] = 0;
pending->ipv6[0] = 0;
pending->has_v4 = 0;
pending->has_v6 = 0;
memcpy(&pending->flags, flags, sizeof(struct client_dns_server_flags));
pthread_mutex_lock(&pending_server_mutex);
list_add_tail(&pending->list, &pending_servers);
pthread_mutex_unlock(&pending_server_mutex);
return 0;
errout:
if (pending) {
free(pending);
}
return -1;
}
static int _dns_client_add_server_pending(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags, int ispending)
{
int ret;
if (server_type >= DNS_SERVER_TYPE_END) {
tlog(TLOG_ERROR, "server type is invalid.");
return -1;
}
switch (server_type) {
case DNS_SERVER_UDP:
sock_type = SOCK_DGRAM;
break;
case DNS_SERVER_TCP:
case DNS_SERVER_TLS:
case DNS_SERVER_HTTPS:
sock_type = SOCK_STREAM;
break;
default:
return -1;
break;
if (check_is_ipaddr(server_ip) && ispending) {
ret = _dns_client_server_pending(server_ip, port, server_type, flags);
if (ret == 0) {
tlog(TLOG_INFO, "add pending server %s", server_ip);
return 0;
}
}
/* get addr info */
snprintf(port_s, 8, "%d", port);
gai = _dns_client_getaddr(server_ip, port_s, sock_type, 0);
if (gai == NULL) {
tlog(TLOG_ERROR, "get address failed, %s:%d", server_ip, port);
/* add server */
ret = _dns_client_server_add(server_ip, port, server_type, flags);
if (ret != 0) {
goto errout;
}
if (operate == 0) {
/* add server */
ret = _dns_client_server_add(server_ip, gai, server_type, flags);
if (ret != 0) {
goto errout;
}
} else {
/* remove server */
ret = _dns_client_server_remove(server_ip, gai, server_type);
if (ret != 0) {
goto errout;
}
}
freeaddrinfo(gai);
dns_client_has_bootstrap_dns = 1;
return 0;
errout:
if (gai) {
freeaddrinfo(gai);
}
return -1;
}
int dns_client_add_server(char *server_ip, int port, dns_server_type_t server_type, struct client_dns_server_flags *flags)
{
return _dns_client_server_operate(server_ip, port, server_type, flags, 0);
return _dns_client_add_server_pending(server_ip, port, server_type, flags, 1);
}
int dns_client_remove_server(char *server_ip, int port, dns_server_type_t server_type)
{
return _dns_client_server_operate(server_ip, port, server_type, NULL, 1);
return _dns_client_server_remove(server_ip, port, server_type);
}
int dns_server_num(void)
@@ -2378,10 +2389,87 @@ static void _dns_client_check_servers(void)
pthread_mutex_unlock(&client.server_list_lock);
}
static int _dns_client_pending_server_resolve(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, unsigned int ping_time, void *user_ptr)
{
struct dns_server_pending *pending = user_ptr;
if (addr_type == DNS_T_A) {
pending->has_v4 = 1;
pending->ping_time_v4 = -1;
if (rtcode == DNS_RC_NOERROR) {
pending->ping_time_v4 = ping_time;
strncpy(pending->ipv4, ip, DNS_HOSTNAME_LEN);
}
} else if (addr_type == DNS_T_AAAA) {
pending->has_v6 = 1;
pending->ping_time_v6 = -1;
if (rtcode == DNS_RC_NOERROR) {
pending->ping_time_v6 = ping_time;
strncpy(pending->ipv6, ip, DNS_HOSTNAME_LEN);
}
} else {
return -1;
}
return 0;
}
static void _dns_client_add_pending_servers(void)
{
struct dns_server_pending *pending, *tmp;
pthread_mutex_lock(&pending_server_mutex);
list_for_each_entry_safe(pending, tmp, &pending_servers, list)
{
/* send dns type A, AAAA query to bootstrap DNS server */
if (pending->query_v4 == 0) {
pending->query_v4 = 1;
dns_server_query(pending->host, DNS_T_A, _dns_client_pending_server_resolve, pending);
}
if (pending->query_v6 == 0) {
pending->query_v6 = 1;
dns_server_query(pending->host, DNS_T_AAAA, _dns_client_pending_server_resolve, pending);
}
/* if both A, AAAA has query result, select fastest IP address */
if (pending->has_v4 && pending->has_v6) {
char *ip = NULL;
if (pending->ping_time_v4 <= pending->ping_time_v6 && pending->ipv4[0]) {
ip = pending->ipv4;
} else {
ip = pending->ipv6;
}
if (ip[0]) {
if (_dns_client_add_server_pending(ip, pending->port, pending->type, &pending->flags, 0) != 0) {
tlog(TLOG_WARN, "add server %s failed.", pending->host);
}
}
list_del_init(&pending->list);
free(pending);
}
/* if has no bootstrap DNS, just call getaddrinfo to get address */
if (dns_client_has_bootstrap_dns == 0) {
if (_dns_client_add_server_pending(pending->host, pending->port, pending->type, &pending->flags, 0) != 0) {
tlog(TLOG_ERROR, "Get DNS server failed");
exit(1);
pthread_mutex_unlock(&pending_server_mutex);
return;
}
list_del_init(&pending->list);
free(pending);
}
}
pthread_mutex_unlock(&pending_server_mutex);
}
static void _dns_client_period_run_second(void)
{
_dns_client_check_tcp();
_dns_client_check_servers();
_dns_client_add_pending_servers();
}
static void _dns_client_period_run(void)

View File

@@ -528,6 +528,7 @@ static int _dns_setup_ipset(struct dns_request *request)
static int _dns_result_callback(struct dns_request *request)
{
char ip[DNS_MAX_CNAME_LEN];
unsigned int ping_time = -1;
if (request->result_callback == NULL) {
return 0;
@@ -540,8 +541,8 @@ static int _dns_result_callback(struct dns_request *request)
}
sprintf(ip, "%d.%d.%d.%d", request->ipv4_addr[0], request->ipv4_addr[1], request->ipv4_addr[2], request->ipv4_addr[3]);
return request->result_callback(request->domain, request->rcode, request->qtype, ip, request->user_ptr);
ping_time = request->ping_ttl_v4;
return request->result_callback(request->domain, request->rcode, request->qtype, ip, ping_time, request->user_ptr);
} else if (request->qtype == DNS_T_AAAA) {
if (request->has_ipv6 == 0) {
goto out;
@@ -551,16 +552,16 @@ static int _dns_result_callback(struct dns_request *request)
request->ipv6_addr[2], request->ipv6_addr[3], request->ipv6_addr[4], request->ipv6_addr[5], request->ipv6_addr[6], request->ipv6_addr[7],
request->ipv6_addr[8], request->ipv6_addr[9], request->ipv6_addr[10], request->ipv6_addr[11], request->ipv6_addr[12], request->ipv6_addr[13],
request->ipv6_addr[14], request->ipv6_addr[15]);
return request->result_callback(request->domain, request->rcode, request->qtype, ip, request->user_ptr);
ping_time = request->ping_ttl_v6;
return request->result_callback(request->domain, request->rcode, request->qtype, ip, ping_time, request->user_ptr);
}
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, request->user_ptr);
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, ping_time, request->user_ptr);
return 0;
out:
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, request->user_ptr);
request->result_callback(request->domain, DNS_RC_NXDOMAIN, request->qtype, ip, ping_time, request->user_ptr);
return 0;
}

View File

@@ -18,7 +18,7 @@ void dns_server_stop(void);
void dns_server_exit(void);
/* query result notify function */
typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, void *user_ptr);
typedef int (*dns_result_callback)(char *domain, dns_rtcode_t rtcode, dns_type_t addr_type, char *ip, unsigned int ping_time, void *user_ptr);
/* query domain */
int dns_server_query(char *domain, int qtype, dns_result_callback callback, void *user_ptr);

View File

@@ -193,6 +193,98 @@ int parse_ip(const char *value, char *ip, int *port)
return 0;
}
static int _check_is_ipv4(const char *ip)
{
const char *ptr = ip;
char c = 0;
int dot_num = 0;
int dig_num = 0;
while ( (c = *ptr++) != '\0') {
if (c == '.') {
dot_num++;
dig_num = 0;
continue;
}
/* check number count of one field */
if (dig_num >= 4) {
return -1;
}
if (c >= '0' && c <= '9') {
dig_num++;
continue;
}
return -1;
}
/* check field number */
if (dot_num != 3) {
return -1;
}
return 0;
}
static int _check_is_ipv6(const char *ip)
{
const char *ptr = ip;
char c = 0;
int colon_num = 0;
int dig_num = 0;
while ( (c = *ptr++) != '\0') {
if (c == '[' || c == ']') {
continue;
}
if (c == ':') {
colon_num++;
dig_num = 0;
continue;
}
/* check number count of one field */
if (dig_num >= 5) {
return -1;
}
dig_num++;
if (c >= '0' && c <= '9') {
continue;
}
if (c >= 'a' && c <= 'f') {
continue;
}
if (c >= 'A' && c <= 'F') {
continue;
}
return -1;
}
/* check field number */
if (colon_num > 7) {
return -1;
}
return 0;
}
int check_is_ipaddr(const char *ip)
{
if (strstr(ip, ".")) {
/* IPV4 */
return _check_is_ipv4(ip);
} else if (strstr(ip, ":")) {
/* IPV6 */
return _check_is_ipv6(ip);
}
return -1;
}
int parse_uri(char *value, char *scheme, char *host, int *port, char *path)
{
char *scheme_end = NULL;

View File

@@ -16,6 +16,8 @@ int getaddr_by_host(char *host, struct sockaddr *addr, socklen_t *addr_len);
int parse_ip(const char *value, char *ip, int *port);
int check_is_ipaddr(const char *ip);
int parse_uri(char *value, char *scheme, char *host, int *port, char *path);
int set_fd_nonblock(int fd, int nonblock);