Fix wildcard bind to at least bind IPv4 or else IPv6 on OpenBSD
OpenBSD refuses to implement IPV6_V6ONLY, in violation of RFC 3493. RFC 4038 frowningly recognizes this practice. The only way to bind both IPv4 and IPv4 there is two separate sockets. Requires more surgery than I can do now. Since we can't have both IPv6 and IPv6 on OpenBSD with our single socket, prefer IPv4, but if that doesn't work, do IPv6. To prefer IPv6 instead, put 'listen_addr "::"' into econfig. Document that in listen_addr's doc string.
This commit is contained in:
parent
36578f463e
commit
da154ffd06
2 changed files with 34 additions and 3 deletions
|
@ -84,7 +84,11 @@ EMPCF_COMMENT("# Set this to your source tree's src/lib/global to run the server
|
||||||
"# without installing it, else leave it alone.")
|
"# without installing it, else leave it alone.")
|
||||||
EMPCFBOTH("listen_addr", listen_addr, char *, NSC_STRING, KM_INTERNAL,
|
EMPCFBOTH("listen_addr", listen_addr, char *, NSC_STRING, KM_INTERNAL,
|
||||||
"Local IP address the server should listen on")
|
"Local IP address the server should listen on")
|
||||||
EMPCF_COMMENT("# \"\" listens on all, localhost just on the loopback interface.")
|
EMPCF_COMMENT("# \"\" listens on all, localhost just on the loopback interface.\n"
|
||||||
|
"# OpenBSD restriction: when the system has both IPv4 and IPv6\n"
|
||||||
|
"# addresses configured, \"\" listens on all IPv4 addresses, and \"::\"\n"
|
||||||
|
"# on all IPv6 addresses. There is no way to listen both on all IPv4\n"
|
||||||
|
"# and on all IPv6 interfaces.")
|
||||||
EMPCFBOTH("port", loginport, char *, NSC_STRING, KM_INTERNAL,
|
EMPCFBOTH("port", loginport, char *, NSC_STRING, KM_INTERNAL,
|
||||||
"TCP port the server will bind")
|
"TCP port the server will bind")
|
||||||
EMPCFBOTH("keep_journal", keep_journal, int, NSC_INT, KM_INTERNAL,
|
EMPCFBOTH("keep_journal", keep_journal, int, NSC_INT, KM_INTERNAL,
|
||||||
|
|
|
@ -66,6 +66,8 @@ tcp_listen(char *host, char *serv, size_t *addrlenp)
|
||||||
*/
|
*/
|
||||||
int err;
|
int err;
|
||||||
struct addrinfo hints, *first_ai, *ai;
|
struct addrinfo hints, *first_ai, *ai;
|
||||||
|
/* Crap necessary for OpenBSD, see below */
|
||||||
|
int try_v6only_off = 1, v6only_stuck = 0;
|
||||||
|
|
||||||
memset(&hints, 0, sizeof(struct addrinfo));
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
|
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
|
||||||
|
@ -76,17 +78,31 @@ tcp_listen(char *host, char *serv, size_t *addrlenp)
|
||||||
cant_listen(host, serv, gai_strerror(err));
|
cant_listen(host, serv, gai_strerror(err));
|
||||||
assert(first_ai);
|
assert(first_ai);
|
||||||
|
|
||||||
|
again:
|
||||||
for (ai = first_ai; ai; ai = ai->ai_next) {
|
for (ai = first_ai; ai; ai = ai->ai_next) {
|
||||||
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||||
if (fd < 0)
|
if (fd < 0)
|
||||||
continue; /* error, try next one */
|
continue; /* error, try next one */
|
||||||
|
|
||||||
#ifdef IPV6_V6ONLY
|
#ifdef IPV6_V6ONLY
|
||||||
if (ai->ai_family == AF_INET6) {
|
if (ai->ai_family == AF_INET6 && try_v6only_off) {
|
||||||
int off = 0;
|
int off = 0;
|
||||||
|
|
||||||
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off));
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY,
|
||||||
|
&off, sizeof(off)) < 0) {
|
||||||
|
/*
|
||||||
|
* IPV6_V6ONLY is stuck on, violating RFC 3493 (gee,
|
||||||
|
* thanks, OpenBSD!). Address is good only for IPv6,
|
||||||
|
* not for IPv4. Means we can't have both on this
|
||||||
|
* system. Continue looking for one that's good for
|
||||||
|
* IPv4.
|
||||||
|
*/
|
||||||
|
v6only_stuck = 1;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
(void)try_v6only_off;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||||
|
@ -96,6 +112,17 @@ tcp_listen(char *host, char *serv, size_t *addrlenp)
|
||||||
close(fd); /* error, close and try next one */
|
close(fd); /* error, close and try next one */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* More crap for OpenBSD */
|
||||||
|
if (ai == NULL && v6only_stuck) {
|
||||||
|
/*
|
||||||
|
* No go. But we skipped IPv6 addresses that don't work for
|
||||||
|
* IPv4, but could for IPv6. Try again without skipping
|
||||||
|
* these.
|
||||||
|
*/
|
||||||
|
try_v6only_off = 0;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
if (ai == NULL) /* errno from final socket() or bind() */
|
if (ai == NULL) /* errno from final socket() or bind() */
|
||||||
cant_listen(host, serv, strerror(errno));
|
cant_listen(host, serv, strerror(errno));
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue