From 43ea70f626c2f064b2307a44562e889ed38e49af Mon Sep 17 00:00:00 2001 From: Yh793 Date: Sun, 1 Jan 2023 22:17:39 +0800 Subject: [PATCH] dnsmasq: clean sources of 2.80, add patches instead - Support TCP DNS, e.g. server=/google.com/8.8.8.8~53 (hanwckf#418) - Support load gfwlist.txt (equals to add with --server and --ipset one by one) (hanwckf#419) - Fix TCP DNS crash in dnsmasq 2.8 (hanwckf#754) --- trunk/user/dnsmasq/Makefile | 4 + trunk/user/dnsmasq/dnsmasq-2.8x/Makefile | 2 +- trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h | 1 - trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c | 11 +- trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c | 11 +- trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c | 87 +------- trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c | 60 ------ .../dnsmasq/patchs/101-support-tcp-dns.patch | 200 ++++++++++++++++++ .../patchs/102-support-load-gfwlist.patch | 130 ++++++++++++ 9 files changed, 345 insertions(+), 161 deletions(-) delete mode 100644 trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c create mode 100644 trunk/user/dnsmasq/patchs/101-support-tcp-dns.patch create mode 100644 trunk/user/dnsmasq/patchs/102-support-load-gfwlist.patch diff --git a/trunk/user/dnsmasq/Makefile b/trunk/user/dnsmasq/Makefile index 59efd2a9677..cb598342ec6 100644 --- a/trunk/user/dnsmasq/Makefile +++ b/trunk/user/dnsmasq/Makefile @@ -11,6 +11,10 @@ endif ifneq ($(CONFIG_FIRMWARE_INCLUDE_IPSET),y) COPTS += -DNO_IPSET endif +ifeq ($(CONFIG_FIRMWARE_INCLUDE_WING),y) +patch -d $(SRC_NAME) -p1 -i ../patchs/101-support-tcp-dns.patch; \ +patch -d $(SRC_NAME) -p1 -i ../patchs/102-support-load-gfwlist; \ +endif all: $(MAKE) -j$(HOST_NCPU) -C $(SRC_NAME) COPTS="$(COPTS)" diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile b/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile index 6a3049b72f1..0963e93893d 100644 --- a/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile +++ b/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile @@ -79,7 +79,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ - poll.o rrfilter.o edns0.o arp.o tcpdns.o crypto.o dump.o \ + poll.o rrfilter.o edns0.o arp.o crypto.o dump.o \ ubus.o metrics.o hash_questions.o hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h b/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h index 902a83934c2..ac07189aec1 100644 --- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h +++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h @@ -523,7 +523,6 @@ union mysockaddr { #define SERV_LOOP 8192 /* server causes forwarding loop */ #define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */ #define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */ -#define SERV_IS_TCP 65536 /* Is TCP server */ struct serverfd { int fd; diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c index 28874a4a1d6..c95cebe2edd 100644 --- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c +++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c @@ -534,9 +534,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, } #endif - ssize_t tcpdns_sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); - ssize_t (*sendto_ptr)(int, const void *, size_t, int, const struct sockaddr *, socklen_t) = (start->flags & SERV_IS_TCP) ? tcpdns_sendto : sendto; - if (retry_send(sendto_ptr(fd, (char *)header, plen, 0, + if (retry_send(sendto(fd, (char *)header, plen, 0, &start->addr.sa, sa_len(&start->addr)))) continue; @@ -815,13 +813,10 @@ void reply_query(int fd, time_t now) break; if (!server) - { - if (serveraddr.sa.sa_family == AF_INET ? (serveraddr.in.sin_addr.s_addr != INADDR_ANY && htonl(serveraddr.in.sin_addr.s_addr) != INADDR_LOOPBACK) : (memcmp(&serveraddr.in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) && memcmp(&serveraddr.in6.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))) - return; - } + return; /* If sufficient time has elapsed, try and expand UDP buffer size again. */ - else if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) + if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) server->edns_pktsz = daemon->edns_pktsz; hash = hash_questions(header, n, daemon->namebuff); diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c index 7685dd9eeb0..cafee3820dc 100644 --- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c +++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c @@ -1672,7 +1672,7 @@ void check_servers(void) } /* Do we need a socket set? */ - if (!serv->sfd && !(serv->flags & SERV_IS_TCP) && + if (!serv->sfd && !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && errno != 0) { @@ -1694,7 +1694,6 @@ void check_servers(void) if (++count > SERVERS_LOGGED) continue; - char is_tcp = (serv->flags & SERV_IS_TCP) ? '~' : '#'; if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) { char *s1, *s2, *s3 = ""; @@ -1718,16 +1717,16 @@ void check_servers(void) else if (serv->flags & SERV_USE_RESOLV) my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2); else - my_syslog(LOG_INFO, _("using nameserver %s%c%d for %s %s %s"), daemon->namebuff, is_tcp, port, s1, s2, s3); + my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3); } #ifdef HAVE_LOOP else if (serv->flags & SERV_LOOP) - my_syslog(LOG_INFO, _("NOT using nameserver %s%c%d - query loop detected"), daemon->namebuff, is_tcp, port); + my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port); #endif else if (serv->interface[0] != 0) - my_syslog(LOG_INFO, _("using nameserver %s%c%d(via %s)"), daemon->namebuff, is_tcp, port, serv->interface); + my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); else - my_syslog(LOG_INFO, _("using nameserver %s%c%d"), daemon->namebuff, is_tcp, port); + my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); } } diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c index 891749b8a8b..52367ae0256 100644 --- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c +++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c @@ -168,7 +168,6 @@ struct myoption { #define LOPT_NAME_MATCH 355 #define LOPT_CAA 356 #define LOPT_FILTER_AAAA 357 -#define LOPT_GFWLIST 358 #define LOPT_DHCP_TO_HOST 359 #ifdef HAVE_GETOPT_LONG @@ -343,7 +342,6 @@ static const struct myoption opts[] = { "dumpfile", 1, 0, LOPT_DUMPFILE }, { "dumpmask", 1, 0, LOPT_DUMPMASK }, { "filter-aaaa", 0, 0, LOPT_FILTER_AAAA }, - { "gfwlist", 1, 0, LOPT_GFWLIST }, { "dhcp-to-host", 0, 0, LOPT_DHCP_TO_HOST }, { NULL, 0, 0, 0 } }; @@ -525,7 +523,6 @@ static struct { { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file"), NULL }, { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump"), NULL }, { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Filter all AAAA requests."), NULL }, - { LOPT_GFWLIST, ARG_DUP, "[@server][^ipset]", gettext_noop("Gfwlist path or domain to special server (default 8.8.8.8~53) and ipset (default gfwlist, pass ^ only to skip default ipset)"), NULL }, { LOPT_DHCP_TO_HOST, OPT_DHCP_TO_HOST, NULL, gettext_noop("Keep DHCP hostname valid at all times."), NULL }, { 0, 0, NULL, NULL, NULL } }; @@ -807,14 +804,8 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a !atoi_check16(portno, &source_port)) return _("bad port"); - portno = split_chr(arg, '#'); /* is there a port no. */ - if (portno == NULL) { - portno = split_chr(arg, '~'); /* is there a TCP port no. */ - if (portno) { - *flags |= SERV_IS_TCP; - } - } - if (portno && !atoi_check16(portno, &serv_port)) + if ((portno = split_chr(arg, '#')) && /* is there a port no. */ + !atoi_check16(portno, &serv_port)) return _("bad port"); scope_id = split_chr(arg, '%'); @@ -1933,13 +1924,6 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_SERVERS_FILE: daemon->servers_file = opt_string_alloc(arg); break; - - case LOPT_GFWLIST: - { - void load_gfwlist(char *gfwlist); - load_gfwlist(arg); - } - break; case 'm': /* --mx-host */ { @@ -4823,73 +4807,6 @@ void read_servers_file(void) read_file(daemon->servers_file, f, LOPT_REV_SERV); } - -void add_gfwline(char *gfwline, const char *server, const char *ipset) -{ - char *end = gfwline + 1; - while (*end && *end != '#' && *end != '\n' && *end != '\r') end++; - for (char *buf = end; buf >= gfwline; buf--) { - if (*buf == ',' || buf == gfwline) { - if (buf + 1 < end) { - *buf = '/'; - *end++ = '/'; - -#ifdef HAVE_IPSET - if (*ipset) { - strcpy(end, ipset); - one_opt(LOPT_IPSET, buf, _("gfwlist"), _("error"), 0, 0); - end[-1] = '/'; - } -#endif - strcpy(end, server); - one_opt('S', buf, _("gfwlist"), _("error"), 0, 0); - } - end = buf; - } - } -} - -void load_gfwlist(char *gfwlist) -{ - char *cfg_server = NULL, *cfg_ipset = NULL; - for (char *p = gfwlist; *p; p++) { - if (*p == '@') cfg_server = p; - else if (*p == '^') cfg_ipset = p; - } - - const char *server, *ipset; - if (cfg_server) { - *cfg_server = 0; - server = cfg_server + 1; - } else { - server = "8.8.8.8~53"; - } - if (cfg_ipset) { - *cfg_ipset = 0; - ipset = cfg_ipset + 1; - } else { - ipset = "gfwlist"; - } - - FILE *f = NULL; - do { - if (*gfwlist == '/') { - if (!(f = fopen(gfwlist, "r"))) { - my_syslog(LOG_ERR, _("cannot read %s: %s"), gfwlist, strerror(errno)); - break; - } - for (char buf[MAXDNAME]; fgets(buf+ 1, MAXDNAME - 1, f); add_gfwline(buf, server, ipset)); - } else { - char old = gfwlist[-1]; - add_gfwline(gfwlist - 1, server, ipset); - gfwlist[-1] = old; - } - } while (0); - - if (f) fclose(f); - if (cfg_server) *cfg_server = '@'; - if (cfg_ipset) *cfg_ipset = '^'; -} #ifdef HAVE_DHCP static void clear_dynamic_conf(void) diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c deleted file mode 100644 index c8d5e37feff..00000000000 --- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "dnsmasq.h" -#include - -// TCPDNS session (per query) -typedef struct _TCPDNS_SESSION { - int fd; // Original UDP socket to server (but skip sendto by us) - size_t len; // UDP DNS payload length - socklen_t tolen; // Server address length - union mysockaddr to; // Server address - unsigned short request; // TCPDNS request = length + DNS payload - unsigned char reqbuf[]; // DNS request payload - //unsigned short response; // TCPDNS response - //unsigned char resbuf[]; // DNS response payload -} TCPDNS_SESSION; - -// TCPDNS session worker -static void tcpdns_worker(TCPDNS_SESSION *session) -{ - session->request = htons(session->len); - unsigned short *response = (unsigned short *)(session->reqbuf + session->len); - - int server = socket(session->to.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); - for (int i = 0; i < 3; i++) { - if (connect(server, &session->to.sa, session->tolen) == 0 ) { - ssize_t nsend = session->len + 2; - int success = send(server, &session->request, nsend, 0) == nsend; - if (success) { - ssize_t nrecv = recv(server, response, 2 + daemon->packet_buff_sz, 0); - success = (nrecv - 2 == htons(*response)); - if (success) { - union mysockaddr loopback; - socklen_t looplen = sizeof(loopback); - getsockname(session->fd, &loopback.sa, &looplen); - sendto(session->fd, &response[1], nrecv - 2, 0, &loopback.sa, looplen); - break; - } - } - shutdown(server, SHUT_RDWR); - } - sleep(1); - } - close(server); - - free(session); - pthread_detach(pthread_self()); -} - -// Send to TCPDNS (instead of UDPDNS) -ssize_t tcpdns_sendto(int fd, const void *buf, size_t len, int flags __attribute__((unused)), const struct sockaddr *to, socklen_t tolen) -{ - TCPDNS_SESSION *session = safe_malloc(sizeof(TCPDNS_SESSION) + 2 + len + 2 + daemon->packet_buff_sz); - session->fd = fd; - session->len = len; - session->tolen = tolen; - memcpy(&session->to, to, tolen); - memcpy(session->reqbuf, buf, len); - - pthread_t tid; - return pthread_create(&tid, NULL, (void *(*)(void *))tcpdns_worker, session) ? (size_t)-1 : len; -} diff --git a/trunk/user/dnsmasq/patchs/101-support-tcp-dns.patch b/trunk/user/dnsmasq/patchs/101-support-tcp-dns.patch new file mode 100644 index 00000000000..06f0742321e --- /dev/null +++ b/trunk/user/dnsmasq/patchs/101-support-tcp-dns.patch @@ -0,0 +1,200 @@ +From 3fce840df6d23a0359e3e448e34476bb25146562 Mon Sep 17 00:00:00 2001 +From: Yonsm +Date: Sun, 13 Sep 2020 00:25:10 +0800 +Subject: [PATCH] Support TCP DNS, e.g. server=/google.com/8.8.8.8~53 (#418) + +--- + trunk/user/dnsmasq/dnsmasq-2.8x/Makefile | 2 +- + trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h | 1 + + trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c | 11 +++- + trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c | 11 ++-- + trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c | 10 +++- + trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c | 60 +++++++++++++++++++ + 6 files changed, 84 insertions(+), 11 deletions(-) + create mode 100644 trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c + +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile b/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile +index 0963e93893d..dffdb4e4ad8 100644 +--- a/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/Makefile +@@ -80,7 +80,7 @@ objs = cache.o rfc1035.o util.o option.o forward.o network.o \ + dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \ + domain.o dnssec.o blockdata.o tables.o loop.o inotify.o \ + poll.o rrfilter.o edns0.o arp.o crypto.o dump.o \ +- ubus.o metrics.o hash_questions.o ++ ubus.o metrics.o hash_questions.o tcpdns.o + + hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \ + dns-protocol.h radv-protocol.h ip6addr.h metrics.h +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h b/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h +index ac07189aec1..902a83934c2 100644 +--- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/dnsmasq.h +@@ -523,6 +523,7 @@ union mysockaddr { + #define SERV_LOOP 8192 /* server causes forwarding loop */ + #define SERV_DO_DNSSEC 16384 /* Validate DNSSEC when using this server */ + #define SERV_GOT_TCP 32768 /* Got some data from the TCP connection */ ++#define SERV_IS_TCP 65536 /* Is TCP server */ + + struct serverfd { + int fd; +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c +index c95cebe2edd..61a5e40b04f 100644 +--- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/forward.c +@@ -534,7 +534,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr, + } + #endif + +- if (retry_send(sendto(fd, (char *)header, plen, 0, ++ ssize_t tcpdns_sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); ++ ssize_t (*sendto_ptr)(int, const void *, size_t, int, const struct sockaddr *, socklen_t) = (start->flags & SERV_IS_TCP) ? tcpdns_sendto : sendto; ++ if (retry_send(sendto_ptr(fd, (char *)header, plen, 0, + &start->addr.sa, + sa_len(&start->addr)))) + continue; +@@ -813,10 +815,13 @@ void reply_query(int fd, time_t now) + break; + + if (!server) +- return; ++ { ++ if (serveraddr.sa.sa_family == AF_INET ? (serveraddr.in.sin_addr.s_addr != INADDR_ANY && htonl(serveraddr.in.sin_addr.s_addr) != INADDR_LOOPBACK) : (memcmp(&serveraddr.in6.sin6_addr, &in6addr_any, sizeof(in6addr_any)) && memcmp(&serveraddr.in6.sin6_addr, &in6addr_loopback, sizeof(in6addr_loopback)))) ++ return; ++ } + + /* If sufficient time has elapsed, try and expand UDP buffer size again. */ +- if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) ++ else if (difftime(now, server->pktsz_reduced) > UDP_TEST_TIME) + server->edns_pktsz = daemon->edns_pktsz; + + hash = hash_questions(header, n, daemon->namebuff); +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c +index cafee3820dc..97fe526e1a3 100644 +--- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/network.c +@@ -1672,7 +1672,7 @@ void check_servers(void) + } + + /* Do we need a socket set? */ +- if (!serv->sfd && ++ if (!serv->sfd && !(serv->flags & SERV_IS_TCP) && + !(serv->sfd = allocate_sfd(&serv->source_addr, serv->interface, serv->ifindex)) && + errno != 0) + { +@@ -1694,6 +1694,7 @@ void check_servers(void) + if (++count > SERVERS_LOGGED) + continue; + ++ char is_tcp = (serv->flags & SERV_IS_TCP) ? '~' : '#'; + if (serv->flags & (SERV_HAS_DOMAIN | SERV_FOR_NODOTS | SERV_USE_RESOLV)) + { + char *s1, *s2, *s3 = ""; +@@ -1717,16 +1718,16 @@ void check_servers(void) + else if (serv->flags & SERV_USE_RESOLV) + my_syslog(LOG_INFO, _("using standard nameservers for %s %s"), s1, s2); + else +- my_syslog(LOG_INFO, _("using nameserver %s#%d for %s %s %s"), daemon->namebuff, port, s1, s2, s3); ++ my_syslog(LOG_INFO, _("using nameserver %s%c%d for %s %s %s"), daemon->namebuff, is_tcp, port, s1, s2, s3); + } + #ifdef HAVE_LOOP + else if (serv->flags & SERV_LOOP) +- my_syslog(LOG_INFO, _("NOT using nameserver %s#%d - query loop detected"), daemon->namebuff, port); ++ my_syslog(LOG_INFO, _("NOT using nameserver %s%c%d - query loop detected"), daemon->namebuff, is_tcp, port); + #endif + else if (serv->interface[0] != 0) +- my_syslog(LOG_INFO, _("using nameserver %s#%d(via %s)"), daemon->namebuff, port, serv->interface); ++ my_syslog(LOG_INFO, _("using nameserver %s%c%d(via %s)"), daemon->namebuff, is_tcp, port, serv->interface); + else +- my_syslog(LOG_INFO, _("using nameserver %s#%d"), daemon->namebuff, port); ++ my_syslog(LOG_INFO, _("using nameserver %s%c%d"), daemon->namebuff, is_tcp, port); + } + } + +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c +index 52367ae0256..f05b2f2287b 100644 +--- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c +@@ -804,8 +804,14 @@ char *parse_server(char *arg, union mysockaddr *addr, union mysockaddr *source_a + !atoi_check16(portno, &source_port)) + return _("bad port"); + +- if ((portno = split_chr(arg, '#')) && /* is there a port no. */ +- !atoi_check16(portno, &serv_port)) ++ portno = split_chr(arg, '#'); /* is there a port no. */ ++ if (portno == NULL) { ++ portno = split_chr(arg, '~'); /* is there a TCP port no. */ ++ if (portno) { ++ *flags |= SERV_IS_TCP; ++ } ++ } ++ if (portno && !atoi_check16(portno, &serv_port)) + return _("bad port"); + + scope_id = split_chr(arg, '%'); +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c +new file mode 100644 +index 00000000000..c8d5e37feff +--- /dev/null ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/tcpdns.c +@@ -0,0 +1,60 @@ ++#include "dnsmasq.h" ++#include ++ ++// TCPDNS session (per query) ++typedef struct _TCPDNS_SESSION { ++ int fd; // Original UDP socket to server (but skip sendto by us) ++ size_t len; // UDP DNS payload length ++ socklen_t tolen; // Server address length ++ union mysockaddr to; // Server address ++ unsigned short request; // TCPDNS request = length + DNS payload ++ unsigned char reqbuf[]; // DNS request payload ++ //unsigned short response; // TCPDNS response ++ //unsigned char resbuf[]; // DNS response payload ++} TCPDNS_SESSION; ++ ++// TCPDNS session worker ++static void tcpdns_worker(TCPDNS_SESSION *session) ++{ ++ session->request = htons(session->len); ++ unsigned short *response = (unsigned short *)(session->reqbuf + session->len); ++ ++ int server = socket(session->to.sa.sa_family, SOCK_STREAM, IPPROTO_TCP); ++ for (int i = 0; i < 3; i++) { ++ if (connect(server, &session->to.sa, session->tolen) == 0 ) { ++ ssize_t nsend = session->len + 2; ++ int success = send(server, &session->request, nsend, 0) == nsend; ++ if (success) { ++ ssize_t nrecv = recv(server, response, 2 + daemon->packet_buff_sz, 0); ++ success = (nrecv - 2 == htons(*response)); ++ if (success) { ++ union mysockaddr loopback; ++ socklen_t looplen = sizeof(loopback); ++ getsockname(session->fd, &loopback.sa, &looplen); ++ sendto(session->fd, &response[1], nrecv - 2, 0, &loopback.sa, looplen); ++ break; ++ } ++ } ++ shutdown(server, SHUT_RDWR); ++ } ++ sleep(1); ++ } ++ close(server); ++ ++ free(session); ++ pthread_detach(pthread_self()); ++} ++ ++// Send to TCPDNS (instead of UDPDNS) ++ssize_t tcpdns_sendto(int fd, const void *buf, size_t len, int flags __attribute__((unused)), const struct sockaddr *to, socklen_t tolen) ++{ ++ TCPDNS_SESSION *session = safe_malloc(sizeof(TCPDNS_SESSION) + 2 + len + 2 + daemon->packet_buff_sz); ++ session->fd = fd; ++ session->len = len; ++ session->tolen = tolen; ++ memcpy(&session->to, to, tolen); ++ memcpy(session->reqbuf, buf, len); ++ ++ pthread_t tid; ++ return pthread_create(&tid, NULL, (void *(*)(void *))tcpdns_worker, session) ? (size_t)-1 : len; ++} \ No newline at end of file diff --git a/trunk/user/dnsmasq/patchs/102-support-load-gfwlist.patch b/trunk/user/dnsmasq/patchs/102-support-load-gfwlist.patch new file mode 100644 index 00000000000..97e09dee325 --- /dev/null +++ b/trunk/user/dnsmasq/patchs/102-support-load-gfwlist.patch @@ -0,0 +1,130 @@ +From dbe29adbf95d88cf35adcc00a8b994d350122d12 Mon Sep 17 00:00:00 2001 +From: Yonsm +Date: Sun, 13 Sep 2020 00:25:33 +0800 +Subject: [PATCH] Support load gfwlist.txt (equals to add with --server and + --ipset one by one) (#419) + +e.g. "--gfwlist=/etc/storage/dnsmasq/gfwlist.conf@8.8.8.8#53^gfwlist" equals to multiple line as below: +server=/xxx.xxx/8.8.8.8#53 +ipset=/xxx.xxx/gfwlist +--- + trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c | 79 +++++++++++++++++++- + 1 file changed, 78 insertions(+), 1 deletion(-) + +diff --git a/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c b/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c +index f05b2f2287b..28a44ab007e 100644 +--- a/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c ++++ b/trunk/user/dnsmasq/dnsmasq-2.8x/src/option.c +@@ -168,6 +168,7 @@ struct myoption { + #define LOPT_NAME_MATCH 355 + #define LOPT_CAA 356 + #define LOPT_FILTER_AAAA 357 ++#define LOPT_GFWLIST 358 + #define LOPT_DHCP_TO_HOST 359 + + #ifdef HAVE_GETOPT_LONG +@@ -342,6 +343,7 @@ static const struct myoption opts[] = + { "dumpfile", 1, 0, LOPT_DUMPFILE }, + { "dumpmask", 1, 0, LOPT_DUMPMASK }, + { "filter-aaaa", 0, 0, LOPT_FILTER_AAAA }, ++ { "gfwlist", 1, 0, LOPT_GFWLIST }, + { "dhcp-to-host", 0, 0, LOPT_DHCP_TO_HOST }, + { NULL, 0, 0, 0 } + }; +@@ -523,6 +525,7 @@ static struct { + { LOPT_DUMPFILE, ARG_ONE, "", gettext_noop("Path to debug packet dump file"), NULL }, + { LOPT_DUMPMASK, ARG_ONE, "", gettext_noop("Mask which packets to dump"), NULL }, + { LOPT_FILTER_AAAA, OPT_FILTER_AAAA, NULL, gettext_noop("Filter all AAAA requests."), NULL }, ++ { LOPT_GFWLIST, ARG_DUP, "[@server][^ipset]", gettext_noop("Gfwlist path or domain to special server (default 8.8.8.8~53) and ipset (default gfwlist, pass ^ only to skip default ipset)"), NULL }, + { LOPT_DHCP_TO_HOST, OPT_DHCP_TO_HOST, NULL, gettext_noop("Keep DHCP hostname valid at all times."), NULL }, + { 0, 0, NULL, NULL, NULL } + }; +@@ -1930,7 +1933,14 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma + case LOPT_SERVERS_FILE: + daemon->servers_file = opt_string_alloc(arg); + break; + ++ case LOPT_GFWLIST: ++ { ++ void load_gfwlist(char *gfwlist); ++ load_gfwlist(arg); ++ } ++ break; ++ + case 'm': /* --mx-host */ + { + int pref = 1; +@@ -4814,6 +4824,73 @@ void read_servers_file(void) + read_file(daemon->servers_file, f, LOPT_REV_SERV); + } + ++void add_gfwline(char *gfwline, const char *server, const char *ipset) ++{ ++ char *end = gfwline + 1; ++ while (*end && *end != '#' && *end != '\n' && *end != '\r') end++; ++ for (char *buf = end; buf >= gfwline; buf--) { ++ if (*buf == ',' || buf == gfwline) { ++ if (buf + 1 < end) { ++ *buf = '/'; ++ *end++ = '/'; ++ ++#ifdef HAVE_IPSET ++ if (*ipset) { ++ strcpy(end, ipset); ++ one_opt(LOPT_IPSET, buf, _("gfwlist"), _("error"), 0, 0); ++ end[-1] = '/'; ++ } ++#endif ++ strcpy(end, server); ++ one_opt('S', buf, _("gfwlist"), _("error"), 0, 0); ++ } ++ end = buf; ++ } ++ } ++} ++ ++void load_gfwlist(char *gfwlist) ++{ ++ char *cfg_server = NULL, *cfg_ipset = NULL; ++ for (char *p = gfwlist; *p; p++) { ++ if (*p == '@') cfg_server = p; ++ else if (*p == '^') cfg_ipset = p; ++ } ++ ++ const char *server, *ipset; ++ if (cfg_server) { ++ *cfg_server = 0; ++ server = cfg_server + 1; ++ } else { ++ server = "8.8.8.8~53"; ++ } ++ if (cfg_ipset) { ++ *cfg_ipset = 0; ++ ipset = cfg_ipset + 1; ++ } else { ++ ipset = "gfwlist"; ++ } ++ ++ FILE *f = NULL; ++ do { ++ if (*gfwlist == '/') { ++ if (!(f = fopen(gfwlist, "r"))) { ++ my_syslog(LOG_ERR, _("cannot read %s: %s"), gfwlist, strerror(errno)); ++ break; ++ } ++ for (char buf[MAXDNAME]; fgets(buf+ 1, MAXDNAME - 1, f); add_gfwline(buf, server, ipset)); ++ } else { ++ char old = gfwlist[-1]; ++ add_gfwline(gfwlist - 1, server, ipset); ++ gfwlist[-1] = old; ++ } ++ } while (0); ++ ++ if (f) fclose(f); ++ if (cfg_server) *cfg_server = '@'; ++ if (cfg_ipset) *cfg_ipset = '^'; ++} ++ + #ifdef HAVE_DHCP + static void clear_dynamic_conf(void) + { \ No newline at end of file