be more social about killing attached processes (vhangup takes care of it)
[unix-history] / usr / src / libexec / telnetd / telnetd.c
index 5f365aa..a2d93ce 100644 (file)
@@ -1,21 +1,41 @@
-/*     telnetd.c       4.2     82/03/01        */
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved.  The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1983 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)telnetd.c  5.4 (Berkeley) %G%";
+#endif not lint
 
 /*
  * Stripped-down telnet server.
  */
 
 /*
  * Stripped-down telnet server.
  */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+
+#include <arpa/telnet.h>
+
 #include <stdio.h>
 #include <signal.h>
 #include <errno.h>
 #include <sgtty.h>
 #include <stdio.h>
 #include <signal.h>
 #include <errno.h>
 #include <sgtty.h>
-#include <wait.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <net/in.h>
-#include "telnet.h"
+#include <netdb.h>
+#include <syslog.h>
 
 
-#define        INFINITY        10000000
-#define        BELL            '\07'
-#define        swab(x)         ((((x) >> 8) | ((x) << 8)) & 0xffff)
+#define        BELL    '\07'
+#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s"
 
 char   hisopts[256];
 char   myopts[256];
 
 char   hisopts[256];
 char   myopts[256];
@@ -31,104 +51,92 @@ char       wont[] = { IAC, WONT, '%', 'c', 0 };
 char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
 char   ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
 char   netibuf[BUFSIZ], *netip = netibuf;
 char   ptyibuf[BUFSIZ], *ptyip = ptyibuf;
 char   ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;
 char   netibuf[BUFSIZ], *netip = netibuf;
-char   netobuf[BUFSIZ] =
-       { IAC, DO, TELOPT_ECHO, '\r', '\n' },
-       *nfrontp = netobuf + 5, *nbackp = netobuf;
+char   netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
 int    pcc, ncc;
 
 int    pty, net;
 int    inter;
 int    pcc, ncc;
 
 int    pty, net;
 int    inter;
+extern char **environ;
 extern int errno;
 extern int errno;
-char   line[] = "/dev/ptyp0";
-
-struct sockaddr_in sin = { AF_INET, swab(IPPORT_TELNET) };
-int    options = SO_ACCEPTCONN;
-
-/*
- * Debugging hooks.  Turned on with a SIGTERM.
- * Successive SIGTERM's toggle the switch.
- */
-int    toggle();
-int    debug;
-FILE   *log;
-char   logfile[80] = "/tmp/teldebugx";
+char   *line;
 
 main(argc, argv)
        char *argv[];
 {
 
 main(argc, argv)
        char *argv[];
 {
-       int s, pid;
-       union wait status;
-
-       argc--, argv++;
-       if (argc > 0 && !strcmp(argv[0], "-d"))
-               options |= SO_DEBUG;
-       for (;;) {
-               errno = 0;
-               if ((s = socket(SOCK_STREAM, 0, &sin, options)) < 0) {
-                       perror("socket");
-                       sleep(5);
-                       continue;
-               }
-               if (accept(s, 0) < 0) {
-                       perror("accept");
-                       close(s);
-                       sleep(1);
-                       continue;
-               }
-               if ((pid = fork()) < 0)
-                       printf("Out of processes\n");
-               else if (pid == 0)
-                       doit(s);
-               close(s);
-               while (wait3(status, WNOHANG, 0) > 0)
-                       continue;
+       struct sockaddr_in from;
+       int on = 1, fromlen;
+
+       openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
+       fromlen = sizeof (from);
+       if (getpeername(0, &from, &fromlen) < 0) {
+               fprintf(stderr, "%s: ", argv[0]);
+               perror("getpeername");
+               _exit(1);
        }
        }
-       /*NOTREACHED*/
+       if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {
+               syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
+       }
+       doit(0, &from);
 }
 
 }
 
+char   *envinit[] = { "TERM=network", 0 };
 int    cleanup();
 
 /*
  * Get a pty, scan input lines.
  */
 int    cleanup();
 
 /*
  * Get a pty, scan input lines.
  */
-doit(f)
+doit(f, who)
+       int f;
+       struct sockaddr_in *who;
 {
 {
-       char *cp = line;
-       int i, p, cc, t;
+       char *host, *inet_ntoa();
+       int i, p, t;
        struct sgttyb b;
        struct sgttyb b;
+       struct hostent *hp;
+       char c;
 
 
-       for (i = 0; i < 16; i++) {
-               cp[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
-               p = open(cp, 2);
-               if (p > 0)
-                       goto gotpty;
+       for (c = 'p'; c <= 's'; c++) {
+               struct stat stb;
+
+               line = "/dev/ptyXX";
+               line[strlen("/dev/pty")] = c;
+               line[strlen("/dev/ptyp")] = '0';
+               if (stat(line, &stb) < 0)
+                       break;
+               for (i = 0; i < 16; i++) {
+                       line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];
+                       p = open(line, 2);
+                       if (p > 0)
+                               goto gotpty;
+               }
        }
        }
-       dup2(f, 1);
-       printf("All network ports in use.\n");
-       exit(1);
+       fatal(f, "All network ports in use");
+       /*NOTREACHED*/
 gotpty:
 gotpty:
-       logfile[strlen("/tmp/teldebug")] = "0123456789abcdef"[i];
        dup2(f, 0);
        dup2(f, 0);
-       cp[strlen("/dev/")] = 't';
-       t = open("/dev/tty", 2);
+       line[strlen("/dev/")] = 't';
+       t = open("/dev/tty", O_RDWR);
        if (t >= 0) {
                ioctl(t, TIOCNOTTY, 0);
                close(t);
        }
        if (t >= 0) {
                ioctl(t, TIOCNOTTY, 0);
                close(t);
        }
-       t = open(cp, 2);
-       if (t < 0) {
-               dup2(f, 2);
-               perror(cp);
-               exit(1);
-       }
+       t = open(line, O_RDWR);
+       if (t < 0)
+               fatalperror(f, line, errno);
        ioctl(t, TIOCGETP, &b);
        ioctl(t, TIOCGETP, &b);
-       b.sg_flags = ECHO|CRMOD|XTABS|ANYP;
+       b.sg_flags = CRMOD|XTABS|ANYP;
        ioctl(t, TIOCSETP, &b);
        ioctl(t, TIOCSETP, &b);
-       if ((i = fork()) < 0) {
-               dup2(f, 2);
-               perror("fork");
-               exit(1);
-       }
+       ioctl(p, TIOCGETP, &b);
+       b.sg_flags &= ~ECHO;
+       ioctl(p, TIOCSETP, &b);
+       hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),
+               who->sin_family);
+       if (hp)
+               host = hp->h_name;
+       else
+               host = inet_ntoa(who->sin_addr);
+       if ((i = fork()) < 0)
+               fatalperror(f, "fork", errno);
        if (i)
                telnet(f, p);
        close(f);
        if (i)
                telnet(f, p);
        close(f);
@@ -137,11 +145,35 @@ gotpty:
        dup2(t, 1);
        dup2(t, 2);
        close(t);
        dup2(t, 1);
        dup2(t, 2);
        close(t);
-       execl("/bin/login", "telnet-login", 0);
-       perror("/bin/login");
+       environ = envinit;
+       execl("/bin/login", "login", "-h", host, 0);
+       fatalperror(f, "/bin/login", errno);
+       /*NOTREACHED*/
+}
+
+fatal(f, msg)
+       int f;
+       char *msg;
+{
+       char buf[BUFSIZ];
+
+       (void) sprintf(buf, "telnetd: %s.\r\n", msg);
+       (void) write(f, buf, strlen(buf));
        exit(1);
 }
 
        exit(1);
 }
 
+fatalperror(f, msg, errno)
+       int f;
+       char *msg;
+       int errno;
+{
+       char buf[BUFSIZ];
+       extern char *sys_errlist[];
+
+       (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);
+       fatal(f, buf);
+}
+
 /*
  * Main loop.  Select from pty and network, and
  * hand data to telnet receiver finite state machine.
 /*
  * Main loop.  Select from pty and network, and
  * hand data to telnet receiver finite state machine.
@@ -149,14 +181,25 @@ gotpty:
 telnet(f, p)
 {
        int on = 1;
 telnet(f, p)
 {
        int on = 1;
+       char hostname[32];
 
        net = f, pty = p;
        ioctl(f, FIONBIO, &on);
        ioctl(p, FIONBIO, &on);
        signal(SIGTSTP, SIG_IGN);
 
        net = f, pty = p;
        ioctl(f, FIONBIO, &on);
        ioctl(p, FIONBIO, &on);
        signal(SIGTSTP, SIG_IGN);
-       sigset(SIGCHLD, cleanup);
-       sigset(SIGTERM, toggle);
-
+       signal(SIGCHLD, cleanup);
+
+       /*
+        * Request to do remote echo.
+        */
+       dooption(TELOPT_ECHO);
+       myopts[TELOPT_ECHO] = 1;
+       /*
+        * Show banner that getty never gave.
+        */
+       gethostname(hostname, sizeof (hostname));
+       sprintf(nfrontp, BANNER, hostname, "");
+       nfrontp += strlen(nfrontp);
        for (;;) {
                int ibits = 0, obits = 0;
                register int c;
        for (;;) {
                int ibits = 0, obits = 0;
                register int c;
@@ -165,22 +208,17 @@ telnet(f, p)
                 * Never look for input if there's still
                 * stuff in the corresponding output buffer
                 */
                 * Never look for input if there's still
                 * stuff in the corresponding output buffer
                 */
-               if (nfrontp - nbackp)
+               if (nfrontp - nbackp || pcc > 0)
                        obits |= (1 << f);
                else
                        ibits |= (1 << p);
                        obits |= (1 << f);
                else
                        ibits |= (1 << p);
-               if (pfrontp - pbackp)
+               if (pfrontp - pbackp || ncc > 0)
                        obits |= (1 << p);
                else
                        ibits |= (1 << f);
                if (ncc < 0 && pcc < 0)
                        break;
                        obits |= (1 << p);
                else
                        ibits |= (1 << f);
                if (ncc < 0 && pcc < 0)
                        break;
-               if (debug)
-                       fprintf(log, "select: ibits=%d, obits=%d\n",
-                               ibits, obits);
-               select(32, &ibits, &obits, INFINITY);
-               if (debug)
-                       fprintf(log, "ibits=%d, obits=%d\n", ibits, obits);
+               select(16, &ibits, &obits, 0, 0);
                if (ibits == 0 && obits == 0) {
                        sleep(5);
                        continue;
                if (ibits == 0 && obits == 0) {
                        sleep(5);
                        continue;
@@ -191,8 +229,6 @@ telnet(f, p)
                 */
                if (ibits & (1 << f)) {
                        ncc = read(f, netibuf, BUFSIZ);
                 */
                if (ibits & (1 << f)) {
                        ncc = read(f, netibuf, BUFSIZ);
-                       if (debug)
-                               fprintf(log, "read %d from net\n", ncc);
                        if (ncc < 0 && errno == EWOULDBLOCK)
                                ncc = 0;
                        else {
                        if (ncc < 0 && errno == EWOULDBLOCK)
                                ncc = 0;
                        else {
@@ -207,8 +243,6 @@ telnet(f, p)
                 */
                if (ibits & (1 << p)) {
                        pcc = read(p, ptyibuf, BUFSIZ);
                 */
                if (ibits & (1 << p)) {
                        pcc = read(p, ptyibuf, BUFSIZ);
-                       if (debug)
-                               fprintf(log, "read %d from pty\n", pcc);
                        if (pcc < 0 && errno == EWOULDBLOCK)
                                pcc = 0;
                        else {
                        if (pcc < 0 && errno == EWOULDBLOCK)
                                pcc = 0;
                        else {
@@ -296,7 +330,8 @@ telrcv()
                         * Are You There?
                         */
                        case AYT:
                         * Are You There?
                         */
                        case AYT:
-                               *pfrontp++ = BELL;
+                               strcpy(nfrontp, "\r\n[Yes]\r\n");
+                               nfrontp += 9;
                                break;
 
                        /*
                                break;
 
                        /*
@@ -369,13 +404,13 @@ telrcv()
                        if (myopts[c]) {
                                myopts[c] = 0;
                                sprintf(nfrontp, wont, c);
                        if (myopts[c]) {
                                myopts[c] = 0;
                                sprintf(nfrontp, wont, c);
-                               nfrontp += sizeof(wont) - 2;
+                               nfrontp += sizeof (wont) - 2;
                        }
                        state = TS_DATA;
                        continue;
 
                default:
                        }
                        state = TS_DATA;
                        continue;
 
                default:
-                       printf("netser: panic state=%d\n", state);
+                       printf("telnetd: panic state=%d\n", state);
                        exit(1);
                }
        }
                        exit(1);
                }
        }
@@ -411,7 +446,7 @@ willoption(option)
                break;
        }
        sprintf(nfrontp, fmt, option);
                break;
        }
        sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof(dont) - 2;
+       nfrontp += sizeof (dont) - 2;
 }
 
 wontoption(option)
 }
 
 wontoption(option)
@@ -439,7 +474,7 @@ wontoption(option)
                fmt = dont;
        }
        sprintf(nfrontp, fmt, option);
                fmt = dont;
        }
        sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof(doopt) - 2;
+       nfrontp += sizeof (doopt) - 2;
 }
 
 dooption(option)
 }
 
 dooption(option)
@@ -471,7 +506,7 @@ dooption(option)
                break;
        }
        sprintf(nfrontp, fmt, option);
                break;
        }
        sprintf(nfrontp, fmt, option);
-       nfrontp += sizeof(doopt) - 2;
+       nfrontp += sizeof (doopt) - 2;
 }
 
 mode(on, off)
 }
 
 mode(on, off)
@@ -512,8 +547,8 @@ ptyflush()
 
        if ((n = pfrontp - pbackp) > 0)
                n = write(pty, pbackp, n);
 
        if ((n = pfrontp - pbackp) > 0)
                n = write(pty, pbackp, n);
-       if (n < 0 && errno == EWOULDBLOCK)
-               n = 0;
+       if (n < 0)
+               return;
        pbackp += n;
        if (pbackp == pfrontp)
                pbackp = pfrontp = ptyobuf;
        pbackp += n;
        if (pbackp == pfrontp)
                pbackp = pfrontp = ptyobuf;
@@ -525,38 +560,23 @@ netflush()
 
        if ((n = nfrontp - nbackp) > 0)
                n = write(net, nbackp, n);
 
        if ((n = nfrontp - nbackp) > 0)
                n = write(net, nbackp, n);
-       if (n < 0 && errno == EWOULDBLOCK)
-               n = 0;
+       if (n < 0) {
+               if (errno == EWOULDBLOCK)
+                       return;
+               /* should blow this guy away... */
+               return;
+       }
        nbackp += n;
        if (nbackp == nfrontp)
                nbackp = nfrontp = netobuf;
 }
 
        nbackp += n;
        if (nbackp == nfrontp)
                nbackp = nfrontp = netobuf;
 }
 
-toggle()
-{
-       if (debug) {
-               fprintf(log, "log stopped\n");
-               if (log)
-                       fclose(log);
-       } else {
-               if ((log = fopen(logfile, "a")) != NULL) {
-                       setbuf(log, 0);
-                       fprintf(log, "log started on /dev/pty%c\n",
-                               logfile[strlen("/tmp/teldebug")]);
-                       fprintf(log, "net=%d, pty=%d\n", net, pty);
-               }
-       }
-       debug = !debug;
-}
-
 cleanup()
 {
 cleanup()
 {
-       int how = 2;
 
        rmut();
 
        rmut();
-       vhangup();
-       ioctl(net, SIOCDONE, &how);
-       kill(0, SIGKILL);
+       vhangup();      /* XXX */
+       shutdown(net, 2);
        exit(1);
 }
 
        exit(1);
 }
 
@@ -564,7 +584,7 @@ cleanup()
 
 struct utmp wtmp;
 char   wtmpf[] = "/usr/adm/wtmp";
 
 struct utmp wtmp;
 char   wtmpf[] = "/usr/adm/wtmp";
-char   utmp[] = "/etc/utmp";
+char   utmpf[] = "/etc/utmp";
 #define SCPYN(a, b)    strncpy(a, b, sizeof(a))
 #define SCMPN(a, b)    strncmp(a, b, sizeof(a))
 
 #define SCPYN(a, b)    strncpy(a, b, sizeof(a))
 #define SCMPN(a, b)    strncmp(a, b, sizeof(a))
 
@@ -572,27 +592,41 @@ rmut()
 {
        register f;
        int found = 0;
 {
        register f;
        int found = 0;
+       struct utmp *u, *utmp;
+       int nutmp;
+       struct stat statbf;
 
 
-       f = open(utmp, 2);
+       f = open(utmpf, O_RDWR);
        if (f >= 0) {
        if (f >= 0) {
-               while(read(f, (char *)&wtmp, sizeof(wtmp)) == sizeof(wtmp)) {
-                       if (SCMPN(wtmp.ut_line, line+5) || wtmp.ut_name[0]==0)
-                               continue;
-                       lseek(f, -(long)sizeof(wtmp), 1);
-                       SCPYN(wtmp.ut_name, "");
-                       time(&wtmp.ut_time);
-                       write(f, (char *)&wtmp, sizeof(wtmp));
-                       found++;
+               fstat(f, &statbf);
+               utmp = (struct utmp *)malloc(statbf.st_size);
+               if (!utmp)
+                       syslog(LOG_ERR, "utmp malloc failed");
+               if (statbf.st_size && utmp) {
+                       nutmp = read(f, utmp, statbf.st_size);
+                       nutmp /= sizeof(struct utmp);
+               
+                       for (u = utmp ; u < &utmp[nutmp] ; u++) {
+                               if (SCMPN(u->ut_line, line+5) ||
+                                   u->ut_name[0]==0)
+                                       continue;
+                               lseek(f, ((long)u)-((long)utmp), L_SET);
+                               SCPYN(u->ut_name, "");
+                               SCPYN(u->ut_host, "");
+                               time(&u->ut_time);
+                               write(f, (char *)u, sizeof(wtmp));
+                               found++;
+                       }
                }
                close(f);
        }
        if (found) {
                }
                close(f);
        }
        if (found) {
-               f = open(wtmpf, 1);
+               f = open(wtmpf, O_WRONLY|O_APPEND);
                if (f >= 0) {
                        SCPYN(wtmp.ut_line, line+5);
                        SCPYN(wtmp.ut_name, "");
                if (f >= 0) {
                        SCPYN(wtmp.ut_line, line+5);
                        SCPYN(wtmp.ut_name, "");
+                       SCPYN(wtmp.ut_host, "");
                        time(&wtmp.ut_time);
                        time(&wtmp.ut_time);
-                       lseek(f, (long)0, 2);
                        write(f, (char *)&wtmp, sizeof(wtmp));
                        close(f);
                }
                        write(f, (char *)&wtmp, sizeof(wtmp));
                        close(f);
                }