121182Sdist /* 233687Sbostic * Copyright (c) 1983, 1986 Regents of the University of California. 333687Sbostic * All rights reserved. 433687Sbostic * 533687Sbostic * Redistribution and use in source and binary forms are permitted 634778Sbostic * provided that the above copyright notice and this paragraph are 734778Sbostic * duplicated in all such forms and that any documentation, 834778Sbostic * advertising materials, and other materials related to such 934778Sbostic * distribution and use acknowledge that the software was developed 1034778Sbostic * by the University of California, Berkeley. The name of the 1134778Sbostic * University may not be used to endorse or promote products derived 1234778Sbostic * from this software without specific prior written permission. 1334778Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434778Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534778Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621182Sdist */ 1721182Sdist 186295Sroot #ifndef lint 1921182Sdist char copyright[] = 2033687Sbostic "@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\ 2121182Sdist All rights reserved.\n"; 2233687Sbostic #endif /* not lint */ 236295Sroot 2421182Sdist #ifndef lint 25*36878Skarels static char sccsid[] = "@(#)telnetd.c 5.31 (Berkeley) 02/23/89"; 2633687Sbostic #endif /* not lint */ 2721182Sdist 286002Sroot /* 2927898Skarels * Telnet server. 306002Sroot */ 3127898Skarels #include <sys/param.h> 329218Ssam #include <sys/socket.h> 3313608Ssam #include <sys/wait.h> 3417583Ssam #include <sys/file.h> 3520188Skarels #include <sys/stat.h> 3627185Sminshall #include <sys/time.h> 379218Ssam 389218Ssam #include <netinet/in.h> 399218Ssam 4012216Ssam #include <arpa/telnet.h> 4112216Ssam 426002Sroot #include <stdio.h> 436002Sroot #include <signal.h> 446002Sroot #include <errno.h> 456002Sroot #include <sgtty.h> 468346Ssam #include <netdb.h> 4717187Sralph #include <syslog.h> 4827649Sminshall #include <ctype.h> 499218Ssam 5027983Sminshall #define OPT_NO 0 /* won't do this option */ 5127983Sminshall #define OPT_YES 1 /* will do this option */ 5227983Sminshall #define OPT_YES_BUT_ALWAYS_LOOK 2 5327983Sminshall #define OPT_NO_BUT_ALWAYS_LOOK 3 546002Sroot char hisopts[256]; 556002Sroot char myopts[256]; 566002Sroot 576002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 586002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 596002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 606002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 616002Sroot 626002Sroot /* 636002Sroot * I/O data buffers, pointers, and counters. 646002Sroot */ 656002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 6627649Sminshall 676002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 6827649Sminshall 696002Sroot char netibuf[BUFSIZ], *netip = netibuf; 7027649Sminshall #define NIACCUM(c) { *netip++ = c; \ 7127649Sminshall ncc++; \ 7227649Sminshall } 7327649Sminshall 746388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 7527185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 7627649Sminshall /* the remote system seems to NOT be an old 4.2 */ 7727649Sminshall int not42 = 1; 7827649Sminshall 7933271Sminshall #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r" 8027649Sminshall 8127983Sminshall /* buffer for sub-options */ 8227983Sminshall char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 8327649Sminshall #define SB_CLEAR() subpointer = subbuffer; 8427983Sminshall #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 8527649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 8627649Sminshall *subpointer++ = (c); \ 8727649Sminshall } 8827983Sminshall #define SB_GET() ((*subpointer++)&0xff) 8927983Sminshall #define SB_EOF() (subpointer >= subend) 9027649Sminshall 916002Sroot int pcc, ncc; 926002Sroot 936002Sroot int pty, net; 946002Sroot int inter; 9513799Ssam extern char **environ; 966002Sroot extern int errno; 9720188Skarels char *line; 9827185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 9927185Sminshall /* 10027185Sminshall * The following are some clocks used to decide how to interpret 10127185Sminshall * the relationship between various variables. 10227185Sminshall */ 1036002Sroot 10427185Sminshall struct { 10527185Sminshall int 10627185Sminshall system, /* what the current time is */ 10727185Sminshall echotoggle, /* last time user entered echo character */ 10827185Sminshall modenegotiated, /* last time operating mode negotiated */ 10927185Sminshall didnetreceive, /* last time we read data from network */ 11027983Sminshall ttypeopt, /* ttype will/won't received */ 11127983Sminshall ttypesubopt, /* ttype subopt is received */ 11227983Sminshall getterminal, /* time started to get terminal information */ 11327185Sminshall gotDM; /* when did we last see a data mark */ 11427185Sminshall } clocks; 11527185Sminshall 11627983Sminshall #define settimer(x) (clocks.x = ++clocks.system) 11727983Sminshall #define sequenceIs(x,y) (clocks.x < clocks.y) 11827185Sminshall 1196002Sroot main(argc, argv) 1206002Sroot char *argv[]; 1216002Sroot { 12216371Skarels struct sockaddr_in from; 12317156Ssam int on = 1, fromlen; 1246002Sroot 12527185Sminshall #if defined(DEBUG) 12627185Sminshall { 12727185Sminshall int s, ns, foo; 12827185Sminshall struct servent *sp; 12927185Sminshall static struct sockaddr_in sin = { AF_INET }; 13027185Sminshall 13127185Sminshall sp = getservbyname("telnet", "tcp"); 13227185Sminshall if (sp == 0) { 13327185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 13427185Sminshall exit(1); 13527185Sminshall } 13627185Sminshall sin.sin_port = sp->s_port; 13727185Sminshall argc--, argv++; 13827185Sminshall if (argc > 0) { 13927185Sminshall sin.sin_port = atoi(*argv); 14027185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 14127185Sminshall } 14227185Sminshall 14327185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 14427185Sminshall if (s < 0) { 14527185Sminshall perror("telnetd: socket");; 14627185Sminshall exit(1); 14727185Sminshall } 14827185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 14927185Sminshall perror("bind"); 15027185Sminshall exit(1); 15127185Sminshall } 15227185Sminshall if (listen(s, 1) < 0) { 15327185Sminshall perror("listen"); 15427185Sminshall exit(1); 15527185Sminshall } 15627185Sminshall foo = sizeof sin; 15727185Sminshall ns = accept(s, &sin, &foo); 15827185Sminshall if (ns < 0) { 15927185Sminshall perror("accept"); 16027185Sminshall exit(1); 16127185Sminshall } 16227185Sminshall dup2(ns, 0); 16327185Sminshall close(s); 16427185Sminshall } 16527185Sminshall #endif /* defined(DEBUG) */ 16624855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 16716371Skarels fromlen = sizeof (from); 16816371Skarels if (getpeername(0, &from, &fromlen) < 0) { 16916371Skarels fprintf(stderr, "%s: ", argv[0]); 17016371Skarels perror("getpeername"); 17116371Skarels _exit(1); 1728346Ssam } 17317156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 17417187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 17510418Ssam } 17616371Skarels doit(0, &from); 1776002Sroot } 1786002Sroot 17927983Sminshall char *terminaltype = 0; 18027983Sminshall char *envinit[2]; 18127983Sminshall int cleanup(); 18227649Sminshall 18327649Sminshall /* 18427983Sminshall * ttloop 18527649Sminshall * 18627983Sminshall * A small subroutine to flush the network output buffer, get some data 18727983Sminshall * from the network, and pass it through the telnet state machine. We 18827983Sminshall * also flush the pty input buffer (by dropping its data) if it becomes 18927983Sminshall * too full. 19027983Sminshall */ 19127983Sminshall 19227983Sminshall void 19327983Sminshall ttloop() 19427983Sminshall { 19527983Sminshall if (nfrontp-nbackp) { 19627983Sminshall netflush(); 19727983Sminshall } 19827983Sminshall ncc = read(net, netibuf, sizeof netibuf); 19927983Sminshall if (ncc < 0) { 20027983Sminshall syslog(LOG_INFO, "ttloop: read: %m\n"); 20128044Sminshall exit(1); 20228044Sminshall } else if (ncc == 0) { 20328044Sminshall syslog(LOG_INFO, "ttloop: peer died: %m\n"); 20428044Sminshall exit(1); 20527983Sminshall } 20627983Sminshall netip = netibuf; 20727983Sminshall telrcv(); /* state machine */ 20827983Sminshall if (ncc > 0) { 20927983Sminshall pfrontp = pbackp = ptyobuf; 21027983Sminshall telrcv(); 21127983Sminshall } 21227983Sminshall } 21327983Sminshall 21427983Sminshall /* 21527983Sminshall * getterminaltype 21627649Sminshall * 21727983Sminshall * Ask the other end to send along its terminal type. 21827983Sminshall * Output is the variable terminaltype filled in. 21927649Sminshall */ 22027649Sminshall 22127983Sminshall void 22227983Sminshall getterminaltype() 22327649Sminshall { 22427983Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 22527649Sminshall 22627983Sminshall settimer(getterminal); 22727983Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 22827983Sminshall nfrontp += sizeof sbuf; 22928044Sminshall hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; 23027983Sminshall while (sequenceIs(ttypeopt, getterminal)) { 23127983Sminshall ttloop(); 23227649Sminshall } 23327983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES) { 23427983Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 23527983Sminshall 23627983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 23727983Sminshall nfrontp += sizeof sbbuf; 23827983Sminshall while (sequenceIs(ttypesubopt, getterminal)) { 23927983Sminshall ttloop(); 24027983Sminshall } 24127983Sminshall } 24227649Sminshall } 24327649Sminshall 2446002Sroot /* 2456002Sroot * Get a pty, scan input lines. 2466002Sroot */ 24712683Ssam doit(f, who) 24812683Ssam int f; 24912683Ssam struct sockaddr_in *who; 2506002Sroot { 25120188Skarels char *host, *inet_ntoa(); 25217583Ssam int i, p, t; 2536002Sroot struct sgttyb b; 25412683Ssam struct hostent *hp; 25527649Sminshall int c; 2566002Sroot 25720188Skarels for (c = 'p'; c <= 's'; c++) { 25820188Skarels struct stat stb; 25920188Skarels 26020188Skarels line = "/dev/ptyXX"; 26120188Skarels line[strlen("/dev/pty")] = c; 26220188Skarels line[strlen("/dev/ptyp")] = '0'; 26320188Skarels if (stat(line, &stb) < 0) 26420188Skarels break; 26517583Ssam for (i = 0; i < 16; i++) { 26634424Sbostic line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; 26734424Sbostic p = open(line, O_RDWR); 26817583Ssam if (p > 0) 26917583Ssam goto gotpty; 27017583Ssam } 2716002Sroot } 2729244Ssam fatal(f, "All network ports in use"); 2739244Ssam /*NOTREACHED*/ 2746002Sroot gotpty: 2756002Sroot dup2(f, 0); 27620188Skarels line[strlen("/dev/")] = 't'; 27717583Ssam t = open("/dev/tty", O_RDWR); 2786002Sroot if (t >= 0) { 2796002Sroot ioctl(t, TIOCNOTTY, 0); 2806002Sroot close(t); 2816002Sroot } 28220188Skarels t = open(line, O_RDWR); 2839244Ssam if (t < 0) 28434424Sbostic fatalperror(f, line); 28534424Sbostic if (fchmod(t, 0)) 28634424Sbostic fatalperror(f, line); 28734424Sbostic (void)signal(SIGHUP, SIG_IGN); 28834424Sbostic vhangup(); 28934424Sbostic (void)signal(SIGHUP, SIG_DFL); 29034424Sbostic t = open(line, O_RDWR); 29134424Sbostic if (t < 0) 29234424Sbostic fatalperror(f, line); 2936002Sroot ioctl(t, TIOCGETP, &b); 2946388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 2956002Sroot ioctl(t, TIOCSETP, &b); 2966388Ssam ioctl(p, TIOCGETP, &b); 2978379Ssam b.sg_flags &= ~ECHO; 2986388Ssam ioctl(p, TIOCSETP, &b); 29912683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 30012683Ssam who->sin_family); 30112683Ssam if (hp) 30212683Ssam host = hp->h_name; 30312683Ssam else 30417444Sralph host = inet_ntoa(who->sin_addr); 30527983Sminshall 30627983Sminshall net = f; 30727983Sminshall pty = p; 30827983Sminshall 30927983Sminshall /* 31027983Sminshall * get terminal type. 31127983Sminshall */ 31227983Sminshall getterminaltype(); 31327983Sminshall 3149244Ssam if ((i = fork()) < 0) 31534424Sbostic fatalperror(f, "fork"); 3166002Sroot if (i) 3176002Sroot telnet(f, p); 3186002Sroot close(f); 3196002Sroot close(p); 3206002Sroot dup2(t, 0); 3216002Sroot dup2(t, 1); 3226002Sroot dup2(t, 2); 3236002Sroot close(t); 32427983Sminshall envinit[0] = terminaltype; 32527983Sminshall envinit[1] = 0; 32613799Ssam environ = envinit; 32727649Sminshall /* 32827649Sminshall * -h : pass on name of host. 32927983Sminshall * WARNING: -h is accepted by login if and only if 33027983Sminshall * getuid() == 0. 33127649Sminshall * -p : don't clobber the environment (so terminal type stays set). 33227649Sminshall */ 33327649Sminshall execl("/bin/login", "login", "-h", host, 33427983Sminshall terminaltype ? "-p" : 0, 0); 335*36878Skarels syslog(LOG_ERR, "/bin/login: %m\n"); 336*36878Skarels fatalperror(2, "/bin/login"); 3379244Ssam /*NOTREACHED*/ 3389244Ssam } 3399244Ssam 3409244Ssam fatal(f, msg) 3419244Ssam int f; 3429244Ssam char *msg; 3439244Ssam { 3449244Ssam char buf[BUFSIZ]; 3459244Ssam 34617583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3479244Ssam (void) write(f, buf, strlen(buf)); 3486002Sroot exit(1); 3496002Sroot } 3506002Sroot 35134424Sbostic fatalperror(f, msg) 3529244Ssam int f; 3539244Ssam char *msg; 3549244Ssam { 3559244Ssam char buf[BUFSIZ]; 3569244Ssam extern char *sys_errlist[]; 3579244Ssam 35817583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3599244Ssam fatal(f, buf); 3609244Ssam } 3619244Ssam 36227185Sminshall 3636002Sroot /* 36427185Sminshall * Check a descriptor to see if out of band data exists on it. 36527185Sminshall */ 36627185Sminshall 36727185Sminshall 36827185Sminshall stilloob(s) 36927185Sminshall int s; /* socket number */ 37027185Sminshall { 37127185Sminshall static struct timeval timeout = { 0 }; 37227185Sminshall fd_set excepts; 37327185Sminshall int value; 37427185Sminshall 37527185Sminshall do { 37627185Sminshall FD_ZERO(&excepts); 37727185Sminshall FD_SET(s, &excepts); 37827185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 37927898Skarels } while ((value == -1) && (errno == EINTR)); 38027185Sminshall 38127185Sminshall if (value < 0) { 38234424Sbostic fatalperror(pty, "select"); 38327185Sminshall } 38427185Sminshall if (FD_ISSET(s, &excepts)) { 38527185Sminshall return 1; 38627185Sminshall } else { 38727185Sminshall return 0; 38827185Sminshall } 38927185Sminshall } 39027185Sminshall 39127185Sminshall /* 3926002Sroot * Main loop. Select from pty and network, and 3936002Sroot * hand data to telnet receiver finite state machine. 3946002Sroot */ 3956002Sroot telnet(f, p) 3966002Sroot { 3976002Sroot int on = 1; 39827898Skarels char hostname[MAXHOSTNAMELEN]; 39933271Sminshall #define TABBUFSIZ 512 40033271Sminshall char defent[TABBUFSIZ]; 40133271Sminshall char defstrs[TABBUFSIZ]; 40233271Sminshall #undef TABBUFSIZ 40333271Sminshall char *HE; 40433271Sminshall char *HN; 40533271Sminshall char *IM; 4066002Sroot 4076002Sroot ioctl(f, FIONBIO, &on); 4086002Sroot ioctl(p, FIONBIO, &on); 40933267Sminshall ioctl(p, TIOCPKT, &on); 41027649Sminshall #if defined(SO_OOBINLINE) 41127649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 41227649Sminshall #endif /* defined(SO_OOBINLINE) */ 4136002Sroot signal(SIGTSTP, SIG_IGN); 41432400Sminshall /* 41532400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 41632400Sminshall * in ttioctl() in /sys/tty.c. 41732400Sminshall */ 41832400Sminshall signal(SIGTTOU, SIG_IGN); 41913028Ssam signal(SIGCHLD, cleanup); 42026083Slepreau setpgrp(0, 0); 4216002Sroot 4228379Ssam /* 42327185Sminshall * Request to do remote echo and to suppress go ahead. 4248379Ssam */ 42527983Sminshall if (!myopts[TELOPT_ECHO]) { 42627983Sminshall dooption(TELOPT_ECHO); 42727983Sminshall } 42827983Sminshall if (!myopts[TELOPT_SGA]) { 42927983Sminshall dooption(TELOPT_SGA); 43027983Sminshall } 43112713Ssam /* 43227649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 43327649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 43427649Sminshall * 43527649Sminshall * To find out, we send out a "DO ECHO". If the remote system 43627649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 43727649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 43827649Sminshall * WE, the server, sends it; it does NOT mean that the client will 43927649Sminshall * echo the terminal input). 44027649Sminshall */ 44132452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 44227649Sminshall nfrontp += sizeof doopt-2; 44327983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 44427649Sminshall 44527649Sminshall /* 44612713Ssam * Show banner that getty never gave. 44727797Sminshall * 44833271Sminshall * We put the banner in the pty input buffer. This way, it 44933271Sminshall * gets carriage return null processing, etc., just like all 45033271Sminshall * other pty --> client data. 45112713Ssam */ 45227797Sminshall 45312713Ssam gethostname(hostname, sizeof (hostname)); 45433271Sminshall if (getent(defent, "default") == 1) { 45533271Sminshall char *getstr(); 45633271Sminshall char *p=defstrs; 45727649Sminshall 45833271Sminshall HE = getstr("he", &p); 45933271Sminshall HN = getstr("hn", &p); 46033271Sminshall IM = getstr("im", &p); 46133271Sminshall if (HN && *HN) 46233271Sminshall strcpy(hostname, HN); 46333271Sminshall edithost(HE, hostname); 46433271Sminshall if (IM && *IM) 46534424Sbostic putf(IM, ptyibuf+1); 46633271Sminshall } else { 46733271Sminshall sprintf(ptyibuf+1, BANNER, hostname); 46833271Sminshall } 46927797Sminshall 47033271Sminshall ptyip = ptyibuf+1; /* Prime the pump */ 47133271Sminshall pcc = strlen(ptyip); /* ditto */ 47233271Sminshall 47333267Sminshall /* Clear ptybuf[0] - where the packet information is received */ 47433267Sminshall ptyibuf[0] = 0; 47533271Sminshall 47627649Sminshall /* 47727649Sminshall * Call telrcv() once to pick up anything received during 47827649Sminshall * terminal type negotiation. 47927649Sminshall */ 48027649Sminshall telrcv(); 48127649Sminshall 4826002Sroot for (;;) { 48327185Sminshall fd_set ibits, obits, xbits; 4846002Sroot register int c; 4856002Sroot 48627185Sminshall if (ncc < 0 && pcc < 0) 48727185Sminshall break; 48827185Sminshall 48927185Sminshall FD_ZERO(&ibits); 49027185Sminshall FD_ZERO(&obits); 49127185Sminshall FD_ZERO(&xbits); 4926002Sroot /* 4936002Sroot * Never look for input if there's still 4946002Sroot * stuff in the corresponding output buffer 4956002Sroot */ 49627185Sminshall if (nfrontp - nbackp || pcc > 0) { 49727185Sminshall FD_SET(f, &obits); 49833267Sminshall FD_SET(p, &xbits); 49927185Sminshall } else { 50027185Sminshall FD_SET(p, &ibits); 50127185Sminshall } 50227185Sminshall if (pfrontp - pbackp || ncc > 0) { 50327185Sminshall FD_SET(p, &obits); 50427185Sminshall } else { 50527185Sminshall FD_SET(f, &ibits); 50627185Sminshall } 50727185Sminshall if (!SYNCHing) { 50827185Sminshall FD_SET(f, &xbits); 50927185Sminshall } 51027185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 51127185Sminshall (struct timeval *)0)) < 1) { 51227185Sminshall if (c == -1) { 51327185Sminshall if (errno == EINTR) { 51427185Sminshall continue; 51527185Sminshall } 51627185Sminshall } 5176002Sroot sleep(5); 5186002Sroot continue; 5196002Sroot } 5206002Sroot 5216002Sroot /* 52227185Sminshall * Any urgent data? 52327185Sminshall */ 52427185Sminshall if (FD_ISSET(net, &xbits)) { 52527185Sminshall SYNCHing = 1; 52627185Sminshall } 52727185Sminshall 52827185Sminshall /* 5296002Sroot * Something to read from the network... 5306002Sroot */ 53127185Sminshall if (FD_ISSET(net, &ibits)) { 53227649Sminshall #if !defined(SO_OOBINLINE) 53327185Sminshall /* 53427898Skarels * In 4.2 (and 4.3 beta) systems, the 53527185Sminshall * OOB indication and data handling in the kernel 53627185Sminshall * is such that if two separate TCP Urgent requests 53727185Sminshall * come in, one byte of TCP data will be overlaid. 53827185Sminshall * This is fatal for Telnet, but we try to live 53927185Sminshall * with it. 54027185Sminshall * 54127185Sminshall * In addition, in 4.2 (and...), a special protocol 54227185Sminshall * is needed to pick up the TCP Urgent data in 54327185Sminshall * the correct sequence. 54427185Sminshall * 54527185Sminshall * What we do is: if we think we are in urgent 54627185Sminshall * mode, we look to see if we are "at the mark". 54727185Sminshall * If we are, we do an OOB receive. If we run 54827185Sminshall * this twice, we will do the OOB receive twice, 54927185Sminshall * but the second will fail, since the second 55027185Sminshall * time we were "at the mark", but there wasn't 55127185Sminshall * any data there (the kernel doesn't reset 55227185Sminshall * "at the mark" until we do a normal read). 55327185Sminshall * Once we've read the OOB data, we go ahead 55427185Sminshall * and do normal reads. 55527185Sminshall * 55627185Sminshall * There is also another problem, which is that 55727185Sminshall * since the OOB byte we read doesn't put us 55827185Sminshall * out of OOB state, and since that byte is most 55927185Sminshall * likely the TELNET DM (data mark), we would 56027185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 56127185Sminshall * So, clocks to the rescue. If we've "just" 56227185Sminshall * received a DM, then we test for the 56327185Sminshall * presence of OOB data when the receive OOB 56427185Sminshall * fails (and AFTER we did the normal mode read 56527185Sminshall * to clear "at the mark"). 56627185Sminshall */ 56727185Sminshall if (SYNCHing) { 56827185Sminshall int atmark; 56927185Sminshall 57027185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 57127185Sminshall if (atmark) { 57227185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 57327185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 57427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 57527983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 57627185Sminshall SYNCHing = stilloob(net); 57727185Sminshall } 57827185Sminshall } 57927185Sminshall } else { 58027185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5816002Sroot } 58227185Sminshall } else { 58327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 58427185Sminshall } 58527185Sminshall settimer(didnetreceive); 58627649Sminshall #else /* !defined(SO_OOBINLINE)) */ 58727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 58827649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 58927185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 59027185Sminshall ncc = 0; 59127185Sminshall else { 59227185Sminshall if (ncc <= 0) { 59327185Sminshall break; 59427185Sminshall } 59527185Sminshall netip = netibuf; 59627185Sminshall } 5976002Sroot } 5986002Sroot 5996002Sroot /* 6006002Sroot * Something to read from the pty... 6016002Sroot */ 60233267Sminshall if (FD_ISSET(p, &xbits)) { 60333267Sminshall if (read(p, ptyibuf, 1) != 1) { 60433267Sminshall break; 60533267Sminshall } 60633267Sminshall } 60727185Sminshall if (FD_ISSET(p, &ibits)) { 6086002Sroot pcc = read(p, ptyibuf, BUFSIZ); 6096002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 6106002Sroot pcc = 0; 6116002Sroot else { 6126002Sroot if (pcc <= 0) 6136002Sroot break; 61433267Sminshall /* Skip past "packet" */ 61533267Sminshall pcc--; 61633267Sminshall ptyip = ptyibuf+1; 6176002Sroot } 6186002Sroot } 61933267Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 62033267Sminshall netclear(); /* clear buffer back */ 62133267Sminshall *nfrontp++ = IAC; 62233267Sminshall *nfrontp++ = DM; 62333267Sminshall neturg = nfrontp-1; /* off by one XXX */ 62433267Sminshall ptyibuf[0] = 0; 62533267Sminshall } 6266002Sroot 6276002Sroot while (pcc > 0) { 6286002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6296002Sroot break; 6306002Sroot c = *ptyip++ & 0377, pcc--; 6316002Sroot if (c == IAC) 6326002Sroot *nfrontp++ = c; 6336002Sroot *nfrontp++ = c; 63431940Sbostic /* Don't do CR-NUL if we are in binary mode */ 63531940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 63627020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 63727020Sminshall *nfrontp++ = *ptyip++ & 0377; 63827020Sminshall pcc--; 63927020Sminshall } else 64027020Sminshall *nfrontp++ = '\0'; 64127020Sminshall } 6426002Sroot } 64327185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6446002Sroot netflush(); 6456002Sroot if (ncc > 0) 6466002Sroot telrcv(); 64727185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6486002Sroot ptyflush(); 6496002Sroot } 6506002Sroot cleanup(); 6516002Sroot } 6526002Sroot 6536002Sroot /* 6546002Sroot * State for recv fsm 6556002Sroot */ 6566002Sroot #define TS_DATA 0 /* base state */ 6576002Sroot #define TS_IAC 1 /* look for double IAC's */ 6586002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 65927649Sminshall #define TS_SB 3 /* throw away begin's... */ 66027649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6616002Sroot #define TS_WILL 5 /* will option negotiation */ 6626002Sroot #define TS_WONT 6 /* wont " */ 6636002Sroot #define TS_DO 7 /* do " */ 6646002Sroot #define TS_DONT 8 /* dont " */ 6656002Sroot 6666002Sroot telrcv() 6676002Sroot { 6686002Sroot register int c; 6696002Sroot static int state = TS_DATA; 6706002Sroot 6716002Sroot while (ncc > 0) { 6726002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6736002Sroot return; 6746002Sroot c = *netip++ & 0377, ncc--; 6756002Sroot switch (state) { 6766002Sroot 67726090Sminshall case TS_CR: 67826090Sminshall state = TS_DATA; 67932097Sminshall /* Strip off \n or \0 after a \r */ 68026499Sminshall if ((c == 0) || (c == '\n')) { 68126090Sminshall break; 68226499Sminshall } 68326090Sminshall /* FALL THROUGH */ 68426090Sminshall 6856002Sroot case TS_DATA: 6866002Sroot if (c == IAC) { 6876002Sroot state = TS_IAC; 6886002Sroot break; 6896002Sroot } 6906002Sroot if (inter > 0) 6916002Sroot break; 69227020Sminshall /* 69332097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 69432097Sminshall * Many client implementations send \r\n when 69532097Sminshall * the user hits the CarriageReturn key. 69632097Sminshall * 69732097Sminshall * We USED to map \r\n ==> \n, since \r\n says 69827020Sminshall * that we want to be in column 1 of the next 69927020Sminshall * printable line, and \n is the standard 70027020Sminshall * unix way of saying that (\r is only good 70127020Sminshall * if CRMOD is set, which it normally is). 70227020Sminshall */ 70331940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 70432097Sminshall state = TS_CR; 70526499Sminshall } 70626499Sminshall *pfrontp++ = c; 7076002Sroot break; 7086002Sroot 7096002Sroot case TS_IAC: 7106002Sroot switch (c) { 7116002Sroot 7126002Sroot /* 7136002Sroot * Send the process on the pty side an 7146002Sroot * interrupt. Do this with a NULL or 7156002Sroot * interrupt char; depending on the tty mode. 7166002Sroot */ 7176002Sroot case IP: 7186002Sroot interrupt(); 7196002Sroot break; 7206002Sroot 72127229Sminshall case BREAK: 72227229Sminshall sendbrk(); 72327229Sminshall break; 72427229Sminshall 7256002Sroot /* 7266002Sroot * Are You There? 7276002Sroot */ 7286002Sroot case AYT: 72917583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 73017583Ssam nfrontp += 9; 7316002Sroot break; 7326002Sroot 7336002Sroot /* 73427185Sminshall * Abort Output 73527185Sminshall */ 73627185Sminshall case AO: { 73727185Sminshall struct ltchars tmpltc; 73827185Sminshall 73927185Sminshall ptyflush(); /* half-hearted */ 74027185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 74127185Sminshall if (tmpltc.t_flushc != '\377') { 74227185Sminshall *pfrontp++ = tmpltc.t_flushc; 74327185Sminshall } 74427260Sminshall netclear(); /* clear buffer back */ 74527185Sminshall *nfrontp++ = IAC; 74627185Sminshall *nfrontp++ = DM; 74727187Sminshall neturg = nfrontp-1; /* off by one XXX */ 74827185Sminshall break; 74927185Sminshall } 75027185Sminshall 75127185Sminshall /* 7526002Sroot * Erase Character and 7536002Sroot * Erase Line 7546002Sroot */ 7556002Sroot case EC: 75627185Sminshall case EL: { 75727185Sminshall struct sgttyb b; 75827185Sminshall char ch; 7596002Sroot 76027185Sminshall ptyflush(); /* half-hearted */ 76127185Sminshall ioctl(pty, TIOCGETP, &b); 76227185Sminshall ch = (c == EC) ? 76327185Sminshall b.sg_erase : b.sg_kill; 76427185Sminshall if (ch != '\377') { 76527185Sminshall *pfrontp++ = ch; 76627185Sminshall } 76727185Sminshall break; 76827185Sminshall } 76927185Sminshall 7706002Sroot /* 7716002Sroot * Check for urgent data... 7726002Sroot */ 7736002Sroot case DM: 77427185Sminshall SYNCHing = stilloob(net); 77527185Sminshall settimer(gotDM); 7766002Sroot break; 7776002Sroot 77827185Sminshall 7796002Sroot /* 7806002Sroot * Begin option subnegotiation... 7816002Sroot */ 7826002Sroot case SB: 78327649Sminshall state = TS_SB; 7846002Sroot continue; 7856002Sroot 7866002Sroot case WILL: 78727188Sminshall state = TS_WILL; 78827188Sminshall continue; 78927188Sminshall 7906002Sroot case WONT: 79127188Sminshall state = TS_WONT; 79227188Sminshall continue; 79327188Sminshall 7946002Sroot case DO: 79527188Sminshall state = TS_DO; 79627188Sminshall continue; 79727188Sminshall 7986002Sroot case DONT: 79927188Sminshall state = TS_DONT; 8006002Sroot continue; 8016002Sroot 8026002Sroot case IAC: 8036002Sroot *pfrontp++ = c; 8046002Sroot break; 8056002Sroot } 8066002Sroot state = TS_DATA; 8076002Sroot break; 8086002Sroot 80927649Sminshall case TS_SB: 81027649Sminshall if (c == IAC) { 81127649Sminshall state = TS_SE; 81227649Sminshall } else { 81327649Sminshall SB_ACCUM(c); 81427649Sminshall } 8156002Sroot break; 8166002Sroot 81727649Sminshall case TS_SE: 81827649Sminshall if (c != SE) { 81927649Sminshall if (c != IAC) { 82027649Sminshall SB_ACCUM(IAC); 82127649Sminshall } 82227649Sminshall SB_ACCUM(c); 82327649Sminshall state = TS_SB; 82427649Sminshall } else { 82527649Sminshall SB_TERM(); 82627649Sminshall suboption(); /* handle sub-option */ 82727649Sminshall state = TS_DATA; 82827649Sminshall } 8296002Sroot break; 8306002Sroot 8316002Sroot case TS_WILL: 83227983Sminshall if (hisopts[c] != OPT_YES) 8336002Sroot willoption(c); 8346002Sroot state = TS_DATA; 8356002Sroot continue; 8366002Sroot 8376002Sroot case TS_WONT: 83827983Sminshall if (hisopts[c] != OPT_NO) 8396002Sroot wontoption(c); 8406002Sroot state = TS_DATA; 8416002Sroot continue; 8426002Sroot 8436002Sroot case TS_DO: 84427983Sminshall if (myopts[c] != OPT_YES) 8456002Sroot dooption(c); 8466002Sroot state = TS_DATA; 8476002Sroot continue; 8486002Sroot 8496002Sroot case TS_DONT: 85027983Sminshall if (myopts[c] != OPT_NO) { 85127649Sminshall dontoption(c); 8526002Sroot } 8536002Sroot state = TS_DATA; 8546002Sroot continue; 8556002Sroot 8566002Sroot default: 85727898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8589218Ssam printf("telnetd: panic state=%d\n", state); 8596002Sroot exit(1); 8606002Sroot } 8616002Sroot } 8626002Sroot } 8636002Sroot 8646002Sroot willoption(option) 8656002Sroot int option; 8666002Sroot { 8676002Sroot char *fmt; 8686002Sroot 8696002Sroot switch (option) { 8706002Sroot 8716002Sroot case TELOPT_BINARY: 8726002Sroot mode(RAW, 0); 87327188Sminshall fmt = doopt; 87427188Sminshall break; 8756002Sroot 8766002Sroot case TELOPT_ECHO: 87727649Sminshall not42 = 0; /* looks like a 4.2 system */ 87827649Sminshall /* 87927649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 88027649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 88127649Sminshall * Kludge upon kludge! 88227649Sminshall */ 88327983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 88427649Sminshall dooption(TELOPT_ECHO); 88527649Sminshall } 88627649Sminshall fmt = dont; 88727188Sminshall break; 8886002Sroot 88927649Sminshall case TELOPT_TTYPE: 89027983Sminshall settimer(ttypeopt); 89127983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 89227983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 89327983Sminshall return; 89427983Sminshall } 89527983Sminshall fmt = doopt; 89627983Sminshall break; 89727983Sminshall 8986002Sroot case TELOPT_SGA: 8996002Sroot fmt = doopt; 9006002Sroot break; 9016002Sroot 9026002Sroot case TELOPT_TM: 9036002Sroot fmt = dont; 9046002Sroot break; 9056002Sroot 9066002Sroot default: 9076002Sroot fmt = dont; 9086002Sroot break; 9096002Sroot } 91027188Sminshall if (fmt == doopt) { 91127983Sminshall hisopts[option] = OPT_YES; 91227188Sminshall } else { 91327983Sminshall hisopts[option] = OPT_NO; 91427188Sminshall } 91532452Sbostic (void) sprintf(nfrontp, fmt, option); 9168379Ssam nfrontp += sizeof (dont) - 2; 9176002Sroot } 9186002Sroot 9196002Sroot wontoption(option) 9206002Sroot int option; 9216002Sroot { 9226002Sroot char *fmt; 9236002Sroot 9246002Sroot switch (option) { 9256002Sroot case TELOPT_ECHO: 92627649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 92727188Sminshall break; 9286002Sroot 9296002Sroot case TELOPT_BINARY: 9306002Sroot mode(0, RAW); 9316002Sroot break; 93228044Sminshall 93328044Sminshall case TELOPT_TTYPE: 93428044Sminshall settimer(ttypeopt); 93528044Sminshall break; 9366002Sroot } 93728044Sminshall 93827188Sminshall fmt = dont; 93927983Sminshall hisopts[option] = OPT_NO; 94032452Sbostic (void) sprintf(nfrontp, fmt, option); 9418379Ssam nfrontp += sizeof (doopt) - 2; 9426002Sroot } 9436002Sroot 9446002Sroot dooption(option) 9456002Sroot int option; 9466002Sroot { 9476002Sroot char *fmt; 9486002Sroot 9496002Sroot switch (option) { 9506002Sroot 9516002Sroot case TELOPT_TM: 9526002Sroot fmt = wont; 9536002Sroot break; 9546002Sroot 9556002Sroot case TELOPT_ECHO: 9566002Sroot mode(ECHO|CRMOD, 0); 95727188Sminshall fmt = will; 95827188Sminshall break; 9596002Sroot 9606002Sroot case TELOPT_BINARY: 9616002Sroot mode(RAW, 0); 96227188Sminshall fmt = will; 96327188Sminshall break; 9646002Sroot 9656002Sroot case TELOPT_SGA: 9666002Sroot fmt = will; 9676002Sroot break; 9686002Sroot 9696002Sroot default: 9706002Sroot fmt = wont; 9716002Sroot break; 9726002Sroot } 97327188Sminshall if (fmt == will) { 97427983Sminshall myopts[option] = OPT_YES; 97527188Sminshall } else { 97627983Sminshall myopts[option] = OPT_NO; 97727188Sminshall } 97832452Sbostic (void) sprintf(nfrontp, fmt, option); 9798379Ssam nfrontp += sizeof (doopt) - 2; 9806002Sroot } 9816002Sroot 98227649Sminshall 98327649Sminshall dontoption(option) 98427649Sminshall int option; 98527649Sminshall { 98627649Sminshall char *fmt; 98727649Sminshall 98827649Sminshall switch (option) { 98927649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 99032401Sminshall mode(0, ECHO); 99127649Sminshall fmt = wont; 99227649Sminshall break; 99327983Sminshall 99427649Sminshall default: 99527649Sminshall fmt = wont; 99627649Sminshall break; 99727649Sminshall } 99827983Sminshall 99927649Sminshall if (fmt = wont) { 100027983Sminshall myopts[option] = OPT_NO; 100127649Sminshall } else { 100227983Sminshall myopts[option] = OPT_YES; 100327649Sminshall } 100432452Sbostic (void) sprintf(nfrontp, fmt, option); 100527649Sminshall nfrontp += sizeof (wont) - 2; 100627649Sminshall } 100727649Sminshall 100827649Sminshall /* 100927649Sminshall * suboption() 101027649Sminshall * 101127649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 101227649Sminshall * side. 101327649Sminshall * 101427649Sminshall * Currently we recognize: 101527649Sminshall * 101627983Sminshall * Terminal type is 101727649Sminshall */ 101827649Sminshall 101927649Sminshall suboption() 102027649Sminshall { 102127983Sminshall switch (SB_GET()) { 102227983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 102327983Sminshall static char terminalname[5+41] = "TERM="; 102427983Sminshall 102527983Sminshall settimer(ttypesubopt); 102627983Sminshall 102727983Sminshall if (SB_GET() != TELQUAL_IS) { 102827983Sminshall return; /* ??? XXX but, this is the most robust */ 102927983Sminshall } 103027983Sminshall 103127983Sminshall terminaltype = terminalname+strlen(terminalname); 103227983Sminshall 103327983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 103427983Sminshall !SB_EOF()) { 103527983Sminshall register int c; 103627983Sminshall 103727983Sminshall c = SB_GET(); 103827983Sminshall if (isupper(c)) { 103927983Sminshall c = tolower(c); 104027983Sminshall } 104127983Sminshall *terminaltype++ = c; /* accumulate name */ 104227983Sminshall } 104327983Sminshall *terminaltype = 0; 104427983Sminshall terminaltype = terminalname; 104527983Sminshall break; 104627983Sminshall } 104727983Sminshall 104827649Sminshall default: 104927649Sminshall ; 105027649Sminshall } 105127649Sminshall } 105227649Sminshall 10536002Sroot mode(on, off) 10546002Sroot int on, off; 10556002Sroot { 10566002Sroot struct sgttyb b; 10576002Sroot 10586002Sroot ptyflush(); 10596002Sroot ioctl(pty, TIOCGETP, &b); 10606002Sroot b.sg_flags |= on; 10616002Sroot b.sg_flags &= ~off; 10626002Sroot ioctl(pty, TIOCSETP, &b); 10636002Sroot } 10646002Sroot 10656002Sroot /* 10666002Sroot * Send interrupt to process on other side of pty. 10676002Sroot * If it is in raw mode, just write NULL; 10686002Sroot * otherwise, write intr char. 10696002Sroot */ 10706002Sroot interrupt() 10716002Sroot { 10726002Sroot struct sgttyb b; 10736002Sroot struct tchars tchars; 10746002Sroot 10756002Sroot ptyflush(); /* half-hearted */ 10766002Sroot ioctl(pty, TIOCGETP, &b); 10776002Sroot if (b.sg_flags & RAW) { 10786002Sroot *pfrontp++ = '\0'; 10796002Sroot return; 10806002Sroot } 10816002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10826002Sroot '\177' : tchars.t_intrc; 10836002Sroot } 10846002Sroot 108527229Sminshall /* 108627229Sminshall * Send quit to process on other side of pty. 108727229Sminshall * If it is in raw mode, just write NULL; 108827229Sminshall * otherwise, write quit char. 108927229Sminshall */ 109027229Sminshall sendbrk() 109127229Sminshall { 109227229Sminshall struct sgttyb b; 109327229Sminshall struct tchars tchars; 109427229Sminshall 109527229Sminshall ptyflush(); /* half-hearted */ 109627229Sminshall ioctl(pty, TIOCGETP, &b); 109727229Sminshall if (b.sg_flags & RAW) { 109827229Sminshall *pfrontp++ = '\0'; 109927229Sminshall return; 110027229Sminshall } 110127229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 110227229Sminshall '\034' : tchars.t_quitc; 110327229Sminshall } 110427229Sminshall 11056002Sroot ptyflush() 11066002Sroot { 11076002Sroot int n; 11086002Sroot 11096002Sroot if ((n = pfrontp - pbackp) > 0) 11106002Sroot n = write(pty, pbackp, n); 11118346Ssam if (n < 0) 11128346Ssam return; 11136002Sroot pbackp += n; 11146002Sroot if (pbackp == pfrontp) 11156002Sroot pbackp = pfrontp = ptyobuf; 11166002Sroot } 111727260Sminshall 111827260Sminshall /* 111927260Sminshall * nextitem() 112027260Sminshall * 112127260Sminshall * Return the address of the next "item" in the TELNET data 112227260Sminshall * stream. This will be the address of the next character if 112327260Sminshall * the current address is a user data character, or it will 112427260Sminshall * be the address of the character following the TELNET command 112527260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 112627260Sminshall * character. 112727260Sminshall */ 11286002Sroot 112927260Sminshall char * 113027260Sminshall nextitem(current) 113127260Sminshall char *current; 11326002Sroot { 113327260Sminshall if ((*current&0xff) != IAC) { 113427260Sminshall return current+1; 113527260Sminshall } 113627260Sminshall switch (*(current+1)&0xff) { 113727260Sminshall case DO: 113827260Sminshall case DONT: 113927260Sminshall case WILL: 114027260Sminshall case WONT: 114127260Sminshall return current+3; 114227260Sminshall case SB: /* loop forever looking for the SE */ 114327260Sminshall { 114427260Sminshall register char *look = current+2; 11456002Sroot 114627260Sminshall for (;;) { 114727260Sminshall if ((*look++&0xff) == IAC) { 114827260Sminshall if ((*look++&0xff) == SE) { 114927260Sminshall return look; 115027260Sminshall } 115127260Sminshall } 115227260Sminshall } 11538346Ssam } 115427260Sminshall default: 115527260Sminshall return current+2; 115627260Sminshall } 11576002Sroot } 11586002Sroot 115927185Sminshall 116027185Sminshall /* 116127260Sminshall * netclear() 116227260Sminshall * 116327260Sminshall * We are about to do a TELNET SYNCH operation. Clear 116427260Sminshall * the path to the network. 116527260Sminshall * 116627260Sminshall * Things are a bit tricky since we may have sent the first 116727260Sminshall * byte or so of a previous TELNET command into the network. 116827260Sminshall * So, we have to scan the network buffer from the beginning 116927260Sminshall * until we are up to where we want to be. 117027260Sminshall * 117127260Sminshall * A side effect of what we do, just to keep things 117227260Sminshall * simple, is to clear the urgent data pointer. The principal 117327260Sminshall * caller should be setting the urgent data pointer AFTER calling 117427260Sminshall * us in any case. 117527260Sminshall */ 117627260Sminshall 117727260Sminshall netclear() 117827260Sminshall { 117927260Sminshall register char *thisitem, *next; 118027260Sminshall char *good; 118127260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 118227260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 118327260Sminshall 118427260Sminshall thisitem = netobuf; 118527260Sminshall 118627260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 118727260Sminshall thisitem = next; 118827260Sminshall } 118927260Sminshall 119027260Sminshall /* Now, thisitem is first before/at boundary. */ 119127260Sminshall 119227260Sminshall good = netobuf; /* where the good bytes go */ 119327260Sminshall 119427260Sminshall while (nfrontp > thisitem) { 119527260Sminshall if (wewant(thisitem)) { 119627260Sminshall int length; 119727260Sminshall 119827260Sminshall next = thisitem; 119927260Sminshall do { 120027260Sminshall next = nextitem(next); 120127260Sminshall } while (wewant(next) && (nfrontp > next)); 120227260Sminshall length = next-thisitem; 120327260Sminshall bcopy(thisitem, good, length); 120427260Sminshall good += length; 120527260Sminshall thisitem = next; 120627260Sminshall } else { 120727260Sminshall thisitem = nextitem(thisitem); 120827260Sminshall } 120927260Sminshall } 121027260Sminshall 121127260Sminshall nbackp = netobuf; 121227260Sminshall nfrontp = good; /* next byte to be sent */ 121327260Sminshall neturg = 0; 121427260Sminshall } 121527260Sminshall 121627260Sminshall /* 121727185Sminshall * netflush 121827185Sminshall * Send as much data as possible to the network, 121927185Sminshall * handling requests for urgent data. 122027185Sminshall */ 122127185Sminshall 122227185Sminshall 122327185Sminshall netflush() 122427185Sminshall { 122527185Sminshall int n; 122627185Sminshall 122727185Sminshall if ((n = nfrontp - nbackp) > 0) { 122827649Sminshall /* 122927649Sminshall * if no urgent data, or if the other side appears to be an 123027649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 123127649Sminshall * write the entire buffer in non-OOB mode. 123227649Sminshall */ 123327649Sminshall if ((neturg == 0) || (not42 == 0)) { 123427185Sminshall n = write(net, nbackp, n); /* normal write */ 123527185Sminshall } else { 123627185Sminshall n = neturg - nbackp; 123727185Sminshall /* 123827185Sminshall * In 4.2 (and 4.3) systems, there is some question about 123927185Sminshall * what byte in a sendOOB operation is the "OOB" data. 124027185Sminshall * To make ourselves compatible, we only send ONE byte 124127185Sminshall * out of band, the one WE THINK should be OOB (though 124227185Sminshall * we really have more the TCP philosophy of urgent data 124327185Sminshall * rather than the Unix philosophy of OOB data). 124427185Sminshall */ 124527185Sminshall if (n > 1) { 124627185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 124727185Sminshall } else { 124827185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 124927185Sminshall } 125027185Sminshall } 125127185Sminshall } 125227185Sminshall if (n < 0) { 125327185Sminshall if (errno == EWOULDBLOCK) 125427185Sminshall return; 125527185Sminshall /* should blow this guy away... */ 125627185Sminshall return; 125727185Sminshall } 125827185Sminshall nbackp += n; 125927185Sminshall if (nbackp >= neturg) { 126027185Sminshall neturg = 0; 126127185Sminshall } 126227185Sminshall if (nbackp == nfrontp) { 126327185Sminshall nbackp = nfrontp = netobuf; 126427185Sminshall } 126527185Sminshall } 126627185Sminshall 12676002Sroot cleanup() 12686002Sroot { 126935443Sbostic char *p; 12706002Sroot 127135443Sbostic p = line + sizeof("/dev/") - 1; 127235443Sbostic if (logout(p)) 127335443Sbostic logwtmp(p, "", ""); 127435443Sbostic (void)chmod(line, 0666); 127535443Sbostic (void)chown(line, 0, 0); 127635443Sbostic *p = 'p'; 127735443Sbostic (void)chmod(line, 0666); 127835443Sbostic (void)chown(line, 0, 0); 127910191Ssam shutdown(net, 2); 12806002Sroot exit(1); 12816002Sroot } 12826002Sroot 128333271Sminshall char editedhost[32]; 128433271Sminshall 128533271Sminshall edithost(pat, host) 128633271Sminshall register char *pat; 128733271Sminshall register char *host; 128833271Sminshall { 128933271Sminshall register char *res = editedhost; 129033271Sminshall 129133271Sminshall if (!pat) 129233271Sminshall pat = ""; 129333271Sminshall while (*pat) { 129433271Sminshall switch (*pat) { 129533271Sminshall 129633271Sminshall case '#': 129733271Sminshall if (*host) 129833271Sminshall host++; 129933271Sminshall break; 130033271Sminshall 130133271Sminshall case '@': 130233271Sminshall if (*host) 130333271Sminshall *res++ = *host++; 130433271Sminshall break; 130533271Sminshall 130633271Sminshall default: 130733271Sminshall *res++ = *pat; 130833271Sminshall break; 130933271Sminshall 131033271Sminshall } 131133271Sminshall if (res == &editedhost[sizeof editedhost - 1]) { 131233271Sminshall *res = '\0'; 131333271Sminshall return; 131433271Sminshall } 131533271Sminshall pat++; 131633271Sminshall } 131733271Sminshall if (*host) 131833271Sminshall strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); 131933271Sminshall else 132033271Sminshall *res = '\0'; 132133271Sminshall editedhost[sizeof editedhost - 1] = '\0'; 132233271Sminshall } 132333271Sminshall 132433271Sminshall static char *putlocation; 132533271Sminshall 132633271Sminshall puts(s) 132733271Sminshall register char *s; 132833271Sminshall { 132933271Sminshall 133033271Sminshall while (*s) 133133271Sminshall putchr(*s++); 133233271Sminshall } 133333271Sminshall 133433271Sminshall putchr(cc) 133533271Sminshall { 133633271Sminshall *putlocation++ = cc; 133733271Sminshall } 133833271Sminshall 133934424Sbostic putf(cp, where) 134033271Sminshall register char *cp; 134133271Sminshall char *where; 134233271Sminshall { 134333271Sminshall char *slash; 134433271Sminshall char datebuffer[60]; 134533271Sminshall extern char *rindex(); 134633271Sminshall 134733271Sminshall putlocation = where; 134833271Sminshall 134933271Sminshall while (*cp) { 135033271Sminshall if (*cp != '%') { 135133271Sminshall putchr(*cp++); 135233271Sminshall continue; 135333271Sminshall } 135433271Sminshall switch (*++cp) { 135533271Sminshall 135633271Sminshall case 't': 135733271Sminshall slash = rindex(line, '/'); 135833271Sminshall if (slash == (char *) 0) 135933271Sminshall puts(line); 136033271Sminshall else 136133271Sminshall puts(&slash[1]); 136233271Sminshall break; 136333271Sminshall 136433271Sminshall case 'h': 136533271Sminshall puts(editedhost); 136633271Sminshall break; 136733271Sminshall 136833271Sminshall case 'd': 136933271Sminshall get_date(datebuffer); 137033271Sminshall puts(datebuffer); 137133271Sminshall break; 137233271Sminshall 137333271Sminshall case '%': 137433271Sminshall putchr('%'); 137533271Sminshall break; 137633271Sminshall } 137733271Sminshall cp++; 137833271Sminshall } 137933271Sminshall } 1380