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 6*34778Sbostic * provided that the above copyright notice and this paragraph are 7*34778Sbostic * duplicated in all such forms and that any documentation, 8*34778Sbostic * advertising materials, and other materials related to such 9*34778Sbostic * distribution and use acknowledge that the software was developed 10*34778Sbostic * by the University of California, Berkeley. The name of the 11*34778Sbostic * University may not be used to endorse or promote products derived 12*34778Sbostic * from this software without specific prior written permission. 13*34778Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14*34778Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15*34778Sbostic * 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*34778Sbostic static char sccsid[] = "@(#)telnetd.c 5.29 (Berkeley) 06/18/88"; 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); 33534424Sbostic fatalperror(f, "/bin/login"); 3369244Ssam /*NOTREACHED*/ 3379244Ssam } 3389244Ssam 3399244Ssam fatal(f, msg) 3409244Ssam int f; 3419244Ssam char *msg; 3429244Ssam { 3439244Ssam char buf[BUFSIZ]; 3449244Ssam 34517583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3469244Ssam (void) write(f, buf, strlen(buf)); 3476002Sroot exit(1); 3486002Sroot } 3496002Sroot 35034424Sbostic fatalperror(f, msg) 3519244Ssam int f; 3529244Ssam char *msg; 3539244Ssam { 3549244Ssam char buf[BUFSIZ]; 3559244Ssam extern char *sys_errlist[]; 3569244Ssam 35717583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3589244Ssam fatal(f, buf); 3599244Ssam } 3609244Ssam 36127185Sminshall 3626002Sroot /* 36327185Sminshall * Check a descriptor to see if out of band data exists on it. 36427185Sminshall */ 36527185Sminshall 36627185Sminshall 36727185Sminshall stilloob(s) 36827185Sminshall int s; /* socket number */ 36927185Sminshall { 37027185Sminshall static struct timeval timeout = { 0 }; 37127185Sminshall fd_set excepts; 37227185Sminshall int value; 37327185Sminshall 37427185Sminshall do { 37527185Sminshall FD_ZERO(&excepts); 37627185Sminshall FD_SET(s, &excepts); 37727185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 37827898Skarels } while ((value == -1) && (errno == EINTR)); 37927185Sminshall 38027185Sminshall if (value < 0) { 38134424Sbostic fatalperror(pty, "select"); 38227185Sminshall } 38327185Sminshall if (FD_ISSET(s, &excepts)) { 38427185Sminshall return 1; 38527185Sminshall } else { 38627185Sminshall return 0; 38727185Sminshall } 38827185Sminshall } 38927185Sminshall 39027185Sminshall /* 3916002Sroot * Main loop. Select from pty and network, and 3926002Sroot * hand data to telnet receiver finite state machine. 3936002Sroot */ 3946002Sroot telnet(f, p) 3956002Sroot { 3966002Sroot int on = 1; 39727898Skarels char hostname[MAXHOSTNAMELEN]; 39833271Sminshall #define TABBUFSIZ 512 39933271Sminshall char defent[TABBUFSIZ]; 40033271Sminshall char defstrs[TABBUFSIZ]; 40133271Sminshall #undef TABBUFSIZ 40233271Sminshall char *HE; 40333271Sminshall char *HN; 40433271Sminshall char *IM; 4056002Sroot 4066002Sroot ioctl(f, FIONBIO, &on); 4076002Sroot ioctl(p, FIONBIO, &on); 40833267Sminshall ioctl(p, TIOCPKT, &on); 40927649Sminshall #if defined(SO_OOBINLINE) 41027649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 41127649Sminshall #endif /* defined(SO_OOBINLINE) */ 4126002Sroot signal(SIGTSTP, SIG_IGN); 41332400Sminshall /* 41432400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 41532400Sminshall * in ttioctl() in /sys/tty.c. 41632400Sminshall */ 41732400Sminshall signal(SIGTTOU, SIG_IGN); 41813028Ssam signal(SIGCHLD, cleanup); 41926083Slepreau setpgrp(0, 0); 4206002Sroot 4218379Ssam /* 42227185Sminshall * Request to do remote echo and to suppress go ahead. 4238379Ssam */ 42427983Sminshall if (!myopts[TELOPT_ECHO]) { 42527983Sminshall dooption(TELOPT_ECHO); 42627983Sminshall } 42727983Sminshall if (!myopts[TELOPT_SGA]) { 42827983Sminshall dooption(TELOPT_SGA); 42927983Sminshall } 43012713Ssam /* 43127649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 43227649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 43327649Sminshall * 43427649Sminshall * To find out, we send out a "DO ECHO". If the remote system 43527649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 43627649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 43727649Sminshall * WE, the server, sends it; it does NOT mean that the client will 43827649Sminshall * echo the terminal input). 43927649Sminshall */ 44032452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 44127649Sminshall nfrontp += sizeof doopt-2; 44227983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 44327649Sminshall 44427649Sminshall /* 44512713Ssam * Show banner that getty never gave. 44627797Sminshall * 44733271Sminshall * We put the banner in the pty input buffer. This way, it 44833271Sminshall * gets carriage return null processing, etc., just like all 44933271Sminshall * other pty --> client data. 45012713Ssam */ 45127797Sminshall 45212713Ssam gethostname(hostname, sizeof (hostname)); 45333271Sminshall if (getent(defent, "default") == 1) { 45433271Sminshall char *getstr(); 45533271Sminshall char *p=defstrs; 45627649Sminshall 45733271Sminshall HE = getstr("he", &p); 45833271Sminshall HN = getstr("hn", &p); 45933271Sminshall IM = getstr("im", &p); 46033271Sminshall if (HN && *HN) 46133271Sminshall strcpy(hostname, HN); 46233271Sminshall edithost(HE, hostname); 46333271Sminshall if (IM && *IM) 46434424Sbostic putf(IM, ptyibuf+1); 46533271Sminshall } else { 46633271Sminshall sprintf(ptyibuf+1, BANNER, hostname); 46733271Sminshall } 46827797Sminshall 46933271Sminshall ptyip = ptyibuf+1; /* Prime the pump */ 47033271Sminshall pcc = strlen(ptyip); /* ditto */ 47133271Sminshall 47233267Sminshall /* Clear ptybuf[0] - where the packet information is received */ 47333267Sminshall ptyibuf[0] = 0; 47433271Sminshall 47527649Sminshall /* 47627649Sminshall * Call telrcv() once to pick up anything received during 47727649Sminshall * terminal type negotiation. 47827649Sminshall */ 47927649Sminshall telrcv(); 48027649Sminshall 4816002Sroot for (;;) { 48227185Sminshall fd_set ibits, obits, xbits; 4836002Sroot register int c; 4846002Sroot 48527185Sminshall if (ncc < 0 && pcc < 0) 48627185Sminshall break; 48727185Sminshall 48827185Sminshall FD_ZERO(&ibits); 48927185Sminshall FD_ZERO(&obits); 49027185Sminshall FD_ZERO(&xbits); 4916002Sroot /* 4926002Sroot * Never look for input if there's still 4936002Sroot * stuff in the corresponding output buffer 4946002Sroot */ 49527185Sminshall if (nfrontp - nbackp || pcc > 0) { 49627185Sminshall FD_SET(f, &obits); 49733267Sminshall FD_SET(p, &xbits); 49827185Sminshall } else { 49927185Sminshall FD_SET(p, &ibits); 50027185Sminshall } 50127185Sminshall if (pfrontp - pbackp || ncc > 0) { 50227185Sminshall FD_SET(p, &obits); 50327185Sminshall } else { 50427185Sminshall FD_SET(f, &ibits); 50527185Sminshall } 50627185Sminshall if (!SYNCHing) { 50727185Sminshall FD_SET(f, &xbits); 50827185Sminshall } 50927185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 51027185Sminshall (struct timeval *)0)) < 1) { 51127185Sminshall if (c == -1) { 51227185Sminshall if (errno == EINTR) { 51327185Sminshall continue; 51427185Sminshall } 51527185Sminshall } 5166002Sroot sleep(5); 5176002Sroot continue; 5186002Sroot } 5196002Sroot 5206002Sroot /* 52127185Sminshall * Any urgent data? 52227185Sminshall */ 52327185Sminshall if (FD_ISSET(net, &xbits)) { 52427185Sminshall SYNCHing = 1; 52527185Sminshall } 52627185Sminshall 52727185Sminshall /* 5286002Sroot * Something to read from the network... 5296002Sroot */ 53027185Sminshall if (FD_ISSET(net, &ibits)) { 53127649Sminshall #if !defined(SO_OOBINLINE) 53227185Sminshall /* 53327898Skarels * In 4.2 (and 4.3 beta) systems, the 53427185Sminshall * OOB indication and data handling in the kernel 53527185Sminshall * is such that if two separate TCP Urgent requests 53627185Sminshall * come in, one byte of TCP data will be overlaid. 53727185Sminshall * This is fatal for Telnet, but we try to live 53827185Sminshall * with it. 53927185Sminshall * 54027185Sminshall * In addition, in 4.2 (and...), a special protocol 54127185Sminshall * is needed to pick up the TCP Urgent data in 54227185Sminshall * the correct sequence. 54327185Sminshall * 54427185Sminshall * What we do is: if we think we are in urgent 54527185Sminshall * mode, we look to see if we are "at the mark". 54627185Sminshall * If we are, we do an OOB receive. If we run 54727185Sminshall * this twice, we will do the OOB receive twice, 54827185Sminshall * but the second will fail, since the second 54927185Sminshall * time we were "at the mark", but there wasn't 55027185Sminshall * any data there (the kernel doesn't reset 55127185Sminshall * "at the mark" until we do a normal read). 55227185Sminshall * Once we've read the OOB data, we go ahead 55327185Sminshall * and do normal reads. 55427185Sminshall * 55527185Sminshall * There is also another problem, which is that 55627185Sminshall * since the OOB byte we read doesn't put us 55727185Sminshall * out of OOB state, and since that byte is most 55827185Sminshall * likely the TELNET DM (data mark), we would 55927185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 56027185Sminshall * So, clocks to the rescue. If we've "just" 56127185Sminshall * received a DM, then we test for the 56227185Sminshall * presence of OOB data when the receive OOB 56327185Sminshall * fails (and AFTER we did the normal mode read 56427185Sminshall * to clear "at the mark"). 56527185Sminshall */ 56627185Sminshall if (SYNCHing) { 56727185Sminshall int atmark; 56827185Sminshall 56927185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 57027185Sminshall if (atmark) { 57127185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 57227185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 57327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 57427983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 57527185Sminshall SYNCHing = stilloob(net); 57627185Sminshall } 57727185Sminshall } 57827185Sminshall } else { 57927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5806002Sroot } 58127185Sminshall } else { 58227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 58327185Sminshall } 58427185Sminshall settimer(didnetreceive); 58527649Sminshall #else /* !defined(SO_OOBINLINE)) */ 58627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 58727649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 58827185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 58927185Sminshall ncc = 0; 59027185Sminshall else { 59127185Sminshall if (ncc <= 0) { 59227185Sminshall break; 59327185Sminshall } 59427185Sminshall netip = netibuf; 59527185Sminshall } 5966002Sroot } 5976002Sroot 5986002Sroot /* 5996002Sroot * Something to read from the pty... 6006002Sroot */ 60133267Sminshall if (FD_ISSET(p, &xbits)) { 60233267Sminshall if (read(p, ptyibuf, 1) != 1) { 60333267Sminshall break; 60433267Sminshall } 60533267Sminshall } 60627185Sminshall if (FD_ISSET(p, &ibits)) { 6076002Sroot pcc = read(p, ptyibuf, BUFSIZ); 6086002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 6096002Sroot pcc = 0; 6106002Sroot else { 6116002Sroot if (pcc <= 0) 6126002Sroot break; 61333267Sminshall /* Skip past "packet" */ 61433267Sminshall pcc--; 61533267Sminshall ptyip = ptyibuf+1; 6166002Sroot } 6176002Sroot } 61833267Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 61933267Sminshall netclear(); /* clear buffer back */ 62033267Sminshall *nfrontp++ = IAC; 62133267Sminshall *nfrontp++ = DM; 62233267Sminshall neturg = nfrontp-1; /* off by one XXX */ 62333267Sminshall ptyibuf[0] = 0; 62433267Sminshall } 6256002Sroot 6266002Sroot while (pcc > 0) { 6276002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6286002Sroot break; 6296002Sroot c = *ptyip++ & 0377, pcc--; 6306002Sroot if (c == IAC) 6316002Sroot *nfrontp++ = c; 6326002Sroot *nfrontp++ = c; 63331940Sbostic /* Don't do CR-NUL if we are in binary mode */ 63431940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 63527020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 63627020Sminshall *nfrontp++ = *ptyip++ & 0377; 63727020Sminshall pcc--; 63827020Sminshall } else 63927020Sminshall *nfrontp++ = '\0'; 64027020Sminshall } 6416002Sroot } 64227185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6436002Sroot netflush(); 6446002Sroot if (ncc > 0) 6456002Sroot telrcv(); 64627185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6476002Sroot ptyflush(); 6486002Sroot } 6496002Sroot cleanup(); 6506002Sroot } 6516002Sroot 6526002Sroot /* 6536002Sroot * State for recv fsm 6546002Sroot */ 6556002Sroot #define TS_DATA 0 /* base state */ 6566002Sroot #define TS_IAC 1 /* look for double IAC's */ 6576002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 65827649Sminshall #define TS_SB 3 /* throw away begin's... */ 65927649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6606002Sroot #define TS_WILL 5 /* will option negotiation */ 6616002Sroot #define TS_WONT 6 /* wont " */ 6626002Sroot #define TS_DO 7 /* do " */ 6636002Sroot #define TS_DONT 8 /* dont " */ 6646002Sroot 6656002Sroot telrcv() 6666002Sroot { 6676002Sroot register int c; 6686002Sroot static int state = TS_DATA; 6696002Sroot 6706002Sroot while (ncc > 0) { 6716002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6726002Sroot return; 6736002Sroot c = *netip++ & 0377, ncc--; 6746002Sroot switch (state) { 6756002Sroot 67626090Sminshall case TS_CR: 67726090Sminshall state = TS_DATA; 67832097Sminshall /* Strip off \n or \0 after a \r */ 67926499Sminshall if ((c == 0) || (c == '\n')) { 68026090Sminshall break; 68126499Sminshall } 68226090Sminshall /* FALL THROUGH */ 68326090Sminshall 6846002Sroot case TS_DATA: 6856002Sroot if (c == IAC) { 6866002Sroot state = TS_IAC; 6876002Sroot break; 6886002Sroot } 6896002Sroot if (inter > 0) 6906002Sroot break; 69127020Sminshall /* 69232097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 69332097Sminshall * Many client implementations send \r\n when 69432097Sminshall * the user hits the CarriageReturn key. 69532097Sminshall * 69632097Sminshall * We USED to map \r\n ==> \n, since \r\n says 69727020Sminshall * that we want to be in column 1 of the next 69827020Sminshall * printable line, and \n is the standard 69927020Sminshall * unix way of saying that (\r is only good 70027020Sminshall * if CRMOD is set, which it normally is). 70127020Sminshall */ 70231940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 70332097Sminshall state = TS_CR; 70426499Sminshall } 70526499Sminshall *pfrontp++ = c; 7066002Sroot break; 7076002Sroot 7086002Sroot case TS_IAC: 7096002Sroot switch (c) { 7106002Sroot 7116002Sroot /* 7126002Sroot * Send the process on the pty side an 7136002Sroot * interrupt. Do this with a NULL or 7146002Sroot * interrupt char; depending on the tty mode. 7156002Sroot */ 7166002Sroot case IP: 7176002Sroot interrupt(); 7186002Sroot break; 7196002Sroot 72027229Sminshall case BREAK: 72127229Sminshall sendbrk(); 72227229Sminshall break; 72327229Sminshall 7246002Sroot /* 7256002Sroot * Are You There? 7266002Sroot */ 7276002Sroot case AYT: 72817583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 72917583Ssam nfrontp += 9; 7306002Sroot break; 7316002Sroot 7326002Sroot /* 73327185Sminshall * Abort Output 73427185Sminshall */ 73527185Sminshall case AO: { 73627185Sminshall struct ltchars tmpltc; 73727185Sminshall 73827185Sminshall ptyflush(); /* half-hearted */ 73927185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 74027185Sminshall if (tmpltc.t_flushc != '\377') { 74127185Sminshall *pfrontp++ = tmpltc.t_flushc; 74227185Sminshall } 74327260Sminshall netclear(); /* clear buffer back */ 74427185Sminshall *nfrontp++ = IAC; 74527185Sminshall *nfrontp++ = DM; 74627187Sminshall neturg = nfrontp-1; /* off by one XXX */ 74727185Sminshall break; 74827185Sminshall } 74927185Sminshall 75027185Sminshall /* 7516002Sroot * Erase Character and 7526002Sroot * Erase Line 7536002Sroot */ 7546002Sroot case EC: 75527185Sminshall case EL: { 75627185Sminshall struct sgttyb b; 75727185Sminshall char ch; 7586002Sroot 75927185Sminshall ptyflush(); /* half-hearted */ 76027185Sminshall ioctl(pty, TIOCGETP, &b); 76127185Sminshall ch = (c == EC) ? 76227185Sminshall b.sg_erase : b.sg_kill; 76327185Sminshall if (ch != '\377') { 76427185Sminshall *pfrontp++ = ch; 76527185Sminshall } 76627185Sminshall break; 76727185Sminshall } 76827185Sminshall 7696002Sroot /* 7706002Sroot * Check for urgent data... 7716002Sroot */ 7726002Sroot case DM: 77327185Sminshall SYNCHing = stilloob(net); 77427185Sminshall settimer(gotDM); 7756002Sroot break; 7766002Sroot 77727185Sminshall 7786002Sroot /* 7796002Sroot * Begin option subnegotiation... 7806002Sroot */ 7816002Sroot case SB: 78227649Sminshall state = TS_SB; 7836002Sroot continue; 7846002Sroot 7856002Sroot case WILL: 78627188Sminshall state = TS_WILL; 78727188Sminshall continue; 78827188Sminshall 7896002Sroot case WONT: 79027188Sminshall state = TS_WONT; 79127188Sminshall continue; 79227188Sminshall 7936002Sroot case DO: 79427188Sminshall state = TS_DO; 79527188Sminshall continue; 79627188Sminshall 7976002Sroot case DONT: 79827188Sminshall state = TS_DONT; 7996002Sroot continue; 8006002Sroot 8016002Sroot case IAC: 8026002Sroot *pfrontp++ = c; 8036002Sroot break; 8046002Sroot } 8056002Sroot state = TS_DATA; 8066002Sroot break; 8076002Sroot 80827649Sminshall case TS_SB: 80927649Sminshall if (c == IAC) { 81027649Sminshall state = TS_SE; 81127649Sminshall } else { 81227649Sminshall SB_ACCUM(c); 81327649Sminshall } 8146002Sroot break; 8156002Sroot 81627649Sminshall case TS_SE: 81727649Sminshall if (c != SE) { 81827649Sminshall if (c != IAC) { 81927649Sminshall SB_ACCUM(IAC); 82027649Sminshall } 82127649Sminshall SB_ACCUM(c); 82227649Sminshall state = TS_SB; 82327649Sminshall } else { 82427649Sminshall SB_TERM(); 82527649Sminshall suboption(); /* handle sub-option */ 82627649Sminshall state = TS_DATA; 82727649Sminshall } 8286002Sroot break; 8296002Sroot 8306002Sroot case TS_WILL: 83127983Sminshall if (hisopts[c] != OPT_YES) 8326002Sroot willoption(c); 8336002Sroot state = TS_DATA; 8346002Sroot continue; 8356002Sroot 8366002Sroot case TS_WONT: 83727983Sminshall if (hisopts[c] != OPT_NO) 8386002Sroot wontoption(c); 8396002Sroot state = TS_DATA; 8406002Sroot continue; 8416002Sroot 8426002Sroot case TS_DO: 84327983Sminshall if (myopts[c] != OPT_YES) 8446002Sroot dooption(c); 8456002Sroot state = TS_DATA; 8466002Sroot continue; 8476002Sroot 8486002Sroot case TS_DONT: 84927983Sminshall if (myopts[c] != OPT_NO) { 85027649Sminshall dontoption(c); 8516002Sroot } 8526002Sroot state = TS_DATA; 8536002Sroot continue; 8546002Sroot 8556002Sroot default: 85627898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8579218Ssam printf("telnetd: panic state=%d\n", state); 8586002Sroot exit(1); 8596002Sroot } 8606002Sroot } 8616002Sroot } 8626002Sroot 8636002Sroot willoption(option) 8646002Sroot int option; 8656002Sroot { 8666002Sroot char *fmt; 8676002Sroot 8686002Sroot switch (option) { 8696002Sroot 8706002Sroot case TELOPT_BINARY: 8716002Sroot mode(RAW, 0); 87227188Sminshall fmt = doopt; 87327188Sminshall break; 8746002Sroot 8756002Sroot case TELOPT_ECHO: 87627649Sminshall not42 = 0; /* looks like a 4.2 system */ 87727649Sminshall /* 87827649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 87927649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 88027649Sminshall * Kludge upon kludge! 88127649Sminshall */ 88227983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 88327649Sminshall dooption(TELOPT_ECHO); 88427649Sminshall } 88527649Sminshall fmt = dont; 88627188Sminshall break; 8876002Sroot 88827649Sminshall case TELOPT_TTYPE: 88927983Sminshall settimer(ttypeopt); 89027983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 89127983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 89227983Sminshall return; 89327983Sminshall } 89427983Sminshall fmt = doopt; 89527983Sminshall break; 89627983Sminshall 8976002Sroot case TELOPT_SGA: 8986002Sroot fmt = doopt; 8996002Sroot break; 9006002Sroot 9016002Sroot case TELOPT_TM: 9026002Sroot fmt = dont; 9036002Sroot break; 9046002Sroot 9056002Sroot default: 9066002Sroot fmt = dont; 9076002Sroot break; 9086002Sroot } 90927188Sminshall if (fmt == doopt) { 91027983Sminshall hisopts[option] = OPT_YES; 91127188Sminshall } else { 91227983Sminshall hisopts[option] = OPT_NO; 91327188Sminshall } 91432452Sbostic (void) sprintf(nfrontp, fmt, option); 9158379Ssam nfrontp += sizeof (dont) - 2; 9166002Sroot } 9176002Sroot 9186002Sroot wontoption(option) 9196002Sroot int option; 9206002Sroot { 9216002Sroot char *fmt; 9226002Sroot 9236002Sroot switch (option) { 9246002Sroot case TELOPT_ECHO: 92527649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 92627188Sminshall break; 9276002Sroot 9286002Sroot case TELOPT_BINARY: 9296002Sroot mode(0, RAW); 9306002Sroot break; 93128044Sminshall 93228044Sminshall case TELOPT_TTYPE: 93328044Sminshall settimer(ttypeopt); 93428044Sminshall break; 9356002Sroot } 93628044Sminshall 93727188Sminshall fmt = dont; 93827983Sminshall hisopts[option] = OPT_NO; 93932452Sbostic (void) sprintf(nfrontp, fmt, option); 9408379Ssam nfrontp += sizeof (doopt) - 2; 9416002Sroot } 9426002Sroot 9436002Sroot dooption(option) 9446002Sroot int option; 9456002Sroot { 9466002Sroot char *fmt; 9476002Sroot 9486002Sroot switch (option) { 9496002Sroot 9506002Sroot case TELOPT_TM: 9516002Sroot fmt = wont; 9526002Sroot break; 9536002Sroot 9546002Sroot case TELOPT_ECHO: 9556002Sroot mode(ECHO|CRMOD, 0); 95627188Sminshall fmt = will; 95727188Sminshall break; 9586002Sroot 9596002Sroot case TELOPT_BINARY: 9606002Sroot mode(RAW, 0); 96127188Sminshall fmt = will; 96227188Sminshall break; 9636002Sroot 9646002Sroot case TELOPT_SGA: 9656002Sroot fmt = will; 9666002Sroot break; 9676002Sroot 9686002Sroot default: 9696002Sroot fmt = wont; 9706002Sroot break; 9716002Sroot } 97227188Sminshall if (fmt == will) { 97327983Sminshall myopts[option] = OPT_YES; 97427188Sminshall } else { 97527983Sminshall myopts[option] = OPT_NO; 97627188Sminshall } 97732452Sbostic (void) sprintf(nfrontp, fmt, option); 9788379Ssam nfrontp += sizeof (doopt) - 2; 9796002Sroot } 9806002Sroot 98127649Sminshall 98227649Sminshall dontoption(option) 98327649Sminshall int option; 98427649Sminshall { 98527649Sminshall char *fmt; 98627649Sminshall 98727649Sminshall switch (option) { 98827649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 98932401Sminshall mode(0, ECHO); 99027649Sminshall fmt = wont; 99127649Sminshall break; 99227983Sminshall 99327649Sminshall default: 99427649Sminshall fmt = wont; 99527649Sminshall break; 99627649Sminshall } 99727983Sminshall 99827649Sminshall if (fmt = wont) { 99927983Sminshall myopts[option] = OPT_NO; 100027649Sminshall } else { 100127983Sminshall myopts[option] = OPT_YES; 100227649Sminshall } 100332452Sbostic (void) sprintf(nfrontp, fmt, option); 100427649Sminshall nfrontp += sizeof (wont) - 2; 100527649Sminshall } 100627649Sminshall 100727649Sminshall /* 100827649Sminshall * suboption() 100927649Sminshall * 101027649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 101127649Sminshall * side. 101227649Sminshall * 101327649Sminshall * Currently we recognize: 101427649Sminshall * 101527983Sminshall * Terminal type is 101627649Sminshall */ 101727649Sminshall 101827649Sminshall suboption() 101927649Sminshall { 102027983Sminshall switch (SB_GET()) { 102127983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 102227983Sminshall static char terminalname[5+41] = "TERM="; 102327983Sminshall 102427983Sminshall settimer(ttypesubopt); 102527983Sminshall 102627983Sminshall if (SB_GET() != TELQUAL_IS) { 102727983Sminshall return; /* ??? XXX but, this is the most robust */ 102827983Sminshall } 102927983Sminshall 103027983Sminshall terminaltype = terminalname+strlen(terminalname); 103127983Sminshall 103227983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 103327983Sminshall !SB_EOF()) { 103427983Sminshall register int c; 103527983Sminshall 103627983Sminshall c = SB_GET(); 103727983Sminshall if (isupper(c)) { 103827983Sminshall c = tolower(c); 103927983Sminshall } 104027983Sminshall *terminaltype++ = c; /* accumulate name */ 104127983Sminshall } 104227983Sminshall *terminaltype = 0; 104327983Sminshall terminaltype = terminalname; 104427983Sminshall break; 104527983Sminshall } 104627983Sminshall 104727649Sminshall default: 104827649Sminshall ; 104927649Sminshall } 105027649Sminshall } 105127649Sminshall 10526002Sroot mode(on, off) 10536002Sroot int on, off; 10546002Sroot { 10556002Sroot struct sgttyb b; 10566002Sroot 10576002Sroot ptyflush(); 10586002Sroot ioctl(pty, TIOCGETP, &b); 10596002Sroot b.sg_flags |= on; 10606002Sroot b.sg_flags &= ~off; 10616002Sroot ioctl(pty, TIOCSETP, &b); 10626002Sroot } 10636002Sroot 10646002Sroot /* 10656002Sroot * Send interrupt to process on other side of pty. 10666002Sroot * If it is in raw mode, just write NULL; 10676002Sroot * otherwise, write intr char. 10686002Sroot */ 10696002Sroot interrupt() 10706002Sroot { 10716002Sroot struct sgttyb b; 10726002Sroot struct tchars tchars; 10736002Sroot 10746002Sroot ptyflush(); /* half-hearted */ 10756002Sroot ioctl(pty, TIOCGETP, &b); 10766002Sroot if (b.sg_flags & RAW) { 10776002Sroot *pfrontp++ = '\0'; 10786002Sroot return; 10796002Sroot } 10806002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10816002Sroot '\177' : tchars.t_intrc; 10826002Sroot } 10836002Sroot 108427229Sminshall /* 108527229Sminshall * Send quit to process on other side of pty. 108627229Sminshall * If it is in raw mode, just write NULL; 108727229Sminshall * otherwise, write quit char. 108827229Sminshall */ 108927229Sminshall sendbrk() 109027229Sminshall { 109127229Sminshall struct sgttyb b; 109227229Sminshall struct tchars tchars; 109327229Sminshall 109427229Sminshall ptyflush(); /* half-hearted */ 109527229Sminshall ioctl(pty, TIOCGETP, &b); 109627229Sminshall if (b.sg_flags & RAW) { 109727229Sminshall *pfrontp++ = '\0'; 109827229Sminshall return; 109927229Sminshall } 110027229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 110127229Sminshall '\034' : tchars.t_quitc; 110227229Sminshall } 110327229Sminshall 11046002Sroot ptyflush() 11056002Sroot { 11066002Sroot int n; 11076002Sroot 11086002Sroot if ((n = pfrontp - pbackp) > 0) 11096002Sroot n = write(pty, pbackp, n); 11108346Ssam if (n < 0) 11118346Ssam return; 11126002Sroot pbackp += n; 11136002Sroot if (pbackp == pfrontp) 11146002Sroot pbackp = pfrontp = ptyobuf; 11156002Sroot } 111627260Sminshall 111727260Sminshall /* 111827260Sminshall * nextitem() 111927260Sminshall * 112027260Sminshall * Return the address of the next "item" in the TELNET data 112127260Sminshall * stream. This will be the address of the next character if 112227260Sminshall * the current address is a user data character, or it will 112327260Sminshall * be the address of the character following the TELNET command 112427260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 112527260Sminshall * character. 112627260Sminshall */ 11276002Sroot 112827260Sminshall char * 112927260Sminshall nextitem(current) 113027260Sminshall char *current; 11316002Sroot { 113227260Sminshall if ((*current&0xff) != IAC) { 113327260Sminshall return current+1; 113427260Sminshall } 113527260Sminshall switch (*(current+1)&0xff) { 113627260Sminshall case DO: 113727260Sminshall case DONT: 113827260Sminshall case WILL: 113927260Sminshall case WONT: 114027260Sminshall return current+3; 114127260Sminshall case SB: /* loop forever looking for the SE */ 114227260Sminshall { 114327260Sminshall register char *look = current+2; 11446002Sroot 114527260Sminshall for (;;) { 114627260Sminshall if ((*look++&0xff) == IAC) { 114727260Sminshall if ((*look++&0xff) == SE) { 114827260Sminshall return look; 114927260Sminshall } 115027260Sminshall } 115127260Sminshall } 11528346Ssam } 115327260Sminshall default: 115427260Sminshall return current+2; 115527260Sminshall } 11566002Sroot } 11576002Sroot 115827185Sminshall 115927185Sminshall /* 116027260Sminshall * netclear() 116127260Sminshall * 116227260Sminshall * We are about to do a TELNET SYNCH operation. Clear 116327260Sminshall * the path to the network. 116427260Sminshall * 116527260Sminshall * Things are a bit tricky since we may have sent the first 116627260Sminshall * byte or so of a previous TELNET command into the network. 116727260Sminshall * So, we have to scan the network buffer from the beginning 116827260Sminshall * until we are up to where we want to be. 116927260Sminshall * 117027260Sminshall * A side effect of what we do, just to keep things 117127260Sminshall * simple, is to clear the urgent data pointer. The principal 117227260Sminshall * caller should be setting the urgent data pointer AFTER calling 117327260Sminshall * us in any case. 117427260Sminshall */ 117527260Sminshall 117627260Sminshall netclear() 117727260Sminshall { 117827260Sminshall register char *thisitem, *next; 117927260Sminshall char *good; 118027260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 118127260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 118227260Sminshall 118327260Sminshall thisitem = netobuf; 118427260Sminshall 118527260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 118627260Sminshall thisitem = next; 118727260Sminshall } 118827260Sminshall 118927260Sminshall /* Now, thisitem is first before/at boundary. */ 119027260Sminshall 119127260Sminshall good = netobuf; /* where the good bytes go */ 119227260Sminshall 119327260Sminshall while (nfrontp > thisitem) { 119427260Sminshall if (wewant(thisitem)) { 119527260Sminshall int length; 119627260Sminshall 119727260Sminshall next = thisitem; 119827260Sminshall do { 119927260Sminshall next = nextitem(next); 120027260Sminshall } while (wewant(next) && (nfrontp > next)); 120127260Sminshall length = next-thisitem; 120227260Sminshall bcopy(thisitem, good, length); 120327260Sminshall good += length; 120427260Sminshall thisitem = next; 120527260Sminshall } else { 120627260Sminshall thisitem = nextitem(thisitem); 120727260Sminshall } 120827260Sminshall } 120927260Sminshall 121027260Sminshall nbackp = netobuf; 121127260Sminshall nfrontp = good; /* next byte to be sent */ 121227260Sminshall neturg = 0; 121327260Sminshall } 121427260Sminshall 121527260Sminshall /* 121627185Sminshall * netflush 121727185Sminshall * Send as much data as possible to the network, 121827185Sminshall * handling requests for urgent data. 121927185Sminshall */ 122027185Sminshall 122127185Sminshall 122227185Sminshall netflush() 122327185Sminshall { 122427185Sminshall int n; 122527185Sminshall 122627185Sminshall if ((n = nfrontp - nbackp) > 0) { 122727649Sminshall /* 122827649Sminshall * if no urgent data, or if the other side appears to be an 122927649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 123027649Sminshall * write the entire buffer in non-OOB mode. 123127649Sminshall */ 123227649Sminshall if ((neturg == 0) || (not42 == 0)) { 123327185Sminshall n = write(net, nbackp, n); /* normal write */ 123427185Sminshall } else { 123527185Sminshall n = neturg - nbackp; 123627185Sminshall /* 123727185Sminshall * In 4.2 (and 4.3) systems, there is some question about 123827185Sminshall * what byte in a sendOOB operation is the "OOB" data. 123927185Sminshall * To make ourselves compatible, we only send ONE byte 124027185Sminshall * out of band, the one WE THINK should be OOB (though 124127185Sminshall * we really have more the TCP philosophy of urgent data 124227185Sminshall * rather than the Unix philosophy of OOB data). 124327185Sminshall */ 124427185Sminshall if (n > 1) { 124527185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 124627185Sminshall } else { 124727185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 124827185Sminshall } 124927185Sminshall } 125027185Sminshall } 125127185Sminshall if (n < 0) { 125227185Sminshall if (errno == EWOULDBLOCK) 125327185Sminshall return; 125427185Sminshall /* should blow this guy away... */ 125527185Sminshall return; 125627185Sminshall } 125727185Sminshall nbackp += n; 125827185Sminshall if (nbackp >= neturg) { 125927185Sminshall neturg = 0; 126027185Sminshall } 126127185Sminshall if (nbackp == nfrontp) { 126227185Sminshall nbackp = nfrontp = netobuf; 126327185Sminshall } 126427185Sminshall } 126527185Sminshall 12666002Sroot cleanup() 12676002Sroot { 12686002Sroot 12696002Sroot rmut(); 127010191Ssam shutdown(net, 2); 12716002Sroot exit(1); 12726002Sroot } 12736002Sroot 12746002Sroot #include <utmp.h> 12756002Sroot 12766002Sroot struct utmp wtmp; 12776002Sroot char wtmpf[] = "/usr/adm/wtmp"; 127823567Sbloom char utmpf[] = "/etc/utmp"; 127923567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 128023567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12816002Sroot 12826002Sroot rmut() 12836002Sroot { 12846002Sroot register f; 12856002Sroot int found = 0; 128623567Sbloom struct utmp *u, *utmp; 128723567Sbloom int nutmp; 128823567Sbloom struct stat statbf; 12896002Sroot 129023567Sbloom f = open(utmpf, O_RDWR); 12916002Sroot if (f >= 0) { 129223567Sbloom fstat(f, &statbf); 129323567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 129423567Sbloom if (!utmp) 129523567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 129623567Sbloom if (statbf.st_size && utmp) { 129723567Sbloom nutmp = read(f, utmp, statbf.st_size); 129823567Sbloom nutmp /= sizeof(struct utmp); 129923567Sbloom 130023567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 130123567Sbloom if (SCMPN(u->ut_line, line+5) || 130223567Sbloom u->ut_name[0]==0) 130323567Sbloom continue; 130423567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 130523567Sbloom SCPYN(u->ut_name, ""); 130623567Sbloom SCPYN(u->ut_host, ""); 130723567Sbloom time(&u->ut_time); 130823567Sbloom write(f, (char *)u, sizeof(wtmp)); 130923567Sbloom found++; 131023567Sbloom } 13116002Sroot } 13126002Sroot close(f); 13136002Sroot } 13146002Sroot if (found) { 131517583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 13166002Sroot if (f >= 0) { 13176002Sroot SCPYN(wtmp.ut_line, line+5); 13186002Sroot SCPYN(wtmp.ut_name, ""); 131912683Ssam SCPYN(wtmp.ut_host, ""); 13206002Sroot time(&wtmp.ut_time); 132123567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 13226002Sroot close(f); 13236002Sroot } 13246002Sroot } 13256002Sroot chmod(line, 0666); 13266002Sroot chown(line, 0, 0); 13276002Sroot line[strlen("/dev/")] = 'p'; 13286002Sroot chmod(line, 0666); 13296002Sroot chown(line, 0, 0); 13306002Sroot } 133133271Sminshall 133233271Sminshall char editedhost[32]; 133333271Sminshall 133433271Sminshall edithost(pat, host) 133533271Sminshall register char *pat; 133633271Sminshall register char *host; 133733271Sminshall { 133833271Sminshall register char *res = editedhost; 133933271Sminshall 134033271Sminshall if (!pat) 134133271Sminshall pat = ""; 134233271Sminshall while (*pat) { 134333271Sminshall switch (*pat) { 134433271Sminshall 134533271Sminshall case '#': 134633271Sminshall if (*host) 134733271Sminshall host++; 134833271Sminshall break; 134933271Sminshall 135033271Sminshall case '@': 135133271Sminshall if (*host) 135233271Sminshall *res++ = *host++; 135333271Sminshall break; 135433271Sminshall 135533271Sminshall default: 135633271Sminshall *res++ = *pat; 135733271Sminshall break; 135833271Sminshall 135933271Sminshall } 136033271Sminshall if (res == &editedhost[sizeof editedhost - 1]) { 136133271Sminshall *res = '\0'; 136233271Sminshall return; 136333271Sminshall } 136433271Sminshall pat++; 136533271Sminshall } 136633271Sminshall if (*host) 136733271Sminshall strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); 136833271Sminshall else 136933271Sminshall *res = '\0'; 137033271Sminshall editedhost[sizeof editedhost - 1] = '\0'; 137133271Sminshall } 137233271Sminshall 137333271Sminshall static char *putlocation; 137433271Sminshall 137533271Sminshall puts(s) 137633271Sminshall register char *s; 137733271Sminshall { 137833271Sminshall 137933271Sminshall while (*s) 138033271Sminshall putchr(*s++); 138133271Sminshall } 138233271Sminshall 138333271Sminshall putchr(cc) 138433271Sminshall { 138533271Sminshall *putlocation++ = cc; 138633271Sminshall } 138733271Sminshall 138834424Sbostic putf(cp, where) 138933271Sminshall register char *cp; 139033271Sminshall char *where; 139133271Sminshall { 139233271Sminshall char *slash; 139333271Sminshall char datebuffer[60]; 139433271Sminshall extern char *rindex(); 139533271Sminshall 139633271Sminshall putlocation = where; 139733271Sminshall 139833271Sminshall while (*cp) { 139933271Sminshall if (*cp != '%') { 140033271Sminshall putchr(*cp++); 140133271Sminshall continue; 140233271Sminshall } 140333271Sminshall switch (*++cp) { 140433271Sminshall 140533271Sminshall case 't': 140633271Sminshall slash = rindex(line, '/'); 140733271Sminshall if (slash == (char *) 0) 140833271Sminshall puts(line); 140933271Sminshall else 141033271Sminshall puts(&slash[1]); 141133271Sminshall break; 141233271Sminshall 141333271Sminshall case 'h': 141433271Sminshall puts(editedhost); 141533271Sminshall break; 141633271Sminshall 141733271Sminshall case 'd': 141833271Sminshall get_date(datebuffer); 141933271Sminshall puts(datebuffer); 142033271Sminshall break; 142133271Sminshall 142233271Sminshall case '%': 142333271Sminshall putchr('%'); 142433271Sminshall break; 142533271Sminshall } 142633271Sminshall cp++; 142733271Sminshall } 142833271Sminshall } 1429