From c67031eb7cc9e9ca60c6bd45e294413e9016fb08 Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Fri, 22 Dec 2023 22:39:14 +0800 Subject: [PATCH] feature: support lookup local network host name or ip via mDNS --- etc/smartdns/smartdns.conf | 6 +- .../files/luci/i18n/smartdns.zh-cn.po | 6 + .../luci/model/cbi/smartdns/smartdns.lua | 10 +- .../luci/files/luci/i18n/smartdns.zh-cn.po | 6 + .../resources/view/smartdns/smartdns.js | 7 +- package/openwrt/files/etc/init.d/smartdns | 3 + src/dns_client.c | 251 +++++++++++++++++- src/dns_client.h | 8 + src/dns_conf.c | 20 +- src/dns_conf.h | 1 + src/dns_server.c | 124 +++++++-- src/util.c | 27 ++ src/util.h | 2 + 13 files changed, 425 insertions(+), 46 deletions(-) diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 524dd5d057..0980675c04 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -199,7 +199,7 @@ log-level info # g|-group [group]: set server to group, use with nameserver /domain/group. # e|-exclude-default-group: exclude this server from default group. # p|-proxy [proxy-name]: use proxy to connect to server. -# -bootstrap-dns: set as bootstrap dns server. +# b|-bootstrap-dns: set as bootstrap dns server. # -set-mark: set mark on packets. # -subnet [ip/subnet]: set edns client subnet. # -host-ip [ip]: set dns server host ip. @@ -307,6 +307,10 @@ log-level info # set ddns domain # ddns-domain domain +# lookup local network hostname or ip address from mdns +# mdns-lookup [yes|no] +# mdns-lookup no + # set domain rules # domain-rules /domain/ [-speed-check-mode [...]] # rules: diff --git a/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po b/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po index db393b6968..0cf31fa2b9 100644 --- a/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po +++ b/package/luci-compat/files/luci/i18n/smartdns.zh-cn.po @@ -350,6 +350,9 @@ msgstr "日志数量" msgid "Log File" msgstr "日志文件路径" +msgid "mDNS Lookup" +msgstr "mDNS查询" + msgid "Marking Packets" msgstr "数据包标记" @@ -434,6 +437,9 @@ msgstr "解析本地主机名" msgid "Resolve local hostnames by reading Dnsmasq lease file." msgstr "读取Dnsmasq的租约文件解析本地主机名。" +msgid "Resolve local network hostname via mDNS protocol." +msgstr "使用mDNS协议解析本地网络主机名。" + msgid "Response Mode" msgstr "响应模式" diff --git a/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua b/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua index dd65e316eb..db24d6eda9 100644 --- a/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua +++ b/package/luci-compat/files/luci/model/cbi/smartdns/smartdns.lua @@ -239,7 +239,7 @@ o.cfgvalue = function(...) return Flag.cfgvalue(...) or "1" end --- cache-size +-- resolve local hostname o = s:taboption("advanced", Flag, "resolve_local_hostnames", translate("Resolve Local Hostnames"), translate("Resolve local hostnames by reading Dnsmasq lease file.")) o.rmempty = false o.default = o.enabled @@ -247,6 +247,14 @@ o.cfgvalue = function(...) return Flag.cfgvalue(...) or "1" end +-- resolve local network hostname via mDNS +o = s:taboption("advanced", Flag, "mdns_lookup", translate("mDNS Lookup"), translate("Resolve local network hostname via mDNS protocol.")) +o.rmempty = true +o.default = o.disabled +o.cfgvalue = function(...) + return Flag.cfgvalue(...) or "0" +end + -- Force AAAA SOA o = s:taboption("advanced", Flag, "force_aaaa_soa", translate("Force AAAA SOA"), translate("Force AAAA SOA.")) o.rmempty = true diff --git a/package/luci/files/luci/i18n/smartdns.zh-cn.po b/package/luci/files/luci/i18n/smartdns.zh-cn.po index 86d3ebedd6..d79c8ca87d 100644 --- a/package/luci/files/luci/i18n/smartdns.zh-cn.po +++ b/package/luci/files/luci/i18n/smartdns.zh-cn.po @@ -393,6 +393,9 @@ msgstr "日志数量" msgid "Log File" msgstr "日志文件路径" +msgid "mDNS Lookup" +msgstr "mDNS查询" + msgid "Marking Packets" msgstr "数据包标记" @@ -483,6 +486,9 @@ msgstr "解析本地主机名" msgid "Resolve local hostnames by reading Dnsmasq lease file." msgstr "读取Dnsmasq的租约文件解析本地主机名。" +msgid "Resolve local network hostname via mDNS protocol." +msgstr "使用mDNS协议解析本地网络主机名。" + msgid "Response Mode" msgstr "响应模式" diff --git a/package/luci/files/root/www/luci-static/resources/view/smartdns/smartdns.js b/package/luci/files/root/www/luci-static/resources/view/smartdns/smartdns.js index 530dd09b9f..c3b9536af5 100644 --- a/package/luci/files/root/www/luci-static/resources/view/smartdns/smartdns.js +++ b/package/luci/files/root/www/luci-static/resources/view/smartdns/smartdns.js @@ -311,11 +311,16 @@ return view.extend({ o.rmempty = false; o.default = o.enabled; - // cache-size; + // resolve local hostname; o = s.taboption("advanced", form.Flag, "resolve_local_hostnames", _("Resolve Local Hostnames"), _("Resolve local hostnames by reading Dnsmasq lease file.")); o.rmempty = false; o.default = o.enabled; + // resolve local network hostname via mDNS; + o = s.taboption("advanced", form.Flag, "mdns_lookup", _("mDNS Lookup"), _("Resolve local network hostname via mDNS protocol.")); + o.rmempty = true; + o.default = o.disabled; + // Force AAAA SOA o = s.taboption("advanced", form.Flag, "force_aaaa_soa", _("Force AAAA SOA"), _("Force AAAA SOA.")); o.rmempty = true; diff --git a/package/openwrt/files/etc/init.d/smartdns b/package/openwrt/files/etc/init.d/smartdns index 11e4502b17..f9288a2f7f 100644 --- a/package/openwrt/files/etc/init.d/smartdns +++ b/package/openwrt/files/etc/init.d/smartdns @@ -644,6 +644,9 @@ load_service() config_get dns64 "$section" "dns64" "" [ -z "$dns64" ] || conf_append "dns64" "$dns64" + config_get_bool mdns_lookup "$section" "mdns_lookup" "0" + [ "$mdns_lookup" = "1" ] && conf_append "mdns-lookup" "yes" + config_get redirect "$section" "redirect" "" config_get old_port "$section" "old_port" "0" config_get old_enabled "$section" "old_enabled" "0" diff --git a/src/dns_client.c b/src/dns_client.c index 8871f03dbc..25fe39de69 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -33,7 +33,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +70,9 @@ #define SOCKET_PRIORITY (6) #define SOCKET_IP_TOS (IPTOS_LOWDELAY | IPTOS_RELIABILITY) +#define DNS_MDNS_IP "224.0.0.251" +#define DNS_MDNS_PORT 5353 + /* ECS info */ struct dns_client_ecs { int enable; @@ -400,6 +406,42 @@ int dns_client_get_server_port(struct dns_server_info *server_info) return server_info->port; } +static inline void _dns_server_inc_server_num(struct dns_server_info *server_info) +{ + if (server_info->type == DNS_SERVER_MDNS) { + return; + } + + atomic_inc(&client.dns_server_num); +} + +static inline void _dns_server_dec_server_num(struct dns_server_info *server_info) +{ + if (server_info->type == DNS_SERVER_MDNS) { + return; + } + + atomic_dec(&client.dns_server_num); +} + +static inline void _dns_server_inc_prohibit_server_num(struct dns_server_info *server_info) +{ + if (server_info->type == DNS_SERVER_MDNS) { + return; + } + + atomic_inc(&client.dns_server_prohibit_num); +} + +static inline void _dns_server_dec_prohibit_server_num(struct dns_server_info *server_info) +{ + if (server_info->type == DNS_SERVER_MDNS) { + return; + } + + atomic_dec(&client.dns_server_prohibit_num); +} + dns_server_type_t dns_client_get_server_type(struct dns_server_info *server_info) { if (server_info == NULL) { @@ -426,6 +468,9 @@ static const char *_dns_server_get_type_string(dns_server_type_t type) case DNS_SERVER_HTTPS: type_str = "https"; break; + case DNS_SERVER_MDNS: + type_str = "mdns"; + break; default: break; } @@ -760,8 +805,7 @@ int dns_client_add_group(const char *group_name) } if (_dns_client_get_group(group_name) != NULL) { - tlog(TLOG_ERROR, "add group %s failed, group already exists", group_name); - return -1; + return 0; } group = malloc(sizeof(*group)); @@ -872,6 +916,8 @@ static char *_dns_client_server_get_tls_host_verify(struct dns_server_info *serv } break; case DNS_SERVER_TCP: break; + case DNS_SERVER_MDNS: + break; default: return NULL; break; @@ -905,6 +951,8 @@ static char *_dns_client_server_get_spki(struct dns_server_info *server_info, in } break; case DNS_SERVER_TCP: break; + case DNS_SERVER_MDNS: + break; default: return NULL; break; @@ -1064,6 +1112,11 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port, case DNS_SERVER_TCP: sock_type = SOCK_STREAM; break; + case DNS_SERVER_MDNS: { + struct client_dns_server_flag_mdns *flag_mdns = &flags->mdns; + safe_strncpy(flag_mdns->ifname, server_host, DNS_MAX_CNAME_LEN); + sock_type = SOCK_DGRAM; + } break; default: return -1; break; @@ -1168,9 +1221,8 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port, list_add(&server_info->list, &client.dns_server_list); pthread_mutex_unlock(&client.server_list_lock); - atomic_inc(&client.dns_server_num); + _dns_server_inc_server_num(server_info); freeaddrinfo(gai); - tlog(TLOG_INFO, "add server %s:%d, type: %s", server_ip, port, _dns_server_get_type_string(server_info->type)); return 0; @@ -1250,6 +1302,8 @@ static void _dns_client_shutdown_socket(struct dns_server_info *server_info) shutdown(server_info->fd, SHUT_RDWR); } break; + case DNS_SERVER_MDNS: + break; default: break; } @@ -1312,8 +1366,8 @@ static int _dns_client_server_remove(char *server_ip, int port, dns_server_type_ _dns_client_server_close(server_info); pthread_mutex_unlock(&client.server_list_lock); _dns_client_remove_server_from_groups(server_info); + _dns_server_dec_server_num(server_info); free(server_info); - atomic_dec(&client.dns_server_num); return 0; } pthread_mutex_unlock(&client.server_list_lock); @@ -1585,7 +1639,7 @@ static void _dns_client_check_tcp(void) pthread_mutex_lock(&client.server_list_lock); list_for_each_entry(server_info, &client.dns_server_list, list) { - if (server_info->type == DNS_SERVER_UDP) { + if (server_info->type == DNS_SERVER_UDP || server_info->type == DNS_SERVER_MDNS) { /* no need to check udp server */ continue; } @@ -1929,6 +1983,74 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info) return -1; } +#include +#include + +static int _dns_client_create_socket_udp_mdns(struct dns_server_info *server_info) +{ + int fd = 0; + struct epoll_event event; + const int on = 1; + const int val = 1; + const int priority = SOCKET_PRIORITY; + const int ip_tos = SOCKET_IP_TOS; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + tlog(TLOG_ERROR, "create socket failed, %s", strerror(errno)); + goto errout; + } + + if (set_fd_nonblock(fd, 1) != 0) { + tlog(TLOG_ERROR, "set socket non block failed, %s", strerror(errno)); + goto errout; + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + safe_strncpy(ifr.ifr_name, server_info->flags.mdns.ifname, sizeof(ifr.ifr_name)); + ioctl(fd, SIOCGIFINDEX, &ifr); + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(struct ifreq)) < 0) { + tlog(TLOG_ERROR, "bind socket to device %s failed, %s\n", ifr.ifr_name, strerror(errno)); + goto errout; + } + + server_info->fd = fd; + server_info->status = DNS_SERVER_STATUS_CONNECTIONLESS; + + memset(&event, 0, sizeof(event)); + event.events = EPOLLIN; + event.data.ptr = server_info; + if (epoll_ctl(client.epoll_fd, EPOLL_CTL_ADD, fd, &event) != 0) { + tlog(TLOG_ERROR, "epoll ctl failed."); + return -1; + } + + setsockopt(server_info->fd, IPPROTO_IP, IP_RECVTTL, &on, sizeof(on)); + setsockopt(server_info->fd, SOL_IP, IP_TTL, &val, sizeof(val)); + setsockopt(server_info->fd, SOL_SOCKET, SO_PRIORITY, &priority, sizeof(priority)); + setsockopt(server_info->fd, IPPROTO_IP, IP_TOS, &ip_tos, sizeof(ip_tos)); + setsockopt(server_info->fd, IPPROTO_IP, IP_MULTICAST_TTL, &val, sizeof(val)); + if (server_info->ai_family == AF_INET6) { + /* for receiving ip ttl value */ + setsockopt(server_info->fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on, sizeof(on)); + setsockopt(server_info->fd, IPPROTO_IPV6, IPV6_2292HOPLIMIT, &on, sizeof(on)); + setsockopt(server_info->fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &on, sizeof(on)); + setsockopt(server_info->fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, sizeof(val)); + } + + return 0; +errout: + if (fd > 0) { + close(fd); + } + + server_info->fd = -1; + server_info->status = DNS_SERVER_STATUS_DISCONNECTED; + + return -1; +} + static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info) { int fd = 0; @@ -2171,6 +2293,8 @@ static int _dns_client_create_socket(struct dns_server_info *server_info) if (server_info->type == DNS_SERVER_UDP) { return _dns_client_create_socket_udp(server_info); + } else if (server_info->type == DNS_SERVER_MDNS) { + return _dns_client_create_socket_udp_mdns(server_info); } else if (server_info->type == DNS_SERVER_TCP) { return _DNS_client_create_socket_tcp(server_info); } else if (server_info->type == DNS_SERVER_TLS) { @@ -2556,6 +2680,8 @@ static int _dns_client_socket_send(struct dns_server_info *server_info) } } return ret; + } else if (server_info->type == DNS_SERVER_MDNS) { + return -1; } else { return -1; } @@ -2579,6 +2705,8 @@ static int _dns_client_socket_recv(struct dns_server_info *server_info) } return ret; + } else if (server_info->type == DNS_SERVER_MDNS) { + return -1; } else { return -1; } @@ -3181,7 +3309,7 @@ static int _dns_client_process(struct dns_server_info *server_info, struct epoll } } - if (server_info->type == DNS_SERVER_UDP) { + if (server_info->type == DNS_SERVER_UDP || server_info->type == DNS_SERVER_MDNS) { /* receive from udp */ return _dns_client_process_udp(server_info, event, now); } else if (server_info->type == DNS_SERVER_TCP) { @@ -3261,6 +3389,27 @@ static int _dns_client_send_udp(struct dns_server_info *server_info, void *packe return -1; } +static int _dns_client_send_udp_mdns(struct dns_server_info *server_info, void *packet, int len) +{ + int send_len = 0; + const struct sockaddr *addr = &server_info->addr; + socklen_t addrlen = server_info->ai_addrlen; + + if (server_info->fd <= 0) { + return -1; + } + + send_len = sendto(server_info->fd, packet, len, 0, addr, addrlen); + if (send_len != len) { + goto errout; + } + + return 0; + +errout: + return -1; +} + static int _dns_client_send_data_to_buffer(struct dns_server_info *server_info, void *packet, int len) { struct epoll_event event; @@ -3483,7 +3632,7 @@ static int _dns_client_setup_server_packet(struct dns_server_info *server_info, dns_set_OPT_option(packet, DNS_OPT_FLAG_DO); } - if (server_info->type != DNS_SERVER_UDP) { + if (server_info->type != DNS_SERVER_UDP && server_info->type != DNS_SERVER_MDNS) { dns_add_OPT_TCP_KEEPALIVE(packet, 6000); } @@ -3549,7 +3698,7 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, if (server_info->prohibit) { if (server_info->is_already_prohibit == 0) { server_info->is_already_prohibit = 1; - atomic_inc(&client.dns_server_prohibit_num); + _dns_server_inc_prohibit_server_num(server_info); time(&server_info->last_send); time(&server_info->last_recv); tlog(TLOG_INFO, "server %s not alive, prohibit", server_info->ip); @@ -3563,7 +3712,7 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, } server_info->prohibit = 0; server_info->is_already_prohibit = 0; - atomic_dec(&client.dns_server_prohibit_num); + _dns_server_dec_prohibit_server_num(server_info); if (now - 60 > server_info->last_send) { _dns_client_close_socket(server_info); } @@ -3609,6 +3758,11 @@ static int _dns_client_send_packet(struct dns_query_struct *query, void *packet, ret = _dns_client_send_https(server_info, packet_data, packet_data_len); send_err = errno; break; + case DNS_SERVER_MDNS: + /* mdns query */ + ret = _dns_client_send_udp_mdns(server_info, packet_data, packet_data_len); + send_err = errno; + break; default: /* unsupported query type */ ret = -1; @@ -4440,6 +4594,78 @@ static void _dns_client_do_wakeup_event(void) unused = write(client.fd_wakeup, &val, sizeof(val)); } +static int _dns_client_add_mdns_server(void) +{ + struct client_dns_server_flags server_flags; + int ret = 0; + struct ifaddrs *ifaddr = NULL; + struct ifaddrs *ifa = NULL; + + if (dns_conf_mdns_lookup != 1) { + return 0; + } + + memset(&server_flags, 0, sizeof(server_flags)); + server_flags.server_flag |= SERVER_FLAG_EXCLUDE_DEFAULT | DOMAIN_FLAG_IPSET_IGN | DOMAIN_FLAG_NFTSET_INET_IGN; + + if (dns_client_add_group(DNS_SERVER_GROUP_MDNS) != 0) { + tlog(TLOG_ERROR, "add default server group failed."); + goto errout; + } + + if (getifaddrs(&ifaddr) == -1) { + goto errout; + } + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + const unsigned char *addr = NULL; + int addr_len = 0; + + if (ifa->ifa_addr == NULL) { + continue; + } + + if (AF_INET != ifa->ifa_addr->sa_family) { + continue; + } + + addr = (const unsigned char *)&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; + addr_len = sizeof(struct in_addr); + + // Skip the local interface + if (strcmp(ifa->ifa_name, "lo") == 0 || strcmp(ifa->ifa_name, "localhost") == 0) { + continue; + } + + if (is_private_addr(addr, addr_len) == 0) { + continue; + } + + ret = _dns_client_server_add(DNS_MDNS_IP, ifa->ifa_name, DNS_MDNS_PORT, DNS_SERVER_MDNS, &server_flags); + if (ret != 0) { + tlog(TLOG_ERROR, "add mdns server failed."); + goto errout; + } + + if (dns_client_add_to_group(DNS_SERVER_GROUP_MDNS, DNS_MDNS_IP, DNS_MDNS_PORT, DNS_SERVER_MDNS, + &server_flags) != 0) { + tlog(TLOG_ERROR, "add mdns server to group failed."); + goto errout; + } + } + + freeifaddrs(ifaddr); + + return 0; + +errout: + if (ifaddr) { + freeifaddrs(ifaddr); + } + + return -1; +} + int dns_client_init(void) { pthread_attr_t attr; @@ -4480,6 +4706,11 @@ int dns_client_init(void) goto errout; } + if (_dns_client_add_mdns_server() != 0) { + tlog(TLOG_ERROR, "add mdns server failed."); + goto errout; + } + client.default_group = _dns_client_get_group(DNS_SERVER_GROUP_DEFAULT); client.epoll_fd = epollfd; atomic_set(&client.run, 1); diff --git a/src/dns_client.h b/src/dns_client.h index aeb132e279..38ee9af225 100644 --- a/src/dns_client.h +++ b/src/dns_client.h @@ -27,12 +27,15 @@ extern "C" { #define DNS_SERVER_SPKI_LEN 64 #define DNS_SERVER_GROUP_DEFAULT "default" +#define DNS_SERVER_GROUP_MDNS "mdns" +#define DNS_SERVER_GROUP_LOCAL "local" typedef enum { DNS_SERVER_UDP, DNS_SERVER_TCP, DNS_SERVER_TLS, DNS_SERVER_HTTPS, + DNS_SERVER_MDNS, DNS_SERVER_TYPE_END, } dns_server_type_t; @@ -90,6 +93,10 @@ struct client_dns_server_flag_udp { int ttl; }; +struct client_dns_server_flag_mdns { + char ifname[DNS_MAX_CNAME_LEN]; +}; + struct client_dns_server_flag_tls { char spki[DNS_SERVER_SPKI_LEN]; int spi_len; @@ -129,6 +136,7 @@ struct client_dns_server_flags { struct client_dns_server_flag_udp udp; struct client_dns_server_flag_tls tls; struct client_dns_server_flag_https https; + struct client_dns_server_flag_mdns mdns; }; }; diff --git a/src/dns_conf.c b/src/dns_conf.c index fae389400f..c39cc91f26 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -162,6 +162,7 @@ struct dns_ipset_names dns_conf_ipset_no_speed; int dns_conf_nftset_timeout_enable; struct dns_nftset_names dns_conf_nftset_no_speed; int dns_conf_nftset_debug_enable; +int dns_conf_mdns_lookup; char dns_conf_user[DNS_CONF_USERNAME_LEN]; @@ -3967,16 +3968,15 @@ static struct dns_hosts *_dns_conf_get_hosts(const char *hostname, int dns_type) { uint32_t key = 0; struct dns_hosts *host = NULL; - char hostname_lower[DNS_MAX_CNAME_LEN]; - key = hash_string(to_lower_case(hostname_lower, hostname, DNS_MAX_CNAME_LEN)); + key = hash_string_case(hostname); key = jhash(&dns_type, sizeof(dns_type), key); hash_for_each_possible(dns_hosts_table.hosts, host, node, key) { if (host->dns_type != dns_type) { continue; } - if (strncmp(host->domain, hostname_lower, DNS_MAX_CNAME_LEN) != 0) { + if (strncasecmp(host->domain, hostname, DNS_MAX_CNAME_LEN) != 0) { continue; } @@ -3988,7 +3988,7 @@ static struct dns_hosts *_dns_conf_get_hosts(const char *hostname, int dns_type) goto errout; } - safe_strncpy(host->domain, hostname_lower, DNS_MAX_CNAME_LEN); + safe_strncpy(host->domain, hostname, DNS_MAX_CNAME_LEN); host->dns_type = dns_type; host->is_soa = 1; hash_add(dns_hosts_table.hosts, &host->node, key); @@ -4275,6 +4275,15 @@ static void _config_setup_smartdns_domain(void) _config_domain_rule_flag_set("smartdns", DOMAIN_FLAG_SMARTDNS_DOMAIN, 0); } +static int _dns_conf_setup_mdns(void) +{ + if (dns_conf_mdns_lookup != 1) { + return 0; + } + + return _conf_domain_rule_nameserver(DNS_SERVER_GROUP_LOCAL, DNS_SERVER_GROUP_MDNS); +} + static struct config_item _config_item[] = { CONF_STRING("server-name", (char *)dns_conf_server_name, DNS_MAX_SERVER_NAME_LEN), CONF_YESNO("resolv-hostname", &dns_conf_resolv_hostname), @@ -4289,6 +4298,7 @@ static struct config_item _config_item[] = { CONF_CUSTOM("server-tcp", _config_server_tcp, NULL), CONF_CUSTOM("server-tls", _config_server_tls, NULL), CONF_CUSTOM("server-https", _config_server_https, NULL), + CONF_YESNO("mdns-lookup", &dns_conf_mdns_lookup), CONF_CUSTOM("nameserver", _config_nameserver, NULL), CONF_YESNO("expand-ptr-from-address", &dns_conf_expand_ptr_from_address), CONF_CUSTOM("address", _config_address, NULL), @@ -4728,6 +4738,8 @@ static int _dns_conf_load_post(void) _dns_conf_auto_set_cache_size(); + _dns_conf_setup_mdns(); + if (dns_conf_cachesize == 0 && dns_conf_response_mode == DNS_RESPONSE_MODE_FASTEST_RESPONSE) { dns_conf_response_mode = DNS_RESPONSE_MODE_FASTEST_IP; tlog(TLOG_WARN, "force set response to %s as cache size is 0", diff --git a/src/dns_conf.h b/src/dns_conf.h index 127c2b510c..542beb7fa7 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -578,6 +578,7 @@ extern int dns_conf_ipset_timeout_enable; extern int dns_conf_nftset_timeout_enable; extern int dns_conf_nftset_debug_enable; extern int dns_conf_local_ttl; +extern int dns_conf_mdns_lookup; extern int dns_conf_force_no_cname; diff --git a/src/dns_server.c b/src/dns_server.c index 87b16a7f60..c903b5d0d9 100644 --- a/src/dns_server.c +++ b/src/dns_server.c @@ -280,6 +280,8 @@ struct dns_request { int has_soa; int force_soa; + int is_mdns_lookup; + struct dns_srv_records *srv_records; atomic_t notified; @@ -365,6 +367,7 @@ static int _dns_server_do_query(struct dns_request *request, int skip_notify_eve static int _dns_request_post(struct dns_server_post_context *context); static int _dns_server_reply_all_pending_list(struct dns_request *request, struct dns_server_post_context *context); static void *_dns_server_get_dns_rule(struct dns_request *request, enum domain_rule rule); +static int _dns_server_get_local_ttl(struct dns_request *request); static const char *_dns_server_get_request_groupname(struct dns_request *request); static int _dns_server_tcp_socket_send(struct dns_server_conn_tcp_client *tcp_client, void *data, int data_len); static int _dns_server_update_request_connection_timeout(struct dns_server_conn_head *conn, int timeout); @@ -761,8 +764,8 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) continue; } - if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && - strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncasecmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -782,8 +785,8 @@ static void _dns_server_audit_log(struct dns_server_post_context *context) continue; } - if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && - strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncasecmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -1559,7 +1562,7 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context) continue; } - if (strncmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -1575,7 +1578,7 @@ static int _dns_cache_cname_packet(struct dns_server_post_context *context) continue; } - if (strncmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(request->cname, name, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -2770,6 +2773,9 @@ static void _dns_server_ping_result(struct ping_host_struct *ping_host, const ch } int rtt = tv->tv_sec * 10000 + tv->tv_usec / 100; + if (rtt == 0) { + rtt = 1; + } if (result == PING_RESULT_RESPONSE) { tlog(TLOG_DEBUG, "from %s: seq=%d time=%d, lasttime=%d id=%d", host, seqno, rtt, last_rtt, request->id); @@ -3127,7 +3133,7 @@ static int _dns_server_process_answer_A(struct dns_rrs *rrs, struct dns_request tlog(TLOG_DEBUG, "domain: %s TTL: %d IP: %d.%d.%d.%d", name, ttl, addr[0], addr[1], addr[2], addr[3]); /* if domain is not match */ - if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { + if (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncasecmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { return -1; } @@ -3214,7 +3220,7 @@ static int _dns_server_process_answer_AAAA(struct dns_rrs *rrs, struct dns_reque addr[11], addr[12], addr[13], addr[14], addr[15]); /* if domain is not match */ - if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { + if (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncasecmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { return -1; } @@ -3338,8 +3344,8 @@ static int _dns_server_process_answer(struct dns_request *request, const char *d char domain_name[DNS_MAX_CNAME_LEN] = {0}; char domain_cname[DNS_MAX_CNAME_LEN] = {0}; dns_get_CNAME(rrs, domain_name, DNS_MAX_CNAME_LEN, &ttl, domain_cname, DNS_MAX_CNAME_LEN); - if (strncmp(domain_name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && - strncmp(domain_name, cname, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(domain_name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncasecmp(domain_name, cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } safe_strncpy(cname, domain_cname, DNS_MAX_CNAME_LEN); @@ -3431,7 +3437,8 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl_tmp, addr); /* if domain is not match */ - if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { + if (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && + strncasecmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { _dns_server_request_release(request); continue; } @@ -3470,7 +3477,8 @@ static int _dns_server_passthrough_rule_check(struct dns_request *request, const dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl_tmp, addr); /* if domain is not match */ - if (strncmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && strncmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { + if (strncasecmp(name, domain, DNS_MAX_CNAME_LEN) != 0 && + strncasecmp(cname, name, DNS_MAX_CNAME_LEN) != 0) { _dns_server_request_release(request); continue; } @@ -3552,8 +3560,8 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) /* get A result */ dns_get_A(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); - if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && - strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncasecmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -3584,8 +3592,8 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) } dns_get_AAAA(rrs, name, DNS_MAX_CNAME_LEN, &ttl, addr); - if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && - strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncasecmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -3619,8 +3627,8 @@ static int _dns_server_get_answer(struct dns_server_post_context *context) dns_get_CNAME(rrs, name, DNS_MAX_CNAME_LEN, &ttl, cname, DNS_MAX_CNAME_LEN); tlog(TLOG_DEBUG, "name: %s, ttl: %d, cname: %s\n", name, ttl, cname); - if (strncmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && - strncmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { + if (strncasecmp(name, request->domain, DNS_MAX_CNAME_LEN - 1) != 0 && + strncasecmp(name, request->cname, DNS_MAX_CNAME_LEN - 1) != 0) { continue; } @@ -3698,6 +3706,13 @@ static void _dns_server_query_end(struct dns_request *request) int ip_num = 0; int request_wait = 0; + /* if mdns request timeout */ + if (request->is_mdns_lookup == 1 && request->rcode == DNS_RC_SERVFAIL) { + request->rcode = DNS_RC_NOERROR; + request->force_soa = 1; + request->ip_ttl = _dns_server_get_local_ttl(request); + } + pthread_mutex_lock(&request->ip_map_lock); ip_num = atomic_read(&request->ip_map_num); request_wait = request->request_wait; @@ -4100,7 +4115,7 @@ static int _dns_server_process_local_ptr(struct dns_request *request) } /* Determine if the smartdns service is in effect. */ - if (found == 0 && strncmp(request->domain, "smartdns", sizeof("smartdns")) == 0) { + if (found == 0 && strncasecmp(request->domain, "smartdns", sizeof("smartdns")) == 0) { found = 1; } @@ -4179,6 +4194,15 @@ static int _dns_server_get_local_ttl(struct dns_request *request) return DNS_SERVER_ADDR_TTL; } +static void _dns_server_set_request_mdns(struct dns_request *request) +{ + if (dns_conf_mdns_lookup != 1) { + return; + } + + request->is_mdns_lookup = 1; +} + static int _dns_server_process_private_ptr(struct dns_request *request) { int a, b, c, d; @@ -4188,9 +4212,12 @@ static int _dns_server_process_private_ptr(struct dns_request *request) } if (d == 10 || (d == 172 && c >= 16 && c <= 31) || (d == 192 && c == 168)) { - request->has_soa = 1; - _dns_server_setup_soa(request); - return 0; + if (dns_conf_mdns_lookup == 0) { + request->has_soa = 1; + _dns_server_setup_soa(request); + return 0; + } + _dns_server_set_request_mdns(request); } return -1; @@ -4253,7 +4280,7 @@ static int _dns_server_process_srv(struct dns_request *request) static int _dns_server_process_svcb(struct dns_request *request) { - if (strncmp("_dns.resolver.arpa", request->domain, DNS_MAX_CNAME_LEN) == 0) { + if (strncasecmp("_dns.resolver.arpa", request->domain, DNS_MAX_CNAME_LEN) == 0) { return _dns_server_process_DDR(request); } @@ -4842,7 +4869,7 @@ static int _dns_server_process_cname(struct dns_request *request) struct dns_cname_rule *child_cname = _dns_server_get_dns_rule(child_request, DOMAIN_RULE_CNAME); /* sub domain rule*/ - if (child_cname != NULL && strncmp(child_request->domain, child_cname->cname, DNS_MAX_CNAME_LEN) == 0) { + if (child_cname != NULL && strncasecmp(child_request->domain, child_cname->cname, DNS_MAX_CNAME_LEN) == 0) { child_request->domain_rule.rules[DOMAIN_RULE_CNAME] = NULL; child_request->has_cname_loop = 1; } @@ -5462,13 +5489,12 @@ static int _dns_server_process_host(struct dns_request *request) struct dns_hosts *host = NULL; struct dns_hosts *host_tmp = NULL; int dns_type = request->qtype; - char hostname_lower[DNS_MAX_CNAME_LEN]; if (dns_hosts_record_num <= 0) { return -1; } - key = hash_string(to_lower_case(hostname_lower, request->domain, DNS_MAX_CNAME_LEN)); + key = hash_string_case(request->domain); key = jhash(&dns_type, sizeof(dns_type), key); hash_for_each_possible(dns_hosts_table.hosts, host_tmp, node, key) { @@ -5476,7 +5502,7 @@ static int _dns_server_process_host(struct dns_request *request) continue; } - if (strncmp(host_tmp->domain, hostname_lower, DNS_MAX_CNAME_LEN) != 0) { + if (strncasecmp(host_tmp->domain, request->domain, DNS_MAX_CNAME_LEN) != 0) { continue; } @@ -5536,6 +5562,35 @@ static int _dns_server_setup_query_option(struct dns_request *request, struct dn return 0; } +static int _dns_server_mdns_query_setup(struct dns_request *request, const char *group_name, char **request_domain, + char *domain_buffer, int domain_buffer_len) +{ + + if (dns_conf_mdns_lookup != 1) { + return 0; + } + + switch (request->qtype) { + case DNS_T_A: + case DNS_T_AAAA: + case DNS_T_SRV: + if (request->domain[0] != '\0' && strstr(request->domain, ".") == NULL) { + snprintf(domain_buffer, domain_buffer_len, "%s.%s", request->domain, DNS_SERVER_GROUP_LOCAL); + *request_domain = domain_buffer; + _dns_server_set_request_mdns(request); + } + + if (group_name != NULL && strncmp(group_name, DNS_SERVER_GROUP_MDNS, DNS_GROUP_NAME_LEN) == 0) { + _dns_server_set_request_mdns(request); + } + break; + default: + break; + } + + return 0; +} + static int _dns_server_query_dualstack(struct dns_request *request) { int ret = -1; @@ -5598,6 +5653,8 @@ static int _dns_server_do_query(struct dns_request *request, int skip_notify_eve const char *group_name = NULL; const char *dns_group = NULL; struct dns_query_options options; + char *request_domain = request->domain; + char domain_buffer[DNS_MAX_CNAME_LEN * 2]; if (request->conn) { dns_group = request->conn->dns_group; @@ -5691,10 +5748,19 @@ static int _dns_server_do_query(struct dns_request *request, int skip_notify_eve list_add_tail(&request->list, &server.request_list); pthread_mutex_unlock(&server.request_list_lock); + if (_dns_server_mdns_query_setup(request, group_name, &request_domain, domain_buffer, sizeof(domain_buffer)) != 0) { + goto errout; + } + + /* if request MDNS */ + if (request->is_mdns_lookup) { + group_name = DNS_SERVER_GROUP_MDNS; + } + // Get reference for DNS query request->request_wait++; _dns_server_request_get(request); - if (dns_client_query(request->domain, request->qtype, dns_server_resolve_callback, request, group_name, &options) != + if (dns_client_query(request_domain, request->qtype, dns_server_resolve_callback, request, group_name, &options) != 0) { request->request_wait--; _dns_server_request_release(request); @@ -6464,7 +6530,7 @@ static int _dns_server_tcp_process_one_request(struct dns_server_conn_tcp_client const char *content_type = http_head_get_fields_value(http_head, "Content-Type"); if (content_type == NULL || - strncmp(content_type, "application/dns-message", sizeof("application/dns-message")) != 0) { + strncasecmp(content_type, "application/dns-message", sizeof("application/dns-message")) != 0) { tlog(TLOG_DEBUG, "content type not supported, %s", content_type); goto errout; } diff --git a/src/util.c b/src/util.c index fe16d35c51..a20e0ed814 100644 --- a/src/util.c +++ b/src/util.c @@ -218,6 +218,33 @@ int generate_addr_map(const unsigned char *addr_from, const unsigned char *addr_ return 0; } +int is_private_addr(const unsigned char *addr, int addr_len) +{ + if (addr_len == IPV4_ADDR_LEN) { + if (addr[0] == 10) { + return 1; + } + + if (addr[0] == 172 && addr[1] >= 16 && addr[1] <= 31) { + return 1; + } + + if (addr[0] == 192 && addr[1] == 168) { + return 1; + } + } else if (addr_len == IPV6_ADDR_LEN) { + if (addr[0] == 0xFD) { + return 1; + } + + if (addr[0] == 0xFE && addr[1] == 0x80) { + return 1; + } + } + + return 0; +} + int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len) { struct addrinfo hints; diff --git a/src/util.h b/src/util.h index 8783815d98..eb205615c4 100644 --- a/src/util.h +++ b/src/util.h @@ -64,6 +64,8 @@ int generate_random_addr(unsigned char *addr, int addr_len, int mask); int generate_addr_map(const unsigned char *addr_from, const unsigned char *addr_to, unsigned char *addr_out, int addr_len, int mask); +int is_private_addr(const unsigned char *addr, int addr_len); + int getaddr_by_host(const char *host, struct sockaddr *addr, socklen_t *addr_len); int getsocket_inet(int fd, struct sockaddr *addr, socklen_t *addr_len);