Commit Diff


commit - 05a099a8f45a2d2132479e114f734065e2e12f45
commit + e9c398966c3cf9b821ba8aa353db97274b181522
blob - 05cf30a0f5855a076be4af9a203d7805172f6cbd
blob + e26dec5893d3a98a88df8c26ac2018f6b0ad3e9e
--- parse.y
+++ parse.y
@@ -1,4 +1,4 @@
-/*	$OpenBSD: parse.y,v 1.263 2026/05/15 13:57:24 rsadowski Exp $	*/
+/*	$OpenBSD: parse.y,v 1.264 2026/05/17 09:11:01 kirill Exp $	*/
 
 /*
  * Copyright (c) 2007 - 2014 Reyk Floeter <reyk@openbsd.org>
@@ -131,8 +131,10 @@ int		 host_dns(const char *, struct addresslist *,
 		    int, struct portrange *, const char *, int);
 int		 host_if(const char *, struct addresslist *,
 		    int, struct portrange *, const char *, int);
+int		 host_ifaddr(struct sockaddr_storage *, struct ifaddrs *);
 int		 host(const char *, struct addresslist *,
 		    int, struct portrange *, const char *, int);
+int		 host_local(struct addresslist *);
 void		 host_free(struct addresslist *);
 
 struct table	*table_inherit(struct table *);
@@ -1995,35 +1997,60 @@ relayopts_l	: relayopts_l relayoptsl nl
 relayoptsl	: LISTEN ON STRING port opttls {
 			struct addresslist	 al;
 			struct address		*h;
-			struct relay		*r;
+			struct relay		*nr, *r;
+			int			 cnt = 0;
 
 			if (rlay->rl_conf.ss.ss_family != AF_UNSPEC) {
-				if ((r = calloc(1, sizeof (*r))) == NULL)
+				if ((r = calloc(1, sizeof(*r))) == NULL)
 					fatal("out of memory");
-				TAILQ_INSERT_TAIL(&relays, r, rl_entry);
 			} else
 				r = rlay;
 			if ($4.op != PF_OP_EQ) {
 				yyerror("invalid port");
 				free($3);
+				if (r != rlay)
+					free(r);
 				YYERROR;
 			}
 
 			TAILQ_INIT(&al);
-			if (host($3, &al, 1, &$4, NULL, -1) <= 0) {
+			if (host($3, &al, SRV_MAX_VIRTS, &$4, NULL, -1) <= 0) {
 				yyerror("invalid listen ip: %s", $3);
 				free($3);
+				if (r != rlay)
+					free(r);
 				YYERROR;
 			}
+			if (host_local(&al) == 0) {
+				yyerror("no local listen ip: %s", $3);
+				free($3);
+				host_free(&al);
+				if (r != rlay)
+					free(r);
+				YYERROR;
+			}
 			free($3);
-			h = TAILQ_FIRST(&al);
-			bcopy(&h->ss, &r->rl_conf.ss, sizeof(r->rl_conf.ss));
-			r->rl_conf.port = h->port.val[0];
-			if ($5) {
-				r->rl_conf.flags |= F_TLS;
+			TAILQ_FOREACH(h, &al, entry) {
+				if (cnt == 0) {
+					nr = r;
+					if (nr != rlay)
+						TAILQ_INSERT_TAIL(&relays, nr,
+						    rl_entry);
+				} else {
+					if ((nr = calloc(1, sizeof(*nr))) == NULL)
+						fatal("out of memory");
+					TAILQ_INSERT_TAIL(&relays, nr, rl_entry);
+				}
+				bcopy(&h->ss, &nr->rl_conf.ss,
+				    sizeof(nr->rl_conf.ss));
+				nr->rl_conf.port = h->port.val[0];
+				if ($5)
+					nr->rl_conf.flags |= F_TLS;
+				cnt++;
+			}
+			if ($5)
 				conf->sc_conf.flags |= F_TLS;
-			}
-			tableport = h->port.val[0];
+			tableport = $4.val[0];
 			host_free(&al);
 		}
 		| forwardmode opttlsclient TO forwardspec dstaf optproxyproto {
@@ -3378,6 +3405,64 @@ host(const char *s, struct addresslist *al, int max,
 	return (1);
 }
 
+int
+host_ifaddr(struct sockaddr_storage *ss, struct ifaddrs *ifap)
+{
+	struct ifaddrs		*p;
+	struct sockaddr_in	*sin;
+	struct sockaddr_in6	*sin6;
+
+	switch (ss->ss_family) {
+	case AF_INET:
+		sin = (struct sockaddr_in *)ss;
+		if (sin->sin_addr.s_addr == INADDR_ANY)
+			return (1);
+		break;
+	case AF_INET6:
+		sin6 = (struct sockaddr_in6 *)ss;
+		if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr))
+			return (1);
+		break;
+	default:
+		return (0);
+	}
+
+	for (p = ifap; p != NULL; p = p->ifa_next) {
+		if (p->ifa_addr == NULL ||
+		    p->ifa_addr->sa_family != ss->ss_family ||
+		    sockaddr_cmp((struct sockaddr *)ss, p->ifa_addr, -1) != 0)
+			continue;
+		bzero(ss, sizeof(*ss));
+		memcpy(ss, p->ifa_addr, p->ifa_addr->sa_len);
+		return (1);
+	}
+
+	return (0);
+}
+
+int
+host_local(struct addresslist *al)
+{
+	struct ifaddrs		*ifap;
+	struct address		*h, *next;
+	int			 cnt = 0;
+
+	if (getifaddrs(&ifap) == -1)
+		fatal("getifaddrs");
+
+	TAILQ_FOREACH_SAFE(h, al, entry, next) {
+		if (host_ifaddr(&h->ss, ifap)) {
+			cnt++;
+			continue;
+		}
+		TAILQ_REMOVE(al, h, entry);
+		free(h);
+	}
+
+	freeifaddrs(ifap);
+	return (cnt);
+}
+
 void
 host_free(struct addresslist *al)
 {
blob - f51bf540fa6ed755484a9b290e57de2668fb7337
blob + cd370c15d2655405557ff5c0a3b722de6bc31832
--- relayd.conf.5
+++ relayd.conf.5
@@ -1,4 +1,4 @@
-.\"	$OpenBSD: relayd.conf.5,v 1.216 2026/05/15 13:57:24 rsadowski Exp $
+.\"	$OpenBSD: relayd.conf.5,v 1.217 2026/05/17 09:11:01 kirill Exp $
 .\"
 .\" Copyright (c) 2006 - 2016 Reyk Floeter <reyk@openbsd.org>
 .\" Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: May 15 2026 $
+.Dd $Mdocdate: May 17 2026 $
 .Dt RELAYD.CONF 5
 .Os
 .Sh NAME
@@ -727,7 +727,19 @@ Like the previous directive, but for redirections with
 .Op Ic tls
 .Xc
 Specify the address and port for the relay to listen on.
-The relay will accept incoming connections to the specified address.
+The relay will accept incoming connections to the specified address or
+addresses.
+If
+.Ar address
+resolves to multiple IPv4 or IPv6 addresses, such as an interface
+name, interface group, or DNS hostname,
+.Xr relayd 8
+will create a listener for each local address.
+For DNS hostnames, all resolved IPv4 and IPv6 addresses are considered,
+but only addresses configured on a local interface are used.
+Addresses that are not configured on a local interface are ignored.
+If none of the resolved addresses are local, the configuration is
+invalid.
 If the
 .Ic tls
 keyword is present, the relay will accept connections using the