diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index c86d8e4..3063439 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -44,6 +44,11 @@ cache-size 512 # dualstack-preference [yes|no] # dualstack-preference yes +# edns client subnet +# edns-client-subnet-ipv4 [ip/subnet] +# edns-client-subnet-ipv6 [ip/subnet] +# edns-client-subnet-ipv4 192.168.1.1/24 + # ttl for all resource record # rr-ttl: ttl for all record # rr-ttl-min: minimum ttl for resource record diff --git a/src/dns.c b/src/dns.c index a3868cf..61dac71 100644 --- a/src/dns.c +++ b/src/dns.c @@ -656,7 +656,7 @@ int dns_get_OPT_payload_size(struct dns_packet *packet) return packet->payloadsize; } -int dns_add_OPT_ECS(struct dns_packet *packet, dns_rr_type type, struct dns_opt_ecs *ecs) +int dns_add_OPT_ECS(struct dns_packet *packet, struct dns_opt_ecs *ecs) { unsigned char opt_data[DNS_MAX_OPT_LEN]; struct dns_opt *opt = (struct dns_opt *)opt_data; @@ -1425,7 +1425,7 @@ static int _dns_decode_opt(struct dns_context *context, dns_rr_type type, unsign return -1; } - ret = dns_add_OPT_ECS(packet, type, &ecs); + ret = dns_add_OPT_ECS(packet, &ecs); } break; default: context->ptr += opt_len; diff --git a/src/dns.h b/src/dns.h index c962096..f06d739 100644 --- a/src/dns.h +++ b/src/dns.h @@ -10,10 +10,13 @@ #define DNS_RR_AAAA_LEN 16 #define DNS_MAX_CNAME_LEN 256 #define DNS_MAX_OPT_LEN 256 -#define DNS_IN_PACKSIZE (512 * 4) +#define DNS_IN_PACKSIZE (512 * 2) #define DNS_PACKSIZE (512 * 8) #define DNS_DEFAULT_PACKET_SIZE 512 +#define DNS_ADDR_FAMILY_IP 1 +#define DNS_ADDR_FAMILY_IPV6 2 + typedef enum dns_qr { DNS_QR_QUERY = 0, DNS_QR_ANSWER = 1, @@ -188,7 +191,7 @@ int dns_get_SOA(struct dns_rrs *rrs, char *domain, int maxsize, int *ttl, struct int dns_set_OPT_payload_size(struct dns_packet *packet, int payload_size); int dns_get_OPT_payload_size(struct dns_packet *packet); -int dns_add_OPT_ECS(struct dns_packet *packet, dns_rr_type type, struct dns_opt_ecs *ecs); +int dns_add_OPT_ECS(struct dns_packet *packet, struct dns_opt_ecs *ecs); int dns_get_OPT_ECS(struct dns_rrs *rrs, unsigned short *opt_code, unsigned short *opt_len, struct dns_opt_ecs *ecs); /* * Packet operation diff --git a/src/dns_client.c b/src/dns_client.c index eb1aa87..1ccd266 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -50,6 +50,17 @@ #define DNS_HOSTNAME_LEN 128 #define DNS_TCP_BUFFER (16 * 1024) +struct dns_client_ecs { + int enable; + unsigned int family; + unsigned int bitlen; + union { + unsigned char ipv4_addr[DNS_RR_A_LEN]; + unsigned char ipv6_addr[DNS_RR_AAAA_LEN]; + unsigned char addr[0]; + }; +}; + /* dns client */ struct dns_client { pthread_t tid; @@ -65,6 +76,10 @@ struct dns_client { struct list_head dns_request_list; atomic_t dns_server_num; + /* ECS */ + struct dns_client_ecs ecs_ipv4; + struct dns_client_ecs ecs_ipv6; + /* query doman hash table, key: sid + domain */ pthread_mutex_t domain_map_lock; DECLARE_HASHTABLE(domain_map, 6); @@ -1548,6 +1563,26 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, return 0; } +static int _dns_client_dns_add_ecs(struct dns_packet *packet, int qtype) +{ + if (qtype == DNS_T_A && client.ecs_ipv4.enable) { + struct dns_opt_ecs ecs; + ecs.family = DNS_ADDR_FAMILY_IP; + ecs.source_prefix = client.ecs_ipv4.bitlen; + ecs.scope_prefix = 0; + memcpy(ecs.addr, client.ecs_ipv4.ipv4_addr, DNS_RR_A_LEN); + return dns_add_OPT_ECS(packet, &ecs); + } else if (qtype == DNS_T_AAAA && client.ecs_ipv6.enable) { + struct dns_opt_ecs ecs; + ecs.family = DNS_ADDR_FAMILY_IPV6; + ecs.source_prefix = client.ecs_ipv6.bitlen; + ecs.scope_prefix = 0; + memcpy(ecs.addr, client.ecs_ipv6.ipv6_addr, DNS_RR_AAAA_LEN); + return dns_add_OPT_ECS(packet, &ecs); + } + return 0; +} + static int _dns_client_send_query(struct dns_query_struct *query, char *doamin) { unsigned char packet_buff[DNS_PACKSIZE]; @@ -1571,7 +1606,13 @@ static int _dns_client_send_query(struct dns_query_struct *query, char *doamin) /* add question */ dns_add_domain(packet, doamin, query->qtype, DNS_C_IN); - dns_set_OPT_payload_size(packet, 1024); + dns_set_OPT_payload_size(packet, DNS_IN_PACKSIZE); + + if (_dns_client_dns_add_ecs(packet, query->qtype) != 0) { + tlog(TLOG_ERROR, "add ecs failed."); + return -1; + } + /* encode packet */ encode_len = dns_encode(inpacket, DNS_IN_PACKSIZE, packet); if (encode_len <= 0) { @@ -1642,6 +1683,12 @@ errout: return -1; } +int dns_client_set_ecs(char *ip, int subnet) +{ + + return 0; +} + int dns_client_init() { pthread_attr_t attr; diff --git a/src/dns_client.h b/src/dns_client.h index 2ef63b1..651a11d 100644 --- a/src/dns_client.h +++ b/src/dns_client.h @@ -21,6 +21,8 @@ typedef enum dns_result_type { int dns_client_init(void); +int dns_client_set_ecs(char *ip, int subnet); + /* query result notify function */ typedef int (*dns_client_callback)(char *domain, dns_result_type rtype, unsigned int result_flag, struct dns_packet *packet, unsigned char *inpacket, int inpacket_len, void *user_ptr); diff --git a/src/dns_conf.c b/src/dns_conf.c index 28d7c40..622d10e 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -44,6 +44,9 @@ int dns_conf_rr_ttl_min; int dns_conf_rr_ttl_max; int dns_conf_force_AAAA_SOA; +struct dns_edns_client_subnet dns_conf_ipv4_ecs; +struct dns_edns_client_subnet dns_conf_ipv6_ecs; + int config_server(int argc, char *argv[], dns_server_type_t type, int default_port) { int index = dns_conf_server_num; @@ -477,14 +480,61 @@ int config_iplist_rule(char *subnet, enum address_rule rule) int config_blacklist_ip(void *data, int argc, char *argv[]) { + if (argc <= 1) { + return -1; + } + return config_iplist_rule(argv[1], ADDRESS_RULE_BLACKLIST); } int conf_bogus_nxdomain(void *data, int argc, char *argv[]) { + if (argc <= 1) { + return -1; + } + return config_iplist_rule(argv[1], ADDRESS_RULE_BOGUS); } +int conf_edns_client_subnet(void *data, int argc, char *argv[]) +{ + char *slash = NULL; + char *value = NULL; + int subnet = 0; + struct dns_edns_client_subnet *ecs = data; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + + if (argc <= 1 || data == NULL) { + return -1; + } + + value = argv[1]; + + slash = strstr(value, "/"); + if (slash) { + *slash = 0; + slash++; + subnet = atoi(slash); + if (subnet < 0 || subnet > 128) { + return -1; + } + } + + if (getaddr_by_host(value, (struct sockaddr *)&addr, &addr_len) != 0) { + goto errout; + } + + strncpy(ecs->ip, value, DNS_MAX_IPLEN); + ecs->subnet = subnet; + ecs->enable = 1; + + return 0; + +errout: + return -1; +} + int config_log_level(void *data, int argc, char *argv[]) { /* read log level and set */ @@ -533,6 +583,8 @@ struct config_item config_item[] = { CONF_YESNO("force-AAAA-SOA", &dns_conf_force_AAAA_SOA), CONF_CUSTOM("blacklist-ip", config_blacklist_ip, NULL), CONF_CUSTOM("bogus-nxdomain", conf_bogus_nxdomain, NULL), + CONF_CUSTOM("edns-client-subnet-ipv4", conf_edns_client_subnet, &dns_conf_ipv6_ecs), + CONF_CUSTOM("edns-client-subnet-ipv6", conf_edns_client_subnet, &dns_conf_ipv6_ecs), CONF_CUSTOM("conf-file", config_addtional_file, NULL), CONF_END(), }; diff --git a/src/dns_conf.h b/src/dns_conf.h index a8fc1bd..0b404ef 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -77,6 +77,12 @@ struct dns_ip_address_rule { unsigned int bogus : 1; }; +struct dns_edns_client_subnet { + int enable; + char ip[DNS_MAX_IPLEN]; + int subnet; +}; + 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; @@ -107,6 +113,9 @@ extern int dns_conf_rr_ttl_min; extern int dns_conf_rr_ttl_max; extern int dns_conf_force_AAAA_SOA; +extern struct dns_edns_client_subnet dns_conf_ipv4_ecs; +extern struct dns_edns_client_subnet dns_conf_ipv6_ecs; + void dns_server_load_exit(void); int dns_server_load_conf(const char *file); diff --git a/src/dns_server.c b/src/dns_server.c index f4356f4..14cdf03 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -567,7 +567,8 @@ void _dns_server_request_release(struct dns_request *request) int refcnt = atomic_dec_return(&request->refcnt); if (refcnt) { if (refcnt < 0) { - tlog(TLOG_ERROR, "BUG: refcnt is %d, domain %s", refcnt, request->domain); + tlog(TLOG_ERROR, "BUG: refcnt is %d, domain %s, qtype =%d", refcnt, request->domain, + request->qtype); abort(); } return; @@ -707,10 +708,10 @@ int _dns_ip_address_check_add(struct dns_request *request, unsigned char *addr, } } request->ip_map_num++; - pthread_mutex_unlock(&request->ip_map_lock); addr_map = malloc(sizeof(*addr_map)); if (addr_map == NULL) { + pthread_mutex_unlock(&request->ip_map_lock); tlog(TLOG_ERROR, "malloc failed"); return -1; } @@ -718,6 +719,7 @@ int _dns_ip_address_check_add(struct dns_request *request, unsigned char *addr, addr_map->addr_type = addr_type; memcpy(addr_map->addr, addr, addr_len); hash_add(request->ip_map, &addr_map->node, key); + pthread_mutex_unlock(&request->ip_map_lock); return 0; } @@ -1302,12 +1304,12 @@ static int _dns_server_recv(struct dns_server_conn *client, unsigned char *inpac _dns_server_request_get(request); request->send_tick = get_tick_count(); - dns_client_query(request->domain, qtype, dns_server_resolve_callback, request); request->request_wait++; + dns_client_query(request->domain, qtype, dns_server_resolve_callback, request); if (qtype == DNS_T_AAAA && dns_conf_dualstack_preference) { _dns_server_request_get(request); - dns_client_query(request->domain, DNS_T_A, dns_server_resolve_callback, request); request->request_wait++; + dns_client_query(request->domain, DNS_T_A, dns_server_resolve_callback, request); } return 0; @@ -1361,6 +1363,7 @@ static int _dns_server_prefetch_request(char *domain, dns_type_t qtype) _dns_server_request_get(request); request->send_tick = get_tick_count(); + request->request_wait++; dns_client_query(request->domain, qtype, dns_server_resolve_callback, request); return 0; diff --git a/src/smartdns.c b/src/smartdns.c index cdaf66d..d3da0b6 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -134,6 +134,20 @@ int smartdns_add_servers(void) return 0; } +int smartdns_set_ecs_ip(void) +{ + int ret = 0; + if (dns_conf_ipv4_ecs.enable) { + ret |= dns_client_set_ecs(dns_conf_ipv4_ecs.ip, dns_conf_ipv4_ecs.subnet); + } + + if (dns_conf_ipv6_ecs.enable) { + ret |= dns_client_set_ecs(dns_conf_ipv6_ecs.ip, dns_conf_ipv6_ecs.subnet); + } + + return ret; +} + int create_pid_file(const char *pid_file) { int fd; @@ -251,6 +265,11 @@ int smartdns_init(void) goto errout; } + ret = smartdns_set_ecs_ip(); + if (ret != 0 ) { + tlog(TLOG_WARN, "set ecs ip address failed."); + } + return 0; errout: