zcip: fix unaligned trap on ARM
[oweals/busybox.git] / networking / telnetd.c
index d524ac89169e7279b58c21cd0657d80ca97ea505..b21991212cacd05fa56ffc15758b45f33c4964b1 100644 (file)
  *     Set process group corrections, initial busybox port
  */
 
-/*#define DEBUG 1 */
 #define DEBUG 0
 
-#include "busybox.h"
+#include "libbb.h"
 
 #if DEBUG
 #define TELCMDS
 #include <sys/syslog.h>
 
 
-#define BUFSIZE 4000
-
-#if ENABLE_FEATURE_IPV6
-typedef struct sockaddr_in6 sockaddr_type;
-#else
-typedef struct sockaddr_in sockaddr_type;
-#endif
-
 #if ENABLE_LOGIN
 static const char *loginpath = "/bin/login";
 #else
@@ -50,10 +41,6 @@ static const char *loginpath = DEFAULT_SHELL;
 
 static const char *issuefile = "/etc/issue.net";
 
-/* shell name and arguments */
-
-static const char *argv_init[2];
-
 /* structure that describes a session */
 
 struct tsession {
@@ -66,6 +53,10 @@ struct tsession {
        int rdidx2, wridx2, size2;
 };
 
+/* Two buffers are directly after tsession in malloced memory.
+ * Make whole thing fit in 4k */
+enum { BUFSIZE = (4*1024 - sizeof(struct tsession)) / 2 };
+
 /*
    This is how the buffers are used. The arrows indicate the movement
    of data.
@@ -237,6 +228,7 @@ make_new_session(
                USE_FEATURE_TELNETD_STANDALONE(int sock_r, int sock_w)
                SKIP_FEATURE_TELNETD_STANDALONE(void)
 ) {
+       const char *login_argv[2];
        struct termios termbuf;
        int fd, pid;
        char tty_name[32];
@@ -286,18 +278,21 @@ make_new_session(
                ts->shell_pid = pid;
                return ts;
        }
-       
+
        /* child */
 
+       /* make new session and process group */
+       setsid();
+
        /* open the child's side of the tty. */
-       fd = xopen(tty_name, O_RDWR /*| O_NOCTTY*/);
+       /* NB: setsid() disconnects from any previous ctty's. Therefore
+        * we must open child's side of the tty AFTER setsid! */
+       fd = xopen(tty_name, O_RDWR); /* becomes our ctty */
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        while (fd > 2) close(fd--);
-       /* make new process group */
-       setsid();
-       tcsetpgrp(0, getpid());
+       tcsetpgrp(0, getpid()); /* switch this tty's process group to us */
 
        /* The pseudo-terminal allocated to the client is configured to operate in
         * cooked mode, and with XTABS CRMOD enabled (see tty(4)). */
@@ -311,9 +306,12 @@ make_new_session(
 
        print_login_issue(issuefile, NULL);
 
-       /* exec shell, with correct argv and env */
-       execv(loginpath, (char *const *)argv_init);
-       bb_perror_msg_and_die("execv");
+       /* exec shell / login /whatever */
+       login_argv[0] = loginpath;
+       login_argv[1] = NULL;
+       execv(loginpath, (char **)login_argv);
+       /* Hmmm... this gets sent to the client thru fd#2! Is it ok?? */
+       bb_perror_msg_and_die("execv %s", loginpath);
 }
 
 #if ENABLE_FEATURE_TELNETD_STANDALONE
@@ -341,7 +339,7 @@ free_session(struct tsession *ts)
        free(ts);
 
        /* scan all sessions and find new maxfd */
-        ts = sessions;
+       ts = sessions;
        maxfd = 0;
        while (ts) {
                if (maxfd < ts->ptyfd)
@@ -354,93 +352,16 @@ 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
 
 
-int
-telnetd_main(int argc, char **argv)
+int telnetd_main(int argc, char **argv);
+int telnetd_main(int argc, char **argv)
 {
        fd_set rdfdset, wrfdset;
        unsigned opt;
@@ -465,7 +386,7 @@ telnetd_main(int argc, char **argv)
                OPT_INETD = 0x20 * ENABLE_FEATURE_TELNETD_STANDALONE,
        };
 
-       opt = getopt32(argc, argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
+       opt = getopt32(argv, "f:l:" USE_FEATURE_TELNETD_STANDALONE("p:b:Fi"),
                        &issuefile, &loginpath
                        USE_FEATURE_TELNETD_STANDALONE(, &opt_portnbr, &opt_bindaddr));
        /* Redirect log to syslog early, if needed */
@@ -485,15 +406,15 @@ telnetd_main(int argc, char **argv)
 
        /* Used to check access(loginpath, X_OK) here. Pointless.
         * exec will do this for us for free later. */
-       argv_init[0] = loginpath;
 
 #if ENABLE_FEATURE_TELNETD_STANDALONE
        if (IS_INETD) {
                sessions = make_new_session(0, 1);
        } else {
-               master_fd = create_socket(opt_bindaddr, portnbr);
+               master_fd = create_and_bind_stream_or_die(opt_bindaddr, portnbr);
+               xlisten(master_fd, 1);
                if (!(opt & OPT_FOREGROUND))
-                       xdaemon(0, 0);
+                       bb_daemonize(DAEMON_CHDIR_ROOT);
        }
 #else
        sessions = make_new_session();
@@ -538,13 +459,10 @@ telnetd_main(int argc, char **argv)
 #if ENABLE_FEATURE_TELNETD_STANDALONE
        /* First check for and accept new sessions. */
        if (!IS_INETD && FD_ISSET(master_fd, &rdfdset)) {
-               sockaddr_type sa;
                int fd;
-               socklen_t salen;
                struct tsession *new_ts;
 
-               salen = sizeof(sa);
-               fd = accept(master_fd, (struct sockaddr *)&sa, &salen);
+               fd = accept(master_fd, NULL, 0);
                if (fd < 0)
                        goto again;
                /* Create a new session and link it into our active list */