X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/5eddff6d2cda389f8c4405475804f7002ed043e9..86b6dfea7d552f4740031ba7841d7b7ceedcc1d0:/usr/src/libexec/telnetd/telnetd.c?ds=inline diff --git a/usr/src/libexec/telnetd/telnetd.c b/usr/src/libexec/telnetd/telnetd.c index efb91eeeb2..c649a6896a 100644 --- a/usr/src/libexec/telnetd/telnetd.c +++ b/usr/src/libexec/telnetd/telnetd.c @@ -1,128 +1,150 @@ /* - * Copyright (c) 1983,1986 Regents of the University of California. - * All rights reserved. The Berkeley software License Agreement - * specifies the terms and conditions for redistribution. + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef lint char copyright[] = -"@(#) Copyright (c) 1983 Regents of the University of California.\n\ +"@(#) Copyright (c) 1989 Regents of the University of California.\n\ All rights reserved.\n"; -#endif not lint +#endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)telnetd.c 5.16 (Berkeley) %G%"; -#endif not lint +static char sccsid[] = "@(#)telnetd.c 5.39 (Berkeley) %G%"; +#endif /* not lint */ -/* - * Telnet server. - */ -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#define OPT_DONT 0 /* don't do this option */ -#define OPT_WONT 0 /* won't do this option */ -#define OPT_DO 1 /* do this option */ -#define OPT_WILL 1 /* will do this option */ -#define OPT_ALWAYS_LOOK 2 /* special case for echo */ -char hisopts[256]; -char myopts[256]; - -char doopt[] = { IAC, DO, '%', 'c', 0 }; -char dont[] = { IAC, DONT, '%', 'c', 0 }; -char will[] = { IAC, WILL, '%', 'c', 0 }; -char wont[] = { IAC, WONT, '%', 'c', 0 }; +#include "telnetd.h" /* - * I/O data buffers, pointers, and counters. + * I/O data buffers, + * pointers, and counters. */ char ptyibuf[BUFSIZ], *ptyip = ptyibuf; +char ptyibuf2[BUFSIZ]; -char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; - -char netibuf[BUFSIZ], *netip = netibuf; -#define NIACCUM(c) { *netip++ = c; \ - ncc++; \ - } - -char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; -char *neturg = 0; /* one past last bye of urgent data */ - /* the remote system seems to NOT be an old 4.2 */ -int not42 = 1; +#ifdef CRAY +int hostinfo = 1; /* do we print login banner? */ +#endif +#ifdef CRAY +extern int newmap; /* nonzero if \n maps to ^M^J */ +int lowpty = 0, highpty = 128; /* low, high pty numbers */ +#endif /* CRAY */ -char BANNER1[] = "\r\n\r\n4.3 BSD UNIX (", - BANNER2[] = ")\r\n\r\0\r\n\r\0"; - -char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */ -#define SB_CLEAR() subpointer = subbuffer; -#define SB_TERM() subend = subpointer; -#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ - *subpointer++ = (c); \ - } - -int pcc, ncc; - -int pty, net; -int inter; -extern char **environ; -extern int errno; -char *line; -int SYNCHing = 0; /* we are in TELNET SYNCH mode */ -/* - * The following are some clocks used to decide how to interpret - * the relationship between various variables. - */ +int debug = 0; +char *progname; -struct { - int - system, /* what the current time is */ - echotoggle, /* last time user entered echo character */ - modenegotiated, /* last time operating mode negotiated */ - didnetreceive, /* last time we read data from network */ - gotDM; /* when did we last see a data mark */ -} clocks; - -#define settimer(x) clocks.x = clocks.system++ - main(argc, argv) char *argv[]; { struct sockaddr_in from; int on = 1, fromlen; -#if defined(DEBUG) - { + pfrontp = pbackp = ptyobuf; + netip = netibuf; + nfrontp = nbackp = netobuf; + + progname = *argv; +top: + argc--, argv++; + + if (argc > 0 && strcmp(*argv, "-debug") == 0) { + debug++; + goto top; + } + +#ifdef LINEMODE + if (argc > 0 && !strcmp(*argv, "-l")) { + alwayslinemode = 1; + goto top; + } +#endif /* LINEMODE */ + +#ifdef CRAY + if (argc > 0 && !strcmp(*argv, "-h")) { + hostinfo = 0; + goto top; + } + + if (argc > 0 && !strncmp(*argv, "-r", 2)) { + char *strchr(); + char *c; + + *argv += 2; + if (**argv == '\0' && argc) + argv++, argc--; + c = strchr(*argv, '-'); + if (c) { + *c++ = '\0'; + highpty = atoi(c); + } else + highpty = -1; + lowpty = atoi(*argv); + if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) { + usage: + fprintf(stderr, "Usage: telnetd [-debug] [-h] "); +# ifdef NEWINIT + fprintf(stderr, "[-Iinitid] "); +# endif /* NEWINIT */ + fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n"); + exit(1); + } + goto top; + } +# ifdef NEWINIT + if (argc > 0 && !strncmp(*argv, "-I", 2)) { + extern char *gen_id; + + *argv += 2; + if (**argv == '\0') { + if (argc < 2) + goto usage; + argv++, argc--; + if (**argv == '\0') + goto usage; + } + gen_id = *argv; + goto top; + } +# endif /* NEWINIT */ +#endif /* CRAY */ + + if (debug) { int s, ns, foo; struct servent *sp; static struct sockaddr_in sin = { AF_INET }; - sp = getservbyname("telnet", "tcp"); - if (sp == 0) { - fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); - exit(1); - } - sin.sin_port = sp->s_port; - argc--, argv++; if (argc > 0) { - sin.sin_port = atoi(*argv); - sin.sin_port = htons((u_short)sin.sin_port); + if (sp = getservbyname(*argv, "tcp")) { + sin.sin_port = sp->s_port; + } else { + sin.sin_port = atoi(*argv); + if ((int)sin.sin_port <= 0) { + fprintf(stderr, "telnetd: %s: bad port #\n", *argv); + exit(1); + } + sin.sin_port = htons((u_short)sin.sin_port); + } + } else { + sp = getservbyname("telnet", "tcp"); + if (sp == 0) { + fprintf(stderr, + "telnetd: tcp/telnet: unknown service\n"); + exit(1); + } + sin.sin_port = sp->s_port; } s = socket(AF_INET, SOCK_STREAM, 0); @@ -130,7 +152,8 @@ main(argc, argv) perror("telnetd: socket");; exit(1); } - if (bind(s, &sin, sizeof sin) < 0) { + (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { perror("bind"); exit(1); } @@ -139,302 +162,214 @@ main(argc, argv) exit(1); } foo = sizeof sin; - ns = accept(s, &sin, &foo); + ns = accept(s, (struct sockaddr *)&sin, &foo); if (ns < 0) { perror("accept"); exit(1); } - dup2(ns, 0); - close(s); + (void) dup2(ns, 0); + (void) close(ns); + (void) close(s); } -#endif /* defined(DEBUG) */ + openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); fromlen = sizeof (from); - if (getpeername(0, &from, &fromlen) < 0) { - fprintf(stderr, "%s: ", argv[0]); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + fprintf(stderr, "%s: ", progname); perror("getpeername"); _exit(1); } if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); } - doit(0, &from); -} + net = 0; + doit(&from); + /* NOTREACHED */ +} /* end of main */ +int cleanup(); /* - * Get() + * getterminaltype * - * Return next character from file descriptor. - * - * This is not meant to be very efficient, since it is only - * run during startup. + * Ask the other end to send along its terminal type and speed. + * Output is the variable terminaltype filled in. */ - -Get(f) -int f; /* the file descriptor */ +static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; +void +getterminaltype() { - char input; + void ttloop(); + + settimer(baseline); + willoption(TELOPT_TTYPE, 1); + willoption(TELOPT_TSPEED, 1); + while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) || + (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) { + ttloop(); + } + if (hisopts[TELOPT_TSPEED] == OPT_YES) { + static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; + + bcopy(sbbuf, nfrontp, sizeof sbbuf); + nfrontp += sizeof sbbuf; + } + if (hisopts[TELOPT_TTYPE] == OPT_YES) { + + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + } + if (hisopts[TELOPT_TSPEED] == OPT_YES) { + while (sequenceIs(tspeedsubopt, baseline)) + ttloop(); + } + if (hisopts[TELOPT_TTYPE] == OPT_YES) { + char first[256], last[256]; - if (read(f, &input, 1) != 1) { - syslog(LOG_INFO, "read: %m\n"); - exit(1); + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); + + if (!terminaltypeok(&terminaltype[5])) { + (void) strncpy(first, terminaltype, sizeof(first)); + for(;;) { + /* + * Save the unknown name, and request the next name. + */ + (void) strncpy(last, terminaltype, sizeof(last)); + _gettermname(); + if (terminaltypeok(&terminaltype[5])) + break; + if (strncmp(last, terminaltype, sizeof(last)) == 0) { + /* + * We've hit the end. If this is the same as + * the first name, just go with it. + */ + if (strncmp(first, terminaltype, sizeof(first) == 0)) + break; + /* + * Get the terminal name one more type, so that + * RFC1091 compliant telnets will cycle back to + * the start of the list. + */ + _gettermname(); + if (strncmp(first, terminaltype, sizeof(first) != 0)) + (void) strncpy(terminaltype, first, sizeof(first)); + break; + } + } + } } - return input&0xff; +} /* end of getterminaltype */ + +_gettermname() +{ + settimer(baseline); + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); } -char *terminaltype; -char *envinit[2]; -int cleanup(); +terminaltypeok(s) +char *s; +{ + char buf[1024]; + + if (terminaltype == NULL) + return(1); + + /* + * tgetent() will return 1 if the type is known, and + * 0 if it is not known. If it returns -1, it couldn't + * open the database. But if we can't open the database, + * it won't help to say we failed, because we won't be + * able to verify anything else. So, we treat -1 like 1. + */ + if (tgetent(buf, s) == 0) + return(0); + return(1); +} /* * Get a pty, scan input lines. */ -doit(f, who) - int f; +doit(who) struct sockaddr_in *who; { char *host, *inet_ntoa(); - int i, p, t; - struct sgttyb b; + int t; struct hostent *hp; - int c; - int gotterminaltype = 0; /* - * Try to get a terminal type from the foreign host. + * Find an available pty to use. */ + pty = getpty(); + if (pty < 0) + fatal(net, "All network ports in use"); - { - static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; - - terminaltype = 0; - if (write(f, sbuf, sizeof sbuf) == -1) { - syslog(LOG_INFO, "write sbuf: %m\n"); - exit(1); - } - for (;;) { /* ugly, but we are VERY early */ - while ((c = Get(f)) != IAC) { - NIACCUM(c); - } - if ((c = Get(f)) == WILL) { - if ((c = Get(f)) == TELOPT_TTYPE) { - static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, - TELQUAL_SEND, IAC, SE }; - if (write(f, sbbuf, sizeof sbbuf) == -1) { - syslog(LOG_INFO, "write sbbuf: %m\n"); - exit(1); - } - break; - } else { - NIACCUM(IAC); - NIACCUM(WILL); - NIACCUM(c); - } - } else if (c == WONT) { - if ((c = Get(f)) == TELOPT_TTYPE) { - terminaltype = "TERM=network"; - break; - } else { - NIACCUM(IAC); - NIACCUM(WONT); - NIACCUM(c); - } - } else { - NIACCUM(IAC); - NIACCUM(c); - } - } - if (!terminaltype) { - for (;;) { - while ((c = Get(f)) != IAC) { - NIACCUM(c); - } - if ((c = Get(f)) != SB) { - NIACCUM(IAC); - NIACCUM(c); - } else if ((c = Get(f)) != TELOPT_TTYPE) { - NIACCUM(IAC); - NIACCUM(SB); - NIACCUM(c); - } else if ((c = Get(f)) != TELQUAL_IS) { - NIACCUM(IAC); - NIACCUM(SB); - NIACCUM(TELOPT_TTYPE); - NIACCUM(c); - } else { /* Yaaaay! */ - static char terminalname[5+41] = "TERM="; - - terminaltype = terminalname+strlen(terminalname); - - while (terminaltype < - (terminalname + sizeof terminalname-1)) { - if ((c = Get(f)) == IAC) { - if ((c = Get(f)) == SE) { - break; /* done */ - } else { - *terminaltype++ = IAC; /* ? */ - if (isupper(c)) { - c = tolower(c); - } - *terminaltype++ = c; - } - } else { - if (isupper(c)) { - c = tolower(c); - } - *terminaltype++ = c; /* accumulate name */ - } - } - *terminaltype = 0; - terminaltype = terminalname; - gotterminaltype = 1; - break; - } - } - } - envinit[0] = terminaltype; - envinit[1] = 0; - } - netip = netibuf; - - for (c = 'p'; c <= 's'; c++) { - struct stat stb; + t = getptyslave(); - 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; - } - } - fatal(f, "All network ports in use"); - /*NOTREACHED*/ -gotpty: - dup2(f, 0); - line[strlen("/dev/")] = 't'; - t = open("/dev/tty", O_RDWR); - if (t >= 0) { - ioctl(t, TIOCNOTTY, 0); - close(t); - } - t = open(line, O_RDWR); - if (t < 0) - fatalperror(f, line, errno); - ioctl(t, TIOCGETP, &b); - b.sg_flags = CRMOD|XTABS|ANYP; - ioctl(t, TIOCSETP, &b); - ioctl(p, TIOCGETP, &b); - b.sg_flags &= ~ECHO; - ioctl(p, TIOCSETP, &b); - hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), + /* get name of connected client */ + hp = gethostbyaddr((char *)&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); - close(p); - dup2(t, 0); - dup2(t, 1); - dup2(t, 2); - close(t); - environ = envinit; + /* - * -h : pass on name of host. - * -p : don't clobber the environment (so terminal type stays set). + * get terminal type. */ - execl("/bin/login", "login", "-h", host, - gotterminaltype ? "-p" : 0, 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); -} - -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); -} + getterminaltype(); + if (terminaltype == NULL) + terminaltype = "TERM=network"; + /* + * Start up the login process on the slave side of the terminal + */ + startslave(t, host); -/* - * Check a descriptor to see if out of band data exists on it. - */ - + telnet(net, pty); /* begin server processing */ + /*NOTREACHED*/ +} /* end of doit */ -stilloob(s) -int s; /* socket number */ -{ - static struct timeval timeout = { 0 }; - fd_set excepts; - int value; - - do { - FD_ZERO(&excepts); - FD_SET(s, &excepts); - value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); - } while ((value == -1) && (errno == EINTR)); - - if (value < 0) { - fatalperror(pty, "select", errno); - } - if (FD_ISSET(s, &excepts)) { - return 1; - } else { - return 0; - } -} - +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif MAXHOSTNAMELEN /* * Main loop. Select from pty and network, and * hand data to telnet receiver finite state machine. */ telnet(f, p) +int f, p; { int on = 1; char hostname[MAXHOSTNAMELEN]; - - net = f, pty = p; - ioctl(f, FIONBIO, &on); - ioctl(p, FIONBIO, &on); -#if defined(SO_OOBINLINE) - setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); -#endif /* defined(SO_OOBINLINE) */ - signal(SIGTSTP, SIG_IGN); - signal(SIGCHLD, cleanup); - setpgrp(0, 0); +#ifdef CRAY2 + int termstat(); + int interrupt(), sendbrk(); +#endif +#define TABBUFSIZ 512 + char defent[TABBUFSIZ]; + char defstrs[TABBUFSIZ]; +#undef TABBUFSIZ + char *HE; + char *HN; + char *IM; + void netflush(); + + /* + * Initialize the slc mapping table. + */ + get_slc_defaults(); /* - * Request to do remote echo and to suppress go ahead. + * Do some tests where it is desireable to wait for a response. + * Rather than doing them slowly, one at a time, do them all + * at once. */ - dooption(TELOPT_ECHO); - dooption(TELOPT_SGA); + if (!myopts[TELOPT_SGA]) + dooption(TELOPT_SGA); /* * Is the client side a 4.2 (NOT 4.3) system? We need to know this * because 4.2 clients are unable to deal with TCP urgent data. @@ -445,32 +380,164 @@ telnet(f, p) * WE, the server, sends it; it does NOT mean that the client will * echo the terminal input). */ - sprintf(nfrontp, doopt, TELOPT_ECHO); - nfrontp += sizeof doopt-2; - hisopts[TELOPT_ECHO] = OPT_ALWAYS_LOOK; + willoption(TELOPT_ECHO, 1); + +#ifdef LINEMODE + if (hisopts[TELOPT_LINEMODE] == OPT_NO) { + /* Query the peer for linemode support by trying to negotiate + * the linemode option. + */ + linemode = 1; + editmode = 0; + willoption(TELOPT_LINEMODE, 1); /* send do linemode */ + } +#endif /* LINEMODE */ /* - * Show banner that getty never gave. - * - * The banner includes some null's (for TELNET CR disambiguation), - * so we have to be somewhat complicated. + * Send along a couple of other options that we wish to negotiate. */ + willoption(TELOPT_NAWS, 1); + dooption(TELOPT_STATUS, 1); + flowmode = 1; /* default flow control state */ + willoption(TELOPT_LFLOW, 1); - gethostname(hostname, sizeof (hostname)); + /* + * Spin, waiting for a response from the DO ECHO. However, + * some REALLY DUMB telnets out there might not respond + * to the DO ECHO. So, we spin looking for NAWS, (most dumb + * telnets so far seem to respond with WONT for a DO that + * they don't understand...) because by the time we get the + * response, it will already have processed the DO ECHO. + * Kludge upon kludge. + */ + while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS]) + ttloop(); - bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); - nfrontp += sizeof BANNER1 - 1; - bcopy(hostname, nfrontp, strlen(hostname)); - nfrontp += strlen(hostname); - bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); - nfrontp += sizeof BANNER2 - 1; + /* + * On the off chance that the telnet client is broken and does not + * respond to the DO ECHO we sent, (after all, we did send the + * DO NAWS negotiation after the DO ECHO, and we won't get here + * until a response to the DO NAWS comes back) simulate the + * receipt of a will echo. This will also send a WONT ECHO + * to the client, since we assume that the client failed to + * respond because it believes that it is already in DO ECHO + * mode, which we do not want. + */ + if (hiswants[TELOPT_ECHO] == OPT_YES) + willoption(TELOPT_ECHO, 0); + + /* + * Finally, to clean things up, we turn on our echo. This + * will break stupid 4.2 telnets out of local terminal echo. + */ + + if (!myopts[TELOPT_ECHO]) + dooption(TELOPT_ECHO); + + /* + * Turn on packet mode, and default to line at at time mode. + */ + (void) ioctl(p, TIOCPKT, (char *)&on); +#ifdef LINEMODE + tty_setlinemode(1); + +# ifdef KLUDGELINEMODE + /* + * Continuing line mode support. If client does not support + * real linemode, attempt to negotiate kludge linemode by sending + * the do timing mark sequence. + */ + if (lmodetype < REAL_LINEMODE) + willoption(TELOPT_TM, 1); +# endif /* KLUDGELINEMODE */ +#endif /* LINEMODE */ /* * Call telrcv() once to pick up anything received during - * terminal type negotiation. + * terminal type negotiation, 4.2/4.3 determination, and + * linemode negotiation. */ telrcv(); + (void) ioctl(f, FIONBIO, (char *)&on); + (void) ioctl(p, FIONBIO, (char *)&on); +#ifdef CRAY2 + init_termdriver(f, p, interrupt, sendbrk); +#endif + +#if defined(SO_OOBINLINE) + (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); +#endif /* defined(SO_OOBINLINE) */ + +#ifdef SIGTSTP + (void) signal(SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + /* + * Ignoring SIGTTOU keeps the kernel from blocking us + * in ttioct() in /sys/tty.c. + */ + (void) signal(SIGTTOU, SIG_IGN); +#endif + + (void) signal(SIGCHLD, cleanup); + +#if defined(CRAY2) + /* + * Cray-2 will send a signal when pty modes are changed by slave + * side. Set up signal handler now. + */ + if ((int)signal(SIGUSR1, termstat) < 0) + perror("signal"); + else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) + perror("ioctl:TCSIGME"); + /* + * Make processing loop check terminal characteristics early on. + */ + termstat(); +#endif + + (void) setpgrp(0, 0); + + /* + * Show banner that getty never gave. + * + * We put the banner in the pty input buffer. This way, it + * gets carriage return null processing, etc., just like all + * other pty --> client data. + */ + + (void) gethostname(hostname, sizeof (hostname)); + + if (getent(defent, "default") == 1) { + char *getstr(); + char *cp=defstrs; + + HE = getstr("he", &cp); + HN = getstr("hn", &cp); + IM = getstr("im", &cp); + if (HN && *HN) + (void) strcpy(hostname, HN); + if (IM == 0) + IM = ""; + } else { +#ifdef CRAY + if (hostinfo == 0) + IM = 0; + else +#endif + IM = DEFAULT_IM; + HE = 0; + } + edithost(HE, hostname); + if (IM && *IM) + putf(IM, ptyibuf2); + + if (pcc) + (void) strncat(ptyibuf2, ptyip, pcc+1); + ptyip = ptyibuf2; + pcc = strlen(ptyip); + for (;;) { fd_set ibits, obits, xbits; register int c; @@ -478,6 +545,10 @@ telnet(f, p) if (ncc < 0 && pcc < 0) break; +#ifdef CRAY2 + if (needtermstat) + _termstat(); +#endif /* CRAY2 */ FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&xbits); @@ -558,12 +629,12 @@ telnet(f, p) if (SYNCHing) { int atmark; - ioctl(net, SIOCATMARK, (char *)&atmark); + (void) ioctl(net, SIOCATMARK, (char *)&atmark); if (atmark) { ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); if ((ncc == -1) && (errno == EINVAL)) { ncc = read(net, netibuf, sizeof (netibuf)); - if (clocks.didnetreceive < clocks.gotDM) { + if (sequenceIs(didnetreceive, gotDM)) { SYNCHing = stilloob(net); } } @@ -597,7 +668,42 @@ telnet(f, p) else { if (pcc <= 0) break; - ptyip = ptyibuf; +#ifndef CRAY2 +#ifdef LINEMODE + /* + * If ioctl from pty, pass it through net + */ + if (ptyibuf[0] & TIOCPKT_IOCTL) { + copy_termbuf(ptyibuf+1, pcc-1); + localstat(); + pcc = 1; + } +#endif LINEMODE + if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { + netclear(); /* clear buffer back */ + *nfrontp++ = IAC; + *nfrontp++ = DM; + neturg = nfrontp-1; /* off by one XXX */ + } + if (hisopts[TELOPT_LFLOW] && + (ptyibuf[0] & + (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { + (void) sprintf(nfrontp, "%c%c%c%c%c%c", + IAC, SB, TELOPT_LFLOW, + ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, + IAC, SE); + nfrontp += 6; + } + pcc--; + ptyip = ptyibuf+1; +#else /* CRAY2 */ + if (!uselinemode) { + pcc = term_output(ptyibuf, ptyibuf2, + pcc, BUFSIZ); + ptyip = ptyibuf2; + } else + ptyip = ptyibuf; +#endif /* CRAY2 */ } } @@ -607,8 +713,13 @@ telnet(f, p) c = *ptyip++ & 0377, pcc--; if (c == IAC) *nfrontp++ = c; +#ifdef CRAY2 + else if (c == '\n' && + myopts[TELOPT_BINARY] == OPT_NO && newmap) + *nfrontp++ = '\r'; +#endif /* CRAY2 */ *nfrontp++ = c; - if (c == '\r') { + if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { if (pcc > 0 && ((*ptyip & 0377) == '\n')) { *nfrontp++ = *ptyip++ & 0377; pcc--; @@ -624,378 +735,13 @@ telnet(f, p) ptyflush(); } cleanup(); -} +} /* end of telnet */ -/* - * State for recv fsm - */ -#define TS_DATA 0 /* base state */ -#define TS_IAC 1 /* look for double IAC's */ -#define TS_CR 2 /* CR-LF ->'s CR */ -#define TS_SB 3 /* throw away begin's... */ -#define TS_SE 4 /* ...end's (suboption negotiation) */ -#define TS_WILL 5 /* will option negotiation */ -#define TS_WONT 6 /* wont " */ -#define TS_DO 7 /* do " */ -#define TS_DONT 8 /* dont " */ - -telrcv() -{ - register int c; - static int state = TS_DATA; - - while (ncc > 0) { - if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) - return; - c = *netip++ & 0377, ncc--; - switch (state) { - - case TS_CR: - state = TS_DATA; - if ((c == 0) || (c == '\n')) { - break; - } - /* FALL THROUGH */ - - case TS_DATA: - if (c == IAC) { - state = TS_IAC; - break; - } - if (inter > 0) - break; - /* - * We map \r\n ==> \n, since \r\n says - * that we want to be in column 1 of the next - * printable line, and \n is the standard - * unix way of saying that (\r is only good - * if CRMOD is set, which it normally is). - */ - if ((myopts[TELOPT_BINARY] == OPT_DONT) && c == '\r') { - if ((ncc > 0) && ('\n' == *netip)) { - netip++; ncc--; - c = '\n'; - } else { - state = TS_CR; - } - } - *pfrontp++ = c; - break; - - case TS_IAC: - switch (c) { - - /* - * Send the process on the pty side an - * interrupt. Do this with a NULL or - * interrupt char; depending on the tty mode. - */ - case IP: - interrupt(); - break; - - case BREAK: - sendbrk(); - break; - - /* - * Are You There? - */ - case AYT: - strcpy(nfrontp, "\r\n[Yes]\r\n"); - nfrontp += 9; - break; - - /* - * Abort Output - */ - case AO: { - struct ltchars tmpltc; - - ptyflush(); /* half-hearted */ - ioctl(pty, TIOCGLTC, &tmpltc); - if (tmpltc.t_flushc != '\377') { - *pfrontp++ = tmpltc.t_flushc; - } - netclear(); /* clear buffer back */ - *nfrontp++ = IAC; - *nfrontp++ = DM; - neturg = nfrontp-1; /* off by one XXX */ - break; - } - - /* - * Erase Character and - * Erase Line - */ - case EC: - case EL: { - struct sgttyb b; - char ch; - - ptyflush(); /* half-hearted */ - ioctl(pty, TIOCGETP, &b); - ch = (c == EC) ? - b.sg_erase : b.sg_kill; - if (ch != '\377') { - *pfrontp++ = ch; - } - break; - } - - /* - * Check for urgent data... - */ - case DM: - SYNCHing = stilloob(net); - settimer(gotDM); - break; - - - /* - * Begin option subnegotiation... - */ - case SB: - state = TS_SB; - continue; - - case WILL: - state = TS_WILL; - continue; - - case WONT: - state = TS_WONT; - continue; - - case DO: - state = TS_DO; - continue; - - case DONT: - state = TS_DONT; - continue; - - case IAC: - *pfrontp++ = c; - break; - } - state = TS_DATA; - break; - - case TS_SB: - if (c == IAC) { - state = TS_SE; - } else { - SB_ACCUM(c); - } - break; - - case TS_SE: - if (c != SE) { - if (c != IAC) { - SB_ACCUM(IAC); - } - SB_ACCUM(c); - state = TS_SB; - } else { - SB_TERM(); - suboption(); /* handle sub-option */ - state = TS_DATA; - } - break; - - case TS_WILL: - if (hisopts[c] != OPT_WILL) - willoption(c); - state = TS_DATA; - continue; - - case TS_WONT: - if (hisopts[c] != OPT_WONT) - wontoption(c); - state = TS_DATA; - continue; - - case TS_DO: - if (myopts[c] != OPT_DO) - dooption(c); - state = TS_DATA; - continue; - - case TS_DONT: - if (myopts[c] != OPT_DONT) { - dontoption(c); - } - state = TS_DATA; - continue; - - default: - syslog(LOG_ERR, "telnetd: panic state=%d\n", state); - printf("telnetd: panic state=%d\n", state); - exit(1); - } - } -} - -willoption(option) - int option; -{ - char *fmt; - - switch (option) { - - case TELOPT_BINARY: - mode(RAW, 0); - fmt = doopt; - break; - - case TELOPT_ECHO: - not42 = 0; /* looks like a 4.2 system */ - /* - * Now, in a 4.2 system, to break them out of ECHOing - * (to the terminal) mode, we need to send a "WILL ECHO". - * Kludge upon kludge! - */ - if (myopts[TELOPT_ECHO] == OPT_DO) { - dooption(TELOPT_ECHO); - } - fmt = dont; - break; - - case TELOPT_TTYPE: - case TELOPT_SGA: - fmt = doopt; - break; - - case TELOPT_TM: - fmt = dont; - break; - - default: - fmt = dont; - break; - } - if (fmt == doopt) { - hisopts[option] = OPT_WILL; - } else { - hisopts[option] = OPT_WONT; - } - sprintf(nfrontp, fmt, option); - nfrontp += sizeof (dont) - 2; -} - -wontoption(option) - int option; -{ - char *fmt; - - switch (option) { - case TELOPT_ECHO: - not42 = 1; /* doesn't seem to be a 4.2 system */ - break; - - case TELOPT_BINARY: - mode(0, RAW); - break; - } - fmt = dont; - hisopts[option] = OPT_WONT; - sprintf(nfrontp, fmt, option); - nfrontp += sizeof (doopt) - 2; -} - -dooption(option) - int option; -{ - char *fmt; - - switch (option) { - - case TELOPT_TM: - fmt = wont; - break; - - case TELOPT_ECHO: - mode(ECHO|CRMOD, 0); - fmt = will; - break; - - case TELOPT_BINARY: - mode(RAW, 0); - fmt = will; - break; - - case TELOPT_SGA: - fmt = will; - break; - - default: - fmt = wont; - break; - } - if (fmt == will) { - myopts[option] = OPT_DO; - } else { - myopts[option] = OPT_DONT; - } - sprintf(nfrontp, fmt, option); - nfrontp += sizeof (doopt) - 2; -} - - -dontoption(option) -int option; -{ - char *fmt; - - switch (option) { - case TELOPT_ECHO: /* we should stop echoing */ - mode(0, ECHO|CRMOD); - fmt = wont; - break; - default: - fmt = wont; - break; - } - if (fmt = wont) { - myopts[option] = OPT_DONT; - } else { - myopts[option] = OPT_DO; - } - sprintf(nfrontp, fmt, option); - nfrontp += sizeof (wont) - 2; -} - -/* - * suboption() - * - * Look at the sub-option buffer, and try to be helpful to the other - * side. - * - * Currently we recognize: - * - * (nothing - we only do terminal type at start-up time) - */ - -suboption() -{ - switch (subbuffer[0]&0xff) { - default: - ; - } -} - -mode(on, off) - int on, off; -{ - struct sgttyb b; - - ptyflush(); - ioctl(pty, TIOCGETP, &b); - b.sg_flags |= on; - b.sg_flags &= ~off; - ioctl(pty, TIOCSETP, &b); -} +#ifndef TCSIG +# ifdef TIOCSIG +# define TCSIG TIOCSIG +# endif +#endif /* * Send interrupt to process on other side of pty. @@ -1004,17 +750,14 @@ mode(on, off) */ interrupt() { - struct sgttyb b; - struct tchars tchars; - ptyflush(); /* half-hearted */ - ioctl(pty, TIOCGETP, &b); - if (b.sg_flags & RAW) { - *pfrontp++ = '\0'; - return; - } - *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? - '\177' : tchars.t_intrc; + +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGINT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177'; +#endif /* TCSIG */ } /* @@ -1024,244 +767,30 @@ interrupt() */ sendbrk() { - struct sgttyb b; - struct tchars tchars; - ptyflush(); /* half-hearted */ - ioctl(pty, TIOCGETP, &b); - if (b.sg_flags & RAW) { - *pfrontp++ = '\0'; - return; - } - *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? - '\034' : tchars.t_quitc; -} - -ptyflush() -{ - int n; - - if ((n = pfrontp - pbackp) > 0) - n = write(pty, pbackp, n); - if (n < 0) - return; - pbackp += n; - if (pbackp == pfrontp) - pbackp = pfrontp = ptyobuf; -} - -/* - * nextitem() - * - * Return the address of the next "item" in the TELNET data - * stream. This will be the address of the next character if - * the current address is a user data character, or it will - * be the address of the character following the TELNET command - * if the current address is a TELNET IAC ("I Am a Command") - * character. - */ - -char * -nextitem(current) -char *current; -{ - if ((*current&0xff) != IAC) { - return current+1; - } - switch (*(current+1)&0xff) { - case DO: - case DONT: - case WILL: - case WONT: - return current+3; - case SB: /* loop forever looking for the SE */ - { - register char *look = current+2; - - for (;;) { - if ((*look++&0xff) == IAC) { - if ((*look++&0xff) == SE) { - return look; - } - } - } - } - default: - return current+2; - } -} - - -/* - * netclear() - * - * We are about to do a TELNET SYNCH operation. Clear - * the path to the network. - * - * Things are a bit tricky since we may have sent the first - * byte or so of a previous TELNET command into the network. - * So, we have to scan the network buffer from the beginning - * until we are up to where we want to be. - * - * A side effect of what we do, just to keep things - * simple, is to clear the urgent data pointer. The principal - * caller should be setting the urgent data pointer AFTER calling - * us in any case. - */ - -netclear() -{ - register char *thisitem, *next; - char *good; -#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ - ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) - - thisitem = netobuf; - - while ((next = nextitem(thisitem)) <= nbackp) { - thisitem = next; - } - - /* Now, thisitem is first before/at boundary. */ - - good = netobuf; /* where the good bytes go */ - - while (nfrontp > thisitem) { - if (wewant(thisitem)) { - int length; - - next = thisitem; - do { - next = nextitem(next); - } while (wewant(next) && (nfrontp > next)); - length = next-thisitem; - bcopy(thisitem, good, length); - good += length; - thisitem = next; - } else { - thisitem = nextitem(thisitem); - } - } - - nbackp = netobuf; - nfrontp = good; /* next byte to be sent */ - neturg = 0; +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGQUIT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034'; +#endif /* TCSIG */ } - -/* - * netflush - * Send as much data as possible to the network, - * handling requests for urgent data. - */ - -netflush() +sendsusp() { - int n; - - if ((n = nfrontp - nbackp) > 0) { - /* - * if no urgent data, or if the other side appears to be an - * old 4.2 client (and thus unable to survive TCP urgent data), - * write the entire buffer in non-OOB mode. - */ - if ((neturg == 0) || (not42 == 0)) { - n = write(net, nbackp, n); /* normal write */ - } else { - n = neturg - nbackp; - /* - * In 4.2 (and 4.3) systems, there is some question about - * what byte in a sendOOB operation is the "OOB" data. - * To make ourselves compatible, we only send ONE byte - * out of band, the one WE THINK should be OOB (though - * we really have more the TCP philosophy of urgent data - * rather than the Unix philosophy of OOB data). - */ - if (n > 1) { - n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ - } else { - n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ - } - } - } - if (n < 0) { - if (errno == EWOULDBLOCK) - return; - /* should blow this guy away... */ - return; - } - nbackp += n; - if (nbackp >= neturg) { - neturg = 0; - } - if (nbackp == nfrontp) { - nbackp = nfrontp = netobuf; - } +#ifdef SIGTSTP + ptyflush(); /* half-hearted */ +# ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGTSTP); +# else /* TCSIG */ + *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032'; +# endif /* TCSIG */ +#endif /* SIGTSTP */ } -cleanup() +doeof() { + init_termbuf(); - rmut(); - vhangup(); /* XXX */ - shutdown(net, 2); - exit(1); -} - -#include - -struct utmp wtmp; -char wtmpf[] = "/usr/adm/wtmp"; -char utmpf[] = "/etc/utmp"; -#define SCPYN(a, b) strncpy(a, b, sizeof(a)) -#define SCMPN(a, b) strncmp(a, b, sizeof(a)) - -rmut() -{ - register f; - int found = 0; - struct utmp *u, *utmp; - int nutmp; - struct stat statbf; - - f = open(utmpf, O_RDWR); - if (f >= 0) { - 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) { - f = open(wtmpf, O_WRONLY|O_APPEND); - if (f >= 0) { - SCPYN(wtmp.ut_line, line+5); - SCPYN(wtmp.ut_name, ""); - SCPYN(wtmp.ut_host, ""); - time(&wtmp.ut_time); - write(f, (char *)&wtmp, sizeof(wtmp)); - close(f); - } - } - chmod(line, 0666); - chown(line, 0, 0); - line[strlen("/dev/")] = 'p'; - chmod(line, 0666); - chown(line, 0, 0); + *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004'; }