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*32452Sbostic static char sccsid[] = "@(#)telnetd.c 5.23 (Berkeley) 10/22/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); 38932400Sminshall /* 39032400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 39132400Sminshall * in ttioctl() in /sys/tty.c. 39232400Sminshall */ 39332400Sminshall signal(SIGTTOU, SIG_IGN); 39413028Ssam signal(SIGCHLD, cleanup); 39526083Slepreau setpgrp(0, 0); 3966002Sroot 3978379Ssam /* 39827185Sminshall * Request to do remote echo and to suppress go ahead. 3998379Ssam */ 40027983Sminshall if (!myopts[TELOPT_ECHO]) { 40127983Sminshall dooption(TELOPT_ECHO); 40227983Sminshall } 40327983Sminshall if (!myopts[TELOPT_SGA]) { 40427983Sminshall dooption(TELOPT_SGA); 40527983Sminshall } 40612713Ssam /* 40727649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 40827649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 40927649Sminshall * 41027649Sminshall * To find out, we send out a "DO ECHO". If the remote system 41127649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 41227649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 41327649Sminshall * WE, the server, sends it; it does NOT mean that the client will 41427649Sminshall * echo the terminal input). 41527649Sminshall */ 416*32452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 41727649Sminshall nfrontp += sizeof doopt-2; 41827983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 41927649Sminshall 42027649Sminshall /* 42112713Ssam * Show banner that getty never gave. 42227797Sminshall * 42327797Sminshall * The banner includes some null's (for TELNET CR disambiguation), 42427797Sminshall * so we have to be somewhat complicated. 42512713Ssam */ 42627797Sminshall 42712713Ssam gethostname(hostname, sizeof (hostname)); 42827649Sminshall 42927797Sminshall bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); 43027797Sminshall nfrontp += sizeof BANNER1 - 1; 43127797Sminshall bcopy(hostname, nfrontp, strlen(hostname)); 43227797Sminshall nfrontp += strlen(hostname); 43327797Sminshall bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); 43427797Sminshall nfrontp += sizeof BANNER2 - 1; 43527797Sminshall 43627649Sminshall /* 43727649Sminshall * Call telrcv() once to pick up anything received during 43827649Sminshall * terminal type negotiation. 43927649Sminshall */ 44027649Sminshall telrcv(); 44127649Sminshall 4426002Sroot for (;;) { 44327185Sminshall fd_set ibits, obits, xbits; 4446002Sroot register int c; 4456002Sroot 44627185Sminshall if (ncc < 0 && pcc < 0) 44727185Sminshall break; 44827185Sminshall 44927185Sminshall FD_ZERO(&ibits); 45027185Sminshall FD_ZERO(&obits); 45127185Sminshall FD_ZERO(&xbits); 4526002Sroot /* 4536002Sroot * Never look for input if there's still 4546002Sroot * stuff in the corresponding output buffer 4556002Sroot */ 45627185Sminshall if (nfrontp - nbackp || pcc > 0) { 45727185Sminshall FD_SET(f, &obits); 45827185Sminshall } else { 45927185Sminshall FD_SET(p, &ibits); 46027185Sminshall } 46127185Sminshall if (pfrontp - pbackp || ncc > 0) { 46227185Sminshall FD_SET(p, &obits); 46327185Sminshall } else { 46427185Sminshall FD_SET(f, &ibits); 46527185Sminshall } 46627185Sminshall if (!SYNCHing) { 46727185Sminshall FD_SET(f, &xbits); 46827185Sminshall } 46927185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 47027185Sminshall (struct timeval *)0)) < 1) { 47127185Sminshall if (c == -1) { 47227185Sminshall if (errno == EINTR) { 47327185Sminshall continue; 47427185Sminshall } 47527185Sminshall } 4766002Sroot sleep(5); 4776002Sroot continue; 4786002Sroot } 4796002Sroot 4806002Sroot /* 48127185Sminshall * Any urgent data? 48227185Sminshall */ 48327185Sminshall if (FD_ISSET(net, &xbits)) { 48427185Sminshall SYNCHing = 1; 48527185Sminshall } 48627185Sminshall 48727185Sminshall /* 4886002Sroot * Something to read from the network... 4896002Sroot */ 49027185Sminshall if (FD_ISSET(net, &ibits)) { 49127649Sminshall #if !defined(SO_OOBINLINE) 49227185Sminshall /* 49327898Skarels * In 4.2 (and 4.3 beta) systems, the 49427185Sminshall * OOB indication and data handling in the kernel 49527185Sminshall * is such that if two separate TCP Urgent requests 49627185Sminshall * come in, one byte of TCP data will be overlaid. 49727185Sminshall * This is fatal for Telnet, but we try to live 49827185Sminshall * with it. 49927185Sminshall * 50027185Sminshall * In addition, in 4.2 (and...), a special protocol 50127185Sminshall * is needed to pick up the TCP Urgent data in 50227185Sminshall * the correct sequence. 50327185Sminshall * 50427185Sminshall * What we do is: if we think we are in urgent 50527185Sminshall * mode, we look to see if we are "at the mark". 50627185Sminshall * If we are, we do an OOB receive. If we run 50727185Sminshall * this twice, we will do the OOB receive twice, 50827185Sminshall * but the second will fail, since the second 50927185Sminshall * time we were "at the mark", but there wasn't 51027185Sminshall * any data there (the kernel doesn't reset 51127185Sminshall * "at the mark" until we do a normal read). 51227185Sminshall * Once we've read the OOB data, we go ahead 51327185Sminshall * and do normal reads. 51427185Sminshall * 51527185Sminshall * There is also another problem, which is that 51627185Sminshall * since the OOB byte we read doesn't put us 51727185Sminshall * out of OOB state, and since that byte is most 51827185Sminshall * likely the TELNET DM (data mark), we would 51927185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 52027185Sminshall * So, clocks to the rescue. If we've "just" 52127185Sminshall * received a DM, then we test for the 52227185Sminshall * presence of OOB data when the receive OOB 52327185Sminshall * fails (and AFTER we did the normal mode read 52427185Sminshall * to clear "at the mark"). 52527185Sminshall */ 52627185Sminshall if (SYNCHing) { 52727185Sminshall int atmark; 52827185Sminshall 52927185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 53027185Sminshall if (atmark) { 53127185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 53227185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 53327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 53427983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 53527185Sminshall SYNCHing = stilloob(net); 53627185Sminshall } 53727185Sminshall } 53827185Sminshall } else { 53927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5406002Sroot } 54127185Sminshall } else { 54227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 54327185Sminshall } 54427185Sminshall settimer(didnetreceive); 54527649Sminshall #else /* !defined(SO_OOBINLINE)) */ 54627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 54727649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 54827185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 54927185Sminshall ncc = 0; 55027185Sminshall else { 55127185Sminshall if (ncc <= 0) { 55227185Sminshall break; 55327185Sminshall } 55427185Sminshall netip = netibuf; 55527185Sminshall } 5566002Sroot } 5576002Sroot 5586002Sroot /* 5596002Sroot * Something to read from the pty... 5606002Sroot */ 56127185Sminshall if (FD_ISSET(p, &ibits)) { 5626002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5636002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5646002Sroot pcc = 0; 5656002Sroot else { 5666002Sroot if (pcc <= 0) 5676002Sroot break; 5686002Sroot ptyip = ptyibuf; 5696002Sroot } 5706002Sroot } 5716002Sroot 5726002Sroot while (pcc > 0) { 5736002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 5746002Sroot break; 5756002Sroot c = *ptyip++ & 0377, pcc--; 5766002Sroot if (c == IAC) 5776002Sroot *nfrontp++ = c; 5786002Sroot *nfrontp++ = c; 57931940Sbostic /* Don't do CR-NUL if we are in binary mode */ 58031940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 58127020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 58227020Sminshall *nfrontp++ = *ptyip++ & 0377; 58327020Sminshall pcc--; 58427020Sminshall } else 58527020Sminshall *nfrontp++ = '\0'; 58627020Sminshall } 5876002Sroot } 58827185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 5896002Sroot netflush(); 5906002Sroot if (ncc > 0) 5916002Sroot telrcv(); 59227185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 5936002Sroot ptyflush(); 5946002Sroot } 5956002Sroot cleanup(); 5966002Sroot } 5976002Sroot 5986002Sroot /* 5996002Sroot * State for recv fsm 6006002Sroot */ 6016002Sroot #define TS_DATA 0 /* base state */ 6026002Sroot #define TS_IAC 1 /* look for double IAC's */ 6036002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 60427649Sminshall #define TS_SB 3 /* throw away begin's... */ 60527649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6066002Sroot #define TS_WILL 5 /* will option negotiation */ 6076002Sroot #define TS_WONT 6 /* wont " */ 6086002Sroot #define TS_DO 7 /* do " */ 6096002Sroot #define TS_DONT 8 /* dont " */ 6106002Sroot 6116002Sroot telrcv() 6126002Sroot { 6136002Sroot register int c; 6146002Sroot static int state = TS_DATA; 6156002Sroot 6166002Sroot while (ncc > 0) { 6176002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6186002Sroot return; 6196002Sroot c = *netip++ & 0377, ncc--; 6206002Sroot switch (state) { 6216002Sroot 62226090Sminshall case TS_CR: 62326090Sminshall state = TS_DATA; 62432097Sminshall /* Strip off \n or \0 after a \r */ 62526499Sminshall if ((c == 0) || (c == '\n')) { 62626090Sminshall break; 62726499Sminshall } 62826090Sminshall /* FALL THROUGH */ 62926090Sminshall 6306002Sroot case TS_DATA: 6316002Sroot if (c == IAC) { 6326002Sroot state = TS_IAC; 6336002Sroot break; 6346002Sroot } 6356002Sroot if (inter > 0) 6366002Sroot break; 63727020Sminshall /* 63832097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 63932097Sminshall * Many client implementations send \r\n when 64032097Sminshall * the user hits the CarriageReturn key. 64132097Sminshall * 64232097Sminshall * We USED to map \r\n ==> \n, since \r\n says 64327020Sminshall * that we want to be in column 1 of the next 64427020Sminshall * printable line, and \n is the standard 64527020Sminshall * unix way of saying that (\r is only good 64627020Sminshall * if CRMOD is set, which it normally is). 64727020Sminshall */ 64831940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 64932097Sminshall state = TS_CR; 65026499Sminshall } 65126499Sminshall *pfrontp++ = c; 6526002Sroot break; 6536002Sroot 6546002Sroot case TS_IAC: 6556002Sroot switch (c) { 6566002Sroot 6576002Sroot /* 6586002Sroot * Send the process on the pty side an 6596002Sroot * interrupt. Do this with a NULL or 6606002Sroot * interrupt char; depending on the tty mode. 6616002Sroot */ 6626002Sroot case IP: 6636002Sroot interrupt(); 6646002Sroot break; 6656002Sroot 66627229Sminshall case BREAK: 66727229Sminshall sendbrk(); 66827229Sminshall break; 66927229Sminshall 6706002Sroot /* 6716002Sroot * Are You There? 6726002Sroot */ 6736002Sroot case AYT: 67417583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 67517583Ssam nfrontp += 9; 6766002Sroot break; 6776002Sroot 6786002Sroot /* 67927185Sminshall * Abort Output 68027185Sminshall */ 68127185Sminshall case AO: { 68227185Sminshall struct ltchars tmpltc; 68327185Sminshall 68427185Sminshall ptyflush(); /* half-hearted */ 68527185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 68627185Sminshall if (tmpltc.t_flushc != '\377') { 68727185Sminshall *pfrontp++ = tmpltc.t_flushc; 68827185Sminshall } 68927260Sminshall netclear(); /* clear buffer back */ 69027185Sminshall *nfrontp++ = IAC; 69127185Sminshall *nfrontp++ = DM; 69227187Sminshall neturg = nfrontp-1; /* off by one XXX */ 69327185Sminshall break; 69427185Sminshall } 69527185Sminshall 69627185Sminshall /* 6976002Sroot * Erase Character and 6986002Sroot * Erase Line 6996002Sroot */ 7006002Sroot case EC: 70127185Sminshall case EL: { 70227185Sminshall struct sgttyb b; 70327185Sminshall char ch; 7046002Sroot 70527185Sminshall ptyflush(); /* half-hearted */ 70627185Sminshall ioctl(pty, TIOCGETP, &b); 70727185Sminshall ch = (c == EC) ? 70827185Sminshall b.sg_erase : b.sg_kill; 70927185Sminshall if (ch != '\377') { 71027185Sminshall *pfrontp++ = ch; 71127185Sminshall } 71227185Sminshall break; 71327185Sminshall } 71427185Sminshall 7156002Sroot /* 7166002Sroot * Check for urgent data... 7176002Sroot */ 7186002Sroot case DM: 71927185Sminshall SYNCHing = stilloob(net); 72027185Sminshall settimer(gotDM); 7216002Sroot break; 7226002Sroot 72327185Sminshall 7246002Sroot /* 7256002Sroot * Begin option subnegotiation... 7266002Sroot */ 7276002Sroot case SB: 72827649Sminshall state = TS_SB; 7296002Sroot continue; 7306002Sroot 7316002Sroot case WILL: 73227188Sminshall state = TS_WILL; 73327188Sminshall continue; 73427188Sminshall 7356002Sroot case WONT: 73627188Sminshall state = TS_WONT; 73727188Sminshall continue; 73827188Sminshall 7396002Sroot case DO: 74027188Sminshall state = TS_DO; 74127188Sminshall continue; 74227188Sminshall 7436002Sroot case DONT: 74427188Sminshall state = TS_DONT; 7456002Sroot continue; 7466002Sroot 7476002Sroot case IAC: 7486002Sroot *pfrontp++ = c; 7496002Sroot break; 7506002Sroot } 7516002Sroot state = TS_DATA; 7526002Sroot break; 7536002Sroot 75427649Sminshall case TS_SB: 75527649Sminshall if (c == IAC) { 75627649Sminshall state = TS_SE; 75727649Sminshall } else { 75827649Sminshall SB_ACCUM(c); 75927649Sminshall } 7606002Sroot break; 7616002Sroot 76227649Sminshall case TS_SE: 76327649Sminshall if (c != SE) { 76427649Sminshall if (c != IAC) { 76527649Sminshall SB_ACCUM(IAC); 76627649Sminshall } 76727649Sminshall SB_ACCUM(c); 76827649Sminshall state = TS_SB; 76927649Sminshall } else { 77027649Sminshall SB_TERM(); 77127649Sminshall suboption(); /* handle sub-option */ 77227649Sminshall state = TS_DATA; 77327649Sminshall } 7746002Sroot break; 7756002Sroot 7766002Sroot case TS_WILL: 77727983Sminshall if (hisopts[c] != OPT_YES) 7786002Sroot willoption(c); 7796002Sroot state = TS_DATA; 7806002Sroot continue; 7816002Sroot 7826002Sroot case TS_WONT: 78327983Sminshall if (hisopts[c] != OPT_NO) 7846002Sroot wontoption(c); 7856002Sroot state = TS_DATA; 7866002Sroot continue; 7876002Sroot 7886002Sroot case TS_DO: 78927983Sminshall if (myopts[c] != OPT_YES) 7906002Sroot dooption(c); 7916002Sroot state = TS_DATA; 7926002Sroot continue; 7936002Sroot 7946002Sroot case TS_DONT: 79527983Sminshall if (myopts[c] != OPT_NO) { 79627649Sminshall dontoption(c); 7976002Sroot } 7986002Sroot state = TS_DATA; 7996002Sroot continue; 8006002Sroot 8016002Sroot default: 80227898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8039218Ssam printf("telnetd: panic state=%d\n", state); 8046002Sroot exit(1); 8056002Sroot } 8066002Sroot } 8076002Sroot } 8086002Sroot 8096002Sroot willoption(option) 8106002Sroot int option; 8116002Sroot { 8126002Sroot char *fmt; 8136002Sroot 8146002Sroot switch (option) { 8156002Sroot 8166002Sroot case TELOPT_BINARY: 8176002Sroot mode(RAW, 0); 81827188Sminshall fmt = doopt; 81927188Sminshall break; 8206002Sroot 8216002Sroot case TELOPT_ECHO: 82227649Sminshall not42 = 0; /* looks like a 4.2 system */ 82327649Sminshall /* 82427649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 82527649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 82627649Sminshall * Kludge upon kludge! 82727649Sminshall */ 82827983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 82927649Sminshall dooption(TELOPT_ECHO); 83027649Sminshall } 83127649Sminshall fmt = dont; 83227188Sminshall break; 8336002Sroot 83427649Sminshall case TELOPT_TTYPE: 83527983Sminshall settimer(ttypeopt); 83627983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 83727983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 83827983Sminshall return; 83927983Sminshall } 84027983Sminshall fmt = doopt; 84127983Sminshall break; 84227983Sminshall 8436002Sroot case TELOPT_SGA: 8446002Sroot fmt = doopt; 8456002Sroot break; 8466002Sroot 8476002Sroot case TELOPT_TM: 8486002Sroot fmt = dont; 8496002Sroot break; 8506002Sroot 8516002Sroot default: 8526002Sroot fmt = dont; 8536002Sroot break; 8546002Sroot } 85527188Sminshall if (fmt == doopt) { 85627983Sminshall hisopts[option] = OPT_YES; 85727188Sminshall } else { 85827983Sminshall hisopts[option] = OPT_NO; 85927188Sminshall } 860*32452Sbostic (void) sprintf(nfrontp, fmt, option); 8618379Ssam nfrontp += sizeof (dont) - 2; 8626002Sroot } 8636002Sroot 8646002Sroot wontoption(option) 8656002Sroot int option; 8666002Sroot { 8676002Sroot char *fmt; 8686002Sroot 8696002Sroot switch (option) { 8706002Sroot case TELOPT_ECHO: 87127649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 87227188Sminshall break; 8736002Sroot 8746002Sroot case TELOPT_BINARY: 8756002Sroot mode(0, RAW); 8766002Sroot break; 87728044Sminshall 87828044Sminshall case TELOPT_TTYPE: 87928044Sminshall settimer(ttypeopt); 88028044Sminshall break; 8816002Sroot } 88228044Sminshall 88327188Sminshall fmt = dont; 88427983Sminshall hisopts[option] = OPT_NO; 885*32452Sbostic (void) sprintf(nfrontp, fmt, option); 8868379Ssam nfrontp += sizeof (doopt) - 2; 8876002Sroot } 8886002Sroot 8896002Sroot dooption(option) 8906002Sroot int option; 8916002Sroot { 8926002Sroot char *fmt; 8936002Sroot 8946002Sroot switch (option) { 8956002Sroot 8966002Sroot case TELOPT_TM: 8976002Sroot fmt = wont; 8986002Sroot break; 8996002Sroot 9006002Sroot case TELOPT_ECHO: 9016002Sroot mode(ECHO|CRMOD, 0); 90227188Sminshall fmt = will; 90327188Sminshall break; 9046002Sroot 9056002Sroot case TELOPT_BINARY: 9066002Sroot mode(RAW, 0); 90727188Sminshall fmt = will; 90827188Sminshall break; 9096002Sroot 9106002Sroot case TELOPT_SGA: 9116002Sroot fmt = will; 9126002Sroot break; 9136002Sroot 9146002Sroot default: 9156002Sroot fmt = wont; 9166002Sroot break; 9176002Sroot } 91827188Sminshall if (fmt == will) { 91927983Sminshall myopts[option] = OPT_YES; 92027188Sminshall } else { 92127983Sminshall myopts[option] = OPT_NO; 92227188Sminshall } 923*32452Sbostic (void) sprintf(nfrontp, fmt, option); 9248379Ssam nfrontp += sizeof (doopt) - 2; 9256002Sroot } 9266002Sroot 92727649Sminshall 92827649Sminshall dontoption(option) 92927649Sminshall int option; 93027649Sminshall { 93127649Sminshall char *fmt; 93227649Sminshall 93327649Sminshall switch (option) { 93427649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 93532401Sminshall mode(0, ECHO); 93627649Sminshall fmt = wont; 93727649Sminshall break; 93827983Sminshall 93927649Sminshall default: 94027649Sminshall fmt = wont; 94127649Sminshall break; 94227649Sminshall } 94327983Sminshall 94427649Sminshall if (fmt = wont) { 94527983Sminshall myopts[option] = OPT_NO; 94627649Sminshall } else { 94727983Sminshall myopts[option] = OPT_YES; 94827649Sminshall } 949*32452Sbostic (void) sprintf(nfrontp, fmt, option); 95027649Sminshall nfrontp += sizeof (wont) - 2; 95127649Sminshall } 95227649Sminshall 95327649Sminshall /* 95427649Sminshall * suboption() 95527649Sminshall * 95627649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 95727649Sminshall * side. 95827649Sminshall * 95927649Sminshall * Currently we recognize: 96027649Sminshall * 96127983Sminshall * Terminal type is 96227649Sminshall */ 96327649Sminshall 96427649Sminshall suboption() 96527649Sminshall { 96627983Sminshall switch (SB_GET()) { 96727983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 96827983Sminshall static char terminalname[5+41] = "TERM="; 96927983Sminshall 97027983Sminshall settimer(ttypesubopt); 97127983Sminshall 97227983Sminshall if (SB_GET() != TELQUAL_IS) { 97327983Sminshall return; /* ??? XXX but, this is the most robust */ 97427983Sminshall } 97527983Sminshall 97627983Sminshall terminaltype = terminalname+strlen(terminalname); 97727983Sminshall 97827983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 97927983Sminshall !SB_EOF()) { 98027983Sminshall register int c; 98127983Sminshall 98227983Sminshall c = SB_GET(); 98327983Sminshall if (isupper(c)) { 98427983Sminshall c = tolower(c); 98527983Sminshall } 98627983Sminshall *terminaltype++ = c; /* accumulate name */ 98727983Sminshall } 98827983Sminshall *terminaltype = 0; 98927983Sminshall terminaltype = terminalname; 99027983Sminshall break; 99127983Sminshall } 99227983Sminshall 99327649Sminshall default: 99427649Sminshall ; 99527649Sminshall } 99627649Sminshall } 99727649Sminshall 9986002Sroot mode(on, off) 9996002Sroot int on, off; 10006002Sroot { 10016002Sroot struct sgttyb b; 10026002Sroot 10036002Sroot ptyflush(); 10046002Sroot ioctl(pty, TIOCGETP, &b); 10056002Sroot b.sg_flags |= on; 10066002Sroot b.sg_flags &= ~off; 10076002Sroot ioctl(pty, TIOCSETP, &b); 10086002Sroot } 10096002Sroot 10106002Sroot /* 10116002Sroot * Send interrupt to process on other side of pty. 10126002Sroot * If it is in raw mode, just write NULL; 10136002Sroot * otherwise, write intr char. 10146002Sroot */ 10156002Sroot interrupt() 10166002Sroot { 10176002Sroot struct sgttyb b; 10186002Sroot struct tchars tchars; 10196002Sroot 10206002Sroot ptyflush(); /* half-hearted */ 10216002Sroot ioctl(pty, TIOCGETP, &b); 10226002Sroot if (b.sg_flags & RAW) { 10236002Sroot *pfrontp++ = '\0'; 10246002Sroot return; 10256002Sroot } 10266002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10276002Sroot '\177' : tchars.t_intrc; 10286002Sroot } 10296002Sroot 103027229Sminshall /* 103127229Sminshall * Send quit to process on other side of pty. 103227229Sminshall * If it is in raw mode, just write NULL; 103327229Sminshall * otherwise, write quit char. 103427229Sminshall */ 103527229Sminshall sendbrk() 103627229Sminshall { 103727229Sminshall struct sgttyb b; 103827229Sminshall struct tchars tchars; 103927229Sminshall 104027229Sminshall ptyflush(); /* half-hearted */ 104127229Sminshall ioctl(pty, TIOCGETP, &b); 104227229Sminshall if (b.sg_flags & RAW) { 104327229Sminshall *pfrontp++ = '\0'; 104427229Sminshall return; 104527229Sminshall } 104627229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 104727229Sminshall '\034' : tchars.t_quitc; 104827229Sminshall } 104927229Sminshall 10506002Sroot ptyflush() 10516002Sroot { 10526002Sroot int n; 10536002Sroot 10546002Sroot if ((n = pfrontp - pbackp) > 0) 10556002Sroot n = write(pty, pbackp, n); 10568346Ssam if (n < 0) 10578346Ssam return; 10586002Sroot pbackp += n; 10596002Sroot if (pbackp == pfrontp) 10606002Sroot pbackp = pfrontp = ptyobuf; 10616002Sroot } 106227260Sminshall 106327260Sminshall /* 106427260Sminshall * nextitem() 106527260Sminshall * 106627260Sminshall * Return the address of the next "item" in the TELNET data 106727260Sminshall * stream. This will be the address of the next character if 106827260Sminshall * the current address is a user data character, or it will 106927260Sminshall * be the address of the character following the TELNET command 107027260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 107127260Sminshall * character. 107227260Sminshall */ 10736002Sroot 107427260Sminshall char * 107527260Sminshall nextitem(current) 107627260Sminshall char *current; 10776002Sroot { 107827260Sminshall if ((*current&0xff) != IAC) { 107927260Sminshall return current+1; 108027260Sminshall } 108127260Sminshall switch (*(current+1)&0xff) { 108227260Sminshall case DO: 108327260Sminshall case DONT: 108427260Sminshall case WILL: 108527260Sminshall case WONT: 108627260Sminshall return current+3; 108727260Sminshall case SB: /* loop forever looking for the SE */ 108827260Sminshall { 108927260Sminshall register char *look = current+2; 10906002Sroot 109127260Sminshall for (;;) { 109227260Sminshall if ((*look++&0xff) == IAC) { 109327260Sminshall if ((*look++&0xff) == SE) { 109427260Sminshall return look; 109527260Sminshall } 109627260Sminshall } 109727260Sminshall } 10988346Ssam } 109927260Sminshall default: 110027260Sminshall return current+2; 110127260Sminshall } 11026002Sroot } 11036002Sroot 110427185Sminshall 110527185Sminshall /* 110627260Sminshall * netclear() 110727260Sminshall * 110827260Sminshall * We are about to do a TELNET SYNCH operation. Clear 110927260Sminshall * the path to the network. 111027260Sminshall * 111127260Sminshall * Things are a bit tricky since we may have sent the first 111227260Sminshall * byte or so of a previous TELNET command into the network. 111327260Sminshall * So, we have to scan the network buffer from the beginning 111427260Sminshall * until we are up to where we want to be. 111527260Sminshall * 111627260Sminshall * A side effect of what we do, just to keep things 111727260Sminshall * simple, is to clear the urgent data pointer. The principal 111827260Sminshall * caller should be setting the urgent data pointer AFTER calling 111927260Sminshall * us in any case. 112027260Sminshall */ 112127260Sminshall 112227260Sminshall netclear() 112327260Sminshall { 112427260Sminshall register char *thisitem, *next; 112527260Sminshall char *good; 112627260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 112727260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 112827260Sminshall 112927260Sminshall thisitem = netobuf; 113027260Sminshall 113127260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 113227260Sminshall thisitem = next; 113327260Sminshall } 113427260Sminshall 113527260Sminshall /* Now, thisitem is first before/at boundary. */ 113627260Sminshall 113727260Sminshall good = netobuf; /* where the good bytes go */ 113827260Sminshall 113927260Sminshall while (nfrontp > thisitem) { 114027260Sminshall if (wewant(thisitem)) { 114127260Sminshall int length; 114227260Sminshall 114327260Sminshall next = thisitem; 114427260Sminshall do { 114527260Sminshall next = nextitem(next); 114627260Sminshall } while (wewant(next) && (nfrontp > next)); 114727260Sminshall length = next-thisitem; 114827260Sminshall bcopy(thisitem, good, length); 114927260Sminshall good += length; 115027260Sminshall thisitem = next; 115127260Sminshall } else { 115227260Sminshall thisitem = nextitem(thisitem); 115327260Sminshall } 115427260Sminshall } 115527260Sminshall 115627260Sminshall nbackp = netobuf; 115727260Sminshall nfrontp = good; /* next byte to be sent */ 115827260Sminshall neturg = 0; 115927260Sminshall } 116027260Sminshall 116127260Sminshall /* 116227185Sminshall * netflush 116327185Sminshall * Send as much data as possible to the network, 116427185Sminshall * handling requests for urgent data. 116527185Sminshall */ 116627185Sminshall 116727185Sminshall 116827185Sminshall netflush() 116927185Sminshall { 117027185Sminshall int n; 117127185Sminshall 117227185Sminshall if ((n = nfrontp - nbackp) > 0) { 117327649Sminshall /* 117427649Sminshall * if no urgent data, or if the other side appears to be an 117527649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 117627649Sminshall * write the entire buffer in non-OOB mode. 117727649Sminshall */ 117827649Sminshall if ((neturg == 0) || (not42 == 0)) { 117927185Sminshall n = write(net, nbackp, n); /* normal write */ 118027185Sminshall } else { 118127185Sminshall n = neturg - nbackp; 118227185Sminshall /* 118327185Sminshall * In 4.2 (and 4.3) systems, there is some question about 118427185Sminshall * what byte in a sendOOB operation is the "OOB" data. 118527185Sminshall * To make ourselves compatible, we only send ONE byte 118627185Sminshall * out of band, the one WE THINK should be OOB (though 118727185Sminshall * we really have more the TCP philosophy of urgent data 118827185Sminshall * rather than the Unix philosophy of OOB data). 118927185Sminshall */ 119027185Sminshall if (n > 1) { 119127185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 119227185Sminshall } else { 119327185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 119427185Sminshall } 119527185Sminshall } 119627185Sminshall } 119727185Sminshall if (n < 0) { 119827185Sminshall if (errno == EWOULDBLOCK) 119927185Sminshall return; 120027185Sminshall /* should blow this guy away... */ 120127185Sminshall return; 120227185Sminshall } 120327185Sminshall nbackp += n; 120427185Sminshall if (nbackp >= neturg) { 120527185Sminshall neturg = 0; 120627185Sminshall } 120727185Sminshall if (nbackp == nfrontp) { 120827185Sminshall nbackp = nfrontp = netobuf; 120927185Sminshall } 121027185Sminshall } 121127185Sminshall 12126002Sroot cleanup() 12136002Sroot { 12146002Sroot 12156002Sroot rmut(); 121610008Ssam vhangup(); /* XXX */ 121710191Ssam shutdown(net, 2); 12186002Sroot exit(1); 12196002Sroot } 12206002Sroot 12216002Sroot #include <utmp.h> 12226002Sroot 12236002Sroot struct utmp wtmp; 12246002Sroot char wtmpf[] = "/usr/adm/wtmp"; 122523567Sbloom char utmpf[] = "/etc/utmp"; 122623567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 122723567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12286002Sroot 12296002Sroot rmut() 12306002Sroot { 12316002Sroot register f; 12326002Sroot int found = 0; 123323567Sbloom struct utmp *u, *utmp; 123423567Sbloom int nutmp; 123523567Sbloom struct stat statbf; 12366002Sroot 123723567Sbloom f = open(utmpf, O_RDWR); 12386002Sroot if (f >= 0) { 123923567Sbloom fstat(f, &statbf); 124023567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 124123567Sbloom if (!utmp) 124223567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 124323567Sbloom if (statbf.st_size && utmp) { 124423567Sbloom nutmp = read(f, utmp, statbf.st_size); 124523567Sbloom nutmp /= sizeof(struct utmp); 124623567Sbloom 124723567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 124823567Sbloom if (SCMPN(u->ut_line, line+5) || 124923567Sbloom u->ut_name[0]==0) 125023567Sbloom continue; 125123567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 125223567Sbloom SCPYN(u->ut_name, ""); 125323567Sbloom SCPYN(u->ut_host, ""); 125423567Sbloom time(&u->ut_time); 125523567Sbloom write(f, (char *)u, sizeof(wtmp)); 125623567Sbloom found++; 125723567Sbloom } 12586002Sroot } 12596002Sroot close(f); 12606002Sroot } 12616002Sroot if (found) { 126217583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12636002Sroot if (f >= 0) { 12646002Sroot SCPYN(wtmp.ut_line, line+5); 12656002Sroot SCPYN(wtmp.ut_name, ""); 126612683Ssam SCPYN(wtmp.ut_host, ""); 12676002Sroot time(&wtmp.ut_time); 126823567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 12696002Sroot close(f); 12706002Sroot } 12716002Sroot } 12726002Sroot chmod(line, 0666); 12736002Sroot chown(line, 0, 0); 12746002Sroot line[strlen("/dev/")] = 'p'; 12756002Sroot chmod(line, 0666); 12766002Sroot chown(line, 0, 0); 12776002Sroot } 1278