121182Sdist /* 227898Skarels * Copyright (c) 1983,1986 Regents of the University of California. 321182Sdist * All rights reserved. The Berkeley software License Agreement 421182Sdist * specifies the terms and conditions for redistribution. 521182Sdist */ 621182Sdist 76295Sroot #ifndef lint 821182Sdist char copyright[] = 921182Sdist "@(#) Copyright (c) 1983 Regents of the University of California.\n\ 1021182Sdist All rights reserved.\n"; 1121182Sdist #endif not lint 126295Sroot 1321182Sdist #ifndef lint 14*32097Sminshall static char sccsid[] = "@(#)telnetd.c 5.20 (Berkeley) 09/02/87"; 1521182Sdist #endif not lint 1621182Sdist 176002Sroot /* 1827898Skarels * Telnet server. 196002Sroot */ 2027898Skarels #include <sys/param.h> 219218Ssam #include <sys/socket.h> 2213608Ssam #include <sys/wait.h> 2317583Ssam #include <sys/file.h> 2420188Skarels #include <sys/stat.h> 2527185Sminshall #include <sys/time.h> 269218Ssam 279218Ssam #include <netinet/in.h> 289218Ssam 2912216Ssam #include <arpa/telnet.h> 3012216Ssam 316002Sroot #include <stdio.h> 326002Sroot #include <signal.h> 336002Sroot #include <errno.h> 346002Sroot #include <sgtty.h> 358346Ssam #include <netdb.h> 3617187Sralph #include <syslog.h> 3727649Sminshall #include <ctype.h> 389218Ssam 3927983Sminshall #define OPT_NO 0 /* won't do this option */ 4027983Sminshall #define OPT_YES 1 /* will do this option */ 4127983Sminshall #define OPT_YES_BUT_ALWAYS_LOOK 2 4227983Sminshall #define OPT_NO_BUT_ALWAYS_LOOK 3 436002Sroot char hisopts[256]; 446002Sroot char myopts[256]; 456002Sroot 466002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 476002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 486002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 496002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 506002Sroot 516002Sroot /* 526002Sroot * I/O data buffers, pointers, and counters. 536002Sroot */ 546002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 5527649Sminshall 566002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 5727649Sminshall 586002Sroot char netibuf[BUFSIZ], *netip = netibuf; 5927649Sminshall #define NIACCUM(c) { *netip++ = c; \ 6027649Sminshall ncc++; \ 6127649Sminshall } 6227649Sminshall 636388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 6427185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 6527649Sminshall /* the remote system seems to NOT be an old 4.2 */ 6627649Sminshall int not42 = 1; 6727649Sminshall 6827649Sminshall 6927797Sminshall char BANNER1[] = "\r\n\r\n4.3 BSD UNIX (", 7027797Sminshall BANNER2[] = ")\r\n\r\0\r\n\r\0"; 7127797Sminshall 7227983Sminshall /* buffer for sub-options */ 7327983Sminshall char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 7427649Sminshall #define SB_CLEAR() subpointer = subbuffer; 7527983Sminshall #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 7627649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 7727649Sminshall *subpointer++ = (c); \ 7827649Sminshall } 7927983Sminshall #define SB_GET() ((*subpointer++)&0xff) 8027983Sminshall #define SB_EOF() (subpointer >= subend) 8127649Sminshall 826002Sroot int pcc, ncc; 836002Sroot 846002Sroot int pty, net; 856002Sroot int inter; 8613799Ssam extern char **environ; 876002Sroot extern int errno; 8820188Skarels char *line; 8927185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 9027185Sminshall /* 9127185Sminshall * The following are some clocks used to decide how to interpret 9227185Sminshall * the relationship between various variables. 9327185Sminshall */ 946002Sroot 9527185Sminshall struct { 9627185Sminshall int 9727185Sminshall system, /* what the current time is */ 9827185Sminshall echotoggle, /* last time user entered echo character */ 9927185Sminshall modenegotiated, /* last time operating mode negotiated */ 10027185Sminshall didnetreceive, /* last time we read data from network */ 10127983Sminshall ttypeopt, /* ttype will/won't received */ 10227983Sminshall ttypesubopt, /* ttype subopt is received */ 10327983Sminshall getterminal, /* time started to get terminal information */ 10427185Sminshall gotDM; /* when did we last see a data mark */ 10527185Sminshall } clocks; 10627185Sminshall 10727983Sminshall #define settimer(x) (clocks.x = ++clocks.system) 10827983Sminshall #define sequenceIs(x,y) (clocks.x < clocks.y) 10927185Sminshall 1106002Sroot main(argc, argv) 1116002Sroot char *argv[]; 1126002Sroot { 11316371Skarels struct sockaddr_in from; 11417156Ssam int on = 1, fromlen; 1156002Sroot 11627185Sminshall #if defined(DEBUG) 11727185Sminshall { 11827185Sminshall int s, ns, foo; 11927185Sminshall struct servent *sp; 12027185Sminshall static struct sockaddr_in sin = { AF_INET }; 12127185Sminshall 12227185Sminshall sp = getservbyname("telnet", "tcp"); 12327185Sminshall if (sp == 0) { 12427185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 12527185Sminshall exit(1); 12627185Sminshall } 12727185Sminshall sin.sin_port = sp->s_port; 12827185Sminshall argc--, argv++; 12927185Sminshall if (argc > 0) { 13027185Sminshall sin.sin_port = atoi(*argv); 13127185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 13227185Sminshall } 13327185Sminshall 13427185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 13527185Sminshall if (s < 0) { 13627185Sminshall perror("telnetd: socket");; 13727185Sminshall exit(1); 13827185Sminshall } 13927185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 14027185Sminshall perror("bind"); 14127185Sminshall exit(1); 14227185Sminshall } 14327185Sminshall if (listen(s, 1) < 0) { 14427185Sminshall perror("listen"); 14527185Sminshall exit(1); 14627185Sminshall } 14727185Sminshall foo = sizeof sin; 14827185Sminshall ns = accept(s, &sin, &foo); 14927185Sminshall if (ns < 0) { 15027185Sminshall perror("accept"); 15127185Sminshall exit(1); 15227185Sminshall } 15327185Sminshall dup2(ns, 0); 15427185Sminshall close(s); 15527185Sminshall } 15627185Sminshall #endif /* defined(DEBUG) */ 15724855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 15816371Skarels fromlen = sizeof (from); 15916371Skarels if (getpeername(0, &from, &fromlen) < 0) { 16016371Skarels fprintf(stderr, "%s: ", argv[0]); 16116371Skarels perror("getpeername"); 16216371Skarels _exit(1); 1638346Ssam } 16417156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 16517187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 16610418Ssam } 16716371Skarels doit(0, &from); 1686002Sroot } 1696002Sroot 17027983Sminshall char *terminaltype = 0; 17127983Sminshall char *envinit[2]; 17227983Sminshall int cleanup(); 17327649Sminshall 17427649Sminshall /* 17527983Sminshall * ttloop 17627649Sminshall * 17727983Sminshall * A small subroutine to flush the network output buffer, get some data 17827983Sminshall * from the network, and pass it through the telnet state machine. We 17927983Sminshall * also flush the pty input buffer (by dropping its data) if it becomes 18027983Sminshall * too full. 18127983Sminshall */ 18227983Sminshall 18327983Sminshall void 18427983Sminshall ttloop() 18527983Sminshall { 18627983Sminshall if (nfrontp-nbackp) { 18727983Sminshall netflush(); 18827983Sminshall } 18927983Sminshall ncc = read(net, netibuf, sizeof netibuf); 19027983Sminshall if (ncc < 0) { 19127983Sminshall syslog(LOG_INFO, "ttloop: read: %m\n"); 19228044Sminshall exit(1); 19328044Sminshall } else if (ncc == 0) { 19428044Sminshall syslog(LOG_INFO, "ttloop: peer died: %m\n"); 19528044Sminshall exit(1); 19627983Sminshall } 19727983Sminshall netip = netibuf; 19827983Sminshall telrcv(); /* state machine */ 19927983Sminshall if (ncc > 0) { 20027983Sminshall pfrontp = pbackp = ptyobuf; 20127983Sminshall telrcv(); 20227983Sminshall } 20327983Sminshall } 20427983Sminshall 20527983Sminshall /* 20627983Sminshall * getterminaltype 20727649Sminshall * 20827983Sminshall * Ask the other end to send along its terminal type. 20927983Sminshall * Output is the variable terminaltype filled in. 21027649Sminshall */ 21127649Sminshall 21227983Sminshall void 21327983Sminshall getterminaltype() 21427649Sminshall { 21527983Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 21627649Sminshall 21727983Sminshall settimer(getterminal); 21827983Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 21927983Sminshall nfrontp += sizeof sbuf; 22028044Sminshall hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; 22127983Sminshall while (sequenceIs(ttypeopt, getterminal)) { 22227983Sminshall ttloop(); 22327649Sminshall } 22427983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES) { 22527983Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 22627983Sminshall 22727983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 22827983Sminshall nfrontp += sizeof sbbuf; 22927983Sminshall while (sequenceIs(ttypesubopt, getterminal)) { 23027983Sminshall ttloop(); 23127983Sminshall } 23227983Sminshall } 23327649Sminshall } 23427649Sminshall 2356002Sroot /* 2366002Sroot * Get a pty, scan input lines. 2376002Sroot */ 23812683Ssam doit(f, who) 23912683Ssam int f; 24012683Ssam struct sockaddr_in *who; 2416002Sroot { 24220188Skarels char *host, *inet_ntoa(); 24317583Ssam int i, p, t; 2446002Sroot struct sgttyb b; 24512683Ssam struct hostent *hp; 24627649Sminshall int c; 2476002Sroot 24820188Skarels for (c = 'p'; c <= 's'; c++) { 24920188Skarels struct stat stb; 25020188Skarels 25120188Skarels line = "/dev/ptyXX"; 25220188Skarels line[strlen("/dev/pty")] = c; 25320188Skarels line[strlen("/dev/ptyp")] = '0'; 25420188Skarels if (stat(line, &stb) < 0) 25520188Skarels break; 25617583Ssam for (i = 0; i < 16; i++) { 25720188Skarels line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 25820188Skarels p = open(line, 2); 25917583Ssam if (p > 0) 26017583Ssam goto gotpty; 26117583Ssam } 2626002Sroot } 2639244Ssam fatal(f, "All network ports in use"); 2649244Ssam /*NOTREACHED*/ 2656002Sroot gotpty: 2666002Sroot dup2(f, 0); 26720188Skarels line[strlen("/dev/")] = 't'; 26817583Ssam t = open("/dev/tty", O_RDWR); 2696002Sroot if (t >= 0) { 2706002Sroot ioctl(t, TIOCNOTTY, 0); 2716002Sroot close(t); 2726002Sroot } 27320188Skarels t = open(line, O_RDWR); 2749244Ssam if (t < 0) 27520188Skarels fatalperror(f, line, errno); 2766002Sroot ioctl(t, TIOCGETP, &b); 2776388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 2786002Sroot ioctl(t, TIOCSETP, &b); 2796388Ssam ioctl(p, TIOCGETP, &b); 2808379Ssam b.sg_flags &= ~ECHO; 2816388Ssam ioctl(p, TIOCSETP, &b); 28212683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 28312683Ssam who->sin_family); 28412683Ssam if (hp) 28512683Ssam host = hp->h_name; 28612683Ssam else 28717444Sralph host = inet_ntoa(who->sin_addr); 28827983Sminshall 28927983Sminshall net = f; 29027983Sminshall pty = p; 29127983Sminshall 29227983Sminshall /* 29327983Sminshall * get terminal type. 29427983Sminshall */ 29527983Sminshall getterminaltype(); 29627983Sminshall 2979244Ssam if ((i = fork()) < 0) 2989244Ssam fatalperror(f, "fork", errno); 2996002Sroot if (i) 3006002Sroot telnet(f, p); 3016002Sroot close(f); 3026002Sroot close(p); 3036002Sroot dup2(t, 0); 3046002Sroot dup2(t, 1); 3056002Sroot dup2(t, 2); 3066002Sroot close(t); 30727983Sminshall envinit[0] = terminaltype; 30827983Sminshall envinit[1] = 0; 30913799Ssam environ = envinit; 31027649Sminshall /* 31127649Sminshall * -h : pass on name of host. 31227983Sminshall * WARNING: -h is accepted by login if and only if 31327983Sminshall * getuid() == 0. 31427649Sminshall * -p : don't clobber the environment (so terminal type stays set). 31527649Sminshall */ 31627649Sminshall execl("/bin/login", "login", "-h", host, 31727983Sminshall terminaltype ? "-p" : 0, 0); 3189244Ssam fatalperror(f, "/bin/login", errno); 3199244Ssam /*NOTREACHED*/ 3209244Ssam } 3219244Ssam 3229244Ssam fatal(f, msg) 3239244Ssam int f; 3249244Ssam char *msg; 3259244Ssam { 3269244Ssam char buf[BUFSIZ]; 3279244Ssam 32817583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3299244Ssam (void) write(f, buf, strlen(buf)); 3306002Sroot exit(1); 3316002Sroot } 3326002Sroot 3339244Ssam fatalperror(f, msg, errno) 3349244Ssam int f; 3359244Ssam char *msg; 3369244Ssam int errno; 3379244Ssam { 3389244Ssam char buf[BUFSIZ]; 3399244Ssam extern char *sys_errlist[]; 3409244Ssam 34117583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3429244Ssam fatal(f, buf); 3439244Ssam } 3449244Ssam 34527185Sminshall 3466002Sroot /* 34727185Sminshall * Check a descriptor to see if out of band data exists on it. 34827185Sminshall */ 34927185Sminshall 35027185Sminshall 35127185Sminshall stilloob(s) 35227185Sminshall int s; /* socket number */ 35327185Sminshall { 35427185Sminshall static struct timeval timeout = { 0 }; 35527185Sminshall fd_set excepts; 35627185Sminshall int value; 35727185Sminshall 35827185Sminshall do { 35927185Sminshall FD_ZERO(&excepts); 36027185Sminshall FD_SET(s, &excepts); 36127185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 36227898Skarels } while ((value == -1) && (errno == EINTR)); 36327185Sminshall 36427185Sminshall if (value < 0) { 36527185Sminshall fatalperror(pty, "select", errno); 36627185Sminshall } 36727185Sminshall if (FD_ISSET(s, &excepts)) { 36827185Sminshall return 1; 36927185Sminshall } else { 37027185Sminshall return 0; 37127185Sminshall } 37227185Sminshall } 37327185Sminshall 37427185Sminshall /* 3756002Sroot * Main loop. Select from pty and network, and 3766002Sroot * hand data to telnet receiver finite state machine. 3776002Sroot */ 3786002Sroot telnet(f, p) 3796002Sroot { 3806002Sroot int on = 1; 38127898Skarels char hostname[MAXHOSTNAMELEN]; 3826002Sroot 3836002Sroot ioctl(f, FIONBIO, &on); 3846002Sroot ioctl(p, FIONBIO, &on); 38527649Sminshall #if defined(SO_OOBINLINE) 38627649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 38727649Sminshall #endif /* defined(SO_OOBINLINE) */ 3886002Sroot signal(SIGTSTP, SIG_IGN); 38913028Ssam signal(SIGCHLD, cleanup); 39026083Slepreau setpgrp(0, 0); 3916002Sroot 3928379Ssam /* 39327185Sminshall * Request to do remote echo and to suppress go ahead. 3948379Ssam */ 39527983Sminshall if (!myopts[TELOPT_ECHO]) { 39627983Sminshall dooption(TELOPT_ECHO); 39727983Sminshall } 39827983Sminshall if (!myopts[TELOPT_SGA]) { 39927983Sminshall dooption(TELOPT_SGA); 40027983Sminshall } 40112713Ssam /* 40227649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 40327649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 40427649Sminshall * 40527649Sminshall * To find out, we send out a "DO ECHO". If the remote system 40627649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 40727649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 40827649Sminshall * WE, the server, sends it; it does NOT mean that the client will 40927649Sminshall * echo the terminal input). 41027649Sminshall */ 41127649Sminshall sprintf(nfrontp, doopt, TELOPT_ECHO); 41227649Sminshall nfrontp += sizeof doopt-2; 41327983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 41427649Sminshall 41527649Sminshall /* 41612713Ssam * Show banner that getty never gave. 41727797Sminshall * 41827797Sminshall * The banner includes some null's (for TELNET CR disambiguation), 41927797Sminshall * so we have to be somewhat complicated. 42012713Ssam */ 42127797Sminshall 42212713Ssam gethostname(hostname, sizeof (hostname)); 42327649Sminshall 42427797Sminshall bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); 42527797Sminshall nfrontp += sizeof BANNER1 - 1; 42627797Sminshall bcopy(hostname, nfrontp, strlen(hostname)); 42727797Sminshall nfrontp += strlen(hostname); 42827797Sminshall bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); 42927797Sminshall nfrontp += sizeof BANNER2 - 1; 43027797Sminshall 43127649Sminshall /* 43227649Sminshall * Call telrcv() once to pick up anything received during 43327649Sminshall * terminal type negotiation. 43427649Sminshall */ 43527649Sminshall telrcv(); 43627649Sminshall 4376002Sroot for (;;) { 43827185Sminshall fd_set ibits, obits, xbits; 4396002Sroot register int c; 4406002Sroot 44127185Sminshall if (ncc < 0 && pcc < 0) 44227185Sminshall break; 44327185Sminshall 44427185Sminshall FD_ZERO(&ibits); 44527185Sminshall FD_ZERO(&obits); 44627185Sminshall FD_ZERO(&xbits); 4476002Sroot /* 4486002Sroot * Never look for input if there's still 4496002Sroot * stuff in the corresponding output buffer 4506002Sroot */ 45127185Sminshall if (nfrontp - nbackp || pcc > 0) { 45227185Sminshall FD_SET(f, &obits); 45327185Sminshall } else { 45427185Sminshall FD_SET(p, &ibits); 45527185Sminshall } 45627185Sminshall if (pfrontp - pbackp || ncc > 0) { 45727185Sminshall FD_SET(p, &obits); 45827185Sminshall } else { 45927185Sminshall FD_SET(f, &ibits); 46027185Sminshall } 46127185Sminshall if (!SYNCHing) { 46227185Sminshall FD_SET(f, &xbits); 46327185Sminshall } 46427185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 46527185Sminshall (struct timeval *)0)) < 1) { 46627185Sminshall if (c == -1) { 46727185Sminshall if (errno == EINTR) { 46827185Sminshall continue; 46927185Sminshall } 47027185Sminshall } 4716002Sroot sleep(5); 4726002Sroot continue; 4736002Sroot } 4746002Sroot 4756002Sroot /* 47627185Sminshall * Any urgent data? 47727185Sminshall */ 47827185Sminshall if (FD_ISSET(net, &xbits)) { 47927185Sminshall SYNCHing = 1; 48027185Sminshall } 48127185Sminshall 48227185Sminshall /* 4836002Sroot * Something to read from the network... 4846002Sroot */ 48527185Sminshall if (FD_ISSET(net, &ibits)) { 48627649Sminshall #if !defined(SO_OOBINLINE) 48727185Sminshall /* 48827898Skarels * In 4.2 (and 4.3 beta) systems, the 48927185Sminshall * OOB indication and data handling in the kernel 49027185Sminshall * is such that if two separate TCP Urgent requests 49127185Sminshall * come in, one byte of TCP data will be overlaid. 49227185Sminshall * This is fatal for Telnet, but we try to live 49327185Sminshall * with it. 49427185Sminshall * 49527185Sminshall * In addition, in 4.2 (and...), a special protocol 49627185Sminshall * is needed to pick up the TCP Urgent data in 49727185Sminshall * the correct sequence. 49827185Sminshall * 49927185Sminshall * What we do is: if we think we are in urgent 50027185Sminshall * mode, we look to see if we are "at the mark". 50127185Sminshall * If we are, we do an OOB receive. If we run 50227185Sminshall * this twice, we will do the OOB receive twice, 50327185Sminshall * but the second will fail, since the second 50427185Sminshall * time we were "at the mark", but there wasn't 50527185Sminshall * any data there (the kernel doesn't reset 50627185Sminshall * "at the mark" until we do a normal read). 50727185Sminshall * Once we've read the OOB data, we go ahead 50827185Sminshall * and do normal reads. 50927185Sminshall * 51027185Sminshall * There is also another problem, which is that 51127185Sminshall * since the OOB byte we read doesn't put us 51227185Sminshall * out of OOB state, and since that byte is most 51327185Sminshall * likely the TELNET DM (data mark), we would 51427185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 51527185Sminshall * So, clocks to the rescue. If we've "just" 51627185Sminshall * received a DM, then we test for the 51727185Sminshall * presence of OOB data when the receive OOB 51827185Sminshall * fails (and AFTER we did the normal mode read 51927185Sminshall * to clear "at the mark"). 52027185Sminshall */ 52127185Sminshall if (SYNCHing) { 52227185Sminshall int atmark; 52327185Sminshall 52427185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 52527185Sminshall if (atmark) { 52627185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 52727185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 52827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 52927983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 53027185Sminshall SYNCHing = stilloob(net); 53127185Sminshall } 53227185Sminshall } 53327185Sminshall } else { 53427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5356002Sroot } 53627185Sminshall } else { 53727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 53827185Sminshall } 53927185Sminshall settimer(didnetreceive); 54027649Sminshall #else /* !defined(SO_OOBINLINE)) */ 54127185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 54227649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 54327185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 54427185Sminshall ncc = 0; 54527185Sminshall else { 54627185Sminshall if (ncc <= 0) { 54727185Sminshall break; 54827185Sminshall } 54927185Sminshall netip = netibuf; 55027185Sminshall } 5516002Sroot } 5526002Sroot 5536002Sroot /* 5546002Sroot * Something to read from the pty... 5556002Sroot */ 55627185Sminshall if (FD_ISSET(p, &ibits)) { 5576002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5586002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5596002Sroot pcc = 0; 5606002Sroot else { 5616002Sroot if (pcc <= 0) 5626002Sroot break; 5636002Sroot ptyip = ptyibuf; 5646002Sroot } 5656002Sroot } 5666002Sroot 5676002Sroot while (pcc > 0) { 5686002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 5696002Sroot break; 5706002Sroot c = *ptyip++ & 0377, pcc--; 5716002Sroot if (c == IAC) 5726002Sroot *nfrontp++ = c; 5736002Sroot *nfrontp++ = c; 57431940Sbostic /* Don't do CR-NUL if we are in binary mode */ 57531940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 57627020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 57727020Sminshall *nfrontp++ = *ptyip++ & 0377; 57827020Sminshall pcc--; 57927020Sminshall } else 58027020Sminshall *nfrontp++ = '\0'; 58127020Sminshall } 5826002Sroot } 58327185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 5846002Sroot netflush(); 5856002Sroot if (ncc > 0) 5866002Sroot telrcv(); 58727185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 5886002Sroot ptyflush(); 5896002Sroot } 5906002Sroot cleanup(); 5916002Sroot } 5926002Sroot 5936002Sroot /* 5946002Sroot * State for recv fsm 5956002Sroot */ 5966002Sroot #define TS_DATA 0 /* base state */ 5976002Sroot #define TS_IAC 1 /* look for double IAC's */ 5986002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 59927649Sminshall #define TS_SB 3 /* throw away begin's... */ 60027649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6016002Sroot #define TS_WILL 5 /* will option negotiation */ 6026002Sroot #define TS_WONT 6 /* wont " */ 6036002Sroot #define TS_DO 7 /* do " */ 6046002Sroot #define TS_DONT 8 /* dont " */ 6056002Sroot 6066002Sroot telrcv() 6076002Sroot { 6086002Sroot register int c; 6096002Sroot static int state = TS_DATA; 6106002Sroot 6116002Sroot while (ncc > 0) { 6126002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6136002Sroot return; 6146002Sroot c = *netip++ & 0377, ncc--; 6156002Sroot switch (state) { 6166002Sroot 61726090Sminshall case TS_CR: 61826090Sminshall state = TS_DATA; 619*32097Sminshall /* Strip off \n or \0 after a \r */ 62026499Sminshall if ((c == 0) || (c == '\n')) { 62126090Sminshall break; 62226499Sminshall } 62326090Sminshall /* FALL THROUGH */ 62426090Sminshall 6256002Sroot case TS_DATA: 6266002Sroot if (c == IAC) { 6276002Sroot state = TS_IAC; 6286002Sroot break; 6296002Sroot } 6306002Sroot if (inter > 0) 6316002Sroot break; 63227020Sminshall /* 633*32097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 634*32097Sminshall * Many client implementations send \r\n when 635*32097Sminshall * the user hits the CarriageReturn key. 636*32097Sminshall * 637*32097Sminshall * We USED to map \r\n ==> \n, since \r\n says 63827020Sminshall * that we want to be in column 1 of the next 63927020Sminshall * printable line, and \n is the standard 64027020Sminshall * unix way of saying that (\r is only good 64127020Sminshall * if CRMOD is set, which it normally is). 64227020Sminshall */ 64331940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 644*32097Sminshall state = TS_CR; 64526499Sminshall } 64626499Sminshall *pfrontp++ = c; 6476002Sroot break; 6486002Sroot 6496002Sroot case TS_IAC: 6506002Sroot switch (c) { 6516002Sroot 6526002Sroot /* 6536002Sroot * Send the process on the pty side an 6546002Sroot * interrupt. Do this with a NULL or 6556002Sroot * interrupt char; depending on the tty mode. 6566002Sroot */ 6576002Sroot case IP: 6586002Sroot interrupt(); 6596002Sroot break; 6606002Sroot 66127229Sminshall case BREAK: 66227229Sminshall sendbrk(); 66327229Sminshall break; 66427229Sminshall 6656002Sroot /* 6666002Sroot * Are You There? 6676002Sroot */ 6686002Sroot case AYT: 66917583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 67017583Ssam nfrontp += 9; 6716002Sroot break; 6726002Sroot 6736002Sroot /* 67427185Sminshall * Abort Output 67527185Sminshall */ 67627185Sminshall case AO: { 67727185Sminshall struct ltchars tmpltc; 67827185Sminshall 67927185Sminshall ptyflush(); /* half-hearted */ 68027185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 68127185Sminshall if (tmpltc.t_flushc != '\377') { 68227185Sminshall *pfrontp++ = tmpltc.t_flushc; 68327185Sminshall } 68427260Sminshall netclear(); /* clear buffer back */ 68527185Sminshall *nfrontp++ = IAC; 68627185Sminshall *nfrontp++ = DM; 68727187Sminshall neturg = nfrontp-1; /* off by one XXX */ 68827185Sminshall break; 68927185Sminshall } 69027185Sminshall 69127185Sminshall /* 6926002Sroot * Erase Character and 6936002Sroot * Erase Line 6946002Sroot */ 6956002Sroot case EC: 69627185Sminshall case EL: { 69727185Sminshall struct sgttyb b; 69827185Sminshall char ch; 6996002Sroot 70027185Sminshall ptyflush(); /* half-hearted */ 70127185Sminshall ioctl(pty, TIOCGETP, &b); 70227185Sminshall ch = (c == EC) ? 70327185Sminshall b.sg_erase : b.sg_kill; 70427185Sminshall if (ch != '\377') { 70527185Sminshall *pfrontp++ = ch; 70627185Sminshall } 70727185Sminshall break; 70827185Sminshall } 70927185Sminshall 7106002Sroot /* 7116002Sroot * Check for urgent data... 7126002Sroot */ 7136002Sroot case DM: 71427185Sminshall SYNCHing = stilloob(net); 71527185Sminshall settimer(gotDM); 7166002Sroot break; 7176002Sroot 71827185Sminshall 7196002Sroot /* 7206002Sroot * Begin option subnegotiation... 7216002Sroot */ 7226002Sroot case SB: 72327649Sminshall state = TS_SB; 7246002Sroot continue; 7256002Sroot 7266002Sroot case WILL: 72727188Sminshall state = TS_WILL; 72827188Sminshall continue; 72927188Sminshall 7306002Sroot case WONT: 73127188Sminshall state = TS_WONT; 73227188Sminshall continue; 73327188Sminshall 7346002Sroot case DO: 73527188Sminshall state = TS_DO; 73627188Sminshall continue; 73727188Sminshall 7386002Sroot case DONT: 73927188Sminshall state = TS_DONT; 7406002Sroot continue; 7416002Sroot 7426002Sroot case IAC: 7436002Sroot *pfrontp++ = c; 7446002Sroot break; 7456002Sroot } 7466002Sroot state = TS_DATA; 7476002Sroot break; 7486002Sroot 74927649Sminshall case TS_SB: 75027649Sminshall if (c == IAC) { 75127649Sminshall state = TS_SE; 75227649Sminshall } else { 75327649Sminshall SB_ACCUM(c); 75427649Sminshall } 7556002Sroot break; 7566002Sroot 75727649Sminshall case TS_SE: 75827649Sminshall if (c != SE) { 75927649Sminshall if (c != IAC) { 76027649Sminshall SB_ACCUM(IAC); 76127649Sminshall } 76227649Sminshall SB_ACCUM(c); 76327649Sminshall state = TS_SB; 76427649Sminshall } else { 76527649Sminshall SB_TERM(); 76627649Sminshall suboption(); /* handle sub-option */ 76727649Sminshall state = TS_DATA; 76827649Sminshall } 7696002Sroot break; 7706002Sroot 7716002Sroot case TS_WILL: 77227983Sminshall if (hisopts[c] != OPT_YES) 7736002Sroot willoption(c); 7746002Sroot state = TS_DATA; 7756002Sroot continue; 7766002Sroot 7776002Sroot case TS_WONT: 77827983Sminshall if (hisopts[c] != OPT_NO) 7796002Sroot wontoption(c); 7806002Sroot state = TS_DATA; 7816002Sroot continue; 7826002Sroot 7836002Sroot case TS_DO: 78427983Sminshall if (myopts[c] != OPT_YES) 7856002Sroot dooption(c); 7866002Sroot state = TS_DATA; 7876002Sroot continue; 7886002Sroot 7896002Sroot case TS_DONT: 79027983Sminshall if (myopts[c] != OPT_NO) { 79127649Sminshall dontoption(c); 7926002Sroot } 7936002Sroot state = TS_DATA; 7946002Sroot continue; 7956002Sroot 7966002Sroot default: 79727898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 7989218Ssam printf("telnetd: panic state=%d\n", state); 7996002Sroot exit(1); 8006002Sroot } 8016002Sroot } 8026002Sroot } 8036002Sroot 8046002Sroot willoption(option) 8056002Sroot int option; 8066002Sroot { 8076002Sroot char *fmt; 8086002Sroot 8096002Sroot switch (option) { 8106002Sroot 8116002Sroot case TELOPT_BINARY: 8126002Sroot mode(RAW, 0); 81327188Sminshall fmt = doopt; 81427188Sminshall break; 8156002Sroot 8166002Sroot case TELOPT_ECHO: 81727649Sminshall not42 = 0; /* looks like a 4.2 system */ 81827649Sminshall /* 81927649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 82027649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 82127649Sminshall * Kludge upon kludge! 82227649Sminshall */ 82327983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 82427649Sminshall dooption(TELOPT_ECHO); 82527649Sminshall } 82627649Sminshall fmt = dont; 82727188Sminshall break; 8286002Sroot 82927649Sminshall case TELOPT_TTYPE: 83027983Sminshall settimer(ttypeopt); 83127983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 83227983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 83327983Sminshall return; 83427983Sminshall } 83527983Sminshall fmt = doopt; 83627983Sminshall break; 83727983Sminshall 8386002Sroot case TELOPT_SGA: 8396002Sroot fmt = doopt; 8406002Sroot break; 8416002Sroot 8426002Sroot case TELOPT_TM: 8436002Sroot fmt = dont; 8446002Sroot break; 8456002Sroot 8466002Sroot default: 8476002Sroot fmt = dont; 8486002Sroot break; 8496002Sroot } 85027188Sminshall if (fmt == doopt) { 85127983Sminshall hisopts[option] = OPT_YES; 85227188Sminshall } else { 85327983Sminshall hisopts[option] = OPT_NO; 85427188Sminshall } 8556023Ssam sprintf(nfrontp, fmt, option); 8568379Ssam nfrontp += sizeof (dont) - 2; 8576002Sroot } 8586002Sroot 8596002Sroot wontoption(option) 8606002Sroot int option; 8616002Sroot { 8626002Sroot char *fmt; 8636002Sroot 8646002Sroot switch (option) { 8656002Sroot case TELOPT_ECHO: 86627649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 86727188Sminshall break; 8686002Sroot 8696002Sroot case TELOPT_BINARY: 8706002Sroot mode(0, RAW); 8716002Sroot break; 87228044Sminshall 87328044Sminshall case TELOPT_TTYPE: 87428044Sminshall settimer(ttypeopt); 87528044Sminshall break; 8766002Sroot } 87728044Sminshall 87827188Sminshall fmt = dont; 87927983Sminshall hisopts[option] = OPT_NO; 8806002Sroot sprintf(nfrontp, fmt, option); 8818379Ssam nfrontp += sizeof (doopt) - 2; 8826002Sroot } 8836002Sroot 8846002Sroot dooption(option) 8856002Sroot int option; 8866002Sroot { 8876002Sroot char *fmt; 8886002Sroot 8896002Sroot switch (option) { 8906002Sroot 8916002Sroot case TELOPT_TM: 8926002Sroot fmt = wont; 8936002Sroot break; 8946002Sroot 8956002Sroot case TELOPT_ECHO: 8966002Sroot mode(ECHO|CRMOD, 0); 89727188Sminshall fmt = will; 89827188Sminshall break; 8996002Sroot 9006002Sroot case TELOPT_BINARY: 9016002Sroot mode(RAW, 0); 90227188Sminshall fmt = will; 90327188Sminshall break; 9046002Sroot 9056002Sroot case TELOPT_SGA: 9066002Sroot fmt = will; 9076002Sroot break; 9086002Sroot 9096002Sroot default: 9106002Sroot fmt = wont; 9116002Sroot break; 9126002Sroot } 91327188Sminshall if (fmt == will) { 91427983Sminshall myopts[option] = OPT_YES; 91527188Sminshall } else { 91627983Sminshall myopts[option] = OPT_NO; 91727188Sminshall } 9186002Sroot sprintf(nfrontp, fmt, option); 9198379Ssam nfrontp += sizeof (doopt) - 2; 9206002Sroot } 9216002Sroot 92227649Sminshall 92327649Sminshall dontoption(option) 92427649Sminshall int option; 92527649Sminshall { 92627649Sminshall char *fmt; 92727649Sminshall 92827649Sminshall switch (option) { 92927649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 93027649Sminshall mode(0, ECHO|CRMOD); 93127649Sminshall fmt = wont; 93227649Sminshall break; 93327983Sminshall 93427649Sminshall default: 93527649Sminshall fmt = wont; 93627649Sminshall break; 93727649Sminshall } 93827983Sminshall 93927649Sminshall if (fmt = wont) { 94027983Sminshall myopts[option] = OPT_NO; 94127649Sminshall } else { 94227983Sminshall myopts[option] = OPT_YES; 94327649Sminshall } 94427649Sminshall sprintf(nfrontp, fmt, option); 94527649Sminshall nfrontp += sizeof (wont) - 2; 94627649Sminshall } 94727649Sminshall 94827649Sminshall /* 94927649Sminshall * suboption() 95027649Sminshall * 95127649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 95227649Sminshall * side. 95327649Sminshall * 95427649Sminshall * Currently we recognize: 95527649Sminshall * 95627983Sminshall * Terminal type is 95727649Sminshall */ 95827649Sminshall 95927649Sminshall suboption() 96027649Sminshall { 96127983Sminshall switch (SB_GET()) { 96227983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 96327983Sminshall static char terminalname[5+41] = "TERM="; 96427983Sminshall 96527983Sminshall settimer(ttypesubopt); 96627983Sminshall 96727983Sminshall if (SB_GET() != TELQUAL_IS) { 96827983Sminshall return; /* ??? XXX but, this is the most robust */ 96927983Sminshall } 97027983Sminshall 97127983Sminshall terminaltype = terminalname+strlen(terminalname); 97227983Sminshall 97327983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 97427983Sminshall !SB_EOF()) { 97527983Sminshall register int c; 97627983Sminshall 97727983Sminshall c = SB_GET(); 97827983Sminshall if (isupper(c)) { 97927983Sminshall c = tolower(c); 98027983Sminshall } 98127983Sminshall *terminaltype++ = c; /* accumulate name */ 98227983Sminshall } 98327983Sminshall *terminaltype = 0; 98427983Sminshall terminaltype = terminalname; 98527983Sminshall break; 98627983Sminshall } 98727983Sminshall 98827649Sminshall default: 98927649Sminshall ; 99027649Sminshall } 99127649Sminshall } 99227649Sminshall 9936002Sroot mode(on, off) 9946002Sroot int on, off; 9956002Sroot { 9966002Sroot struct sgttyb b; 9976002Sroot 9986002Sroot ptyflush(); 9996002Sroot ioctl(pty, TIOCGETP, &b); 10006002Sroot b.sg_flags |= on; 10016002Sroot b.sg_flags &= ~off; 10026002Sroot ioctl(pty, TIOCSETP, &b); 10036002Sroot } 10046002Sroot 10056002Sroot /* 10066002Sroot * Send interrupt to process on other side of pty. 10076002Sroot * If it is in raw mode, just write NULL; 10086002Sroot * otherwise, write intr char. 10096002Sroot */ 10106002Sroot interrupt() 10116002Sroot { 10126002Sroot struct sgttyb b; 10136002Sroot struct tchars tchars; 10146002Sroot 10156002Sroot ptyflush(); /* half-hearted */ 10166002Sroot ioctl(pty, TIOCGETP, &b); 10176002Sroot if (b.sg_flags & RAW) { 10186002Sroot *pfrontp++ = '\0'; 10196002Sroot return; 10206002Sroot } 10216002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10226002Sroot '\177' : tchars.t_intrc; 10236002Sroot } 10246002Sroot 102527229Sminshall /* 102627229Sminshall * Send quit to process on other side of pty. 102727229Sminshall * If it is in raw mode, just write NULL; 102827229Sminshall * otherwise, write quit char. 102927229Sminshall */ 103027229Sminshall sendbrk() 103127229Sminshall { 103227229Sminshall struct sgttyb b; 103327229Sminshall struct tchars tchars; 103427229Sminshall 103527229Sminshall ptyflush(); /* half-hearted */ 103627229Sminshall ioctl(pty, TIOCGETP, &b); 103727229Sminshall if (b.sg_flags & RAW) { 103827229Sminshall *pfrontp++ = '\0'; 103927229Sminshall return; 104027229Sminshall } 104127229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 104227229Sminshall '\034' : tchars.t_quitc; 104327229Sminshall } 104427229Sminshall 10456002Sroot ptyflush() 10466002Sroot { 10476002Sroot int n; 10486002Sroot 10496002Sroot if ((n = pfrontp - pbackp) > 0) 10506002Sroot n = write(pty, pbackp, n); 10518346Ssam if (n < 0) 10528346Ssam return; 10536002Sroot pbackp += n; 10546002Sroot if (pbackp == pfrontp) 10556002Sroot pbackp = pfrontp = ptyobuf; 10566002Sroot } 105727260Sminshall 105827260Sminshall /* 105927260Sminshall * nextitem() 106027260Sminshall * 106127260Sminshall * Return the address of the next "item" in the TELNET data 106227260Sminshall * stream. This will be the address of the next character if 106327260Sminshall * the current address is a user data character, or it will 106427260Sminshall * be the address of the character following the TELNET command 106527260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 106627260Sminshall * character. 106727260Sminshall */ 10686002Sroot 106927260Sminshall char * 107027260Sminshall nextitem(current) 107127260Sminshall char *current; 10726002Sroot { 107327260Sminshall if ((*current&0xff) != IAC) { 107427260Sminshall return current+1; 107527260Sminshall } 107627260Sminshall switch (*(current+1)&0xff) { 107727260Sminshall case DO: 107827260Sminshall case DONT: 107927260Sminshall case WILL: 108027260Sminshall case WONT: 108127260Sminshall return current+3; 108227260Sminshall case SB: /* loop forever looking for the SE */ 108327260Sminshall { 108427260Sminshall register char *look = current+2; 10856002Sroot 108627260Sminshall for (;;) { 108727260Sminshall if ((*look++&0xff) == IAC) { 108827260Sminshall if ((*look++&0xff) == SE) { 108927260Sminshall return look; 109027260Sminshall } 109127260Sminshall } 109227260Sminshall } 10938346Ssam } 109427260Sminshall default: 109527260Sminshall return current+2; 109627260Sminshall } 10976002Sroot } 10986002Sroot 109927185Sminshall 110027185Sminshall /* 110127260Sminshall * netclear() 110227260Sminshall * 110327260Sminshall * We are about to do a TELNET SYNCH operation. Clear 110427260Sminshall * the path to the network. 110527260Sminshall * 110627260Sminshall * Things are a bit tricky since we may have sent the first 110727260Sminshall * byte or so of a previous TELNET command into the network. 110827260Sminshall * So, we have to scan the network buffer from the beginning 110927260Sminshall * until we are up to where we want to be. 111027260Sminshall * 111127260Sminshall * A side effect of what we do, just to keep things 111227260Sminshall * simple, is to clear the urgent data pointer. The principal 111327260Sminshall * caller should be setting the urgent data pointer AFTER calling 111427260Sminshall * us in any case. 111527260Sminshall */ 111627260Sminshall 111727260Sminshall netclear() 111827260Sminshall { 111927260Sminshall register char *thisitem, *next; 112027260Sminshall char *good; 112127260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 112227260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 112327260Sminshall 112427260Sminshall thisitem = netobuf; 112527260Sminshall 112627260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 112727260Sminshall thisitem = next; 112827260Sminshall } 112927260Sminshall 113027260Sminshall /* Now, thisitem is first before/at boundary. */ 113127260Sminshall 113227260Sminshall good = netobuf; /* where the good bytes go */ 113327260Sminshall 113427260Sminshall while (nfrontp > thisitem) { 113527260Sminshall if (wewant(thisitem)) { 113627260Sminshall int length; 113727260Sminshall 113827260Sminshall next = thisitem; 113927260Sminshall do { 114027260Sminshall next = nextitem(next); 114127260Sminshall } while (wewant(next) && (nfrontp > next)); 114227260Sminshall length = next-thisitem; 114327260Sminshall bcopy(thisitem, good, length); 114427260Sminshall good += length; 114527260Sminshall thisitem = next; 114627260Sminshall } else { 114727260Sminshall thisitem = nextitem(thisitem); 114827260Sminshall } 114927260Sminshall } 115027260Sminshall 115127260Sminshall nbackp = netobuf; 115227260Sminshall nfrontp = good; /* next byte to be sent */ 115327260Sminshall neturg = 0; 115427260Sminshall } 115527260Sminshall 115627260Sminshall /* 115727185Sminshall * netflush 115827185Sminshall * Send as much data as possible to the network, 115927185Sminshall * handling requests for urgent data. 116027185Sminshall */ 116127185Sminshall 116227185Sminshall 116327185Sminshall netflush() 116427185Sminshall { 116527185Sminshall int n; 116627185Sminshall 116727185Sminshall if ((n = nfrontp - nbackp) > 0) { 116827649Sminshall /* 116927649Sminshall * if no urgent data, or if the other side appears to be an 117027649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 117127649Sminshall * write the entire buffer in non-OOB mode. 117227649Sminshall */ 117327649Sminshall if ((neturg == 0) || (not42 == 0)) { 117427185Sminshall n = write(net, nbackp, n); /* normal write */ 117527185Sminshall } else { 117627185Sminshall n = neturg - nbackp; 117727185Sminshall /* 117827185Sminshall * In 4.2 (and 4.3) systems, there is some question about 117927185Sminshall * what byte in a sendOOB operation is the "OOB" data. 118027185Sminshall * To make ourselves compatible, we only send ONE byte 118127185Sminshall * out of band, the one WE THINK should be OOB (though 118227185Sminshall * we really have more the TCP philosophy of urgent data 118327185Sminshall * rather than the Unix philosophy of OOB data). 118427185Sminshall */ 118527185Sminshall if (n > 1) { 118627185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 118727185Sminshall } else { 118827185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 118927185Sminshall } 119027185Sminshall } 119127185Sminshall } 119227185Sminshall if (n < 0) { 119327185Sminshall if (errno == EWOULDBLOCK) 119427185Sminshall return; 119527185Sminshall /* should blow this guy away... */ 119627185Sminshall return; 119727185Sminshall } 119827185Sminshall nbackp += n; 119927185Sminshall if (nbackp >= neturg) { 120027185Sminshall neturg = 0; 120127185Sminshall } 120227185Sminshall if (nbackp == nfrontp) { 120327185Sminshall nbackp = nfrontp = netobuf; 120427185Sminshall } 120527185Sminshall } 120627185Sminshall 12076002Sroot cleanup() 12086002Sroot { 12096002Sroot 12106002Sroot rmut(); 121110008Ssam vhangup(); /* XXX */ 121210191Ssam shutdown(net, 2); 12136002Sroot exit(1); 12146002Sroot } 12156002Sroot 12166002Sroot #include <utmp.h> 12176002Sroot 12186002Sroot struct utmp wtmp; 12196002Sroot char wtmpf[] = "/usr/adm/wtmp"; 122023567Sbloom char utmpf[] = "/etc/utmp"; 122123567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 122223567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12236002Sroot 12246002Sroot rmut() 12256002Sroot { 12266002Sroot register f; 12276002Sroot int found = 0; 122823567Sbloom struct utmp *u, *utmp; 122923567Sbloom int nutmp; 123023567Sbloom struct stat statbf; 12316002Sroot 123223567Sbloom f = open(utmpf, O_RDWR); 12336002Sroot if (f >= 0) { 123423567Sbloom fstat(f, &statbf); 123523567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 123623567Sbloom if (!utmp) 123723567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 123823567Sbloom if (statbf.st_size && utmp) { 123923567Sbloom nutmp = read(f, utmp, statbf.st_size); 124023567Sbloom nutmp /= sizeof(struct utmp); 124123567Sbloom 124223567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 124323567Sbloom if (SCMPN(u->ut_line, line+5) || 124423567Sbloom u->ut_name[0]==0) 124523567Sbloom continue; 124623567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 124723567Sbloom SCPYN(u->ut_name, ""); 124823567Sbloom SCPYN(u->ut_host, ""); 124923567Sbloom time(&u->ut_time); 125023567Sbloom write(f, (char *)u, sizeof(wtmp)); 125123567Sbloom found++; 125223567Sbloom } 12536002Sroot } 12546002Sroot close(f); 12556002Sroot } 12566002Sroot if (found) { 125717583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12586002Sroot if (f >= 0) { 12596002Sroot SCPYN(wtmp.ut_line, line+5); 12606002Sroot SCPYN(wtmp.ut_name, ""); 126112683Ssam SCPYN(wtmp.ut_host, ""); 12626002Sroot time(&wtmp.ut_time); 126323567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 12646002Sroot close(f); 12656002Sroot } 12666002Sroot } 12676002Sroot chmod(line, 0666); 12686002Sroot chown(line, 0, 0); 12696002Sroot line[strlen("/dev/")] = 'p'; 12706002Sroot chmod(line, 0666); 12716002Sroot chown(line, 0, 0); 12726002Sroot } 1273