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) diff --git a/src/dnsmasq/cache.c b/src/dnsmasq/cache.c index a6a8b3c2..449c884f 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)) 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/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..f9204930 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 @@ -1713,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) { diff --git a/src/dnsmasq/dnsmasq.h b/src/dnsmasq/dnsmasq.h index 504e9444..a281c990 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,10 @@ 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); +# ifdef HAVE_SCRIPT +int do_snoop_script_run(void); +# endif #endif /* dhcp-common.c */ diff --git a/src/dnsmasq/domain-match.c b/src/dnsmasq/domain-match.c index ba58610f..4e010920 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; } @@ -541,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) @@ -564,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) @@ -580,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, @@ -625,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 { 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))) { @@ -662,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 */ @@ -683,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); 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/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/helper.c b/src/dnsmasq/helper.c index 8821b975..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"); @@ -441,8 +464,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"); } @@ -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); @@ -624,7 +647,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); } @@ -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/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 diff --git a/src/dnsmasq/option.c b/src/dnsmasq/option.c index d164b996..b0611a52 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 } }; @@ -2523,39 +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); - for (char *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]; - for (u8 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); @@ -4132,7 +4141,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 +4165,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); @@ -4285,26 +4297,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/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. */ diff --git a/src/dnsmasq/rfc3315.c b/src/dnsmasq/rfc3315.c index 5c2ff97b..236df47b 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 */ @@ -2188,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; @@ -2220,11 +2226,76 @@ 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; } +#ifdef HAVE_SCRIPT +int do_snoop_script_run(void) +{ + 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; + } + + return 0; +} +#endif + #endif 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. */