From 436623941a2683a91b761bf1425179e29adf66cc Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 24 Dec 2021 13:22:46 +0100 Subject: [PATCH 01/25] Add human-readable table output mode Signed-off-by: DL6ER --- src/args.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/args.c b/src/args.c index ce85fe4a..38b22a7d 100644 --- a/src/args.c +++ b/src/args.c @@ -101,7 +101,26 @@ void parse_args(int argc, char* argv[]) if(strcmp(argv[i], "sql") == 0 || strcmp(argv[i], "sqlite3") == 0 || strcmp(argv[i], "--sqlite3") == 0) + { + // Human-readable table output mode + if(i+1 < argc && strcmp(argv[i+1], "-h") == 0) + { + int argc2 = argc - i + 5 - 2; + char **argv2 = calloc(argc2, sizeof(char*)); + argv2[0] = argv[0]; // Application name + argv2[1] = (char*)"-column"; + argv2[2] = (char*)"-header"; + argv2[3] = (char*)"-nullvalue"; + argv2[4] = (char*)"(null)"; + // i = "sqlite3" + // i+1 = "-h" + for(int j = 0; j < argc - i - 2; j++) + argv2[5 + j] = argv[i + 2 + j]; + exit(sqlite3_shell_main(argc2, argv2)); + } + else exit(sqlite3_shell_main(argc - i, &argv[i])); + } // Implement dnsmasq's test function, no need to prepare the entire FTL // environment (initialize shared memory, lead queries from long-term From eb9cf5ba3bb0b35f1823deffb087ab03a1a2b18b Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 20 Dec 2021 16:40:41 +0000 Subject: [PATCH 02/25] Generalise --dhcp-relay. Sending via broadcast/multicast is now supported for both IPv4 and IPv6 and the configuration syntax made easier (but backwards compatible). Signed-off-by: DL6ER --- src/dnsmasq/dhcp-common.c | 18 ++++++++++++- src/dnsmasq/dhcp.c | 27 ++++++++++++++++--- src/dnsmasq/option.c | 56 ++++++++++++++++++++++++++++++--------- src/dnsmasq/rfc3315.c | 12 ++++++--- 4 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/dnsmasq/dhcp-common.c b/src/dnsmasq/dhcp-common.c index 37b2f38d..80c1538e 100644 --- a/src/dnsmasq/dhcp-common.c +++ b/src/dnsmasq/dhcp-common.c @@ -985,11 +985,27 @@ void log_context(int family, struct dhcp_context *context) void log_relay(int family, struct dhcp_relay *relay) { + int broadcast = relay->server.addr4.s_addr == 0; inet_ntop(family, &relay->local, daemon->addrbuff, ADDRSTRLEN); inet_ntop(family, &relay->server, daemon->namebuff, ADDRSTRLEN); +#ifdef HAVE_DHCP6 + struct in6_addr multicast; + + inet_pton(AF_INET6, ALL_SERVERS, &multicast); + + if (family == AF_INET6) + broadcast = IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast); +#endif + + if (relay->interface) - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface); + { + if (broadcast) + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s via %s"), daemon->addrbuff, relay->interface); + else + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s via %s"), daemon->addrbuff, daemon->namebuff, relay->interface); + } else my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay from %s to %s"), daemon->addrbuff, daemon->namebuff); } diff --git a/src/dnsmasq/dhcp.c b/src/dnsmasq/dhcp.c index e500bc2f..2c1272f5 100644 --- a/src/dnsmasq/dhcp.c +++ b/src/dnsmasq/dhcp.c @@ -1095,14 +1095,35 @@ static int relay_upstream4(struct dhcp_relay *relay, struct dhcp_packet *mess, to.sa.sa_family = AF_INET; to.in.sin_addr = relay->server.addr4; to.in.sin_port = htons(daemon->dhcp_server_port); - + + /* Broadcasting to server. */ + if (relay->server.addr4.s_addr == 0) + { + struct ifreq ifr; + + if (relay->interface) + safe_strncpy(ifr.ifr_name, relay->interface, IF_NAMESIZE); + + if (!relay->interface || strchr(relay->interface, '*') || + ioctl(daemon->dhcpfd, SIOCGIFBRDADDR, &ifr) == -1) + { + my_syslog(MS_DHCP | LOG_ERR, _("Cannot broadcast DHCP relay via interface %s"), relay->interface); + return 1; + } + + to.in.sin_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr; + } + send_from(daemon->dhcpfd, 0, (char *)mess, sz, &to, &from, 0); if (option_bool(OPT_LOG_OPTS)) { inet_ntop(AF_INET, &relay->local, daemon->addrbuff, ADDRSTRLEN); - inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); + if (relay->server.addr4.s_addr == 0) + snprintf(daemon->dhcp_buff2, DHCP_BUFF_SZ, _("broadcast via %s"), relay->interface); + else + inet_ntop(AF_INET, &relay->server.addr4, daemon->dhcp_buff2, DHCP_BUFF_SZ); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->dhcp_buff2); } /* Save this for replies */ diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index d164b996..768e231d 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -4285,26 +4285,56 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma } } break; - + case LOPT_RELAY: /* --dhcp-relay */ { struct dhcp_relay *new = opt_malloc(sizeof(struct dhcp_relay)); - comma = split(arg); - new->interface = opt_string_alloc(split(comma)); + char *two = split(arg); + char *three = split(two); + new->iface_index = 0; - if (comma && inet_pton(AF_INET, arg, &new->local) && inet_pton(AF_INET, comma, &new->server)) + + if (two) { - new->next = daemon->relay4; - daemon->relay4 = new; - } + if (inet_pton(AF_INET, arg, &new->local)) + { + if (!inet_pton(AF_INET, two, &new->server)) + { + new->server.addr4.s_addr = 0; + + /* Fail for three arg version where there are not two addresses. + Also fail when broadcasting to wildcard address. */ + if (three || strchr(two, '*')) + two = NULL; + else + three = two; + } + + new->next = daemon->relay4; + daemon->relay4 = new; + } #ifdef HAVE_DHCP6 - else if (comma && inet_pton(AF_INET6, arg, &new->local) && inet_pton(AF_INET6, comma, &new->server)) - { - new->next = daemon->relay6; - daemon->relay6 = new; - } + else if (inet_pton(AF_INET6, arg, &new->local)) + { + if (!inet_pton(AF_INET6, two, &new->server)) + { + inet_pton(AF_INET6, ALL_SERVERS, &new->server.addr6); + /* Fail for three arg version where there are not two addresses. + Also fail when multicasting to wildcard address. */ + if (three || strchr(two, '*')) + two = NULL; + else + three = two; + } + new->next = daemon->relay6; + daemon->relay6 = new; + } #endif - else + + new->interface = opt_string_alloc(three); + } + + if (!two) { free(new->interface); ret_err_free(_("Bad dhcp-relay"), new); diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index 5c2ff97b..f54fb789 100644 --- a/src/dnsmasq/rfc3315.c +++ b/src/dnsmasq/rfc3315.c @@ -2170,7 +2170,10 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, if (!relay->interface || strchr(relay->interface, '*') || (multicast_iface = if_nametoindex(relay->interface)) == 0 || setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1) - my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast to DHCPv6 server without correct interface")); + { + my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface); + return; + } } send_from(daemon->dhcp6fd, 0, daemon->outpacket.iov_base, save_counter(-1), &to, &from, 0); @@ -2178,8 +2181,11 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, if (option_bool(OPT_LOG_OPTS)) { inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN); - inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); - my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay %s -> %s"), daemon->addrbuff, daemon->namebuff); + if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast)) + snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface); + else + inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN); + my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff); } /* Save this for replies */ From c7e647ec10de21cf2a6a9345bccda0caac0ff6fe Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Fri, 24 Dec 2021 18:58:35 +0000 Subject: [PATCH 03/25] Fix rare "Internal error in cache" messages. Fix error created in 1ce1c6beae9f683bec54cba4c0d375f85b209b95 Many thanks to Hartmut Birr for finding the bug and bisecting to the guilty commit. The breaking commit creates cache entries which have F_NXDOMAIN set but none of F_IPV4, F_IPV6 or F_SRV. If cache_scan_free() is called to delete such an entry it will fail to do so. If the cache has no free slots and the least-recently-used slot is such an entry, then a new insertion will attempt to make space by calling cache_scan_free(), which will fail when it should be impossible and trigger the internal error. Signed-off-by: DL6ER --- src/dnsmasq/cache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 90eb139b..6ee917e0 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -414,7 +414,7 @@ static struct crec *cache_scan_free(char *name, union all_addr *addr, unsigned s if ((crecp->flags & F_FORWARD) && hostname_isequal(cache_get_name(crecp), name)) { /* Don't delete DNSSEC in favour of a CNAME, they can co-exist */ - if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV)) || + if ((flags & crecp->flags & (F_IPV4 | F_IPV6 | F_SRV | F_NXDOMAIN)) || (((crecp->flags | flags) & F_CNAME) && !(crecp->flags & (F_DNSKEY | F_DS)))) { if (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)) From db153cc59f2f5513ca6df5e3e2b6f065f801790c Mon Sep 17 00:00:00 2001 From: DL6ER Date: Fri, 24 Dec 2021 06:18:38 +0100 Subject: [PATCH 04/25] Christmas is not Easter Signed-off-by: DL6ER --- src/args.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/args.c b/src/args.c index 38b22a7d..64499f5b 100644 --- a/src/args.c +++ b/src/args.c @@ -341,7 +341,8 @@ void parse_args(int argc, char* argv[]) printf("\t--luac, luac FTL's lua compiler\n"); printf("\tdhcp-discover Discover DHCP servers in the local\n"); printf("\t network\n"); - printf("\tsqlite3 FTL's SQLite3 shell\n"); + printf("\tsql, sqlite3 FTL's SQLite3 shell\n"); + printf("\tsql -h, sqlite3 -h FTL's SQLite3 shell (human-readable mode)\n"); printf("\n\nOnline help: https://github.com/pi-hole/FTL\n"); exit(EXIT_SUCCESS); } From 43f79ae0116591359ff8fb78ae77f9ac9bdd5a8a Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 28 Dec 2021 11:22:25 +0100 Subject: [PATCH 05/25] Test: API reports DNS server port Signed-off-by: DL6ER --- test/test_suite.bats | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_suite.bats b/test/test_suite.bats index 63989f3e..536f19f9 100644 --- a/test/test_suite.bats +++ b/test/test_suite.bats @@ -11,6 +11,13 @@ [[ ${lines[6]} == "" ]] } +@test "DNS server port is reported" { + run bash -c 'echo ">dns-port >quit" | nc -v 127.0.0.1 4711' + printf "%s\n" "${lines[@]}" + [[ ${lines[1]} == "53" ]] + [[ ${lines[2]} == "" ]] +} + @test "Running a second instance is detected and prevented" { run bash -c 'su pihole -s /bin/sh -c "/home/pihole/pihole-FTL -f"' printf "%s\n" "${lines[@]}" From 7c0a9c1fd6ec1af6296edf9ba7e1c8e034802819 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Thu, 30 Dec 2021 14:51:41 +0100 Subject: [PATCH 06/25] Count immortal cache entries as valid. They can just never expire. Signed-off-by: DL6ER --- src/dnsmasq/cache.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index 90eb139b..a6a8b3c2 100644 --- a/src/dnsmasq/cache.c +++ b/src/dnsmasq/cache.c @@ -1731,9 +1731,7 @@ void get_dnsmasq_cache_info(struct cache_info *ci) const time_t now = time(NULL); for (int i=0; i < hash_size; i++) for (struct crec *cache = hash_table[i]; cache; cache = cache->hash_next) - if(cache->flags & F_IMMORTAL) - ci->immortal++; - else if(cache->ttd >= now) + if(cache->ttd >= now || cache->flags & F_IMMORTAL) { if (cache->flags & F_IPV4) ci->valid.ipv4++; @@ -1751,6 +1749,9 @@ void get_dnsmasq_cache_info(struct cache_info *ci) #endif else ci->valid.other++; + + if(cache->flags & F_IMMORTAL) + ci->immortal++; } else ci->expired++; From 5c41929968e2e4b07dbd5cf66715f8be5d894605 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sun, 26 Dec 2021 16:35:54 +0000 Subject: [PATCH 07/25] Fix wrong client address for dhcp-script when DHCPv4 relay in use. Signed-off-by: DL6ER --- src/dnsmasq/helper.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dnsmasq/helper.c b/src/dnsmasq/helper.c index 8821b975..e3de3d97 100644 --- a/src/dnsmasq/helper.c +++ b/src/dnsmasq/helper.c @@ -441,8 +441,8 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) buf = grab_extradata_lua(buf, end, "relay_address"); else if (data.giaddr.s_addr != 0) { - inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN); - lua_pushstring(lua, daemon->addrbuff); + inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); + lua_pushstring(lua, daemon->dhcp_buff2); lua_setfield(lua, -2, "relay_address"); } @@ -624,7 +624,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) { const char *giaddr = NULL; if (data.giaddr.s_addr != 0) - giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->addrbuff, ADDRSTRLEN); + giaddr = inet_ntop(AF_INET, &data.giaddr, daemon->dhcp_buff2, ADDRSTRLEN); my_setenv("DNSMASQ_RELAY_ADDRESS", giaddr, &err); } From e519fcfc0d444f12bdd3a92857fc2b4085cc7551 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 30 Dec 2021 21:20:37 +0000 Subject: [PATCH 08/25] Add snooping of DHCPv6 prefix delegation to the DHCP-relay function. Signed-off-by: DL6ER --- src/dnsmasq/dhcp6-protocol.h | 2 + src/dnsmasq/dhcp6.c | 3 +- src/dnsmasq/dnsmasq.c | 15 +++++++- src/dnsmasq/dnsmasq.h | 19 +++++++++- src/dnsmasq/helper.c | 57 ++++++++++++++++++++++++++-- src/dnsmasq/rfc3315.c | 73 ++++++++++++++++++++++++++++++++++-- 6 files changed, 157 insertions(+), 12 deletions(-) diff --git a/src/dnsmasq/dhcp6-protocol.h b/src/dnsmasq/dhcp6-protocol.h index ebe8dfd4..f1d09914 100644 --- a/src/dnsmasq/dhcp6-protocol.h +++ b/src/dnsmasq/dhcp6-protocol.h @@ -55,6 +55,8 @@ #define OPTION6_RECONF_ACCEPT 20 #define OPTION6_DNS_SERVER 23 #define OPTION6_DOMAIN_SEARCH 24 +#define OPTION6_IA_PD 25 +#define OPTION6_IAPREFIX 26 #define OPTION6_REFRESH_TIME 32 #define OPTION6_REMOTE_ID 37 #define OPTION6_SUBSCRIBER_ID 38 diff --git a/src/dnsmasq/dhcp6.c b/src/dnsmasq/dhcp6.c index ae1f5c16..c061879c 100644 --- a/src/dnsmasq/dhcp6.c +++ b/src/dnsmasq/dhcp6.c @@ -135,9 +135,8 @@ void dhcp6_packet(time_t now) if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name)) return; - if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0) + if (relay_reply6(&from, sz, ifr.ifr_name)) { - from.sin6_port = htons(port); while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from)))); diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index e9c4d942..6e6dc3f4 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -743,7 +743,11 @@ int main_dnsmasq (int argc, char **argv) /* if we are to run scripts, we need to fork a helper before dropping root. */ daemon->helperfd = -1; #ifdef HAVE_SCRIPT - if ((daemon->dhcp || daemon->dhcp6 || option_bool(OPT_TFTP) || option_bool(OPT_SCRIPT_ARP)) && + if ((daemon->dhcp || + daemon->dhcp6 || + daemon->relay6 || + option_bool(OPT_TFTP) || + option_bool(OPT_SCRIPT_ARP)) && (daemon->lease_change_command || daemon->luascript)) daemon->helperfd = create_helper(pipewrite, err_pipe[1], script_uid, script_gid, max_fd); #endif @@ -1152,6 +1156,10 @@ int main_dnsmasq (int argc, char **argv) while (helper_buf_empty() && do_tftp_script_run()); # endif +# ifdef HAVE_DHCP6 + while (helper_buf_empty() && do_snoop_script_run()); +# endif + if (!helper_buf_empty()) poll_listen(daemon->helperfd, POLLOUT); #else @@ -1166,6 +1174,11 @@ int main_dnsmasq (int argc, char **argv) while (do_tftp_script_run()); # endif +# ifdef HAVE_DHCP6 + while (helper_buf_empty() && do_snoop_script_run()); +# endif + + #endif diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 504e9444..de1531be 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -790,6 +790,7 @@ struct frec { #define ACTION_TFTP 5 #define ACTION_ARP 6 #define ACTION_ARP_DEL 7 +#define ACTION_RELAY_SNOOP 8 #define LEASE_NEW 1 /* newly created */ #define LEASE_CHANGED 2 /* modified */ @@ -1088,6 +1089,13 @@ struct dhcp_relay { union all_addr local, server; char *interface; /* Allowable interface for replies from server, and dest for IPv6 multicast */ int iface_index; /* working - interface in which requests arrived, for return */ +#ifdef HAVE_SCRIPT + struct snoop_record { + struct in6_addr client, prefix; + int prefix_len; + struct snoop_record *next; + } *snoop_records; +#endif struct dhcp_relay *current, *next; }; @@ -1239,13 +1247,18 @@ extern struct daemon { unsigned char *duid; struct iovec outpacket; int dhcp6fd, icmp6fd; +# ifdef HAVE_SCRIPT + struct snoop_record *free_snoops; +# endif #endif + /* DBus stuff */ /* void * here to avoid depending on dbus headers outside dbus.c */ void *dbus; #ifdef HAVE_DBUS struct watch *watches; #endif + /* UBus stuff */ #ifdef HAVE_UBUS /* void * here to avoid depending on ubus headers outside ubus.c */ @@ -1659,6 +1672,9 @@ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer); void queue_arp(int action, unsigned char *mac, int maclen, int family, union all_addr *addr); int helper_buf_empty(void); +#ifdef HAVE_DHCP6 +void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len); +#endif #endif /* tftp.c */ @@ -1704,7 +1720,8 @@ unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *if void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer_address, u32 scope_id, time_t now); -unsigned short relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); +int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); +int do_snoop_script_run(void); #endif /* dhcp-common.c */ diff --git a/src/dnsmasq/helper.c b/src/dnsmasq/helper.c index e3de3d97..aba66389 100644 --- a/src/dnsmasq/helper.c +++ b/src/dnsmasq/helper.c @@ -238,8 +238,13 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) is6 = (data.flags != AF_INET); data.action = ACTION_ARP; } - else - continue; + else if (data.action == ACTION_RELAY_SNOOP) + { + is6 = 1; + action_str = "relay-snoop"; + } + else + continue; /************************** Pi-hole modification **************************/ FTL_log_helper(1, action_str); @@ -295,7 +300,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) char *dot; hostname = (char *)buf; hostname[data.hostname_len - 1] = 0; - if (data.action != ACTION_TFTP) + if (data.action != ACTION_TFTP && data.action != ACTION_RELAY_SNOOP) { if (!legal_hostname(hostname)) hostname = NULL; @@ -341,6 +346,24 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ } } + else if (data.action == ACTION_RELAY_SNOOP) + { + lua_getglobal(lua, "snoop"); + if (lua_type(lua, -1) != LUA_TFUNCTION) + lua_pop(lua, 1); /* tftp function optional */ + else + { + lua_pushstring(lua, action_str); /* arg1 - action */ + lua_newtable(lua); /* arg2 - data table */ + lua_pushstring(lua, daemon->addrbuff); + lua_setfield(lua, -2, "client_address"); + lua_pushstring(lua, hostname); + lua_setfield(lua, -2, "prefix"); + lua_pushstring(lua, data.interface); + lua_setfield(lua, -2, "client_interface"); + lua_call(lua, 2, 0); /* pass 2 values, expect 0 */ + } + } else if (data.action == ACTION_ARP) { lua_getglobal(lua, "arp"); @@ -562,7 +585,7 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) close(pipeout[1]); } - if (data.action != ACTION_TFTP && data.action != ACTION_ARP) + if (data.action != ACTION_TFTP && data.action != ACTION_ARP && data.action != ACTION_RELAY_SNOOP) { #ifdef HAVE_DHCP6 my_setenv("DNSMASQ_IAID", is6 ? daemon->dhcp_buff3 : NULL, &err); @@ -649,6 +672,9 @@ int create_helper(int event_fd, int err_fd, uid_t uid, gid_t gid, long max_fd) fcntl(event_fd, F_SETFD, i | FD_CLOEXEC); close(pipefd[0]); + if (data.action == ACTION_RELAY_SNOOP) + strcpy(daemon->packet, data.interface); + /**************************** Pi-hole modification ****************************/ FTL_log_helper(5, daemon->lease_change_command, action_str, (is6 && data.action != ACTION_ARP) ? daemon->packet : daemon->dhcp_buff, @@ -828,6 +854,29 @@ void queue_script(int action, struct dhcp_lease *lease, char *hostname, time_t n bytes_in_buf = p - (unsigned char *)buf; } +#ifdef HAVE_DHCP6 +void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *prefix, int prefix_len) +{ + /* no script */ + if (daemon->helperfd == -1) + return; + + inet_ntop(AF_INET6, prefix, daemon->addrbuff, ADDRSTRLEN); + + /* 5 for /nnn and zero on the end of the prefix. */ + buff_alloc(sizeof(struct script_data) + ADDRSTRLEN + 5); + memset(buf, 0, sizeof(struct script_data)); + + buf->action = ACTION_RELAY_SNOOP; + buf->addr6 = *client; + buf->hostname_len = sprintf((char *)(buf+1), "%s/%u", daemon->addrbuff, prefix_len) + 1; + + indextoname(daemon->dhcp6fd, if_index, buf->interface); + + bytes_in_buf = sizeof(struct script_data) + buf->hostname_len; +} +#endif + #ifdef HAVE_TFTP /* This nastily re-uses DHCP-fields for TFTP stuff */ void queue_tftp(off_t file_len, char *filename, union mysockaddr *peer) diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index f54fb789..fb387a49 100644 --- a/src/dnsmasq/rfc3315.c +++ b/src/dnsmasq/rfc3315.c @@ -2194,7 +2194,7 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, } } -unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) +int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) { struct dhcp_relay *relay; struct in6_addr link; @@ -2226,10 +2226,75 @@ unsigned short relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival put_opt6(opt6_ptr(opt, 0), opt6_len(opt)); memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); peer->sin6_scope_id = relay->iface_index; - return encap_type == DHCP6RELAYREPL ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT; - } - } + if (encap_type == DHCP6RELAYREPL) + { + peer->sin6_port = ntohs(DHCPV6_SERVER_PORT); + return 1; + } + + peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT); + +#ifdef HAVE_SCRIPT + if (daemon->lease_change_command && encap_type == DHCP6REPLY) + { + /* decapsulate relayed message */ + opts = opt6_ptr(opt, 4); + end = opt6_ptr(opt, opt6_len(opt)); + + for (opt = opts; opt; opt = opt6_next(opt, end)) + if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12) + { + void *ia_opts = opt6_ptr(opt, 12); + void *ia_end = opt6_ptr(opt, opt6_len(opt)); + void *ia_opt; + + for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end)) + /* valid lifetime must not be zero. */ + if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0) + { + if (daemon->free_snoops || + (daemon->free_snoops = whine_malloc(sizeof(struct snoop_record)))) + { + struct snoop_record *snoop = daemon->free_snoops; + + daemon->free_snoops = snoop->next; + snoop->client = peer->sin6_addr; + snoop->prefix_len = opt6_uint(ia_opt, 8, 1); + memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ); + snoop->next = relay->snoop_records; + relay->snoop_records = snoop; + } + } + } + } +#endif + return 1; + } + + } + + return 0; +} + +int do_snoop_script_run(void) +{ +#ifdef HAVE_SCRIPT + struct dhcp_relay *relay; + struct snoop_record *snoop; + + for (relay = daemon->relay6; relay; relay = relay->next) + if ((snoop = relay->snoop_records)) + { + relay->snoop_records = snoop->next; + snoop->next = daemon->free_snoops; + daemon->free_snoops = snoop; + + queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len); + return 1; + } +#endif + return 0; } From edf70fcd5ef0741e88a88cd564e631df310e18c0 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Thu, 30 Dec 2021 23:22:43 +0000 Subject: [PATCH 09/25] Finesse parsing of --dhcp-remoteid and --dhcp-subscrid. To be treated as hex, the pattern must consist of only hex digits AND contain at least one ':'. Thanks to Bengt-Erik Sandstrom who tripped over a pattern consisting of a decimal number which was interpreted surprisingly. Signed-off-by: DL6ER --- src/dnsmasq/option.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 768e231d..1c2c55ba 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -4132,7 +4132,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_SUBSCR: /* --dhcp-subscrid */ { unsigned char *p; - int dig = 0; + int dig, colon; struct dhcp_vendor *new = opt_malloc(sizeof(struct dhcp_vendor)); if (!(comma = split(arg))) @@ -4156,13 +4156,16 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma else comma = arg; - for (p = (unsigned char *)comma; *p; p++) + for (dig = 0, colon = 0, p = (unsigned char *)comma; *p; p++) if (isxdigit(*p)) dig = 1; - else if (*p != ':') + else if (*p == ':') + colon = 1; + else break; + unhide_metas(comma); - if (option == 'U' || option == 'j' || *p || !dig) + if (option == 'U' || option == 'j' || *p || !dig || !colon) { new->len = strlen(comma); new->data = opt_malloc(new->len); From a5af39f33ce0a1c6e121e3300074694ca1241d56 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 2 Jan 2022 14:41:49 +0100 Subject: [PATCH 10/25] Check E(ffective) in addition of P(ermitted) capabilities flag. E is what the kernel actually checks, P is a limiting superset for the capabilities that could be added to the E and I(nheritable) sets. We need both as TCP workers will be forks. Signed-off-by: DL6ER --- src/capabilities.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/capabilities.c b/src/capabilities.c index 774e23a4..fbaccbfe 100644 --- a/src/capabilities.c +++ b/src/capabilities.c @@ -69,37 +69,43 @@ bool check_capabilities(void) } bool capabilities_okay = true; - if (!(data->permitted & (1 << CAP_NET_ADMIN))) + if (!(data->permitted & (1 << CAP_NET_ADMIN)) || + !(data->effective & (1 << CAP_NET_ADMIN))) { // Needed for ARP-injection (used when we're the DHCP server) logg("WARNING: Required Linux capability CAP_NET_ADMIN not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_NET_RAW))) + if (!(data->permitted & (1 << CAP_NET_RAW)) || + !(data->effective & (1 << CAP_NET_RAW))) { // Needed for raw socket access (necessary for ICMP) logg("WARNING: Required Linux capability CAP_NET_RAW not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_NET_BIND_SERVICE))) + if (!(data->permitted & (1 << CAP_NET_BIND_SERVICE)) || + !(data->effective & (1 << CAP_NET_BIND_SERVICE))) { // Necessary for dynamic port binding logg("WARNING: Required Linux capability CAP_NET_BIND_SERVICE not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_SYS_NICE))) + if (!(data->permitted & (1 << CAP_SYS_NICE)) || + !(data->effective & (1 << CAP_SYS_NICE))) { // Necessary for dynamic port binding logg("WARNING: Required Linux capability CAP_SYS_NICE not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_IPC_LOCK))) + if (!(data->permitted & (1 << CAP_IPC_LOCK)) || + !(data->effective & (1 << CAP_IPC_LOCK))) { // Necessary for mmap() to work correctly logg("WARNING: Required Linux capability CAP_IPC_LOCK not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_CHOWN))) + if (!(data->permitted & (1 << CAP_CHOWN)) || + !(data->effective & (1 << CAP_CHOWN))) { // Necessary for chown() to work correctly logg("WARNING: Required Linux capability CAP_CHOWN not available"); From 646e709fb1f794a8bd5837aaeb69ce41cd18f0ca Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 2 Jan 2022 14:52:17 +0100 Subject: [PATCH 11/25] Do not warn about missing capabilities during startup. The embedded dnsmasq does the same but config-aware, i.e. it will not complain about missing CAP_NET_ADMIN when DHCP is not used. As its warnings are now much more present in all logs, we don't need to do the check twice. The existing checks remain there but are only used in debug mode (DEBUG_CAPS). Signed-off-by: DL6ER --- src/capabilities.c | 27 ++++++++++++--------------- src/main.c | 6 +++--- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/capabilities.c b/src/capabilities.c index fbaccbfe..90ab5894 100644 --- a/src/capabilities.c +++ b/src/capabilities.c @@ -52,21 +52,18 @@ bool check_capabilities(void) data = calloc(sizeof(*data), capsize); capget(hdr, data); - if(config.debug & DEBUG_CAPS) + logg("***************************************"); + logg("* Linux capability debugging enabled *"); + for(unsigned int i = 0u; i < numCaps; i++) { - logg("***************************************"); - logg("* Linux capability debugging enabled *"); - for(unsigned int i = 0u; i < numCaps; i++) - { - const unsigned int capid = capabilityIDs[i]; - logg("* %-24s (%02u) = %s%s%s *", - capabilityNames[capid], capid, - ((data->permitted & (1 << capid)) ? "P":"-"), - ((data->inheritable & (1 << capid)) ? "I":"-"), - ((data->effective & (1 << capid)) ? "E":"-")); - } - logg("***************************************"); + const unsigned int capid = capabilityIDs[i]; + logg("* %-24s (%02u) = %s%s%s *", + capabilityNames[capid], capid, + ((data->permitted & (1 << capid)) ? "P":"-"), + ((data->inheritable & (1 << capid)) ? "I":"-"), + ((data->effective & (1 << capid)) ? "E":"-")); } + logg("***************************************"); bool capabilities_okay = true; if (!(data->permitted & (1 << CAP_NET_ADMIN)) || @@ -93,7 +90,7 @@ bool check_capabilities(void) if (!(data->permitted & (1 << CAP_SYS_NICE)) || !(data->effective & (1 << CAP_SYS_NICE))) { - // Necessary for dynamic port binding + // Necessary for setting higher process priority through nice logg("WARNING: Required Linux capability CAP_SYS_NICE not available"); capabilities_okay = false; } @@ -107,7 +104,7 @@ bool check_capabilities(void) if (!(data->permitted & (1 << CAP_CHOWN)) || !(data->effective & (1 << CAP_CHOWN))) { - // Necessary for chown() to work correctly + // Necessary to chown required files that are owned by another user logg("WARNING: Required Linux capability CAP_CHOWN not available"); capabilities_okay = false; } diff --git a/src/main.c b/src/main.c index 748d70eb..0253a56b 100644 --- a/src/main.c +++ b/src/main.c @@ -98,9 +98,9 @@ int main (int argc, char* argv[]) log_counter_info(); check_setupVarsconf(); - // Check for availability of advanced capabilities - // immediately before starting the resolver. - check_capabilities(); + // Check for availability of capabilities in debug mode + if(config.debug & DEBUG_CAPS) + check_capabilities(); // Start the resolver startup = false; From 3634ba254eb6177bdd91043264b392a05aa19b27 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 2 Jan 2022 14:53:44 +0100 Subject: [PATCH 12/25] Remove check for CAP_IPC_LOCK as we are not using mlock() anywhere in the code Signed-off-by: DL6ER --- src/capabilities.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/capabilities.c b/src/capabilities.c index 90ab5894..603cb4f7 100644 --- a/src/capabilities.c +++ b/src/capabilities.c @@ -94,13 +94,6 @@ bool check_capabilities(void) logg("WARNING: Required Linux capability CAP_SYS_NICE not available"); capabilities_okay = false; } - if (!(data->permitted & (1 << CAP_IPC_LOCK)) || - !(data->effective & (1 << CAP_IPC_LOCK))) - { - // Necessary for mmap() to work correctly - logg("WARNING: Required Linux capability CAP_IPC_LOCK not available"); - capabilities_okay = false; - } if (!(data->permitted & (1 << CAP_CHOWN)) || !(data->effective & (1 << CAP_CHOWN))) { From 912ce90e479f9d8f0daf7b35e76b57fb145a8b52 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Sun, 2 Jan 2022 15:19:28 +0100 Subject: [PATCH 13/25] Update SQLite to v3.37.1 Signed-off-by: DL6ER --- src/database/shell.c | 80 ++++++++++++++++++++++++------------------ src/database/sqlite3.c | 41 ++++++++++++++-------- src/database/sqlite3.h | 6 ++-- 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/database/shell.c b/src/database/shell.c index cbb3a5ff..d3530909 100644 --- a/src/database/shell.c +++ b/src/database/shell.c @@ -20001,16 +20001,8 @@ static int do_meta_command(char *zLine, ShellState *p){ char *zNewFilename = 0; /* Name of the database file to open */ int iName = 1; /* Index in azArg[] of the filename */ int newFlag = 0; /* True to delete file before opening */ - /* Close the existing database */ - session_close_all(p, -1); - close_db(p->db); - p->db = 0; - p->pAuxDb->zDbFilename = 0; - sqlite3_free(p->pAuxDb->zFreeOnClose); - p->pAuxDb->zFreeOnClose = 0; - p->openMode = SHELL_OPEN_UNSPEC; - p->openFlags = 0; - p->szMax = 0; + int openMode = SHELL_OPEN_UNSPEC; + /* Check for command-line arguments */ for(iName=1; iNameopenMode = SHELL_OPEN_ZIPFILE; + openMode = SHELL_OPEN_ZIPFILE; #endif }else if( optionMatch(z, "append") ){ - p->openMode = SHELL_OPEN_APPENDVFS; + openMode = SHELL_OPEN_APPENDVFS; }else if( optionMatch(z, "readonly") ){ - p->openMode = SHELL_OPEN_READONLY; + openMode = SHELL_OPEN_READONLY; }else if( optionMatch(z, "nofollow") ){ p->openFlags |= SQLITE_OPEN_NOFOLLOW; #ifndef SQLITE_OMIT_DESERIALIZE }else if( optionMatch(z, "deserialize") ){ - p->openMode = SHELL_OPEN_DESERIALIZE; + openMode = SHELL_OPEN_DESERIALIZE; }else if( optionMatch(z, "hexdb") ){ - p->openMode = SHELL_OPEN_HEXDB; + openMode = SHELL_OPEN_HEXDB; }else if( optionMatch(z, "maxsize") && iName+1szMax = integerValue(azArg[++iName]); #endif /* SQLITE_OMIT_DESERIALIZE */ @@ -20046,6 +20038,18 @@ static int do_meta_command(char *zLine, ShellState *p){ zNewFilename = sqlite3_mprintf("%s", z); } } + + /* Close the existing database */ + session_close_all(p, -1); + close_db(p->db); + p->db = 0; + p->pAuxDb->zDbFilename = 0; + sqlite3_free(p->pAuxDb->zFreeOnClose); + p->pAuxDb->zFreeOnClose = 0; + p->openMode = openMode; + p->openFlags = 0; + p->szMax = 0; + /* If a filename is specified, try to open it first */ if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){ if( newFlag && !p->bSafeMode ) shellDeleteFile(zNewFilename); @@ -21264,30 +21268,31 @@ static int do_meta_command(char *zLine, ShellState *p){ static const struct { const char *zCtrlName; /* Name of a test-control option */ int ctrlCode; /* Integer code for that option */ + int unSafe; /* Not valid for --safe mode */ const char *zUsage; /* Usage notes */ } aCtrl[] = { - { "always", SQLITE_TESTCTRL_ALWAYS, "BOOLEAN" }, - { "assert", SQLITE_TESTCTRL_ASSERT, "BOOLEAN" }, - /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS, "" },*/ - /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, "" },*/ - { "byteorder", SQLITE_TESTCTRL_BYTEORDER, "" }, - { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,"BOOLEAN" }, - /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, "" },*/ - { "imposter", SQLITE_TESTCTRL_IMPOSTER, "SCHEMA ON/OFF ROOTPAGE"}, - { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS, "" }, - { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,"BOOLEAN" }, - { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT, "BOOLEAN" }, - { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS, "DISABLE-MASK" }, + { "always", SQLITE_TESTCTRL_ALWAYS, 1, "BOOLEAN" }, + { "assert", SQLITE_TESTCTRL_ASSERT, 1, "BOOLEAN" }, + /*{ "benign_malloc_hooks",SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS,1, "" },*/ + /*{ "bitvec_test", SQLITE_TESTCTRL_BITVEC_TEST, 1, "" },*/ + { "byteorder", SQLITE_TESTCTRL_BYTEORDER, 0, "" }, + { "extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN" }, + /*{ "fault_install", SQLITE_TESTCTRL_FAULT_INSTALL, 1,"" },*/ + { "imposter", SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"}, + { "internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,"" }, + { "localtime_fault", SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN" }, + { "never_corrupt", SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN" }, + { "optimizations", SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK" }, #ifdef YYCOVERAGE - { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE, "" }, + { "parser_coverage", SQLITE_TESTCTRL_PARSER_COVERAGE,0,"" }, #endif - { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " }, - { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" }, - { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" }, - { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" }, - { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, "" }, - { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, "NMAX" }, - { "tune", SQLITE_TESTCTRL_TUNE, "ID VALUE" }, + { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET " }, + { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE,0, "" }, + { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, 0, "" }, + { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, 0, "SEED ?db?" }, + { "seek_count", SQLITE_TESTCTRL_SEEK_COUNT, 0, "" }, + { "sorter_mmap", SQLITE_TESTCTRL_SORTER_MMAP, 0, "NMAX" }, + { "tune", SQLITE_TESTCTRL_TUNE, 1, "ID VALUE" }, }; int testctrl = -1; int iCtrl = -1; @@ -21335,6 +21340,11 @@ static int do_meta_command(char *zLine, ShellState *p){ if( testctrl<0 ){ utf8_printf(stderr,"Error: unknown test-control: %s\n" "Use \".testctrl --help\" for help\n", zCmd); + }else if( aCtrl[iCtrl].unSafe && p->bSafeMode ){ + utf8_printf(stderr, + "line %d: \".testctrl %s\" may not be used in safe mode\n", + p->lineno, aCtrl[iCtrl].zCtrlName); + exit(1); }else{ switch(testctrl){ diff --git a/src/database/sqlite3.c b/src/database/sqlite3.c index 4f3deb17..fba450a7 100644 --- a/src/database/sqlite3.c +++ b/src/database/sqlite3.c @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.37.0. By combining all the individual C code files into this +** version 3.37.1. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -452,9 +452,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.37.0" -#define SQLITE_VERSION_NUMBER 3037000 -#define SQLITE_SOURCE_ID "2021-11-27 14:13:22 bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a" +#define SQLITE_VERSION "3.37.1" +#define SQLITE_VERSION_NUMBER 3037001 +#define SQLITE_SOURCE_ID "2021-12-30 15:30:28 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62" /* ** CAPI3REF: Run-Time Library Version Numbers @@ -104085,7 +104085,7 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ return ExprHasProperty(p, EP_CanBeNull) || p->y.pTab==0 || /* Reference to column of index on expression */ (p->iColumn>=0 - && ALWAYS(p->y.pTab->aCol!=0) /* Defense against OOM problems */ + && p->y.pTab->aCol!=0 /* Possible due to prior error */ && p->y.pTab->aCol[p->iColumn].notNull==0); default: return 1; @@ -126000,6 +126000,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( if( onError==OE_Replace /* IPK rule is REPLACE */ && onError!=overrideError /* Rules for other constraints are different */ && pTab->pIndex /* There exist other constraints */ + && !upsertIpkDelay /* IPK check already deferred by UPSERT */ ){ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1; VdbeComment((v, "defer IPK REPLACE until last")); @@ -126408,6 +126409,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks( if( ipkTop ){ sqlite3VdbeGoto(v, ipkTop); VdbeComment((v, "Do IPK REPLACE")); + assert( ipkBottom>0 ); sqlite3VdbeJumpHere(v, ipkBottom); } @@ -133005,6 +133007,7 @@ static int sqlite3LockAndPrepare( ** reset is considered a permanent error. */ rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail); assert( rc==SQLITE_OK || *ppStmt==0 ); + if( rc==SQLITE_OK || db->mallocFailed ) break; }while( rc==SQLITE_ERROR_RETRY || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) ); sqlite3BtreeLeaveAll(db); @@ -169411,6 +169414,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){ if( newLimit>=0 ){ /* IMP: R-52476-28732 */ if( newLimit>aHardLimit[limitId] ){ newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */ + }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){ + newLimit = 1; } db->aLimit[limitId] = newLimit; } @@ -170814,12 +170819,16 @@ SQLITE_API int sqlite3_test_control(int op, ...){ */ case SQLITE_TESTCTRL_IMPOSTER: { sqlite3 *db = va_arg(ap, sqlite3*); + int iDb; sqlite3_mutex_enter(db->mutex); - db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); - db->init.busy = db->init.imposterTable = va_arg(ap,int); - db->init.newTnum = va_arg(ap,int); - if( db->init.busy==0 && db->init.newTnum>0 ){ - sqlite3ResetAllSchemasOfConnection(db); + iDb = sqlite3FindDbName(db, va_arg(ap,const char*)); + if( iDb>=0 ){ + db->init.iDb = iDb; + db->init.busy = db->init.imposterTable = va_arg(ap,int); + db->init.newTnum = va_arg(ap,int); + if( db->init.busy==0 && db->init.newTnum>0 ){ + sqlite3ResetAllSchemasOfConnection(db); + } } sqlite3_mutex_leave(db->mutex); break; @@ -177073,7 +177082,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev( assert( nDoclist>0 ); assert( *pbEof==0 ); - assert( p || *piDocid==0 ); + assert_fts3_nc( p || *piDocid==0 ); assert( !p || (p>aDoclist && p<&aDoclist[nDoclist]) ); if( p==0 ){ @@ -224759,8 +224768,12 @@ static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){ int iRowidOff; iRowidOff = fts5LeafFirstRowidOff(pNew); if( iRowidOff ){ - pIter->pLeaf = pNew; - pIter->iLeafOffset = iRowidOff; + if( iRowidOff>=pNew->szLeaf ){ + p->rc = FTS5_CORRUPT; + }else{ + pIter->pLeaf = pNew; + pIter->iLeafOffset = iRowidOff; + } } } @@ -232489,7 +232502,7 @@ static void fts5SourceIdFunc( ){ assert( nArg==0 ); UNUSED_PARAM2(nArg, apUnused); - sqlite3_result_text(pCtx, "fts5: 2021-11-27 14:13:22 bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a", -1, SQLITE_TRANSIENT); + sqlite3_result_text(pCtx, "fts5: 2021-12-30 15:30:28 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62", -1, SQLITE_TRANSIENT); } /* diff --git a/src/database/sqlite3.h b/src/database/sqlite3.h index 1247048a..393e9d20 100644 --- a/src/database/sqlite3.h +++ b/src/database/sqlite3.h @@ -146,9 +146,9 @@ extern "C" { ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ -#define SQLITE_VERSION "3.37.0" -#define SQLITE_VERSION_NUMBER 3037000 -#define SQLITE_SOURCE_ID "2021-11-27 14:13:22 bd41822c7424d393a30e92ff6cb254d25c26769889c1499a18a0b9339f5d6c8a" +#define SQLITE_VERSION "3.37.1" +#define SQLITE_VERSION_NUMBER 3037001 +#define SQLITE_SOURCE_ID "2021-12-30 15:30:28 378629bf2ea546f73eee84063c5358439a12f7300e433f18c9e1bddd948dea62" /* ** CAPI3REF: Run-Time Library Version Numbers From 15a389bcfc2302dcec932203a40d6e69f4180635 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 1 Jan 2022 22:15:16 +0000 Subject: [PATCH 14/25] Include client address if TFTP file-not-found errors. Signed-off-by: DL6ER --- src/dnsmasq/tftp.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/dnsmasq/tftp.c b/src/dnsmasq/tftp.c index 3d87523c..e8474d9a 100644 --- a/src/dnsmasq/tftp.c +++ b/src/dnsmasq/tftp.c @@ -19,9 +19,9 @@ #ifdef HAVE_TFTP static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len); -static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix); +static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client); static void free_transfer(struct tftp_transfer *transfer); -static ssize_t tftp_err(int err, char *packet, char *message, char *file); +static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2); static ssize_t tftp_err_oops(char *packet, const char *file); static ssize_t get_block(char *packet, struct tftp_transfer *transfer); static char *next(char **p, char *end); @@ -362,7 +362,7 @@ void tftp_request(struct listener *listen, time_t now) !(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0)) { - len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff); + len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"), daemon->addrbuff, NULL); is_err = 1; } else @@ -472,7 +472,7 @@ void tftp_request(struct listener *listen, time_t now) strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); /* check permissions and open file */ - if ((transfer->file = check_tftp_fileperm(&len, prefix))) + if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff))) { if ((len = get_block(packet, transfer)) == -1) len = tftp_err_oops(packet, daemon->namebuff); @@ -492,7 +492,7 @@ void tftp_request(struct listener *listen, time_t now) } } -static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) +static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix, char *client) { char *packet = daemon->packet, *namebuff = daemon->namebuff; struct tftp_file *file; @@ -509,7 +509,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) { if (errno == ENOENT) { - *len = tftp_err(ERR_FNF, packet, _("file %s not found"), namebuff); + *len = tftp_err(ERR_FNF, packet, _("file %s not found for %s"), namebuff, client); return NULL; } else if (errno == EACCES) @@ -562,8 +562,7 @@ static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix) return file; perm: - errno = EACCES; - *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff); + *len = tftp_err(ERR_PERM, packet, _("cannot access %s: %s"), namebuff, strerror(EACCES)); if (fd != -1) close(fd); return NULL; @@ -599,7 +598,7 @@ void check_tftp_listeners(time_t now) { /* Wrong source address. See rfc1350 para 4. */ prettyprint_addr(&peer, daemon->addrbuff); - len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff); + len = tftp_err(ERR_TID, daemon->packet, _("ignoring packet from %s (TID mismatch)"), daemon->addrbuff, NULL); while(retry_send(sendto(transfer->sockfd, daemon->packet, len, 0, &peer.sa, sa_len(&peer)))); } } @@ -743,22 +742,21 @@ static void sanitise(char *buf) } #define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ -static ssize_t tftp_err(int err, char *packet, char *message, char *file) +static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *arg2) { struct errmess { unsigned short op, err; char message[]; } *mess = (struct errmess *)packet; ssize_t len, ret = 4; - char *errstr = strerror(errno); - + memset(packet, 0, daemon->packet_buff_sz); if (file) sanitise(file); mess->op = htons(OP_ERR); mess->err = htons(err); - len = snprintf(mess->message, MAXMESSAGE, message, file, errstr); + len = snprintf(mess->message, MAXMESSAGE, message, file, arg2); ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ if (err != ERR_FNF || !option_bool(OPT_QUIET_TFTP)) @@ -772,7 +770,7 @@ static ssize_t tftp_err_oops(char *packet, const char *file) /* May have >1 refs to file, so potentially mangle a copy of the name */ if (file != daemon->namebuff) strcpy(daemon->namebuff, file); - return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff); + return tftp_err(ERR_NOTDEF, packet, _("cannot read %s: %s"), daemon->namebuff, strerror(errno)); } /* return -1 for error, zero for done. */ From 5e4dd996b85f4f37929ac11622ab7fa58fbc5f5e Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Fri, 31 Dec 2021 17:29:44 +0100 Subject: [PATCH 15/25] src/option.c: fix build with gcc 4.8 Fix the following build failure with gcc 4.8 raised since version 2.86: option.c: In function 'one_opt': option.c:2445:11: error: 'for' loop initial declarations are only allowed in C99 mode for (char *p = arg; *p; p++) { ^ option.c:2445:11: note: use option -std=c99 or -std=gnu99 to compile your code option.c:2453:11: error: 'for' loop initial declarations are only allowed in C99 mode for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) { ^ Fixes: - http://autobuild.buildroot.org/results/39b34a4e69fc10f4bd9d4ddb0ed8c0aae5741c84 Signed-off-by: Fabrice Fontaine Signed-off-by: DL6ER --- src/dnsmasq/option.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 1c2c55ba..2550707f 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -2529,7 +2529,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma arg += 9; if (strlen(arg) != 16) ret_err(gen_err); - for (char *p = arg; *p; p++) { + char *p; + for (*p = arg; *p; p++) { if (!isxdigit((int)*p)) ret_err(gen_err); } @@ -2537,7 +2538,8 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma u8 *u = daemon->umbrella_device; char word[3]; - for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) { + u8 i; + for (i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) { memcpy(word, &(arg[0]), 2); *u++ = strtoul(word, NULL, 16); } From 3a6b222bef302143aeea008d0f13570e43db7f8d Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 1 Jan 2022 23:03:26 +0000 Subject: [PATCH 16/25] Fix 46312909d9080ff8743133fbd52427b4b2213171 typo. Signed-off-by: DL6ER --- src/dnsmasq/option.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 2550707f..914c33dc 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -361,7 +361,7 @@ static const struct myoption opts[] = { "dhcp-ignore-clid", 0, 0, LOPT_IGNORE_CLID }, { "dynamic-host", 1, 0, LOPT_DYNHOST }, { "log-debug", 0, 0, LOPT_LOG_DEBUG }, - { "umbrella", 2, 0, LOPT_UMBRELLA }, + { "umbrella", 2, 0, LOPT_UMBRELLA }, { "quiet-tftp", 0, 0, LOPT_QUIET_TFTP }, { NULL, 0, 0, 0 } }; @@ -2530,7 +2530,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma if (strlen(arg) != 16) ret_err(gen_err); char *p; - for (*p = arg; *p; p++) { + for (p = arg; *p; p++) { if (!isxdigit((int)*p)) ret_err(gen_err); } From a94e061a08601ce5288874def0a772f38e674d3e Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Sat, 1 Jan 2022 23:33:39 +0000 Subject: [PATCH 17/25] Tidy code for --umbrella option. Signed-off-by: DL6ER --- src/dnsmasq/edns0.c | 42 +++++++++++++------------ src/dnsmasq/option.c | 75 ++++++++++++++++++++++++-------------------- 2 files changed, 63 insertions(+), 54 deletions(-) diff --git a/src/dnsmasq/edns0.c b/src/dnsmasq/edns0.c index 9cf1d114..5de6cb22 100644 --- a/src/dnsmasq/edns0.c +++ b/src/dnsmasq/edns0.c @@ -460,31 +460,33 @@ static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}}; u8 *u = &opt.fields[0]; - - if (daemon->umbrella_org) { - PUTSHORT(UMBRELLA_ORG, u); - PUTLONG(daemon->umbrella_org, u); - } - int family = source->sa.sa_family; - PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u); int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ; + + if (daemon->umbrella_org) + { + PUTSHORT(UMBRELLA_ORG, u); + PUTLONG(daemon->umbrella_org, u); + } + + PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u); memcpy(u, get_addrp(source, family), size); u += size; + + if (option_bool(OPT_UMBRELLA_DEVID)) + { + PUTSHORT(UMBRELLA_DEVICE, u); + memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ); + u += UMBRELLA_DEVICESZ; + } - if (option_bool(OPT_UMBRELLA_DEVID)) { - PUTSHORT(UMBRELLA_DEVICE, u); - memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ); - u += UMBRELLA_DEVICESZ; - } - - if (daemon->umbrella_asset) { - PUTSHORT(UMBRELLA_ASSET, u); - PUTLONG(daemon->umbrella_asset, u); - } - - int len = u - &opt.magic[0]; - return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1); + if (daemon->umbrella_asset) + { + PUTSHORT(UMBRELLA_ASSET, u); + PUTLONG(daemon->umbrella_asset, u); + } + + return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, u - (u8 *)&opt, 0, 1); } /* Set *check_subnet if we add a client subnet option, which needs to checked diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index 914c33dc..b0611a52 100644 --- a/src/dnsmasq/option.c +++ b/src/dnsmasq/option.c @@ -2523,41 +2523,48 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma case LOPT_UMBRELLA: /* --umbrella */ set_option_bool(OPT_UMBRELLA); - while (arg) { - comma = split(arg); - if (strstr(arg, "deviceid:")) { - arg += 9; - if (strlen(arg) != 16) - ret_err(gen_err); - char *p; - for (p = arg; *p; p++) { - if (!isxdigit((int)*p)) - ret_err(gen_err); - } - set_option_bool(OPT_UMBRELLA_DEVID); - - u8 *u = daemon->umbrella_device; - char word[3]; - u8 i; - for (i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) { - memcpy(word, &(arg[0]), 2); - *u++ = strtoul(word, NULL, 16); - } - } - else if (strstr(arg, "orgid:")) { - if (!strtoul_check(arg+6, &daemon->umbrella_org)) { - ret_err(gen_err); - } - } - else if (strstr(arg, "assetid:")) { - if (!strtoul_check(arg+8, &daemon->umbrella_asset)) { - ret_err(gen_err); - } - } - arg = comma; - } + while (arg) + { + comma = split(arg); + if (strstr(arg, "deviceid:")) + { + char *p; + u8 *u = daemon->umbrella_device; + char word[3]; + + arg += 9; + if (strlen(arg) != 16) + ret_err(gen_err); + + for (p = arg; *p; p++) + if (!isxdigit((int)*p)) + ret_err(gen_err); + + set_option_bool(OPT_UMBRELLA_DEVID); + + for (i = 0; i < (int)sizeof(daemon->umbrella_device); i++, arg+=2) + { + memcpy(word, &(arg[0]), 2); + *u++ = strtoul(word, NULL, 16); + } + } + else if (strstr(arg, "orgid:")) + { + if (!strtoul_check(arg+6, &daemon->umbrella_org)) + ret_err(gen_err); + } + else if (strstr(arg, "assetid:")) + { + if (!strtoul_check(arg+8, &daemon->umbrella_asset)) + ret_err(gen_err); + } + else + ret_err(gen_err); + + arg = comma; + } break; - + case LOPT_ADD_MAC: /* --add-mac */ if (!arg) set_option_bool(OPT_ADD_MAC); From 7b3df66a39dfd439f19fa54a925da49f3f48f8f1 Mon Sep 17 00:00:00 2001 From: Fabrice Fontaine Date: Sun, 2 Jan 2022 22:01:31 +0100 Subject: [PATCH 18/25] src/option.c: fix build with gcc 4.8 Signed-off-by: Fabrice Fontaine Signed-off-by: DL6ER --- src/dnsmasq/pattern.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dnsmasq/pattern.c b/src/dnsmasq/pattern.c index 03e23b9a..928d2593 100644 --- a/src/dnsmasq/pattern.c +++ b/src/dnsmasq/pattern.c @@ -129,9 +129,9 @@ int is_valid_dns_name(const char *value) size_t num_bytes = 0; size_t num_labels = 0; - const char *label = NULL; + const char *c, *label = NULL; int is_label_numeric = 1; - for (const char *c = value;; c++) + for (c = value;; c++) { if (*c && *c != '-' && *c != '.' && @@ -242,11 +242,11 @@ int is_valid_dns_name_pattern(const char *value) size_t num_bytes = 0; size_t num_labels = 0; - const char *label = NULL; + const char *c, *label = NULL; int is_label_numeric = 1; size_t num_wildcards = 0; int previous_label_has_wildcard = 1; - for (const char *c = value;; c++) + for (c = value;; c++) { if (*c && *c != '*' && /* Wildcard. */ From 9deef8458b2f53e62bb9073d1ef0f40497180050 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 3 Jan 2022 23:31:15 +0000 Subject: [PATCH 19/25] Fix fail to build when NO_SCRIPT set. Signed-off-by: DL6ER --- src/dnsmasq/dnsmasq.c | 5 ----- src/dnsmasq/dnsmasq.h | 2 ++ src/dnsmasq/rfc3315.c | 4 ++-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index 6e6dc3f4..e1cb8896 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -1174,11 +1174,6 @@ int main_dnsmasq (int argc, char **argv) while (do_tftp_script_run()); # endif -# ifdef HAVE_DHCP6 - while (helper_buf_empty() && do_snoop_script_run()); -# endif - - #endif diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index de1531be..a281c990 100644 --- a/src/dnsmasq/dnsmasq.h +++ b/src/dnsmasq/dnsmasq.h @@ -1721,7 +1721,9 @@ void relay_upstream6(struct dhcp_relay *relay, ssize_t sz, struct in6_addr *peer u32 scope_id, time_t now); int relay_reply6( struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface); +# ifdef HAVE_SCRIPT int do_snoop_script_run(void); +# endif #endif /* dhcp-common.c */ diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index fb387a49..236df47b 100644 --- a/src/dnsmasq/rfc3315.c +++ b/src/dnsmasq/rfc3315.c @@ -2277,9 +2277,9 @@ int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface) return 0; } +#ifdef HAVE_SCRIPT int do_snoop_script_run(void) { -#ifdef HAVE_SCRIPT struct dhcp_relay *relay; struct snoop_record *snoop; @@ -2293,9 +2293,9 @@ int do_snoop_script_run(void) queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len); return 1; } -#endif return 0; } +#endif #endif From 40557938a645a590e7ab6657b5bc530bff49477f Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 6 Oct 2021 22:31:06 +0100 Subject: [PATCH 20/25] Fix crash after re-reading an empty resolv.conf file. If dnsmasq re-reads a resolv file, and it's empty, it will retry after a delay. In the meantime, the old servers from the resolv file have been deleted, but the servers_array doesn't get updated, leading to dangling pointers and crashes. Thanks to Brad Jorsch for finding and analysing this bug. This problem was introduced in 2.86. Signed-off-by: DL6ER --- src/dnsmasq/dnsmasq.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dnsmasq/dnsmasq.c b/src/dnsmasq/dnsmasq.c index e1cb8896..f9204930 100644 --- a/src/dnsmasq/dnsmasq.c +++ b/src/dnsmasq/dnsmasq.c @@ -1721,6 +1721,11 @@ static void poll_resolv(int force, int do_reload, time_t now) } else { + /* If we're delaying things, we don't call check_servers(), but + reload_servers() may have deleted some servers, rendering the server_array + invalid, so just rebuild that here. Once reload_servers() succeeds, + we call check_servers() above, which calls build_server_array itself. */ + build_server_array(); latest->mtime = 0; if (!warned) { From 6fd459496a5d77867ff5d410d636aacf636e4faf Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 6 Oct 2021 22:54:35 +0100 Subject: [PATCH 21/25] Use host byte-order variable for answer counting. Signed-off-by: DL6ER --- src/dnsmasq/domain-match.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index ba58610f..831ac3b9 100644 --- a/src/dnsmasq/domain-match.c +++ b/src/dnsmasq/domain-match.c @@ -395,7 +395,7 @@ int is_local_answer(time_t now, int first, char *name) size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede) { - int trunc = 0; + int trunc = 0, anscount = 0; unsigned char *p; int start; union all_addr addr; @@ -419,7 +419,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header addr.addr4 = srv->addr; if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_A, C_IN, "4", &addr)) - header->ancount = htons(ntohs(header->ancount) + 1); + anscount++; log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV6, name, (union all_addr *)&addr, NULL, 0); } @@ -434,13 +434,14 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header addr.addr6 = srv->addr; if (add_resource_record(header, limit, &trunc, sizeof(struct dns_header), &p, daemon->local_ttl, NULL, T_AAAA, C_IN, "6", &addr)) - header->ancount = htons(ntohs(header->ancount) + 1); + anscount++; log_query((flags | F_CONFIG | F_FORWARD) & ~F_IPV4, name, (union all_addr *)&addr, NULL, 0); } if (trunc) header->hb3 |= HB3_TC; - + header->ancount = htons(anscount); + return p - (unsigned char *)header; } From 84b0f7e091d8918e92231d5afa1171a91ac09d90 Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Wed, 6 Oct 2021 23:01:14 +0100 Subject: [PATCH 22/25] Fix logic in add_update_server() to make optimisation actually optimise. Signed-off-by: DL6ER --- src/dnsmasq/domain-match.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index 831ac3b9..b457f5b9 100644 --- a/src/dnsmasq/domain-match.c +++ b/src/dnsmasq/domain-match.c @@ -630,7 +630,7 @@ int add_update_server(int flags, /* See if there is a suitable candidate, and unmark only do this for forwarding servers, not address or local, to avoid delays on large numbers. */ - if (flags & SERV_IS_LOCAL) + if (!(flags & SERV_IS_LOCAL)) for (serv = daemon->servers; serv; serv = serv->next) if ((serv->flags & SERV_MARK) && hostname_isequal(alloc_domain, serv->domain)) From 83ec0e70a8ff9145c684cac8a81fdffcae2b1ffc Mon Sep 17 00:00:00 2001 From: Simon Kelley Date: Mon, 3 Jan 2022 23:32:30 +0000 Subject: [PATCH 23/25] Fix massive confusion on server reload. The 2.86 upstream server rewrite severely broke re-reading of server configuration. It would get everyting right the first time, but on re-reading /etc/resolv.conf or --servers-file or setting things with DBUS, the results were just wrong. This should put things right again. Signed-off-by: DL6ER --- src/dnsmasq/domain-match.c | 157 +++++++++++++++++++++---------------- 1 file changed, 88 insertions(+), 69 deletions(-) diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index b457f5b9..4e010920 100644 --- a/src/dnsmasq/domain-match.c +++ b/src/dnsmasq/domain-match.c @@ -542,22 +542,39 @@ static int order_qsort(const void *a, const void *b) return rc; } +/* Must be called before add_update_server() to set daemon->servers_tail */ void mark_servers(int flag) { - struct server *serv; + struct server *serv, **up; + daemon->servers_tail = NULL; + /* mark everything with argument flag */ for (serv = daemon->servers; serv; serv = serv->next) - if (serv->flags & flag) - serv->flags |= SERV_MARK; - else - serv->flags &= ~SERV_MARK; + { + if (serv->flags & flag) + serv->flags |= SERV_MARK; + else + serv->flags &= ~SERV_MARK; - for (serv = daemon->local_domains; serv; serv = serv->next) - if (serv->flags & flag) - serv->flags |= SERV_MARK; - else - serv->flags &= ~SERV_MARK; + daemon->servers_tail = serv; + } + + /* --address etc is different: since they are expected to be + 1) numerous and 2) not reloaded often. We just delete + and recreate. */ + if (flag) + for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = serv->next) + { + if (serv->flags & flag) + { + *up = serv->next; + free(serv->domain); + free(serv); + } + else + up = &serv->next; + } } void cleanup_servers(void) @@ -565,7 +582,7 @@ void cleanup_servers(void) struct server *serv, *tmp, **up; /* unlink and free anything still marked. */ - for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) + for (serv = daemon->servers, up = &daemon->servers, daemon->servers_tail = NULL; serv; serv = tmp) { tmp = serv->next; if (serv->flags & SERV_MARK) @@ -581,19 +598,6 @@ void cleanup_servers(void) daemon->servers_tail = serv; } } - - for (serv = daemon->local_domains, up = &daemon->local_domains; serv; serv = tmp) - { - tmp = serv->next; - if (serv->flags & SERV_MARK) - { - *up = serv->next; - free(serv->domain); - free(serv); - } - else - up = &serv->next; - } } int add_update_server(int flags, @@ -626,36 +630,17 @@ int add_update_server(int flags, if (!alloc_domain) return 0; - - /* See if there is a suitable candidate, and unmark - only do this for forwarding servers, not - address or local, to avoid delays on large numbers. */ - if (!(flags & SERV_IS_LOCAL)) - for (serv = daemon->servers; serv; serv = serv->next) - if ((serv->flags & SERV_MARK) && - hostname_isequal(alloc_domain, serv->domain)) - break; - - if (serv) - { - free(alloc_domain); - alloc_domain = serv->domain; - } - else + + if (flags & SERV_IS_LOCAL) { size_t size; - - if (flags & SERV_IS_LOCAL) - { - if (flags & SERV_6ADDR) - size = sizeof(struct serv_addr6); - else if (flags & SERV_4ADDR) - size = sizeof(struct serv_addr4); - else - size = sizeof(struct serv_local); - } + + if (flags & SERV_6ADDR) + size = sizeof(struct serv_addr6); + else if (flags & SERV_4ADDR) + size = sizeof(struct serv_addr4); else - size = sizeof(struct server); + size = sizeof(struct serv_local); if (!(serv = whine_malloc(size))) { @@ -663,19 +648,53 @@ int add_update_server(int flags, return 0; } - if (flags & SERV_IS_LOCAL) + serv->next = daemon->local_domains; + daemon->local_domains = serv; + + if (flags & SERV_4ADDR) + ((struct serv_addr4*)serv)->addr = local_addr->addr4; + + if (flags & SERV_6ADDR) + ((struct serv_addr6*)serv)->addr = local_addr->addr6; + } + else + { + /* Upstream servers. See if there is a suitable candidate, if so unmark + and move to the end of the list, for order. The entry found may already + be at the end. */ + struct server **up, *tmp; + + for (serv = daemon->servers, up = &daemon->servers; serv; serv = tmp) { - serv->next = daemon->local_domains; - daemon->local_domains = serv; + tmp = serv->next; + if ((serv->flags & SERV_MARK) && + hostname_isequal(alloc_domain, serv->domain)) + { + /* Need to move down? */ + if (serv->next) + { + *up = serv->next; + daemon->servers_tail->next = serv; + daemon->servers_tail = serv; + serv->next = NULL; + } + break; + } + } - if (flags & SERV_4ADDR) - ((struct serv_addr4*)serv)->addr = local_addr->addr4; - - if (flags & SERV_6ADDR) - ((struct serv_addr6*)serv)->addr = local_addr->addr6; + if (serv) + { + free(alloc_domain); + alloc_domain = serv->domain; } else { + if (!(serv = whine_malloc(sizeof(struct server)))) + { + free(alloc_domain); + return 0; + } + memset(serv, 0, sizeof(struct server)); /* Add to the end of the chain, for order */ @@ -684,20 +703,20 @@ int add_update_server(int flags, else daemon->servers = serv; daemon->servers_tail = serv; - + } + #ifdef HAVE_LOOP - serv->uid = rand32(); + serv->uid = rand32(); #endif - if (interface) - safe_strncpy(serv->interface, interface, sizeof(serv->interface)); - if (addr) - serv->addr = *addr; - if (source_addr) - serv->source_addr = *source_addr; - } + if (interface) + safe_strncpy(serv->interface, interface, sizeof(serv->interface)); + if (addr) + serv->addr = *addr; + if (source_addr) + serv->source_addr = *source_addr; } - + serv->flags = flags; serv->domain = alloc_domain; serv->domain_len = strlen(alloc_domain); From 7cb11e2aa2b233c73dc95b0ed8d7ab1c3963300f Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 4 Jan 2022 20:46:12 +0100 Subject: [PATCH 24/25] Update dnsmasq version Signed-off-by: DL6ER --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b182169..ce585519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,6 @@ cmake_minimum_required(VERSION 2.8.12) project(PIHOLE_FTL C) -set(DNSMASQ_VERSION pi-hole-2.87test4-6) +set(DNSMASQ_VERSION pi-hole-2.87test4-18) add_subdirectory(src) From 6a6e2fffd57cd2014bf55cd60704c56f8d9be5de Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 4 Jan 2022 20:59:27 +0100 Subject: [PATCH 25/25] Minor tweaks Signed-off-by: DL6ER --- src/dnsmasq/forward.c | 8 +++++++- src/dnsmasq/network.c | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/dnsmasq/forward.c b/src/dnsmasq/forward.c index 22d11fbb..e1061ecd 100644 --- a/src/dnsmasq/forward.c +++ b/src/dnsmasq/forward.c @@ -1666,6 +1666,9 @@ void receive_query(struct listener *listen, time_t now) if (m >= 1) { +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr); +#endif send_from(listen->fd, option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND), (char *)header, m, &source_addr, &dst_addr, if_index); daemon->metrics[METRIC_DNS_LOCAL_ANSWERED]++; @@ -1679,6 +1682,9 @@ void receive_query(struct listener *listen, time_t now) local_auth, do_bit, have_pseudoheader); if (m >= 1) { +#ifdef HAVE_DUMPFILE + dump_packet(DUMP_REPLY, daemon->packet, m, NULL, &source_addr); +#endif #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) if (local_auth) if (option_bool(OPT_CMARK_ALST_EN) && have_mark && ((u32)mark & daemon->allowlist_mask)) @@ -1978,7 +1984,7 @@ unsigned char *tcp_request(int confd, time_t now, bool piholeblocked = false; /**********************************************/ - if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) + if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1) return packet; #ifdef HAVE_CONNTRACK diff --git a/src/dnsmasq/network.c b/src/dnsmasq/network.c index 1572ae99..5e5c3b91 100644 --- a/src/dnsmasq/network.c +++ b/src/dnsmasq/network.c @@ -492,11 +492,11 @@ static int iface_allowed(struct iface_param *param, int if_index, char *label, } if (addr->sa.sa_family == AF_INET && - !iface_check(AF_INET, (union all_addr *)&addr->in.sin_addr, ifr.ifr_name, &auth_dns)) + !iface_check(AF_INET, (union all_addr *)&addr->in.sin_addr, label, &auth_dns)) return 1; if (addr->sa.sa_family == AF_INET6 && - !iface_check(AF_INET6, (union all_addr *)&addr->in6.sin6_addr, ifr.ifr_name, &auth_dns)) + !iface_check(AF_INET6, (union all_addr *)&addr->in6.sin6_addr, label, &auth_dns)) return 1; #ifdef HAVE_DHCP