From 0801ef28ca655a28543b28754fd804ba0616c21c Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Wed, 22 Nov 2006 15:57:52 +0000 Subject: telnetd: if FEATURE_IPV6 is on, telnet will NOT work on IPv4 which is kind of stupid --- networking/telnetd.c | 86 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 21 deletions(-) (limited to 'networking') diff --git a/networking/telnetd.c b/networking/telnetd.c index 549488507..d524ac891 100644 --- a/networking/telnetd.c +++ b/networking/telnetd.c @@ -37,10 +37,8 @@ #define BUFSIZE 4000 #if ENABLE_FEATURE_IPV6 -#define SOCKET_TYPE AF_INET6 typedef struct sockaddr_in6 sockaddr_type; #else -#define SOCKET_TYPE AF_INET typedef struct sockaddr_in sockaddr_type; #endif @@ -357,31 +355,77 @@ free_session(struct tsession *ts) } static int -create_socket(int port, const char *opt_bindaddr) +dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) +{ + union { + struct in_addr a4; +#if ENABLE_FEATURE_IPV6 + struct in6_addr a6; +#endif + } a; + +#if ENABLE_FEATURE_IPV6 + if (socklen >= sizeof(struct sockaddr_in6) + && inet_pton(AF_INET6, dotted, &a) > 0 + ) { + ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; + ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; + } else +#endif + if (socklen >= sizeof(struct sockaddr_in) + && inet_pton(AF_INET, dotted, &a) > 0 + ) { + ((struct sockaddr_in*)sp)->sin_family = AF_INET; + ((struct sockaddr_in*)sp)->sin_addr = a.a4; + } else + return 1; + + return 0; /* success */ +} + +static int +xsocket_stream_ip4or6(sa_family_t *fp) +{ + int fd = socket(AF_INET6, SOCK_STREAM, 0); + if (fp) *fp = AF_INET6; + if (fd < 0) { + fd = xsocket(AF_INET, SOCK_STREAM, 0); + if (fp) *fp = AF_INET; + } + return fd; +} + +static int +create_socket(const char *hostaddr, int port) { static const int on = 1; int fd; - sockaddr_type sa; -#if !ENABLE_FEATURE_IPV6 - struct in_addr bind_addr = { .s_addr = 0x0 }; + union { + struct sockaddr sa; + struct sockaddr_in sin; +#if ENABLE_FEATURE_IPV6 + struct sockaddr_in6 sin6; +#endif + } sa; - /* TODO: generic string -> sockaddr converter */ - if (opt_bindaddr && inet_aton(opt_bindaddr, &bind_addr) == 0) + memset(&sa, 0, sizeof(sa)); + if (hostaddr && dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) bb_show_usage(); -#endif - fd = xsocket(SOCKET_TYPE, SOCK_STREAM, 0); + + if (!sa.sa.sa_family) + fd = xsocket_stream_ip4or6(&sa.sa.sa_family); + else /* user specified -b ADDR dictates family */ + fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - memset((void *)&sa, 0, sizeof(sa)); + #if ENABLE_FEATURE_IPV6 - sa.sin6_family = AF_INET6; - sa.sin6_port = htons(port); - /* sa.sin6_addr = bind_addr6; */ -#else - sa.sin_family = AF_INET; - sa.sin_port = htons(port); - sa.sin_addr = bind_addr; + if (sa.sa.sa_family == AF_INET6) + sa.sin6.sin6_port = htons(port); #endif - xbind(fd, (struct sockaddr *) &sa, sizeof(sa)); + if (sa.sa.sa_family == AF_INET) + sa.sin.sin_port = htons(port); + + xbind(fd, &sa.sa, sizeof(sa)); xlisten(fd, 1); return fd; } @@ -390,7 +434,7 @@ create_socket(int port, const char *opt_bindaddr) /* Never actually called */ void free_session(struct tsession *ts); -int create_socket(int port, const char *opt_bindaddr); +int create_socket(const char *hostaddr, int port); #endif @@ -447,7 +491,7 @@ telnetd_main(int argc, char **argv) if (IS_INETD) { sessions = make_new_session(0, 1); } else { - master_fd = create_socket(portnbr, opt_bindaddr); + master_fd = create_socket(opt_bindaddr, portnbr); if (!(opt & OPT_FOREGROUND)) xdaemon(0, 0); } -- cgit v1.2.3