diff --git a/dns/dnsmasq/Makefile b/dns/dnsmasq/Makefile index 6beea43b327b..fe4d5d7a73fb 100644 --- a/dns/dnsmasq/Makefile +++ b/dns/dnsmasq/Makefile @@ -1,144 +1,145 @@ # Created by: Steven Honson PORTNAME= dnsmasq DISTVERSION= 2.85 # Leave the PORTREVISION in even if 0 to avoid accidental PORTEPOCH bumps: -PORTREVISION= 0 +PORTREVISION= 1 PORTEPOCH= 1 CATEGORIES= dns MASTER_SITES= https://www.thekelleys.org.uk/dnsmasq/ \ LOCAL/mandree/ +PATCH_STRIP= -p1 MAINTAINER= mandree@FreeBSD.org COMMENT= Lightweight DNS forwarder, DHCP, and TFTP server LICENSE= GPLv2 USES= cpe shebangfix tar:xz CPE_VENDOR= thekelleys SHEBANG_FILES= contrib/dnslist/dnslist.pl \ contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl MAKE_ARGS= CC="${CC}" \ CFLAGS="${CFLAGS}" \ COPTS="${CFLAGS}" \ LIBS="${LDFLAGS}" \ PREFIX="${PREFIX}" \ RPM_OPT_FLAGS="${CPPFLAGS}" CFLAGS+= -Wall -Wno-unused-function -Wno-unused-parameter \ -Wno-unused-value -Wno-unused-variable CPPFLAGS+= -I${LOCALBASE}/include CONFLICTS_INSTALL= dnsmasq-devel-* PATCH_STRIP= -p1 SUB_FILES= pkg-message PORTDOCS= CHANGELOG CHANGELOG.archive FAQ doc.html setup.html OPTIONS_DEFINE= DBUS DNSSEC DOCS IPSET IPV6 LUA OPTIONS_DEFAULT= DNSSEC IPSET OPTIONS_RADIO= INTL OPTIONS_RADIO_INTL= IDN NLS OPTIONS_EXCLUDE+= EXAMPLES DNSSEC_DESC= Enable DNSSEC caching and validation (needs nettle) IDN_DESC= IDN: Int'l Domain Names WITHOUT full NLS INTL_DESC= Internationalization Support Level IPSET_DESC= Dynamic firewall management of resolved names (needs PF) LUA_DESC= Support lease-change scripts written in Lua NLS_DESC= IDN+NLS: Int'l Domain Names & National Language support IPSET_CFLAGS_OFF= -DNO_IPSET IPV6_CFLAGS_OFF= -DNO_IPV6 .include .if ${PORT_OPTIONS:MNLS} USES+= gettext gmake iconv pkgconfig CFLAGS+= -DHAVE_LIBIDN2 LIB_DEPENDS+= libidn2.so:dns/libidn2 PLIST_SUB+= NLS="" ALL_TARGET= all-i18n _intllibs= -lidn2 -lintl .else _intllibs= PLIST_SUB+= NLS="@comment " .if ${PORT_OPTIONS:MIDN} USES+= iconv CFLAGS+= -DHAVE_LIBIDN2 LIB_DEPENDS+= libidn2.so:dns/libidn2 _intllibs+= -lidn2 .endif .endif .if ${PORT_OPTIONS:MDBUS} LIB_DEPENDS+= libdbus-1.so:devel/dbus USES+= pkgconfig CPPFLAGS+= `pkg-config --cflags dbus-1` CFLAGS+= -DHAVE_DBUS LDFLAGS+= `pkg-config --libs dbus-1` .endif .if ${PORT_OPTIONS:MLUA} CPPFLAGS+= -I${LUA_INCDIR} CFLAGS+= -DHAVE_LUASCRIPT LDFLAGS+= -L${LUA_LIBDIR} -llua-${LUA_VER} USES+= lua pkgconfig .endif .if ${PORT_OPTIONS:MDNSSEC} CFLAGS+= -DHAVE_DNSSEC -I${LOCALBASE}/include USES+= pkgconfig LIB_DEPENDS+= libgmp.so:math/gmp \ libnettle.so:security/nettle .endif USE_RC_SUBR= dnsmasq .include LDFLAGS+= -L${LOCALBASE}/lib ${_intllibs} ${ICONV_LIB} post-patch: ${REINPLACE_CMD} -e '/^lua_/s/lua5\.2/lua-${LUA_VER}/' ${WRKSRC}/Makefile pre-configure: pretty-print-config .if ${PORT_OPTIONS:MIDN} .if empty(PORT_OPTIONS:MNLS) @if ${READELF} -d ${LOCALBASE}/lib/libidn2.so \ | ${EGREP} -q '\.*\[libintl\.so' ; \ then ${ECHO} ; ${ECHO} 'WARNING: dns/libidn2 was compiled with NLS support!' ; \ ${ECHO} 'Recompile libidn2 WITHOUT_NLS to get rid of NLS dependencies.' ; ${ECHO} ; \ fi .else @${ECHO} 'WARNING: IDN and NLS enabled, building IDN WITH NLS.' .endif .endif do-install: ${INSTALL_PROGRAM} ${WRKSRC}/src/dnsmasq ${STAGEDIR}${PREFIX}/sbin ${INSTALL_DATA} ${WRKSRC}/dnsmasq.conf.example ${STAGEDIR}${PREFIX}/etc/dnsmasq.conf.sample ${REINPLACE_CMD} -i '' 's}%%PREFIX%%}${PREFIX}}' ${STAGEDIR}${PREFIX}/etc/dnsmasq.conf.sample ${INSTALL_MAN} ${WRKSRC}/man/${PORTNAME}.8 ${STAGEDIR}${PREFIX}/man/man8 ${MKDIR} ${STAGEDIR}${DATADIR} ${INSTALL_DATA} ${WRKSRC}/trust-anchors.conf ${STAGEDIR}${DATADIR}/ .if ${PORT_OPTIONS:MDOCS} @${MKDIR} ${STAGEDIR}${DOCSDIR} cd ${WRKSRC} && ${INSTALL_DATA} ${PORTDOCS} ${STAGEDIR}${DOCSDIR} .endif .if ${PORT_OPTIONS:MNLS} .for i in de es fi fr id it no pl pt_BR ro ${MKDIR} ${STAGEDIR}${PREFIX}/share/locale/${i}/LC_MESSAGES ${INSTALL_DATA} ${WRKSRC}/src/${i}.mo \ ${STAGEDIR}${PREFIX}/share/locale/${i}/LC_MESSAGES/${PORTNAME}.mo .endfor .endif ${MKDIR} ${STAGEDIR}${EXAMPLESDIR}/dynamic-dnsmasq ${STAGEDIR}${EXAMPLESDIR}/dnslist ${INSTALL_SCRIPT} ${WRKSRC}/contrib/dynamic-dnsmasq/dynamic-dnsmasq.pl ${STAGEDIR}${EXAMPLESDIR}/dynamic-dnsmasq/ ${INSTALL_SCRIPT} ${WRKSRC}/contrib/dnslist/dnslist.pl ${STAGEDIR}${EXAMPLESDIR}/dnslist/ ${INSTALL_DATA} ${WRKSRC}/contrib/dnslist/dhcp.css ${STAGEDIR}${EXAMPLESDIR}/dnslist/ ${INSTALL_DATA} ${WRKSRC}/contrib/dnslist/dnslist.tt2 ${STAGEDIR}${EXAMPLESDIR}/dnslist/ .include diff --git a/dns/dnsmasq/files/patch-ad90eb075 b/dns/dnsmasq/files/patch-ad90eb075 new file mode 100644 index 000000000000..72ae3b74dd58 --- /dev/null +++ b/dns/dnsmasq/files/patch-ad90eb075 @@ -0,0 +1,135 @@ +commit ad90eb075dfeeb1936e8bc0f323fcc23f89364d4 +Author: Simon Kelley +Date: Fri Apr 9 16:08:05 2021 +0100 + + Fix bug in TCP process handling. + + Fix bug which caused dnsmasq to lose track of processes forked + to handle TCP DNS connections under heavy load. The code + checked that at least one free process table slot was + available before listening on TCP sockets, but didn't take + into account that more than one TCP connection could + arrive, so that check was not sufficient to ensure that + there would be slots for all new processes. It compounded + this error by silently failing to store the process when + it did run out of slots. Even when this bug is triggered, + all the right things happen, and answers are still returned. + Only under very exceptional circumstances, does the bug + manifest itself: see + https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2021q2/014976.html + + Thanks to Tijs Van Buggenhout for finding the conditions under + which the bug manifests itself, and then working out + exactly what was going on. + +diff --git a/CHANGELOG b/CHANGELOG +index ab4aa42..87adaf7 100644 +--- a/CHANGELOG ++++ b/CHANGELOG +@@ -3,6 +3,24 @@ version 2.86 + Thanks to Aichun Li for spotting this ommision, and the initial + patch. + ++ Fix bug which caused dnsmasq to lose track of processes forked ++ to handle TCP DNS connections under heavy load. The code ++ checked that at least one free process table slot was ++ available before listening on TCP sockets, but didn't take ++ into account that more than one TCP connection could ++ arrive, so that check was not sufficient to ensure that ++ there would be slots for all new processes. It compounded ++ this error by silently failing to store the process when ++ it did run out of slots. Even when this bug is triggered, ++ all the right things happen, and answers are still returned. ++ Only under very exceptional circumstances, does the bug ++ manifest itself: see ++ https://lists.thekelleys.org.uk/pipermail/dnsmasq-discuss/2021q2/014976.html ++ Thanks to Tijs Van Buggenhout for finding the conditions under ++ which the bug manifests itself, and then working out ++ exactly what was going on. ++ ++ + + version 2.85 + Fix problem with DNS retries in 2.83/2.84. +diff --git a/src/dnsmasq.c b/src/dnsmasq.c +index 256d2bc..bf031a1 100644 +--- a/src/dnsmasq.c ++++ b/src/dnsmasq.c +@@ -1716,22 +1716,24 @@ static int set_dns_listeners(time_t now) + for (rfl = daemon->rfl_poll; rfl; rfl = rfl->next) + poll_listen(rfl->rfd->fd, POLLIN); + ++ /* check to see if we have free tcp process slots. */ ++ for (i = MAX_PROCS - 1; i >= 0; i--) ++ if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) ++ break; ++ + for (listener = daemon->listeners; listener; listener = listener->next) + { + /* only listen for queries if we have resources */ + if (listener->fd != -1 && wait == 0) + poll_listen(listener->fd, POLLIN); + +- /* death of a child goes through the select loop, so +- we don't need to explicitly arrange to wake up here */ +- if (listener->tcpfd != -1) +- for (i = 0; i < MAX_PROCS; i++) +- if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) +- { +- poll_listen(listener->tcpfd, POLLIN); +- break; +- } +- ++ /* Only listen for TCP connections when a process slot ++ is available. Death of a child goes through the select loop, so ++ we don't need to explicitly arrange to wake up here, ++ we'll be called again when a slot becomes available. */ ++ if (listener->tcpfd != -1 && i >= 0) ++ poll_listen(listener->tcpfd, POLLIN); ++ + #ifdef HAVE_TFTP + /* tftp == 0 in single-port mode. */ + if (tftp <= daemon->tftp_max && listener->tftpfd != -1) +@@ -1797,7 +1799,16 @@ static void check_dns_listeners(time_t now) + tftp_request(listener, now); + #endif + +- if (listener->tcpfd != -1 && poll_check(listener->tcpfd, POLLIN)) ++ /* check to see if we have a free tcp process slot. ++ Note that we can't assume that because we had ++ at least one a poll() time, that we still do. ++ There may be more waiting connections after ++ poll() returns then free process slots. */ ++ for (i = MAX_PROCS - 1; i >= 0; i--) ++ if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) ++ break; ++ ++ if (listener->tcpfd != -1 && i >= 0 && poll_check(listener->tcpfd, POLLIN)) + { + int confd, client_ok = 1; + struct irec *iface = NULL; +@@ -1887,7 +1898,6 @@ static void check_dns_listeners(time_t now) + close(pipefd[0]); + else + { +- int i; + #ifdef HAVE_LINUX_NETWORK + /* The child process inherits the netlink socket, + which it never uses, but when the parent (us) +@@ -1907,13 +1917,9 @@ static void check_dns_listeners(time_t now) + read_write(pipefd[0], &a, 1, 1); + #endif + +- for (i = 0; i < MAX_PROCS; i++) +- if (daemon->tcp_pids[i] == 0 && daemon->tcp_pipes[i] == -1) +- { +- daemon->tcp_pids[i] = p; +- daemon->tcp_pipes[i] = pipefd[0]; +- break; +- } ++ /* i holds index of free slot */ ++ daemon->tcp_pids[i] = p; ++ daemon->tcp_pipes[i] = pipefd[0]; + } + close(confd); + diff --git a/dns/dnsmasq/files/patch-d55e2d08 b/dns/dnsmasq/files/patch-d55e2d08 new file mode 100644 index 000000000000..137dd0cbe8e9 --- /dev/null +++ b/dns/dnsmasq/files/patch-d55e2d08 @@ -0,0 +1,123 @@ +commit d55e2d086d1ff30c427fa5e0ecc79746de8a81b7 +Author: Simon Kelley +Date: Fri Apr 9 15:19:28 2021 +0100 + + Handle DHCPREBIND requests in the DHCPv6 server. + + Patch by srk, based on submitted patch from liaichun@huawei.com + +diff --git a/CHANGELOG b/CHANGELOG +index ca555ed..ab4aa42 100644 +--- a/CHANGELOG ++++ b/CHANGELOG +@@ -1,3 +1,9 @@ ++version 2.86 ++ Handle DHCPREBIND requests in the DHCPv6 server code. ++ Thanks to Aichun Li for spotting this ommision, and the initial ++ patch. ++ ++ + version 2.85 + Fix problem with DNS retries in 2.83/2.84. + The new logic in 2.83/2.84 which merges distinct requests +diff --git a/src/rfc3315.c b/src/rfc3315.c +index 982c68a..5c2ff97 100644 +--- a/src/rfc3315.c ++++ b/src/rfc3315.c +@@ -919,11 +919,14 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + + + case DHCP6RENEW: ++ case DHCP6REBIND: + { ++ int address_assigned = 0; ++ + /* set reply message type */ + *outmsgtypep = DHCP6REPLY; + +- log6_quiet(state, "DHCPRENEW", NULL, NULL); ++ log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL); + + for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end)) + { +@@ -952,24 +955,35 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, + state->iaid, &req_addr))) + { +- /* If the server cannot find a client entry for the IA the server +- returns the IA containing no addresses with a Status Code option set +- to NoBinding in the Reply message. */ +- save_counter(iacntr); +- t1cntr = 0; +- +- log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found")); +- +- o1 = new_opt6(OPTION6_STATUS_CODE); +- put_opt6_short(DHCP6NOBINDING); +- put_opt6_string(_("no binding found")); +- end_opt6(o1); +- +- preferred_time = valid_time = 0; +- break; ++ if (msg_type == DHCP6REBIND) ++ { ++ /* When rebinding, we can create a lease if it doesn't exist. */ ++ lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA); ++ if (lease) ++ lease_set_iaid(lease, state->iaid); ++ else ++ break; ++ } ++ else ++ { ++ /* If the server cannot find a client entry for the IA the server ++ returns the IA containing no addresses with a Status Code option set ++ to NoBinding in the Reply message. */ ++ save_counter(iacntr); ++ t1cntr = 0; ++ ++ log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found")); ++ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOBINDING); ++ put_opt6_string(_("no binding found")); ++ end_opt6(o1); ++ ++ preferred_time = valid_time = 0; ++ break; ++ } + } + +- + if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) || + (this_context = address6_valid(state->context, &req_addr, tagif, 1))) + { +@@ -1000,6 +1014,8 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + + if (preferred_time == 0) + message = _("deprecated"); ++ ++ address_assigned = 1; + } + else + { +@@ -1022,10 +1038,18 @@ static int dhcp6_no_relay(struct state *state, int msg_type, void *inbuff, size_ + end_ia(t1cntr, min_time, 1); + end_opt6(o); + } ++ ++ if (!address_assigned && msg_type == DHCP6REBIND) ++ { ++ /* can't create lease for any address, return error */ ++ o1 = new_opt6(OPTION6_STATUS_CODE); ++ put_opt6_short(DHCP6NOADDRS); ++ put_opt6_string(_("no addresses available")); ++ end_opt6(o1); ++ } + + tagif = add_options(state, 0); + break; +- + } + + case DHCP6CONFIRM: