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*33267Sminshall static char sccsid[] = "@(#)telnetd.c 5.24 (Berkeley) 01/05/88"; 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); 385*33267Sminshall ioctl(p, TIOCPKT, &on); 38627649Sminshall #if defined(SO_OOBINLINE) 38727649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 38827649Sminshall #endif /* defined(SO_OOBINLINE) */ 3896002Sroot signal(SIGTSTP, SIG_IGN); 39032400Sminshall /* 39132400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 39232400Sminshall * in ttioctl() in /sys/tty.c. 39332400Sminshall */ 39432400Sminshall signal(SIGTTOU, SIG_IGN); 39513028Ssam signal(SIGCHLD, cleanup); 39626083Slepreau setpgrp(0, 0); 3976002Sroot 3988379Ssam /* 39927185Sminshall * Request to do remote echo and to suppress go ahead. 4008379Ssam */ 40127983Sminshall if (!myopts[TELOPT_ECHO]) { 40227983Sminshall dooption(TELOPT_ECHO); 40327983Sminshall } 40427983Sminshall if (!myopts[TELOPT_SGA]) { 40527983Sminshall dooption(TELOPT_SGA); 40627983Sminshall } 40712713Ssam /* 40827649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 40927649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 41027649Sminshall * 41127649Sminshall * To find out, we send out a "DO ECHO". If the remote system 41227649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 41327649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 41427649Sminshall * WE, the server, sends it; it does NOT mean that the client will 41527649Sminshall * echo the terminal input). 41627649Sminshall */ 41732452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 41827649Sminshall nfrontp += sizeof doopt-2; 41927983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 42027649Sminshall 42127649Sminshall /* 42212713Ssam * Show banner that getty never gave. 42327797Sminshall * 42427797Sminshall * The banner includes some null's (for TELNET CR disambiguation), 42527797Sminshall * so we have to be somewhat complicated. 42612713Ssam */ 42727797Sminshall 42812713Ssam gethostname(hostname, sizeof (hostname)); 42927649Sminshall 43027797Sminshall bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); 43127797Sminshall nfrontp += sizeof BANNER1 - 1; 43227797Sminshall bcopy(hostname, nfrontp, strlen(hostname)); 43327797Sminshall nfrontp += strlen(hostname); 43427797Sminshall bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); 43527797Sminshall nfrontp += sizeof BANNER2 - 1; 43627797Sminshall 437*33267Sminshall /* Clear ptybuf[0] - where the packet information is received */ 438*33267Sminshall ptyibuf[0] = 0; 43927649Sminshall /* 44027649Sminshall * Call telrcv() once to pick up anything received during 44127649Sminshall * terminal type negotiation. 44227649Sminshall */ 44327649Sminshall telrcv(); 44427649Sminshall 4456002Sroot for (;;) { 44627185Sminshall fd_set ibits, obits, xbits; 4476002Sroot register int c; 4486002Sroot 44927185Sminshall if (ncc < 0 && pcc < 0) 45027185Sminshall break; 45127185Sminshall 45227185Sminshall FD_ZERO(&ibits); 45327185Sminshall FD_ZERO(&obits); 45427185Sminshall FD_ZERO(&xbits); 4556002Sroot /* 4566002Sroot * Never look for input if there's still 4576002Sroot * stuff in the corresponding output buffer 4586002Sroot */ 45927185Sminshall if (nfrontp - nbackp || pcc > 0) { 46027185Sminshall FD_SET(f, &obits); 461*33267Sminshall FD_SET(p, &xbits); 46227185Sminshall } else { 46327185Sminshall FD_SET(p, &ibits); 46427185Sminshall } 46527185Sminshall if (pfrontp - pbackp || ncc > 0) { 46627185Sminshall FD_SET(p, &obits); 46727185Sminshall } else { 46827185Sminshall FD_SET(f, &ibits); 46927185Sminshall } 47027185Sminshall if (!SYNCHing) { 47127185Sminshall FD_SET(f, &xbits); 47227185Sminshall } 47327185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 47427185Sminshall (struct timeval *)0)) < 1) { 47527185Sminshall if (c == -1) { 47627185Sminshall if (errno == EINTR) { 47727185Sminshall continue; 47827185Sminshall } 47927185Sminshall } 4806002Sroot sleep(5); 4816002Sroot continue; 4826002Sroot } 4836002Sroot 4846002Sroot /* 48527185Sminshall * Any urgent data? 48627185Sminshall */ 48727185Sminshall if (FD_ISSET(net, &xbits)) { 48827185Sminshall SYNCHing = 1; 48927185Sminshall } 49027185Sminshall 49127185Sminshall /* 4926002Sroot * Something to read from the network... 4936002Sroot */ 49427185Sminshall if (FD_ISSET(net, &ibits)) { 49527649Sminshall #if !defined(SO_OOBINLINE) 49627185Sminshall /* 49727898Skarels * In 4.2 (and 4.3 beta) systems, the 49827185Sminshall * OOB indication and data handling in the kernel 49927185Sminshall * is such that if two separate TCP Urgent requests 50027185Sminshall * come in, one byte of TCP data will be overlaid. 50127185Sminshall * This is fatal for Telnet, but we try to live 50227185Sminshall * with it. 50327185Sminshall * 50427185Sminshall * In addition, in 4.2 (and...), a special protocol 50527185Sminshall * is needed to pick up the TCP Urgent data in 50627185Sminshall * the correct sequence. 50727185Sminshall * 50827185Sminshall * What we do is: if we think we are in urgent 50927185Sminshall * mode, we look to see if we are "at the mark". 51027185Sminshall * If we are, we do an OOB receive. If we run 51127185Sminshall * this twice, we will do the OOB receive twice, 51227185Sminshall * but the second will fail, since the second 51327185Sminshall * time we were "at the mark", but there wasn't 51427185Sminshall * any data there (the kernel doesn't reset 51527185Sminshall * "at the mark" until we do a normal read). 51627185Sminshall * Once we've read the OOB data, we go ahead 51727185Sminshall * and do normal reads. 51827185Sminshall * 51927185Sminshall * There is also another problem, which is that 52027185Sminshall * since the OOB byte we read doesn't put us 52127185Sminshall * out of OOB state, and since that byte is most 52227185Sminshall * likely the TELNET DM (data mark), we would 52327185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 52427185Sminshall * So, clocks to the rescue. If we've "just" 52527185Sminshall * received a DM, then we test for the 52627185Sminshall * presence of OOB data when the receive OOB 52727185Sminshall * fails (and AFTER we did the normal mode read 52827185Sminshall * to clear "at the mark"). 52927185Sminshall */ 53027185Sminshall if (SYNCHing) { 53127185Sminshall int atmark; 53227185Sminshall 53327185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 53427185Sminshall if (atmark) { 53527185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 53627185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 53727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 53827983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 53927185Sminshall SYNCHing = stilloob(net); 54027185Sminshall } 54127185Sminshall } 54227185Sminshall } else { 54327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5446002Sroot } 54527185Sminshall } else { 54627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 54727185Sminshall } 54827185Sminshall settimer(didnetreceive); 54927649Sminshall #else /* !defined(SO_OOBINLINE)) */ 55027185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 55127649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 55227185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 55327185Sminshall ncc = 0; 55427185Sminshall else { 55527185Sminshall if (ncc <= 0) { 55627185Sminshall break; 55727185Sminshall } 55827185Sminshall netip = netibuf; 55927185Sminshall } 5606002Sroot } 5616002Sroot 5626002Sroot /* 5636002Sroot * Something to read from the pty... 5646002Sroot */ 565*33267Sminshall if (FD_ISSET(p, &xbits)) { 566*33267Sminshall if (read(p, ptyibuf, 1) != 1) { 567*33267Sminshall break; 568*33267Sminshall } 569*33267Sminshall } 57027185Sminshall if (FD_ISSET(p, &ibits)) { 5716002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5726002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5736002Sroot pcc = 0; 5746002Sroot else { 5756002Sroot if (pcc <= 0) 5766002Sroot break; 577*33267Sminshall /* Skip past "packet" */ 578*33267Sminshall pcc--; 579*33267Sminshall ptyip = ptyibuf+1; 5806002Sroot } 5816002Sroot } 582*33267Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 583*33267Sminshall netclear(); /* clear buffer back */ 584*33267Sminshall *nfrontp++ = IAC; 585*33267Sminshall *nfrontp++ = DM; 586*33267Sminshall neturg = nfrontp-1; /* off by one XXX */ 587*33267Sminshall ptyibuf[0] = 0; 588*33267Sminshall } 5896002Sroot 5906002Sroot while (pcc > 0) { 5916002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 5926002Sroot break; 5936002Sroot c = *ptyip++ & 0377, pcc--; 5946002Sroot if (c == IAC) 5956002Sroot *nfrontp++ = c; 5966002Sroot *nfrontp++ = c; 59731940Sbostic /* Don't do CR-NUL if we are in binary mode */ 59831940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 59927020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 60027020Sminshall *nfrontp++ = *ptyip++ & 0377; 60127020Sminshall pcc--; 60227020Sminshall } else 60327020Sminshall *nfrontp++ = '\0'; 60427020Sminshall } 6056002Sroot } 60627185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6076002Sroot netflush(); 6086002Sroot if (ncc > 0) 6096002Sroot telrcv(); 61027185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6116002Sroot ptyflush(); 6126002Sroot } 6136002Sroot cleanup(); 6146002Sroot } 6156002Sroot 6166002Sroot /* 6176002Sroot * State for recv fsm 6186002Sroot */ 6196002Sroot #define TS_DATA 0 /* base state */ 6206002Sroot #define TS_IAC 1 /* look for double IAC's */ 6216002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 62227649Sminshall #define TS_SB 3 /* throw away begin's... */ 62327649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6246002Sroot #define TS_WILL 5 /* will option negotiation */ 6256002Sroot #define TS_WONT 6 /* wont " */ 6266002Sroot #define TS_DO 7 /* do " */ 6276002Sroot #define TS_DONT 8 /* dont " */ 6286002Sroot 6296002Sroot telrcv() 6306002Sroot { 6316002Sroot register int c; 6326002Sroot static int state = TS_DATA; 6336002Sroot 6346002Sroot while (ncc > 0) { 6356002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6366002Sroot return; 6376002Sroot c = *netip++ & 0377, ncc--; 6386002Sroot switch (state) { 6396002Sroot 64026090Sminshall case TS_CR: 64126090Sminshall state = TS_DATA; 64232097Sminshall /* Strip off \n or \0 after a \r */ 64326499Sminshall if ((c == 0) || (c == '\n')) { 64426090Sminshall break; 64526499Sminshall } 64626090Sminshall /* FALL THROUGH */ 64726090Sminshall 6486002Sroot case TS_DATA: 6496002Sroot if (c == IAC) { 6506002Sroot state = TS_IAC; 6516002Sroot break; 6526002Sroot } 6536002Sroot if (inter > 0) 6546002Sroot break; 65527020Sminshall /* 65632097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 65732097Sminshall * Many client implementations send \r\n when 65832097Sminshall * the user hits the CarriageReturn key. 65932097Sminshall * 66032097Sminshall * We USED to map \r\n ==> \n, since \r\n says 66127020Sminshall * that we want to be in column 1 of the next 66227020Sminshall * printable line, and \n is the standard 66327020Sminshall * unix way of saying that (\r is only good 66427020Sminshall * if CRMOD is set, which it normally is). 66527020Sminshall */ 66631940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 66732097Sminshall state = TS_CR; 66826499Sminshall } 66926499Sminshall *pfrontp++ = c; 6706002Sroot break; 6716002Sroot 6726002Sroot case TS_IAC: 6736002Sroot switch (c) { 6746002Sroot 6756002Sroot /* 6766002Sroot * Send the process on the pty side an 6776002Sroot * interrupt. Do this with a NULL or 6786002Sroot * interrupt char; depending on the tty mode. 6796002Sroot */ 6806002Sroot case IP: 6816002Sroot interrupt(); 6826002Sroot break; 6836002Sroot 68427229Sminshall case BREAK: 68527229Sminshall sendbrk(); 68627229Sminshall break; 68727229Sminshall 6886002Sroot /* 6896002Sroot * Are You There? 6906002Sroot */ 6916002Sroot case AYT: 69217583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 69317583Ssam nfrontp += 9; 6946002Sroot break; 6956002Sroot 6966002Sroot /* 69727185Sminshall * Abort Output 69827185Sminshall */ 69927185Sminshall case AO: { 70027185Sminshall struct ltchars tmpltc; 70127185Sminshall 70227185Sminshall ptyflush(); /* half-hearted */ 70327185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 70427185Sminshall if (tmpltc.t_flushc != '\377') { 70527185Sminshall *pfrontp++ = tmpltc.t_flushc; 70627185Sminshall } 70727260Sminshall netclear(); /* clear buffer back */ 70827185Sminshall *nfrontp++ = IAC; 70927185Sminshall *nfrontp++ = DM; 71027187Sminshall neturg = nfrontp-1; /* off by one XXX */ 71127185Sminshall break; 71227185Sminshall } 71327185Sminshall 71427185Sminshall /* 7156002Sroot * Erase Character and 7166002Sroot * Erase Line 7176002Sroot */ 7186002Sroot case EC: 71927185Sminshall case EL: { 72027185Sminshall struct sgttyb b; 72127185Sminshall char ch; 7226002Sroot 72327185Sminshall ptyflush(); /* half-hearted */ 72427185Sminshall ioctl(pty, TIOCGETP, &b); 72527185Sminshall ch = (c == EC) ? 72627185Sminshall b.sg_erase : b.sg_kill; 72727185Sminshall if (ch != '\377') { 72827185Sminshall *pfrontp++ = ch; 72927185Sminshall } 73027185Sminshall break; 73127185Sminshall } 73227185Sminshall 7336002Sroot /* 7346002Sroot * Check for urgent data... 7356002Sroot */ 7366002Sroot case DM: 73727185Sminshall SYNCHing = stilloob(net); 73827185Sminshall settimer(gotDM); 7396002Sroot break; 7406002Sroot 74127185Sminshall 7426002Sroot /* 7436002Sroot * Begin option subnegotiation... 7446002Sroot */ 7456002Sroot case SB: 74627649Sminshall state = TS_SB; 7476002Sroot continue; 7486002Sroot 7496002Sroot case WILL: 75027188Sminshall state = TS_WILL; 75127188Sminshall continue; 75227188Sminshall 7536002Sroot case WONT: 75427188Sminshall state = TS_WONT; 75527188Sminshall continue; 75627188Sminshall 7576002Sroot case DO: 75827188Sminshall state = TS_DO; 75927188Sminshall continue; 76027188Sminshall 7616002Sroot case DONT: 76227188Sminshall state = TS_DONT; 7636002Sroot continue; 7646002Sroot 7656002Sroot case IAC: 7666002Sroot *pfrontp++ = c; 7676002Sroot break; 7686002Sroot } 7696002Sroot state = TS_DATA; 7706002Sroot break; 7716002Sroot 77227649Sminshall case TS_SB: 77327649Sminshall if (c == IAC) { 77427649Sminshall state = TS_SE; 77527649Sminshall } else { 77627649Sminshall SB_ACCUM(c); 77727649Sminshall } 7786002Sroot break; 7796002Sroot 78027649Sminshall case TS_SE: 78127649Sminshall if (c != SE) { 78227649Sminshall if (c != IAC) { 78327649Sminshall SB_ACCUM(IAC); 78427649Sminshall } 78527649Sminshall SB_ACCUM(c); 78627649Sminshall state = TS_SB; 78727649Sminshall } else { 78827649Sminshall SB_TERM(); 78927649Sminshall suboption(); /* handle sub-option */ 79027649Sminshall state = TS_DATA; 79127649Sminshall } 7926002Sroot break; 7936002Sroot 7946002Sroot case TS_WILL: 79527983Sminshall if (hisopts[c] != OPT_YES) 7966002Sroot willoption(c); 7976002Sroot state = TS_DATA; 7986002Sroot continue; 7996002Sroot 8006002Sroot case TS_WONT: 80127983Sminshall if (hisopts[c] != OPT_NO) 8026002Sroot wontoption(c); 8036002Sroot state = TS_DATA; 8046002Sroot continue; 8056002Sroot 8066002Sroot case TS_DO: 80727983Sminshall if (myopts[c] != OPT_YES) 8086002Sroot dooption(c); 8096002Sroot state = TS_DATA; 8106002Sroot continue; 8116002Sroot 8126002Sroot case TS_DONT: 81327983Sminshall if (myopts[c] != OPT_NO) { 81427649Sminshall dontoption(c); 8156002Sroot } 8166002Sroot state = TS_DATA; 8176002Sroot continue; 8186002Sroot 8196002Sroot default: 82027898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8219218Ssam printf("telnetd: panic state=%d\n", state); 8226002Sroot exit(1); 8236002Sroot } 8246002Sroot } 8256002Sroot } 8266002Sroot 8276002Sroot willoption(option) 8286002Sroot int option; 8296002Sroot { 8306002Sroot char *fmt; 8316002Sroot 8326002Sroot switch (option) { 8336002Sroot 8346002Sroot case TELOPT_BINARY: 8356002Sroot mode(RAW, 0); 83627188Sminshall fmt = doopt; 83727188Sminshall break; 8386002Sroot 8396002Sroot case TELOPT_ECHO: 84027649Sminshall not42 = 0; /* looks like a 4.2 system */ 84127649Sminshall /* 84227649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 84327649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 84427649Sminshall * Kludge upon kludge! 84527649Sminshall */ 84627983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 84727649Sminshall dooption(TELOPT_ECHO); 84827649Sminshall } 84927649Sminshall fmt = dont; 85027188Sminshall break; 8516002Sroot 85227649Sminshall case TELOPT_TTYPE: 85327983Sminshall settimer(ttypeopt); 85427983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 85527983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 85627983Sminshall return; 85727983Sminshall } 85827983Sminshall fmt = doopt; 85927983Sminshall break; 86027983Sminshall 8616002Sroot case TELOPT_SGA: 8626002Sroot fmt = doopt; 8636002Sroot break; 8646002Sroot 8656002Sroot case TELOPT_TM: 8666002Sroot fmt = dont; 8676002Sroot break; 8686002Sroot 8696002Sroot default: 8706002Sroot fmt = dont; 8716002Sroot break; 8726002Sroot } 87327188Sminshall if (fmt == doopt) { 87427983Sminshall hisopts[option] = OPT_YES; 87527188Sminshall } else { 87627983Sminshall hisopts[option] = OPT_NO; 87727188Sminshall } 87832452Sbostic (void) sprintf(nfrontp, fmt, option); 8798379Ssam nfrontp += sizeof (dont) - 2; 8806002Sroot } 8816002Sroot 8826002Sroot wontoption(option) 8836002Sroot int option; 8846002Sroot { 8856002Sroot char *fmt; 8866002Sroot 8876002Sroot switch (option) { 8886002Sroot case TELOPT_ECHO: 88927649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 89027188Sminshall break; 8916002Sroot 8926002Sroot case TELOPT_BINARY: 8936002Sroot mode(0, RAW); 8946002Sroot break; 89528044Sminshall 89628044Sminshall case TELOPT_TTYPE: 89728044Sminshall settimer(ttypeopt); 89828044Sminshall break; 8996002Sroot } 90028044Sminshall 90127188Sminshall fmt = dont; 90227983Sminshall hisopts[option] = OPT_NO; 90332452Sbostic (void) sprintf(nfrontp, fmt, option); 9048379Ssam nfrontp += sizeof (doopt) - 2; 9056002Sroot } 9066002Sroot 9076002Sroot dooption(option) 9086002Sroot int option; 9096002Sroot { 9106002Sroot char *fmt; 9116002Sroot 9126002Sroot switch (option) { 9136002Sroot 9146002Sroot case TELOPT_TM: 9156002Sroot fmt = wont; 9166002Sroot break; 9176002Sroot 9186002Sroot case TELOPT_ECHO: 9196002Sroot mode(ECHO|CRMOD, 0); 92027188Sminshall fmt = will; 92127188Sminshall break; 9226002Sroot 9236002Sroot case TELOPT_BINARY: 9246002Sroot mode(RAW, 0); 92527188Sminshall fmt = will; 92627188Sminshall break; 9276002Sroot 9286002Sroot case TELOPT_SGA: 9296002Sroot fmt = will; 9306002Sroot break; 9316002Sroot 9326002Sroot default: 9336002Sroot fmt = wont; 9346002Sroot break; 9356002Sroot } 93627188Sminshall if (fmt == will) { 93727983Sminshall myopts[option] = OPT_YES; 93827188Sminshall } else { 93927983Sminshall myopts[option] = OPT_NO; 94027188Sminshall } 94132452Sbostic (void) sprintf(nfrontp, fmt, option); 9428379Ssam nfrontp += sizeof (doopt) - 2; 9436002Sroot } 9446002Sroot 94527649Sminshall 94627649Sminshall dontoption(option) 94727649Sminshall int option; 94827649Sminshall { 94927649Sminshall char *fmt; 95027649Sminshall 95127649Sminshall switch (option) { 95227649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 95332401Sminshall mode(0, ECHO); 95427649Sminshall fmt = wont; 95527649Sminshall break; 95627983Sminshall 95727649Sminshall default: 95827649Sminshall fmt = wont; 95927649Sminshall break; 96027649Sminshall } 96127983Sminshall 96227649Sminshall if (fmt = wont) { 96327983Sminshall myopts[option] = OPT_NO; 96427649Sminshall } else { 96527983Sminshall myopts[option] = OPT_YES; 96627649Sminshall } 96732452Sbostic (void) sprintf(nfrontp, fmt, option); 96827649Sminshall nfrontp += sizeof (wont) - 2; 96927649Sminshall } 97027649Sminshall 97127649Sminshall /* 97227649Sminshall * suboption() 97327649Sminshall * 97427649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 97527649Sminshall * side. 97627649Sminshall * 97727649Sminshall * Currently we recognize: 97827649Sminshall * 97927983Sminshall * Terminal type is 98027649Sminshall */ 98127649Sminshall 98227649Sminshall suboption() 98327649Sminshall { 98427983Sminshall switch (SB_GET()) { 98527983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 98627983Sminshall static char terminalname[5+41] = "TERM="; 98727983Sminshall 98827983Sminshall settimer(ttypesubopt); 98927983Sminshall 99027983Sminshall if (SB_GET() != TELQUAL_IS) { 99127983Sminshall return; /* ??? XXX but, this is the most robust */ 99227983Sminshall } 99327983Sminshall 99427983Sminshall terminaltype = terminalname+strlen(terminalname); 99527983Sminshall 99627983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 99727983Sminshall !SB_EOF()) { 99827983Sminshall register int c; 99927983Sminshall 100027983Sminshall c = SB_GET(); 100127983Sminshall if (isupper(c)) { 100227983Sminshall c = tolower(c); 100327983Sminshall } 100427983Sminshall *terminaltype++ = c; /* accumulate name */ 100527983Sminshall } 100627983Sminshall *terminaltype = 0; 100727983Sminshall terminaltype = terminalname; 100827983Sminshall break; 100927983Sminshall } 101027983Sminshall 101127649Sminshall default: 101227649Sminshall ; 101327649Sminshall } 101427649Sminshall } 101527649Sminshall 10166002Sroot mode(on, off) 10176002Sroot int on, off; 10186002Sroot { 10196002Sroot struct sgttyb b; 10206002Sroot 10216002Sroot ptyflush(); 10226002Sroot ioctl(pty, TIOCGETP, &b); 10236002Sroot b.sg_flags |= on; 10246002Sroot b.sg_flags &= ~off; 10256002Sroot ioctl(pty, TIOCSETP, &b); 10266002Sroot } 10276002Sroot 10286002Sroot /* 10296002Sroot * Send interrupt to process on other side of pty. 10306002Sroot * If it is in raw mode, just write NULL; 10316002Sroot * otherwise, write intr char. 10326002Sroot */ 10336002Sroot interrupt() 10346002Sroot { 10356002Sroot struct sgttyb b; 10366002Sroot struct tchars tchars; 10376002Sroot 10386002Sroot ptyflush(); /* half-hearted */ 10396002Sroot ioctl(pty, TIOCGETP, &b); 10406002Sroot if (b.sg_flags & RAW) { 10416002Sroot *pfrontp++ = '\0'; 10426002Sroot return; 10436002Sroot } 10446002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10456002Sroot '\177' : tchars.t_intrc; 10466002Sroot } 10476002Sroot 104827229Sminshall /* 104927229Sminshall * Send quit to process on other side of pty. 105027229Sminshall * If it is in raw mode, just write NULL; 105127229Sminshall * otherwise, write quit char. 105227229Sminshall */ 105327229Sminshall sendbrk() 105427229Sminshall { 105527229Sminshall struct sgttyb b; 105627229Sminshall struct tchars tchars; 105727229Sminshall 105827229Sminshall ptyflush(); /* half-hearted */ 105927229Sminshall ioctl(pty, TIOCGETP, &b); 106027229Sminshall if (b.sg_flags & RAW) { 106127229Sminshall *pfrontp++ = '\0'; 106227229Sminshall return; 106327229Sminshall } 106427229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 106527229Sminshall '\034' : tchars.t_quitc; 106627229Sminshall } 106727229Sminshall 10686002Sroot ptyflush() 10696002Sroot { 10706002Sroot int n; 10716002Sroot 10726002Sroot if ((n = pfrontp - pbackp) > 0) 10736002Sroot n = write(pty, pbackp, n); 10748346Ssam if (n < 0) 10758346Ssam return; 10766002Sroot pbackp += n; 10776002Sroot if (pbackp == pfrontp) 10786002Sroot pbackp = pfrontp = ptyobuf; 10796002Sroot } 108027260Sminshall 108127260Sminshall /* 108227260Sminshall * nextitem() 108327260Sminshall * 108427260Sminshall * Return the address of the next "item" in the TELNET data 108527260Sminshall * stream. This will be the address of the next character if 108627260Sminshall * the current address is a user data character, or it will 108727260Sminshall * be the address of the character following the TELNET command 108827260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 108927260Sminshall * character. 109027260Sminshall */ 10916002Sroot 109227260Sminshall char * 109327260Sminshall nextitem(current) 109427260Sminshall char *current; 10956002Sroot { 109627260Sminshall if ((*current&0xff) != IAC) { 109727260Sminshall return current+1; 109827260Sminshall } 109927260Sminshall switch (*(current+1)&0xff) { 110027260Sminshall case DO: 110127260Sminshall case DONT: 110227260Sminshall case WILL: 110327260Sminshall case WONT: 110427260Sminshall return current+3; 110527260Sminshall case SB: /* loop forever looking for the SE */ 110627260Sminshall { 110727260Sminshall register char *look = current+2; 11086002Sroot 110927260Sminshall for (;;) { 111027260Sminshall if ((*look++&0xff) == IAC) { 111127260Sminshall if ((*look++&0xff) == SE) { 111227260Sminshall return look; 111327260Sminshall } 111427260Sminshall } 111527260Sminshall } 11168346Ssam } 111727260Sminshall default: 111827260Sminshall return current+2; 111927260Sminshall } 11206002Sroot } 11216002Sroot 112227185Sminshall 112327185Sminshall /* 112427260Sminshall * netclear() 112527260Sminshall * 112627260Sminshall * We are about to do a TELNET SYNCH operation. Clear 112727260Sminshall * the path to the network. 112827260Sminshall * 112927260Sminshall * Things are a bit tricky since we may have sent the first 113027260Sminshall * byte or so of a previous TELNET command into the network. 113127260Sminshall * So, we have to scan the network buffer from the beginning 113227260Sminshall * until we are up to where we want to be. 113327260Sminshall * 113427260Sminshall * A side effect of what we do, just to keep things 113527260Sminshall * simple, is to clear the urgent data pointer. The principal 113627260Sminshall * caller should be setting the urgent data pointer AFTER calling 113727260Sminshall * us in any case. 113827260Sminshall */ 113927260Sminshall 114027260Sminshall netclear() 114127260Sminshall { 114227260Sminshall register char *thisitem, *next; 114327260Sminshall char *good; 114427260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 114527260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 114627260Sminshall 114727260Sminshall thisitem = netobuf; 114827260Sminshall 114927260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 115027260Sminshall thisitem = next; 115127260Sminshall } 115227260Sminshall 115327260Sminshall /* Now, thisitem is first before/at boundary. */ 115427260Sminshall 115527260Sminshall good = netobuf; /* where the good bytes go */ 115627260Sminshall 115727260Sminshall while (nfrontp > thisitem) { 115827260Sminshall if (wewant(thisitem)) { 115927260Sminshall int length; 116027260Sminshall 116127260Sminshall next = thisitem; 116227260Sminshall do { 116327260Sminshall next = nextitem(next); 116427260Sminshall } while (wewant(next) && (nfrontp > next)); 116527260Sminshall length = next-thisitem; 116627260Sminshall bcopy(thisitem, good, length); 116727260Sminshall good += length; 116827260Sminshall thisitem = next; 116927260Sminshall } else { 117027260Sminshall thisitem = nextitem(thisitem); 117127260Sminshall } 117227260Sminshall } 117327260Sminshall 117427260Sminshall nbackp = netobuf; 117527260Sminshall nfrontp = good; /* next byte to be sent */ 117627260Sminshall neturg = 0; 117727260Sminshall } 117827260Sminshall 117927260Sminshall /* 118027185Sminshall * netflush 118127185Sminshall * Send as much data as possible to the network, 118227185Sminshall * handling requests for urgent data. 118327185Sminshall */ 118427185Sminshall 118527185Sminshall 118627185Sminshall netflush() 118727185Sminshall { 118827185Sminshall int n; 118927185Sminshall 119027185Sminshall if ((n = nfrontp - nbackp) > 0) { 119127649Sminshall /* 119227649Sminshall * if no urgent data, or if the other side appears to be an 119327649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 119427649Sminshall * write the entire buffer in non-OOB mode. 119527649Sminshall */ 119627649Sminshall if ((neturg == 0) || (not42 == 0)) { 119727185Sminshall n = write(net, nbackp, n); /* normal write */ 119827185Sminshall } else { 119927185Sminshall n = neturg - nbackp; 120027185Sminshall /* 120127185Sminshall * In 4.2 (and 4.3) systems, there is some question about 120227185Sminshall * what byte in a sendOOB operation is the "OOB" data. 120327185Sminshall * To make ourselves compatible, we only send ONE byte 120427185Sminshall * out of band, the one WE THINK should be OOB (though 120527185Sminshall * we really have more the TCP philosophy of urgent data 120627185Sminshall * rather than the Unix philosophy of OOB data). 120727185Sminshall */ 120827185Sminshall if (n > 1) { 120927185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 121027185Sminshall } else { 121127185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 121227185Sminshall } 121327185Sminshall } 121427185Sminshall } 121527185Sminshall if (n < 0) { 121627185Sminshall if (errno == EWOULDBLOCK) 121727185Sminshall return; 121827185Sminshall /* should blow this guy away... */ 121927185Sminshall return; 122027185Sminshall } 122127185Sminshall nbackp += n; 122227185Sminshall if (nbackp >= neturg) { 122327185Sminshall neturg = 0; 122427185Sminshall } 122527185Sminshall if (nbackp == nfrontp) { 122627185Sminshall nbackp = nfrontp = netobuf; 122727185Sminshall } 122827185Sminshall } 122927185Sminshall 12306002Sroot cleanup() 12316002Sroot { 12326002Sroot 12336002Sroot rmut(); 123410008Ssam vhangup(); /* XXX */ 123510191Ssam shutdown(net, 2); 12366002Sroot exit(1); 12376002Sroot } 12386002Sroot 12396002Sroot #include <utmp.h> 12406002Sroot 12416002Sroot struct utmp wtmp; 12426002Sroot char wtmpf[] = "/usr/adm/wtmp"; 124323567Sbloom char utmpf[] = "/etc/utmp"; 124423567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 124523567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12466002Sroot 12476002Sroot rmut() 12486002Sroot { 12496002Sroot register f; 12506002Sroot int found = 0; 125123567Sbloom struct utmp *u, *utmp; 125223567Sbloom int nutmp; 125323567Sbloom struct stat statbf; 12546002Sroot 125523567Sbloom f = open(utmpf, O_RDWR); 12566002Sroot if (f >= 0) { 125723567Sbloom fstat(f, &statbf); 125823567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 125923567Sbloom if (!utmp) 126023567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 126123567Sbloom if (statbf.st_size && utmp) { 126223567Sbloom nutmp = read(f, utmp, statbf.st_size); 126323567Sbloom nutmp /= sizeof(struct utmp); 126423567Sbloom 126523567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 126623567Sbloom if (SCMPN(u->ut_line, line+5) || 126723567Sbloom u->ut_name[0]==0) 126823567Sbloom continue; 126923567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 127023567Sbloom SCPYN(u->ut_name, ""); 127123567Sbloom SCPYN(u->ut_host, ""); 127223567Sbloom time(&u->ut_time); 127323567Sbloom write(f, (char *)u, sizeof(wtmp)); 127423567Sbloom found++; 127523567Sbloom } 12766002Sroot } 12776002Sroot close(f); 12786002Sroot } 12796002Sroot if (found) { 128017583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12816002Sroot if (f >= 0) { 12826002Sroot SCPYN(wtmp.ut_line, line+5); 12836002Sroot SCPYN(wtmp.ut_name, ""); 128412683Ssam SCPYN(wtmp.ut_host, ""); 12856002Sroot time(&wtmp.ut_time); 128623567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 12876002Sroot close(f); 12886002Sroot } 12896002Sroot } 12906002Sroot chmod(line, 0666); 12916002Sroot chown(line, 0, 0); 12926002Sroot line[strlen("/dev/")] = 'p'; 12936002Sroot chmod(line, 0666); 12946002Sroot chown(line, 0, 0); 12956002Sroot } 1296