From 70ec291eee15f9e21ec7e01d73a9764031a648dd Mon Sep 17 00:00:00 2001 From: Nick Peng Date: Thu, 4 Jan 2024 22:05:49 +0800 Subject: [PATCH] dns_conf: add option -interface for server --- etc/smartdns/smartdns.conf | 1 + src/dns_client.c | 70 +++++++++++++++++++++++++++++++++----- src/dns_client.h | 3 +- src/dns_conf.c | 5 +++ src/dns_conf.h | 2 ++ src/smartdns.c | 1 + test/cases/test-server.cc | 27 +++++++++++++++ 7 files changed, 100 insertions(+), 9 deletions(-) diff --git a/etc/smartdns/smartdns.conf b/etc/smartdns/smartdns.conf index 75631c0668..25ed2c87aa 100644 --- a/etc/smartdns/smartdns.conf +++ b/etc/smartdns/smartdns.conf @@ -208,6 +208,7 @@ log-level info # -set-mark: set mark on packets. # -subnet [ip/subnet]: set edns client subnet. # -host-ip [ip]: set dns server host ip. +# -interface [interface]: set dns server interface. # server 8.8.8.8 -blacklist-ip -check-edns -group g1 -group g2 # server tls://dns.google:853 # server https://dns.google/dns-query diff --git a/src/dns_client.c b/src/dns_client.c index a2eebce54a..45505fbc12 100644 --- a/src/dns_client.c +++ b/src/dns_client.c @@ -1074,6 +1074,7 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port, char port_s[8]; int sock_type = 0; char skip_check_cert = 0; + char ifname[IFNAMSIZ * 2] = {0}; switch (server_type) { case DNS_SERVER_UDP: { @@ -1110,8 +1111,10 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port, 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); + if (flags->ifname[0] == '\0') { + tlog(TLOG_ERROR, "mdns server must set ifname."); + return -1; + } sock_type = SOCK_DGRAM; } break; default: @@ -1220,7 +1223,12 @@ static int _dns_client_server_add(char *server_ip, char *server_host, int port, _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)); + + if (flags->ifname[0]) { + snprintf(ifname, sizeof(ifname), "@%s", flags->ifname); + } + + tlog(TLOG_INFO, "add server %s:%d%s, type: %s", server_ip, port, ifname, _dns_server_get_type_string(server_info->type)); return 0; errout: @@ -1876,6 +1884,17 @@ static int _dns_client_create_socket_udp_proxy(struct dns_server_info *server_in } } + if (server_info->flags.ifname[0] != '\0') { + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + safe_strncpy(ifr.ifr_name, server_info->flags.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; + } + } + set_fd_nonblock(fd, 1); set_sock_keepalive(fd, 30, 3, 5); @@ -1932,6 +1951,17 @@ static int _dns_client_create_socket_udp(struct dns_server_info *server_info) goto errout; } + if (server_info->flags.ifname[0] != '\0') { + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + safe_strncpy(ifr.ifr_name, server_info->flags.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; @@ -2005,7 +2035,7 @@ static int _dns_client_create_socket_udp_mdns(struct dns_server_info *server_inf struct ifreq ifr; memset(&ifr, 0, sizeof(struct ifreq)); - safe_strncpy(ifr.ifr_name, server_info->flags.mdns.ifname, sizeof(ifr.ifr_name)); + safe_strncpy(ifr.ifr_name, server_info->flags.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)); @@ -2074,6 +2104,17 @@ static int _DNS_client_create_socket_tcp(struct dns_server_info *server_info) goto errout; } + if (server_info->flags.ifname[0] != '\0') { + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + safe_strncpy(ifr.ifr_name, server_info->flags.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; + } + } + if (set_fd_nonblock(fd, 1) != 0) { tlog(TLOG_ERROR, "set socket non block failed, %s", strerror(errno)); goto errout; @@ -2172,6 +2213,17 @@ static int _DNS_client_create_socket_tls(struct dns_server_info *server_info, ch fd = socket(server_info->ai_family, SOCK_STREAM, 0); } + if (server_info->flags.ifname[0] != '\0') { + struct ifreq ifr; + memset(&ifr, 0, sizeof(struct ifreq)); + safe_strncpy(ifr.ifr_name, server_info->flags.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; + } + } + ssl = SSL_new(server_info->ssl_ctx); if (ssl == NULL) { tlog(TLOG_ERROR, "new ssl failed, %s", server_info->ip); @@ -4611,15 +4663,16 @@ static int _dns_client_add_mdns_server(void) } #ifdef TEST - ret = _dns_client_server_add(DNS_MDNS_IP, "lo", DNS_MDNS_PORT, DNS_SERVER_MDNS, &server_flags); + safe_strncpy(server_flags.ifname, "lo", sizeof(server_flags.ifname)); + ret = _dns_client_server_add(DNS_MDNS_IP, "", DNS_MDNS_PORT, DNS_SERVER_MDNS, &server_flags); if (ret != 0) { - tlog(TLOG_ERROR, "add mdns server failed."); + tlog(TLOG_ERROR, "add mdns server to %s failed.", "lo"); 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."); + tlog(TLOG_ERROR, "add mdns server to group %s failed.", DNS_SERVER_GROUP_MDNS); goto errout; } @@ -4654,7 +4707,8 @@ static int _dns_client_add_mdns_server(void) continue; } - ret = _dns_client_server_add(DNS_MDNS_IP, ifa->ifa_name, DNS_MDNS_PORT, DNS_SERVER_MDNS, &server_flags); + safe_strncpy(server_flags.ifname, ifa->ifa_name, sizeof(server_flags.ifname)); + ret = _dns_client_server_add(DNS_MDNS_IP, "", DNS_MDNS_PORT, DNS_SERVER_MDNS, &server_flags); if (ret != 0) { tlog(TLOG_ERROR, "add mdns server failed."); goto errout; diff --git a/src/dns_client.h b/src/dns_client.h index dba191df2f..48e1a27581 100644 --- a/src/dns_client.h +++ b/src/dns_client.h @@ -25,6 +25,7 @@ extern "C" { #endif +#define DNS_SERVER_IFNAME_LEN 16 #define DNS_SERVER_SPKI_LEN 64 #define DNS_SERVER_GROUP_DEFAULT "default" #define DNS_SERVER_GROUP_MDNS "mdns" @@ -101,7 +102,6 @@ struct client_dns_server_flag_udp { }; struct client_dns_server_flag_mdns { - char ifname[DNS_MAX_CNAME_LEN]; }; struct client_dns_server_flag_tls { @@ -136,6 +136,7 @@ struct client_dns_server_flags { long long set_mark; int drop_packet_latency_ms; char proxyname[DNS_MAX_CNAME_LEN]; + char ifname[DNS_SERVER_IFNAME_LEN]; struct client_dns_server_flag_ecs ipv4_ecs; struct client_dns_server_flag_ecs ipv6_ecs; diff --git a/src/dns_conf.c b/src/dns_conf.c index 504270e0d8..cd847f4474 100644 --- a/src/dns_conf.c +++ b/src/dns_conf.c @@ -559,6 +559,7 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de {"proxy", required_argument, NULL, 'p'}, /* proxy server */ {"no-check-certificate", no_argument, NULL, 'k'}, /* do not check certificate */ {"bootstrap-dns", no_argument, NULL, 'b'}, /* set as bootstrap dns */ + {"interface", required_argument, NULL, 250}, /* interface */ #ifdef FEATURE_CHECK_EDNS /* experimental feature */ {"check-edns", no_argument, NULL, 251}, /* check edns */ @@ -667,6 +668,10 @@ static int _config_server(int argc, char *argv[], dns_server_type_t type, int de is_bootstrap_dns = 1; break; } + case 250: { + safe_strncpy(server->ifname, optarg, MAX_INTERFACE_LEN); + break; + } case 251: { result_flag |= DNSSERVER_FLAG_CHECK_EDNS; break; diff --git a/src/dns_conf.h b/src/dns_conf.h index f71d84a94f..b55a7866a2 100644 --- a/src/dns_conf.h +++ b/src/dns_conf.h @@ -61,6 +61,7 @@ extern "C" { #define DNS_MAX_REPLY_IP_NUM 8 #define DNS_MAX_QUERY_LIMIT 65535 #define DNS_DEFAULT_CHECKPOINT_TIME (3600 * 24) +#define MAX_INTERFACE_LEN 16 #define SMARTDNS_CONF_FILE "/etc/smartdns/smartdns.conf" #define SMARTDNS_LOG_FILE "/var/log/smartdns/smartdns.log" @@ -340,6 +341,7 @@ struct dns_servers { char tls_host_verify[DNS_MAX_CNAME_LEN]; char path[DNS_MAX_URL_LEN]; char proxyname[PROXY_NAME_LEN]; + char ifname[MAX_INTERFACE_LEN]; struct dns_edns_client_subnet ipv4_ecs; struct dns_edns_client_subnet ipv6_ecs; }; diff --git a/src/smartdns.c b/src/smartdns.c index 8cce65e707..536f4f8cfa 100644 --- a/src/smartdns.c +++ b/src/smartdns.c @@ -288,6 +288,7 @@ static int _smartdns_prepare_server_flags(struct client_dns_server_flags *flags, flags->set_mark = server->set_mark; flags->drop_packet_latency_ms = server->drop_packet_latency_ms; safe_strncpy(flags->proxyname, server->proxyname, sizeof(flags->proxyname)); + safe_strncpy(flags->ifname, server->ifname, sizeof(flags->ifname)); if (server->ipv4_ecs.enable) { flags->ipv4_ecs.enable = 1; safe_strncpy(flags->ipv4_ecs.ip, server->ipv4_ecs.ip, sizeof(flags->ipv4_ecs.ip)); diff --git a/test/cases/test-server.cc b/test/cases/test-server.cc index 4643142d01..4fbf74040f 100644 --- a/test/cases/test-server.cc +++ b/test/cases/test-server.cc @@ -218,4 +218,31 @@ max-query-limit 2 EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); } +} + +TEST_F(Server, interface) +{ + smartdns::MockServer server_upstream; + smartdns::Server server; + + server_upstream.Start("udp://0.0.0.0:61053", [](struct smartdns::ServerRequestContext *request) { + if (request->qtype != DNS_T_A) { + return smartdns::SERVER_REQUEST_SOA; + } + smartdns::MockServer::AddIP(request, request->domain.c_str(), "1.2.3.4", 611); + return smartdns::SERVER_REQUEST_OK; + }); + + server.MockPing(PING_TYPE_ICMP, "2001::", 128, 10000); + server.Start(R"""(bind [::]:60053 +bind-tcp [::]:60053 +server 127.0.0.1:61053 -interface lo +)"""); + smartdns::Client client; + ASSERT_TRUE(client.Query("a.com", 60053)); + std::cout << client.GetResult() << std::endl; + ASSERT_EQ(client.GetAnswerNum(), 1); + EXPECT_EQ(client.GetStatus(), "NOERROR"); + EXPECT_EQ(client.GetAnswer()[0].GetName(), "a.com"); + EXPECT_EQ(client.GetAnswer()[0].GetData(), "1.2.3.4"); } \ No newline at end of file