summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoland Hieber <r.hieber@pengutronix.de>2017-07-20 11:07:04 +0200
committerRoland Hieber <r.hieber@pengutronix.de>2017-07-25 21:26:10 +0200
commit8a103f5c91133e3443774cc4a2ba74570484ec0c (patch)
tree3a1b55bc30a3c36543bbccaf3877e74730fdf225
parentd2398162c13915d8ca28f7f1fa582638bb90c678 (diff)
downloadurshd-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.c83
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;
}