telnetd: move generic stuff into libbb. It will make it easy to make other server applets IPv6-aware too.
No binary (build failed)
—
(log yok)
| @@ -226,6 +226,7 @@ extern int xopen3(const char *pathname, int flags, int mode); | |
| extern off_t xlseek(int fd, off_t offset, int whence); | |
| extern off_t fdlength(int fd); | |
| + | |
| extern int xsocket(int domain, int type, int protocol); | |
| extern void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen); | |
| extern void xlisten(int s, int backlog); | |
| @@ -233,6 +234,17 @@ extern void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen); | |
| extern int xconnect_tcp_v4(struct sockaddr_in *s_addr); | |
| extern struct hostent *xgethostbyname(const char *name); | |
| extern struct hostent *xgethostbyname2(const char *name, int af); | |
| + | extern int xsocket_stream_ip4or6(sa_family_t *fp); |
| + | typedef union { |
| + | struct sockaddr sa; |
| + | struct sockaddr_in sin; |
| + | #if ENABLE_FEATURE_IPV6 |
| + | struct sockaddr_in6 sin6; |
| + | #endif |
| + | } sockaddr_inet; |
| + | extern int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen); |
| + | extern int create_and_bind_socket_ip4or6(const char *hostaddr, int port); |
| + | |
| extern char *xstrdup(const char *s); | |
| extern char *xstrndup(const char *s, int n); | |
| @@ -66,3 +66,155 @@ int xconnect_tcp_v4(struct sockaddr_in *s_addr) | |
| xconnect(s, (struct sockaddr*) s_addr, sizeof(*s_addr)); | |
| return s; | |
| } | |
| + | |
| + | int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) |
| + | { |
| + | union { |
| + | struct in_addr a4; |
| + | #if ENABLE_FEATURE_IPV6 |
| + | struct in6_addr a6; |
| + | #endif |
| + | } a; |
| + | |
| + | /* TODO maybe: port spec? like n.n.n.n:nn */ |
| + | |
| + | #if ENABLE_FEATURE_IPV6 |
| + | if (socklen >= sizeof(struct sockaddr_in6) |
| + | && inet_pton(AF_INET6, dotted, &a.a6) > 0 |
| + | ) { |
| + | ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; |
| + | ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; |
| + | /* ((struct sockaddr_in6*)sp)->sin6_port = */ |
| + | return 0; /* success */ |
| + | } |
| + | #endif |
| + | if (socklen >= sizeof(struct sockaddr_in) |
| + | && inet_pton(AF_INET, dotted, &a.a4) > 0 |
| + | ) { |
| + | ((struct sockaddr_in*)sp)->sin_family = AF_INET; |
| + | ((struct sockaddr_in*)sp)->sin_addr = a.a4; |
| + | /* ((struct sockaddr_in*)sp)->sin_port = */ |
| + | return 0; /* success */ |
| + | } |
| + | return 1; |
| + | } |
| + | |
| + | int xsocket_stream_ip4or6(sa_family_t *fp) |
| + | { |
| + | int fd; |
| + | #if ENABLE_FEATURE_IPV6 |
| + | fd = socket(AF_INET6, SOCK_STREAM, 0); |
| + | if (fp) *fp = AF_INET6; |
| + | if (fd < 0) |
| + | #endif |
| + | { |
| + | fd = xsocket(AF_INET, SOCK_STREAM, 0); |
| + | if (fp) *fp = AF_INET; |
| + | } |
| + | return fd; |
| + | } |
| + | |
| + | int create_and_bind_socket_ip4or6(const char *hostaddr, int port) |
| + | { |
| + | static const int on = 1; |
| + | int fd; |
| + | sockaddr_inet sa; |
| + | |
| + | memset(&sa, 0, sizeof(sa)); |
| + | if (hostaddr && dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) |
| + | bb_show_usage(); |
| + | |
| + | if (!sa.sa.sa_family) |
| + | fd = xsocket_stream_ip4or6(&sa.sa.sa_family); |
| + | else /* user specified bind addr dictates family */ |
| + | fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); |
| + | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
| + | |
| + | /* if (port >= 0) { */ |
| + | #if ENABLE_FEATURE_IPV6 |
| + | if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) |
| + | sa.sin6.sin6_port = htons(port); |
| + | #endif |
| + | if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) |
| + | sa.sin.sin_port = htons(port); |
| + | /* } */ |
| + | |
| + | xbind(fd, &sa.sa, sizeof(sa)); |
| + | return fd; |
| + | } |
| + | |
| + | int dotted2sockaddr(const char *dotted, struct sockaddr* sp, int socklen) |
| + | { |
| + | union { |
| + | struct in_addr a4; |
| + | #if ENABLE_FEATURE_IPV6 |
| + | struct in6_addr a6; |
| + | #endif |
| + | } a; |
| + | |
| + | /* TODO maybe: port spec? like n.n.n.n:nn */ |
| + | |
| + | #if ENABLE_FEATURE_IPV6 |
| + | if (socklen >= sizeof(struct sockaddr_in6) |
| + | && inet_pton(AF_INET6, dotted, &a.a6) > 0 |
| + | ) { |
| + | ((struct sockaddr_in6*)sp)->sin6_family = AF_INET6; |
| + | ((struct sockaddr_in6*)sp)->sin6_addr = a.a6; |
| + | /* ((struct sockaddr_in6*)sp)->sin6_port = */ |
| + | return 0; /* success */ |
| + | } |
| + | #endif |
| + | if (socklen >= sizeof(struct sockaddr_in) |
| + | && inet_pton(AF_INET, dotted, &a.a4) > 0 |
| + | ) { |
| + | ((struct sockaddr_in*)sp)->sin_family = AF_INET; |
| + | ((struct sockaddr_in*)sp)->sin_addr = a.a4; |
| + | /* ((struct sockaddr_in*)sp)->sin_port = */ |
| + | return 0; /* success */ |
| + | } |
| + | return 1; |
| + | } |
| + | |
| + | int xsocket_stream_ip4or6(sa_family_t *fp) |
| + | { |
| + | int fd; |
| + | #if ENABLE_FEATURE_IPV6 |
| + | fd = socket(AF_INET6, SOCK_STREAM, 0); |
| + | if (fp) *fp = AF_INET6; |
| + | if (fd < 0) |
| + | #endif |
| + | { |
| + | fd = xsocket(AF_INET, SOCK_STREAM, 0); |
| + | if (fp) *fp = AF_INET; |
| + | } |
| + | return fd; |
| + | } |
| + | |
| + | int create_and_bind_socket_ip4or6(const char *hostaddr, int port) |
| + | { |
| + | static const int on = 1; |
| + | int fd; |
| + | sockaddr_inet sa; |
| + | |
| + | memset(&sa, 0, sizeof(sa)); |
| + | if (hostaddr) { |
| + | if (dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) |
| + | bb_error_msg_and_die("bad address '%s'", hostaddr); |
| + | /* user specified bind addr dictates family */ |
| + | fd = xsocket(sa.sa.sa_family, SOCK_STREAM, 0); |
| + | } else |
| + | fd = xsocket_stream_ip4or6(&sa.sa.sa_family); |
| + | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); |
| + | |
| + | /* if (port >= 0) { */ |
| + | #if ENABLE_FEATURE_IPV6 |
| + | if (sa.sa.sa_family == AF_INET6 /* && !sa.sin6.sin6_port */) |
| + | sa.sin6.sin6_port = htons(port); |
| + | #endif |
| + | if (sa.sa.sa_family == AF_INET /* && !sa.sin.sin_port */) |
| + | sa.sin.sin_port = htons(port); |
| + | /* } */ |
| + | |
| + | xbind(fd, &sa.sa, sizeof(sa)); |
| + | return fd; |
| + | } |
| @@ -110,13 +110,13 @@ static void handlexitsigs(int signum) | |
| /* May succeed. If not, won't care. */ | |
| static void writepid(uid_t nobody, uid_t nogrp) | |
| { | |
| - | char buf[24]; |
| + | char buf[sizeof(int)*3 + 2]; |
| int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0664); | |
| if (fd < 0) | |
| return; | |
| - | snprintf(buf, 23, "%d\n", getpid()); |
| + | sprintf(buf, "%d\n", getpid()); |
| write(fd, buf, strlen(buf)); | |
| fchown(fd, nobody, nogrp); | |
| close(fd); | |
| @@ -354,87 +354,10 @@ free_session(struct tsession *ts) | |
| } | |
| } | |
| - | static int |
| - | 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; |
| - | union { |
| - | struct sockaddr sa; |
| - | struct sockaddr_in sin; |
| - | #if ENABLE_FEATURE_IPV6 |
| - | struct sockaddr_in6 sin6; |
| - | #endif |
| - | } sa; |
| - | |
| - | memset(&sa, 0, sizeof(sa)); |
| - | if (hostaddr && dotted2sockaddr(hostaddr, &sa.sa, sizeof(sa))) |
| - | bb_show_usage(); |
| - | |
| - | 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)); |
| - | |
| - | #if ENABLE_FEATURE_IPV6 |
| - | if (sa.sa.sa_family == AF_INET6) |
| - | sa.sin6.sin6_port = htons(port); |
| - | #endif |
| - | if (sa.sa.sa_family == AF_INET) |
| - | sa.sin.sin_port = htons(port); |
| - | |
| - | xbind(fd, &sa.sa, sizeof(sa)); |
| - | xlisten(fd, 1); |
| - | return fd; |
| - | } |
| - | |
| #else /* !FEATURE_TELNETD_STANDALONE */ | |
| /* Never actually called */ | |
| void free_session(struct tsession *ts); | |
| - | int create_socket(const char *hostaddr, int port); |
| #endif | |
| @@ -491,7 +414,8 @@ telnetd_main(int argc, char **argv) | |
| if (IS_INETD) { | |
| sessions = make_new_session(0, 1); | |
| } else { | |
| - | master_fd = create_socket(opt_bindaddr, portnbr); |
| + | master_fd = create_and_bind_socket_ip4or6(opt_bindaddr, portnbr); |
| + | xlisten(master_fd, 1); |
| if (!(opt & OPT_FOREGROUND)) | |
| xdaemon(0, 0); | |
| } | |