diff options
author | Roland Hieber <r.hieber@pengutronix.de> | 2017-07-20 11:07:04 +0200 |
---|---|---|
committer | Roland Hieber <r.hieber@pengutronix.de> | 2017-07-25 21:26:10 +0200 |
commit | 8a103f5c91133e3443774cc4a2ba74570484ec0c (patch) | |
tree | 3a1b55bc30a3c36543bbccaf3877e74730fdf225 | |
parent | d2398162c13915d8ca28f7f1fa582638bb90c678 (diff) | |
download | urshd-8a103f5c91133e3443774cc4a2ba74570484ec0c.tar.gz urshd-8a103f5c91133e3443774cc4a2ba74570484ec0c.tar.xz |
urshd: IPv6 support with (slightly) more modern networking code
We support dual-stack usage by unsetting the IPV6_V6ONLY option, so IPv4
clients get mapped to IPv6 (IPv4-Mapped IPv6 Address, RFC 4249, like in
"Connection received from ::ffff:192.168.123.1"). This way we can use
struct sockaddr_in6 throughout the code without the need to
differentiate between sockaddr_in and sockaddr_in6 all the time.
The magic of selecting the right protocol, port, etc. is now done by
getaddrinfo instead of getservbyname. The port parameter of mainloop()
is not used anyway, remove it.
While we're at it, also improve setsockopt error handling.
I'm really not sure about the hunk starting at line 496 ("setsockopt
IP_OPTIONS NULL"). See the comment there, feedback is welcome.
Signed-off-by: Roland Hieber <r.hieber@pengutronix.de>
-rw-r--r-- | src/urshd.c | 83 |
1 files changed, 47 insertions, 36 deletions
diff --git a/src/urshd.c b/src/urshd.c index a8eb868..a0af916 100644 --- a/src/urshd.c +++ b/src/urshd.c @@ -124,7 +124,7 @@ static char *envinit[] = {homedir, shell, path, username, NULL}; extern char **environ; static void error(const char *fmt, ...); -static void doit(struct sockaddr_in *fromp); +static void doit(struct sockaddr_in6 *fromp); static void getstr(char *buf, int cnt, const char *err); static void @@ -291,7 +291,7 @@ stderr_parent(int sock, int pype, int pid) } static void -doit(struct sockaddr_in *fromp) +doit(struct sockaddr_in6 *fromp) { char cmdbuf[sysconf(_SC_ARG_MAX) + 1]; const char *theshell, *shellname; @@ -311,7 +311,7 @@ doit(struct sockaddr_in *fromp) if (port != 0) { int lport = IPPORT_RESERVED - 1; - sock = rresvport(&lport); + sock = rresvport_af(&lport, AF_INET6); if (sock < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); @@ -320,7 +320,7 @@ doit(struct sockaddr_in *fromp) syslog(LOG_ERR, "2nd port not reserved\n"); exit(1); } - fromp->sin_port = htons(port); + fromp->sin6_port = htons(port); if (connect(sock, (struct sockaddr *)fromp, sizeof(*fromp)) < 0) { syslog(LOG_INFO, "connect second port: %m"); @@ -449,7 +449,7 @@ doit(struct sockaddr_in *fromp) } static void -network_init(int fd, struct sockaddr_in *fromp) +network_init(int fd, struct sockaddr_in6 *fromp) { struct linger linger; socklen_t fromlen; @@ -474,14 +474,15 @@ network_init(int fd, struct sockaddr_in *fromp) { u_char optbuf[BUFSIZ/3], *cp; char lbuf[BUFSIZ+1], *lp; + char peeraddr[5*8+8]; // 8 groups of "xxxx:" plus some slack socklen_t optsize = sizeof(optbuf); int ipproto; struct protoent *ip; - if ((ip = getprotobyname("ip")) != NULL) + if ((ip = getprotobyname("ipv6")) != NULL) ipproto = ip->p_proto; else - ipproto = IPPROTO_IP; + ipproto = IPPROTO_IPV6; if (!getsockopt(0, ipproto, IP_OPTIONS, (char *)optbuf, &optsize) && optsize != 0) { lp = lbuf; @@ -494,29 +495,40 @@ network_init(int fd, struct sockaddr_in *fromp) for (cp = optbuf; optsize > 0; cp++, optsize--, lp += 3) snprintf(lp, 4, " %2.2x", *cp); + inet_ntop(AF_INET6, &(fromp->sin6_addr), + peeraddr, sizeof(peeraddr)); syslog(LOG_NOTICE, "Connection received from %s using IP options" " (ignored): %s", - inet_ntoa(fromp->sin_addr), lbuf); + peeraddr, lbuf); + /* These lines go back to the oldest netkit-rsh I could find. + * According to the internet, this should clear source routing + * options in the incoming packet. I don't know why this is + * necessary, but for IPv6, there is no corresponding option. + * if (setsockopt(0, ipproto, IP_OPTIONS, NULL, optsize) != 0) { syslog(LOG_ERR, "setsockopt IP_OPTIONS NULL: %m"); exit(1); } + */ } } #endif } static void -mainloop(char *port) +mainloop() { int s; - struct servent *sp; - struct sockaddr_in sn = { - .sin_family = AF_INET, - }; + struct sockaddr_in6 sn; int on = 1; + int off = 0; + struct addrinfo * ai, hints = { + .ai_family = AF_INET6, + .ai_socktype = SOCK_STREAM, + .ai_flags = AI_PASSIVE, + }; signal(SIGCHLD, SIG_IGN); @@ -524,45 +536,44 @@ mainloop(char *port) set_priority(priority); } - if (port) { - if ((sp = getservbyname(port, "tcp")) != NULL) { - sn.sin_port = sp->s_port; - } else { - int pt = atoi(port); - if (pt <= 0) { - fatal("%s: bad port number", port); - } - sn.sin_port = htons(pt); - } - } else { - sp = getservbyname("shell", "tcp"); - if (!sp) { - fatal("tcp/shell: unknown service (/etc/services or \"shell\" entry missing?)"); - } - sn.sin_port = sp->s_port; - } - if (opt_daemon) { if (daemon(0, 0) != 0) { fatal("Could not fork: %s", strerror(errno)); } } - s = socket(AF_INET, SOCK_STREAM, 0); + s = getaddrinfo(NULL, "shell", &hints, &ai); + if (s != 0) { + freeaddrinfo(ai); + fatal("getaddrinfo: %s", gai_strerror(s)); + } + + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s < 0) { + freeaddrinfo(ai); fatal("socket: %s", strerror(errno)); } - (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (bind(s, (struct sockaddr *)&sn, sizeof(sn)) < 0) { + if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) { + freeaddrinfo(ai); + syslog(LOG_WARNING, "setsockopt (SO_REUSEADDR): %m"); + } + if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &off, sizeof(off)) != 0) { + freeaddrinfo(ai); + fatal("setsockopt (IPV6_V6ONLY): %s", strerror(errno)); + } + if (bind(s, ai->ai_addr, ai->ai_addrlen) < 0) { + freeaddrinfo(ai); fatal("bind: %s", strerror(errno)); } if (listen(s, 1) < 0) { + freeaddrinfo(ai); fatal("listen: %s", strerror(errno)); } + freeaddrinfo(ai); while (1) { socklen_t len; int ns, pid; - struct sockaddr_in from; + struct sockaddr_in6 from; syslog(LOG_INFO, "Waiting for connections."); @@ -636,6 +647,6 @@ main(int argc, char *argv[]) usage(STDERR | SYSLOG, "Unexpected argument: %s", argv[optind]); } - mainloop(NULL); + mainloop(); return 0; } |