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*28044Sminshall static char sccsid[] = "@(#)telnetd.c 5.18 (Berkeley) 05/12/86"; 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"); 192*28044Sminshall exit(1); 193*28044Sminshall } else if (ncc == 0) { 194*28044Sminshall syslog(LOG_INFO, "ttloop: peer died: %m\n"); 195*28044Sminshall 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; 220*28044Sminshall 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; 57427020Sminshall if (c == '\r') { 57527020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 57627020Sminshall *nfrontp++ = *ptyip++ & 0377; 57727020Sminshall pcc--; 57827020Sminshall } else 57927020Sminshall *nfrontp++ = '\0'; 58027020Sminshall } 5816002Sroot } 58227185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 5836002Sroot netflush(); 5846002Sroot if (ncc > 0) 5856002Sroot telrcv(); 58627185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 5876002Sroot ptyflush(); 5886002Sroot } 5896002Sroot cleanup(); 5906002Sroot } 5916002Sroot 5926002Sroot /* 5936002Sroot * State for recv fsm 5946002Sroot */ 5956002Sroot #define TS_DATA 0 /* base state */ 5966002Sroot #define TS_IAC 1 /* look for double IAC's */ 5976002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 59827649Sminshall #define TS_SB 3 /* throw away begin's... */ 59927649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6006002Sroot #define TS_WILL 5 /* will option negotiation */ 6016002Sroot #define TS_WONT 6 /* wont " */ 6026002Sroot #define TS_DO 7 /* do " */ 6036002Sroot #define TS_DONT 8 /* dont " */ 6046002Sroot 6056002Sroot telrcv() 6066002Sroot { 6076002Sroot register int c; 6086002Sroot static int state = TS_DATA; 6096002Sroot 6106002Sroot while (ncc > 0) { 6116002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6126002Sroot return; 6136002Sroot c = *netip++ & 0377, ncc--; 6146002Sroot switch (state) { 6156002Sroot 61626090Sminshall case TS_CR: 61726090Sminshall state = TS_DATA; 61826499Sminshall if ((c == 0) || (c == '\n')) { 61926090Sminshall break; 62026499Sminshall } 62126090Sminshall /* FALL THROUGH */ 62226090Sminshall 6236002Sroot case TS_DATA: 6246002Sroot if (c == IAC) { 6256002Sroot state = TS_IAC; 6266002Sroot break; 6276002Sroot } 6286002Sroot if (inter > 0) 6296002Sroot break; 63027020Sminshall /* 63127020Sminshall * We map \r\n ==> \n, since \r\n says 63227020Sminshall * that we want to be in column 1 of the next 63327020Sminshall * printable line, and \n is the standard 63427020Sminshall * unix way of saying that (\r is only good 63527020Sminshall * if CRMOD is set, which it normally is). 63627020Sminshall */ 63727983Sminshall if ((myopts[TELOPT_BINARY] == OPT_NO) && c == '\r') { 63827020Sminshall if ((ncc > 0) && ('\n' == *netip)) { 63927020Sminshall netip++; ncc--; 64027020Sminshall c = '\n'; 64127020Sminshall } else { 64227020Sminshall state = TS_CR; 64327020Sminshall } 64426499Sminshall } 64526499Sminshall *pfrontp++ = c; 6466002Sroot break; 6476002Sroot 6486002Sroot case TS_IAC: 6496002Sroot switch (c) { 6506002Sroot 6516002Sroot /* 6526002Sroot * Send the process on the pty side an 6536002Sroot * interrupt. Do this with a NULL or 6546002Sroot * interrupt char; depending on the tty mode. 6556002Sroot */ 6566002Sroot case IP: 6576002Sroot interrupt(); 6586002Sroot break; 6596002Sroot 66027229Sminshall case BREAK: 66127229Sminshall sendbrk(); 66227229Sminshall break; 66327229Sminshall 6646002Sroot /* 6656002Sroot * Are You There? 6666002Sroot */ 6676002Sroot case AYT: 66817583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 66917583Ssam nfrontp += 9; 6706002Sroot break; 6716002Sroot 6726002Sroot /* 67327185Sminshall * Abort Output 67427185Sminshall */ 67527185Sminshall case AO: { 67627185Sminshall struct ltchars tmpltc; 67727185Sminshall 67827185Sminshall ptyflush(); /* half-hearted */ 67927185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 68027185Sminshall if (tmpltc.t_flushc != '\377') { 68127185Sminshall *pfrontp++ = tmpltc.t_flushc; 68227185Sminshall } 68327260Sminshall netclear(); /* clear buffer back */ 68427185Sminshall *nfrontp++ = IAC; 68527185Sminshall *nfrontp++ = DM; 68627187Sminshall neturg = nfrontp-1; /* off by one XXX */ 68727185Sminshall break; 68827185Sminshall } 68927185Sminshall 69027185Sminshall /* 6916002Sroot * Erase Character and 6926002Sroot * Erase Line 6936002Sroot */ 6946002Sroot case EC: 69527185Sminshall case EL: { 69627185Sminshall struct sgttyb b; 69727185Sminshall char ch; 6986002Sroot 69927185Sminshall ptyflush(); /* half-hearted */ 70027185Sminshall ioctl(pty, TIOCGETP, &b); 70127185Sminshall ch = (c == EC) ? 70227185Sminshall b.sg_erase : b.sg_kill; 70327185Sminshall if (ch != '\377') { 70427185Sminshall *pfrontp++ = ch; 70527185Sminshall } 70627185Sminshall break; 70727185Sminshall } 70827185Sminshall 7096002Sroot /* 7106002Sroot * Check for urgent data... 7116002Sroot */ 7126002Sroot case DM: 71327185Sminshall SYNCHing = stilloob(net); 71427185Sminshall settimer(gotDM); 7156002Sroot break; 7166002Sroot 71727185Sminshall 7186002Sroot /* 7196002Sroot * Begin option subnegotiation... 7206002Sroot */ 7216002Sroot case SB: 72227649Sminshall state = TS_SB; 7236002Sroot continue; 7246002Sroot 7256002Sroot case WILL: 72627188Sminshall state = TS_WILL; 72727188Sminshall continue; 72827188Sminshall 7296002Sroot case WONT: 73027188Sminshall state = TS_WONT; 73127188Sminshall continue; 73227188Sminshall 7336002Sroot case DO: 73427188Sminshall state = TS_DO; 73527188Sminshall continue; 73627188Sminshall 7376002Sroot case DONT: 73827188Sminshall state = TS_DONT; 7396002Sroot continue; 7406002Sroot 7416002Sroot case IAC: 7426002Sroot *pfrontp++ = c; 7436002Sroot break; 7446002Sroot } 7456002Sroot state = TS_DATA; 7466002Sroot break; 7476002Sroot 74827649Sminshall case TS_SB: 74927649Sminshall if (c == IAC) { 75027649Sminshall state = TS_SE; 75127649Sminshall } else { 75227649Sminshall SB_ACCUM(c); 75327649Sminshall } 7546002Sroot break; 7556002Sroot 75627649Sminshall case TS_SE: 75727649Sminshall if (c != SE) { 75827649Sminshall if (c != IAC) { 75927649Sminshall SB_ACCUM(IAC); 76027649Sminshall } 76127649Sminshall SB_ACCUM(c); 76227649Sminshall state = TS_SB; 76327649Sminshall } else { 76427649Sminshall SB_TERM(); 76527649Sminshall suboption(); /* handle sub-option */ 76627649Sminshall state = TS_DATA; 76727649Sminshall } 7686002Sroot break; 7696002Sroot 7706002Sroot case TS_WILL: 77127983Sminshall if (hisopts[c] != OPT_YES) 7726002Sroot willoption(c); 7736002Sroot state = TS_DATA; 7746002Sroot continue; 7756002Sroot 7766002Sroot case TS_WONT: 77727983Sminshall if (hisopts[c] != OPT_NO) 7786002Sroot wontoption(c); 7796002Sroot state = TS_DATA; 7806002Sroot continue; 7816002Sroot 7826002Sroot case TS_DO: 78327983Sminshall if (myopts[c] != OPT_YES) 7846002Sroot dooption(c); 7856002Sroot state = TS_DATA; 7866002Sroot continue; 7876002Sroot 7886002Sroot case TS_DONT: 78927983Sminshall if (myopts[c] != OPT_NO) { 79027649Sminshall dontoption(c); 7916002Sroot } 7926002Sroot state = TS_DATA; 7936002Sroot continue; 7946002Sroot 7956002Sroot default: 79627898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 7979218Ssam printf("telnetd: panic state=%d\n", state); 7986002Sroot exit(1); 7996002Sroot } 8006002Sroot } 8016002Sroot } 8026002Sroot 8036002Sroot willoption(option) 8046002Sroot int option; 8056002Sroot { 8066002Sroot char *fmt; 8076002Sroot 8086002Sroot switch (option) { 8096002Sroot 8106002Sroot case TELOPT_BINARY: 8116002Sroot mode(RAW, 0); 81227188Sminshall fmt = doopt; 81327188Sminshall break; 8146002Sroot 8156002Sroot case TELOPT_ECHO: 81627649Sminshall not42 = 0; /* looks like a 4.2 system */ 81727649Sminshall /* 81827649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 81927649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 82027649Sminshall * Kludge upon kludge! 82127649Sminshall */ 82227983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 82327649Sminshall dooption(TELOPT_ECHO); 82427649Sminshall } 82527649Sminshall fmt = dont; 82627188Sminshall break; 8276002Sroot 82827649Sminshall case TELOPT_TTYPE: 82927983Sminshall settimer(ttypeopt); 83027983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 83127983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 83227983Sminshall return; 83327983Sminshall } 83427983Sminshall fmt = doopt; 83527983Sminshall break; 83627983Sminshall 8376002Sroot case TELOPT_SGA: 8386002Sroot fmt = doopt; 8396002Sroot break; 8406002Sroot 8416002Sroot case TELOPT_TM: 8426002Sroot fmt = dont; 8436002Sroot break; 8446002Sroot 8456002Sroot default: 8466002Sroot fmt = dont; 8476002Sroot break; 8486002Sroot } 84927188Sminshall if (fmt == doopt) { 85027983Sminshall hisopts[option] = OPT_YES; 85127188Sminshall } else { 85227983Sminshall hisopts[option] = OPT_NO; 85327188Sminshall } 8546023Ssam sprintf(nfrontp, fmt, option); 8558379Ssam nfrontp += sizeof (dont) - 2; 8566002Sroot } 8576002Sroot 8586002Sroot wontoption(option) 8596002Sroot int option; 8606002Sroot { 8616002Sroot char *fmt; 8626002Sroot 8636002Sroot switch (option) { 8646002Sroot case TELOPT_ECHO: 86527649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 86627188Sminshall break; 8676002Sroot 8686002Sroot case TELOPT_BINARY: 8696002Sroot mode(0, RAW); 8706002Sroot break; 871*28044Sminshall 872*28044Sminshall case TELOPT_TTYPE: 873*28044Sminshall settimer(ttypeopt); 874*28044Sminshall break; 8756002Sroot } 876*28044Sminshall 87727188Sminshall fmt = dont; 87827983Sminshall hisopts[option] = OPT_NO; 8796002Sroot sprintf(nfrontp, fmt, option); 8808379Ssam nfrontp += sizeof (doopt) - 2; 8816002Sroot } 8826002Sroot 8836002Sroot dooption(option) 8846002Sroot int option; 8856002Sroot { 8866002Sroot char *fmt; 8876002Sroot 8886002Sroot switch (option) { 8896002Sroot 8906002Sroot case TELOPT_TM: 8916002Sroot fmt = wont; 8926002Sroot break; 8936002Sroot 8946002Sroot case TELOPT_ECHO: 8956002Sroot mode(ECHO|CRMOD, 0); 89627188Sminshall fmt = will; 89727188Sminshall break; 8986002Sroot 8996002Sroot case TELOPT_BINARY: 9006002Sroot mode(RAW, 0); 90127188Sminshall fmt = will; 90227188Sminshall break; 9036002Sroot 9046002Sroot case TELOPT_SGA: 9056002Sroot fmt = will; 9066002Sroot break; 9076002Sroot 9086002Sroot default: 9096002Sroot fmt = wont; 9106002Sroot break; 9116002Sroot } 91227188Sminshall if (fmt == will) { 91327983Sminshall myopts[option] = OPT_YES; 91427188Sminshall } else { 91527983Sminshall myopts[option] = OPT_NO; 91627188Sminshall } 9176002Sroot sprintf(nfrontp, fmt, option); 9188379Ssam nfrontp += sizeof (doopt) - 2; 9196002Sroot } 9206002Sroot 92127649Sminshall 92227649Sminshall dontoption(option) 92327649Sminshall int option; 92427649Sminshall { 92527649Sminshall char *fmt; 92627649Sminshall 92727649Sminshall switch (option) { 92827649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 92927649Sminshall mode(0, ECHO|CRMOD); 93027649Sminshall fmt = wont; 93127649Sminshall break; 93227983Sminshall 93327649Sminshall default: 93427649Sminshall fmt = wont; 93527649Sminshall break; 93627649Sminshall } 93727983Sminshall 93827649Sminshall if (fmt = wont) { 93927983Sminshall myopts[option] = OPT_NO; 94027649Sminshall } else { 94127983Sminshall myopts[option] = OPT_YES; 94227649Sminshall } 94327649Sminshall sprintf(nfrontp, fmt, option); 94427649Sminshall nfrontp += sizeof (wont) - 2; 94527649Sminshall } 94627649Sminshall 94727649Sminshall /* 94827649Sminshall * suboption() 94927649Sminshall * 95027649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 95127649Sminshall * side. 95227649Sminshall * 95327649Sminshall * Currently we recognize: 95427649Sminshall * 95527983Sminshall * Terminal type is 95627649Sminshall */ 95727649Sminshall 95827649Sminshall suboption() 95927649Sminshall { 96027983Sminshall switch (SB_GET()) { 96127983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 96227983Sminshall static char terminalname[5+41] = "TERM="; 96327983Sminshall 96427983Sminshall settimer(ttypesubopt); 96527983Sminshall 96627983Sminshall if (SB_GET() != TELQUAL_IS) { 96727983Sminshall return; /* ??? XXX but, this is the most robust */ 96827983Sminshall } 96927983Sminshall 97027983Sminshall terminaltype = terminalname+strlen(terminalname); 97127983Sminshall 97227983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 97327983Sminshall !SB_EOF()) { 97427983Sminshall register int c; 97527983Sminshall 97627983Sminshall c = SB_GET(); 97727983Sminshall if (isupper(c)) { 97827983Sminshall c = tolower(c); 97927983Sminshall } 98027983Sminshall *terminaltype++ = c; /* accumulate name */ 98127983Sminshall } 98227983Sminshall *terminaltype = 0; 98327983Sminshall terminaltype = terminalname; 98427983Sminshall break; 98527983Sminshall } 98627983Sminshall 98727649Sminshall default: 98827649Sminshall ; 98927649Sminshall } 99027649Sminshall } 99127649Sminshall 9926002Sroot mode(on, off) 9936002Sroot int on, off; 9946002Sroot { 9956002Sroot struct sgttyb b; 9966002Sroot 9976002Sroot ptyflush(); 9986002Sroot ioctl(pty, TIOCGETP, &b); 9996002Sroot b.sg_flags |= on; 10006002Sroot b.sg_flags &= ~off; 10016002Sroot ioctl(pty, TIOCSETP, &b); 10026002Sroot } 10036002Sroot 10046002Sroot /* 10056002Sroot * Send interrupt to process on other side of pty. 10066002Sroot * If it is in raw mode, just write NULL; 10076002Sroot * otherwise, write intr char. 10086002Sroot */ 10096002Sroot interrupt() 10106002Sroot { 10116002Sroot struct sgttyb b; 10126002Sroot struct tchars tchars; 10136002Sroot 10146002Sroot ptyflush(); /* half-hearted */ 10156002Sroot ioctl(pty, TIOCGETP, &b); 10166002Sroot if (b.sg_flags & RAW) { 10176002Sroot *pfrontp++ = '\0'; 10186002Sroot return; 10196002Sroot } 10206002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10216002Sroot '\177' : tchars.t_intrc; 10226002Sroot } 10236002Sroot 102427229Sminshall /* 102527229Sminshall * Send quit to process on other side of pty. 102627229Sminshall * If it is in raw mode, just write NULL; 102727229Sminshall * otherwise, write quit char. 102827229Sminshall */ 102927229Sminshall sendbrk() 103027229Sminshall { 103127229Sminshall struct sgttyb b; 103227229Sminshall struct tchars tchars; 103327229Sminshall 103427229Sminshall ptyflush(); /* half-hearted */ 103527229Sminshall ioctl(pty, TIOCGETP, &b); 103627229Sminshall if (b.sg_flags & RAW) { 103727229Sminshall *pfrontp++ = '\0'; 103827229Sminshall return; 103927229Sminshall } 104027229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 104127229Sminshall '\034' : tchars.t_quitc; 104227229Sminshall } 104327229Sminshall 10446002Sroot ptyflush() 10456002Sroot { 10466002Sroot int n; 10476002Sroot 10486002Sroot if ((n = pfrontp - pbackp) > 0) 10496002Sroot n = write(pty, pbackp, n); 10508346Ssam if (n < 0) 10518346Ssam return; 10526002Sroot pbackp += n; 10536002Sroot if (pbackp == pfrontp) 10546002Sroot pbackp = pfrontp = ptyobuf; 10556002Sroot } 105627260Sminshall 105727260Sminshall /* 105827260Sminshall * nextitem() 105927260Sminshall * 106027260Sminshall * Return the address of the next "item" in the TELNET data 106127260Sminshall * stream. This will be the address of the next character if 106227260Sminshall * the current address is a user data character, or it will 106327260Sminshall * be the address of the character following the TELNET command 106427260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 106527260Sminshall * character. 106627260Sminshall */ 10676002Sroot 106827260Sminshall char * 106927260Sminshall nextitem(current) 107027260Sminshall char *current; 10716002Sroot { 107227260Sminshall if ((*current&0xff) != IAC) { 107327260Sminshall return current+1; 107427260Sminshall } 107527260Sminshall switch (*(current+1)&0xff) { 107627260Sminshall case DO: 107727260Sminshall case DONT: 107827260Sminshall case WILL: 107927260Sminshall case WONT: 108027260Sminshall return current+3; 108127260Sminshall case SB: /* loop forever looking for the SE */ 108227260Sminshall { 108327260Sminshall register char *look = current+2; 10846002Sroot 108527260Sminshall for (;;) { 108627260Sminshall if ((*look++&0xff) == IAC) { 108727260Sminshall if ((*look++&0xff) == SE) { 108827260Sminshall return look; 108927260Sminshall } 109027260Sminshall } 109127260Sminshall } 10928346Ssam } 109327260Sminshall default: 109427260Sminshall return current+2; 109527260Sminshall } 10966002Sroot } 10976002Sroot 109827185Sminshall 109927185Sminshall /* 110027260Sminshall * netclear() 110127260Sminshall * 110227260Sminshall * We are about to do a TELNET SYNCH operation. Clear 110327260Sminshall * the path to the network. 110427260Sminshall * 110527260Sminshall * Things are a bit tricky since we may have sent the first 110627260Sminshall * byte or so of a previous TELNET command into the network. 110727260Sminshall * So, we have to scan the network buffer from the beginning 110827260Sminshall * until we are up to where we want to be. 110927260Sminshall * 111027260Sminshall * A side effect of what we do, just to keep things 111127260Sminshall * simple, is to clear the urgent data pointer. The principal 111227260Sminshall * caller should be setting the urgent data pointer AFTER calling 111327260Sminshall * us in any case. 111427260Sminshall */ 111527260Sminshall 111627260Sminshall netclear() 111727260Sminshall { 111827260Sminshall register char *thisitem, *next; 111927260Sminshall char *good; 112027260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 112127260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 112227260Sminshall 112327260Sminshall thisitem = netobuf; 112427260Sminshall 112527260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 112627260Sminshall thisitem = next; 112727260Sminshall } 112827260Sminshall 112927260Sminshall /* Now, thisitem is first before/at boundary. */ 113027260Sminshall 113127260Sminshall good = netobuf; /* where the good bytes go */ 113227260Sminshall 113327260Sminshall while (nfrontp > thisitem) { 113427260Sminshall if (wewant(thisitem)) { 113527260Sminshall int length; 113627260Sminshall 113727260Sminshall next = thisitem; 113827260Sminshall do { 113927260Sminshall next = nextitem(next); 114027260Sminshall } while (wewant(next) && (nfrontp > next)); 114127260Sminshall length = next-thisitem; 114227260Sminshall bcopy(thisitem, good, length); 114327260Sminshall good += length; 114427260Sminshall thisitem = next; 114527260Sminshall } else { 114627260Sminshall thisitem = nextitem(thisitem); 114727260Sminshall } 114827260Sminshall } 114927260Sminshall 115027260Sminshall nbackp = netobuf; 115127260Sminshall nfrontp = good; /* next byte to be sent */ 115227260Sminshall neturg = 0; 115327260Sminshall } 115427260Sminshall 115527260Sminshall /* 115627185Sminshall * netflush 115727185Sminshall * Send as much data as possible to the network, 115827185Sminshall * handling requests for urgent data. 115927185Sminshall */ 116027185Sminshall 116127185Sminshall 116227185Sminshall netflush() 116327185Sminshall { 116427185Sminshall int n; 116527185Sminshall 116627185Sminshall if ((n = nfrontp - nbackp) > 0) { 116727649Sminshall /* 116827649Sminshall * if no urgent data, or if the other side appears to be an 116927649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 117027649Sminshall * write the entire buffer in non-OOB mode. 117127649Sminshall */ 117227649Sminshall if ((neturg == 0) || (not42 == 0)) { 117327185Sminshall n = write(net, nbackp, n); /* normal write */ 117427185Sminshall } else { 117527185Sminshall n = neturg - nbackp; 117627185Sminshall /* 117727185Sminshall * In 4.2 (and 4.3) systems, there is some question about 117827185Sminshall * what byte in a sendOOB operation is the "OOB" data. 117927185Sminshall * To make ourselves compatible, we only send ONE byte 118027185Sminshall * out of band, the one WE THINK should be OOB (though 118127185Sminshall * we really have more the TCP philosophy of urgent data 118227185Sminshall * rather than the Unix philosophy of OOB data). 118327185Sminshall */ 118427185Sminshall if (n > 1) { 118527185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 118627185Sminshall } else { 118727185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 118827185Sminshall } 118927185Sminshall } 119027185Sminshall } 119127185Sminshall if (n < 0) { 119227185Sminshall if (errno == EWOULDBLOCK) 119327185Sminshall return; 119427185Sminshall /* should blow this guy away... */ 119527185Sminshall return; 119627185Sminshall } 119727185Sminshall nbackp += n; 119827185Sminshall if (nbackp >= neturg) { 119927185Sminshall neturg = 0; 120027185Sminshall } 120127185Sminshall if (nbackp == nfrontp) { 120227185Sminshall nbackp = nfrontp = netobuf; 120327185Sminshall } 120427185Sminshall } 120527185Sminshall 12066002Sroot cleanup() 12076002Sroot { 12086002Sroot 12096002Sroot rmut(); 121010008Ssam vhangup(); /* XXX */ 121110191Ssam shutdown(net, 2); 12126002Sroot exit(1); 12136002Sroot } 12146002Sroot 12156002Sroot #include <utmp.h> 12166002Sroot 12176002Sroot struct utmp wtmp; 12186002Sroot char wtmpf[] = "/usr/adm/wtmp"; 121923567Sbloom char utmpf[] = "/etc/utmp"; 122023567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 122123567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12226002Sroot 12236002Sroot rmut() 12246002Sroot { 12256002Sroot register f; 12266002Sroot int found = 0; 122723567Sbloom struct utmp *u, *utmp; 122823567Sbloom int nutmp; 122923567Sbloom struct stat statbf; 12306002Sroot 123123567Sbloom f = open(utmpf, O_RDWR); 12326002Sroot if (f >= 0) { 123323567Sbloom fstat(f, &statbf); 123423567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 123523567Sbloom if (!utmp) 123623567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 123723567Sbloom if (statbf.st_size && utmp) { 123823567Sbloom nutmp = read(f, utmp, statbf.st_size); 123923567Sbloom nutmp /= sizeof(struct utmp); 124023567Sbloom 124123567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 124223567Sbloom if (SCMPN(u->ut_line, line+5) || 124323567Sbloom u->ut_name[0]==0) 124423567Sbloom continue; 124523567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 124623567Sbloom SCPYN(u->ut_name, ""); 124723567Sbloom SCPYN(u->ut_host, ""); 124823567Sbloom time(&u->ut_time); 124923567Sbloom write(f, (char *)u, sizeof(wtmp)); 125023567Sbloom found++; 125123567Sbloom } 12526002Sroot } 12536002Sroot close(f); 12546002Sroot } 12556002Sroot if (found) { 125617583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12576002Sroot if (f >= 0) { 12586002Sroot SCPYN(wtmp.ut_line, line+5); 12596002Sroot SCPYN(wtmp.ut_name, ""); 126012683Ssam SCPYN(wtmp.ut_host, ""); 12616002Sroot time(&wtmp.ut_time); 126223567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 12636002Sroot close(f); 12646002Sroot } 12656002Sroot } 12666002Sroot chmod(line, 0666); 12676002Sroot chown(line, 0, 0); 12686002Sroot line[strlen("/dev/")] = 'p'; 12696002Sroot chmod(line, 0666); 12706002Sroot chown(line, 0, 0); 12716002Sroot } 1272