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*27983Sminshall static char sccsid[] = "@(#)telnetd.c 5.17 (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 39*27983Sminshall #define OPT_NO 0 /* won't do this option */ 40*27983Sminshall #define OPT_YES 1 /* will do this option */ 41*27983Sminshall #define OPT_YES_BUT_ALWAYS_LOOK 2 42*27983Sminshall #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 72*27983Sminshall /* buffer for sub-options */ 73*27983Sminshall char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 7427649Sminshall #define SB_CLEAR() subpointer = subbuffer; 75*27983Sminshall #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 7627649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 7727649Sminshall *subpointer++ = (c); \ 7827649Sminshall } 79*27983Sminshall #define SB_GET() ((*subpointer++)&0xff) 80*27983Sminshall #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 */ 101*27983Sminshall ttypeopt, /* ttype will/won't received */ 102*27983Sminshall ttypesubopt, /* ttype subopt is received */ 103*27983Sminshall getterminal, /* time started to get terminal information */ 10427185Sminshall gotDM; /* when did we last see a data mark */ 10527185Sminshall } clocks; 10627185Sminshall 107*27983Sminshall #define settimer(x) (clocks.x = ++clocks.system) 108*27983Sminshall #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 170*27983Sminshall char *terminaltype = 0; 171*27983Sminshall char *envinit[2]; 172*27983Sminshall int cleanup(); 17327649Sminshall 17427649Sminshall /* 175*27983Sminshall * ttloop 17627649Sminshall * 177*27983Sminshall * A small subroutine to flush the network output buffer, get some data 178*27983Sminshall * from the network, and pass it through the telnet state machine. We 179*27983Sminshall * also flush the pty input buffer (by dropping its data) if it becomes 180*27983Sminshall * too full. 181*27983Sminshall */ 182*27983Sminshall 183*27983Sminshall void 184*27983Sminshall ttloop() 185*27983Sminshall { 186*27983Sminshall if (nfrontp-nbackp) { 187*27983Sminshall netflush(); 188*27983Sminshall } 189*27983Sminshall ncc = read(net, netibuf, sizeof netibuf); 190*27983Sminshall if (ncc < 0) { 191*27983Sminshall syslog(LOG_INFO, "ttloop: read: %m\n"); 192*27983Sminshall } 193*27983Sminshall netip = netibuf; 194*27983Sminshall telrcv(); /* state machine */ 195*27983Sminshall if (ncc > 0) { 196*27983Sminshall pfrontp = pbackp = ptyobuf; 197*27983Sminshall telrcv(); 198*27983Sminshall } 199*27983Sminshall } 200*27983Sminshall 201*27983Sminshall /* 202*27983Sminshall * getterminaltype 20327649Sminshall * 204*27983Sminshall * Ask the other end to send along its terminal type. 205*27983Sminshall * Output is the variable terminaltype filled in. 20627649Sminshall */ 20727649Sminshall 208*27983Sminshall void 209*27983Sminshall getterminaltype() 21027649Sminshall { 211*27983Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 21227649Sminshall 213*27983Sminshall settimer(getterminal); 214*27983Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 215*27983Sminshall nfrontp += sizeof sbuf; 216*27983Sminshall while (sequenceIs(ttypeopt, getterminal)) { 217*27983Sminshall ttloop(); 21827649Sminshall } 219*27983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES) { 220*27983Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 221*27983Sminshall 222*27983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 223*27983Sminshall nfrontp += sizeof sbbuf; 224*27983Sminshall while (sequenceIs(ttypesubopt, getterminal)) { 225*27983Sminshall ttloop(); 226*27983Sminshall } 227*27983Sminshall } 22827649Sminshall } 22927649Sminshall 2306002Sroot /* 2316002Sroot * Get a pty, scan input lines. 2326002Sroot */ 23312683Ssam doit(f, who) 23412683Ssam int f; 23512683Ssam struct sockaddr_in *who; 2366002Sroot { 23720188Skarels char *host, *inet_ntoa(); 23817583Ssam int i, p, t; 2396002Sroot struct sgttyb b; 24012683Ssam struct hostent *hp; 24127649Sminshall int c; 2426002Sroot 24320188Skarels for (c = 'p'; c <= 's'; c++) { 24420188Skarels struct stat stb; 24520188Skarels 24620188Skarels line = "/dev/ptyXX"; 24720188Skarels line[strlen("/dev/pty")] = c; 24820188Skarels line[strlen("/dev/ptyp")] = '0'; 24920188Skarels if (stat(line, &stb) < 0) 25020188Skarels break; 25117583Ssam for (i = 0; i < 16; i++) { 25220188Skarels line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 25320188Skarels p = open(line, 2); 25417583Ssam if (p > 0) 25517583Ssam goto gotpty; 25617583Ssam } 2576002Sroot } 2589244Ssam fatal(f, "All network ports in use"); 2599244Ssam /*NOTREACHED*/ 2606002Sroot gotpty: 2616002Sroot dup2(f, 0); 26220188Skarels line[strlen("/dev/")] = 't'; 26317583Ssam t = open("/dev/tty", O_RDWR); 2646002Sroot if (t >= 0) { 2656002Sroot ioctl(t, TIOCNOTTY, 0); 2666002Sroot close(t); 2676002Sroot } 26820188Skarels t = open(line, O_RDWR); 2699244Ssam if (t < 0) 27020188Skarels fatalperror(f, line, errno); 2716002Sroot ioctl(t, TIOCGETP, &b); 2726388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 2736002Sroot ioctl(t, TIOCSETP, &b); 2746388Ssam ioctl(p, TIOCGETP, &b); 2758379Ssam b.sg_flags &= ~ECHO; 2766388Ssam ioctl(p, TIOCSETP, &b); 27712683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 27812683Ssam who->sin_family); 27912683Ssam if (hp) 28012683Ssam host = hp->h_name; 28112683Ssam else 28217444Sralph host = inet_ntoa(who->sin_addr); 283*27983Sminshall 284*27983Sminshall net = f; 285*27983Sminshall pty = p; 286*27983Sminshall 287*27983Sminshall /* 288*27983Sminshall * get terminal type. 289*27983Sminshall */ 290*27983Sminshall getterminaltype(); 291*27983Sminshall 2929244Ssam if ((i = fork()) < 0) 2939244Ssam fatalperror(f, "fork", errno); 2946002Sroot if (i) 2956002Sroot telnet(f, p); 2966002Sroot close(f); 2976002Sroot close(p); 2986002Sroot dup2(t, 0); 2996002Sroot dup2(t, 1); 3006002Sroot dup2(t, 2); 3016002Sroot close(t); 302*27983Sminshall envinit[0] = terminaltype; 303*27983Sminshall envinit[1] = 0; 30413799Ssam environ = envinit; 30527649Sminshall /* 30627649Sminshall * -h : pass on name of host. 307*27983Sminshall * WARNING: -h is accepted by login if and only if 308*27983Sminshall * getuid() == 0. 30927649Sminshall * -p : don't clobber the environment (so terminal type stays set). 31027649Sminshall */ 31127649Sminshall execl("/bin/login", "login", "-h", host, 312*27983Sminshall terminaltype ? "-p" : 0, 0); 3139244Ssam fatalperror(f, "/bin/login", errno); 3149244Ssam /*NOTREACHED*/ 3159244Ssam } 3169244Ssam 3179244Ssam fatal(f, msg) 3189244Ssam int f; 3199244Ssam char *msg; 3209244Ssam { 3219244Ssam char buf[BUFSIZ]; 3229244Ssam 32317583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3249244Ssam (void) write(f, buf, strlen(buf)); 3256002Sroot exit(1); 3266002Sroot } 3276002Sroot 3289244Ssam fatalperror(f, msg, errno) 3299244Ssam int f; 3309244Ssam char *msg; 3319244Ssam int errno; 3329244Ssam { 3339244Ssam char buf[BUFSIZ]; 3349244Ssam extern char *sys_errlist[]; 3359244Ssam 33617583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3379244Ssam fatal(f, buf); 3389244Ssam } 3399244Ssam 34027185Sminshall 3416002Sroot /* 34227185Sminshall * Check a descriptor to see if out of band data exists on it. 34327185Sminshall */ 34427185Sminshall 34527185Sminshall 34627185Sminshall stilloob(s) 34727185Sminshall int s; /* socket number */ 34827185Sminshall { 34927185Sminshall static struct timeval timeout = { 0 }; 35027185Sminshall fd_set excepts; 35127185Sminshall int value; 35227185Sminshall 35327185Sminshall do { 35427185Sminshall FD_ZERO(&excepts); 35527185Sminshall FD_SET(s, &excepts); 35627185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 35727898Skarels } while ((value == -1) && (errno == EINTR)); 35827185Sminshall 35927185Sminshall if (value < 0) { 36027185Sminshall fatalperror(pty, "select", errno); 36127185Sminshall } 36227185Sminshall if (FD_ISSET(s, &excepts)) { 36327185Sminshall return 1; 36427185Sminshall } else { 36527185Sminshall return 0; 36627185Sminshall } 36727185Sminshall } 36827185Sminshall 36927185Sminshall /* 3706002Sroot * Main loop. Select from pty and network, and 3716002Sroot * hand data to telnet receiver finite state machine. 3726002Sroot */ 3736002Sroot telnet(f, p) 3746002Sroot { 3756002Sroot int on = 1; 37627898Skarels char hostname[MAXHOSTNAMELEN]; 3776002Sroot 3786002Sroot ioctl(f, FIONBIO, &on); 3796002Sroot ioctl(p, FIONBIO, &on); 38027649Sminshall #if defined(SO_OOBINLINE) 38127649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 38227649Sminshall #endif /* defined(SO_OOBINLINE) */ 3836002Sroot signal(SIGTSTP, SIG_IGN); 38413028Ssam signal(SIGCHLD, cleanup); 38526083Slepreau setpgrp(0, 0); 3866002Sroot 3878379Ssam /* 38827185Sminshall * Request to do remote echo and to suppress go ahead. 3898379Ssam */ 390*27983Sminshall if (!myopts[TELOPT_ECHO]) { 391*27983Sminshall dooption(TELOPT_ECHO); 392*27983Sminshall } 393*27983Sminshall if (!myopts[TELOPT_SGA]) { 394*27983Sminshall dooption(TELOPT_SGA); 395*27983Sminshall } 39612713Ssam /* 39727649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 39827649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 39927649Sminshall * 40027649Sminshall * To find out, we send out a "DO ECHO". If the remote system 40127649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 40227649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 40327649Sminshall * WE, the server, sends it; it does NOT mean that the client will 40427649Sminshall * echo the terminal input). 40527649Sminshall */ 40627649Sminshall sprintf(nfrontp, doopt, TELOPT_ECHO); 40727649Sminshall nfrontp += sizeof doopt-2; 408*27983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 40927649Sminshall 41027649Sminshall /* 41112713Ssam * Show banner that getty never gave. 41227797Sminshall * 41327797Sminshall * The banner includes some null's (for TELNET CR disambiguation), 41427797Sminshall * so we have to be somewhat complicated. 41512713Ssam */ 41627797Sminshall 41712713Ssam gethostname(hostname, sizeof (hostname)); 41827649Sminshall 41927797Sminshall bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); 42027797Sminshall nfrontp += sizeof BANNER1 - 1; 42127797Sminshall bcopy(hostname, nfrontp, strlen(hostname)); 42227797Sminshall nfrontp += strlen(hostname); 42327797Sminshall bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); 42427797Sminshall nfrontp += sizeof BANNER2 - 1; 42527797Sminshall 42627649Sminshall /* 42727649Sminshall * Call telrcv() once to pick up anything received during 42827649Sminshall * terminal type negotiation. 42927649Sminshall */ 43027649Sminshall telrcv(); 43127649Sminshall 4326002Sroot for (;;) { 43327185Sminshall fd_set ibits, obits, xbits; 4346002Sroot register int c; 4356002Sroot 43627185Sminshall if (ncc < 0 && pcc < 0) 43727185Sminshall break; 43827185Sminshall 43927185Sminshall FD_ZERO(&ibits); 44027185Sminshall FD_ZERO(&obits); 44127185Sminshall FD_ZERO(&xbits); 4426002Sroot /* 4436002Sroot * Never look for input if there's still 4446002Sroot * stuff in the corresponding output buffer 4456002Sroot */ 44627185Sminshall if (nfrontp - nbackp || pcc > 0) { 44727185Sminshall FD_SET(f, &obits); 44827185Sminshall } else { 44927185Sminshall FD_SET(p, &ibits); 45027185Sminshall } 45127185Sminshall if (pfrontp - pbackp || ncc > 0) { 45227185Sminshall FD_SET(p, &obits); 45327185Sminshall } else { 45427185Sminshall FD_SET(f, &ibits); 45527185Sminshall } 45627185Sminshall if (!SYNCHing) { 45727185Sminshall FD_SET(f, &xbits); 45827185Sminshall } 45927185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 46027185Sminshall (struct timeval *)0)) < 1) { 46127185Sminshall if (c == -1) { 46227185Sminshall if (errno == EINTR) { 46327185Sminshall continue; 46427185Sminshall } 46527185Sminshall } 4666002Sroot sleep(5); 4676002Sroot continue; 4686002Sroot } 4696002Sroot 4706002Sroot /* 47127185Sminshall * Any urgent data? 47227185Sminshall */ 47327185Sminshall if (FD_ISSET(net, &xbits)) { 47427185Sminshall SYNCHing = 1; 47527185Sminshall } 47627185Sminshall 47727185Sminshall /* 4786002Sroot * Something to read from the network... 4796002Sroot */ 48027185Sminshall if (FD_ISSET(net, &ibits)) { 48127649Sminshall #if !defined(SO_OOBINLINE) 48227185Sminshall /* 48327898Skarels * In 4.2 (and 4.3 beta) systems, the 48427185Sminshall * OOB indication and data handling in the kernel 48527185Sminshall * is such that if two separate TCP Urgent requests 48627185Sminshall * come in, one byte of TCP data will be overlaid. 48727185Sminshall * This is fatal for Telnet, but we try to live 48827185Sminshall * with it. 48927185Sminshall * 49027185Sminshall * In addition, in 4.2 (and...), a special protocol 49127185Sminshall * is needed to pick up the TCP Urgent data in 49227185Sminshall * the correct sequence. 49327185Sminshall * 49427185Sminshall * What we do is: if we think we are in urgent 49527185Sminshall * mode, we look to see if we are "at the mark". 49627185Sminshall * If we are, we do an OOB receive. If we run 49727185Sminshall * this twice, we will do the OOB receive twice, 49827185Sminshall * but the second will fail, since the second 49927185Sminshall * time we were "at the mark", but there wasn't 50027185Sminshall * any data there (the kernel doesn't reset 50127185Sminshall * "at the mark" until we do a normal read). 50227185Sminshall * Once we've read the OOB data, we go ahead 50327185Sminshall * and do normal reads. 50427185Sminshall * 50527185Sminshall * There is also another problem, which is that 50627185Sminshall * since the OOB byte we read doesn't put us 50727185Sminshall * out of OOB state, and since that byte is most 50827185Sminshall * likely the TELNET DM (data mark), we would 50927185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 51027185Sminshall * So, clocks to the rescue. If we've "just" 51127185Sminshall * received a DM, then we test for the 51227185Sminshall * presence of OOB data when the receive OOB 51327185Sminshall * fails (and AFTER we did the normal mode read 51427185Sminshall * to clear "at the mark"). 51527185Sminshall */ 51627185Sminshall if (SYNCHing) { 51727185Sminshall int atmark; 51827185Sminshall 51927185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 52027185Sminshall if (atmark) { 52127185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 52227185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 52327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 524*27983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 52527185Sminshall SYNCHing = stilloob(net); 52627185Sminshall } 52727185Sminshall } 52827185Sminshall } else { 52927185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5306002Sroot } 53127185Sminshall } else { 53227185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 53327185Sminshall } 53427185Sminshall settimer(didnetreceive); 53527649Sminshall #else /* !defined(SO_OOBINLINE)) */ 53627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 53727649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 53827185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 53927185Sminshall ncc = 0; 54027185Sminshall else { 54127185Sminshall if (ncc <= 0) { 54227185Sminshall break; 54327185Sminshall } 54427185Sminshall netip = netibuf; 54527185Sminshall } 5466002Sroot } 5476002Sroot 5486002Sroot /* 5496002Sroot * Something to read from the pty... 5506002Sroot */ 55127185Sminshall if (FD_ISSET(p, &ibits)) { 5526002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5536002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5546002Sroot pcc = 0; 5556002Sroot else { 5566002Sroot if (pcc <= 0) 5576002Sroot break; 5586002Sroot ptyip = ptyibuf; 5596002Sroot } 5606002Sroot } 5616002Sroot 5626002Sroot while (pcc > 0) { 5636002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 5646002Sroot break; 5656002Sroot c = *ptyip++ & 0377, pcc--; 5666002Sroot if (c == IAC) 5676002Sroot *nfrontp++ = c; 5686002Sroot *nfrontp++ = c; 56927020Sminshall if (c == '\r') { 57027020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 57127020Sminshall *nfrontp++ = *ptyip++ & 0377; 57227020Sminshall pcc--; 57327020Sminshall } else 57427020Sminshall *nfrontp++ = '\0'; 57527020Sminshall } 5766002Sroot } 57727185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 5786002Sroot netflush(); 5796002Sroot if (ncc > 0) 5806002Sroot telrcv(); 58127185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 5826002Sroot ptyflush(); 5836002Sroot } 5846002Sroot cleanup(); 5856002Sroot } 5866002Sroot 5876002Sroot /* 5886002Sroot * State for recv fsm 5896002Sroot */ 5906002Sroot #define TS_DATA 0 /* base state */ 5916002Sroot #define TS_IAC 1 /* look for double IAC's */ 5926002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 59327649Sminshall #define TS_SB 3 /* throw away begin's... */ 59427649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 5956002Sroot #define TS_WILL 5 /* will option negotiation */ 5966002Sroot #define TS_WONT 6 /* wont " */ 5976002Sroot #define TS_DO 7 /* do " */ 5986002Sroot #define TS_DONT 8 /* dont " */ 5996002Sroot 6006002Sroot telrcv() 6016002Sroot { 6026002Sroot register int c; 6036002Sroot static int state = TS_DATA; 6046002Sroot 6056002Sroot while (ncc > 0) { 6066002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6076002Sroot return; 6086002Sroot c = *netip++ & 0377, ncc--; 6096002Sroot switch (state) { 6106002Sroot 61126090Sminshall case TS_CR: 61226090Sminshall state = TS_DATA; 61326499Sminshall if ((c == 0) || (c == '\n')) { 61426090Sminshall break; 61526499Sminshall } 61626090Sminshall /* FALL THROUGH */ 61726090Sminshall 6186002Sroot case TS_DATA: 6196002Sroot if (c == IAC) { 6206002Sroot state = TS_IAC; 6216002Sroot break; 6226002Sroot } 6236002Sroot if (inter > 0) 6246002Sroot break; 62527020Sminshall /* 62627020Sminshall * We map \r\n ==> \n, since \r\n says 62727020Sminshall * that we want to be in column 1 of the next 62827020Sminshall * printable line, and \n is the standard 62927020Sminshall * unix way of saying that (\r is only good 63027020Sminshall * if CRMOD is set, which it normally is). 63127020Sminshall */ 632*27983Sminshall if ((myopts[TELOPT_BINARY] == OPT_NO) && c == '\r') { 63327020Sminshall if ((ncc > 0) && ('\n' == *netip)) { 63427020Sminshall netip++; ncc--; 63527020Sminshall c = '\n'; 63627020Sminshall } else { 63727020Sminshall state = TS_CR; 63827020Sminshall } 63926499Sminshall } 64026499Sminshall *pfrontp++ = c; 6416002Sroot break; 6426002Sroot 6436002Sroot case TS_IAC: 6446002Sroot switch (c) { 6456002Sroot 6466002Sroot /* 6476002Sroot * Send the process on the pty side an 6486002Sroot * interrupt. Do this with a NULL or 6496002Sroot * interrupt char; depending on the tty mode. 6506002Sroot */ 6516002Sroot case IP: 6526002Sroot interrupt(); 6536002Sroot break; 6546002Sroot 65527229Sminshall case BREAK: 65627229Sminshall sendbrk(); 65727229Sminshall break; 65827229Sminshall 6596002Sroot /* 6606002Sroot * Are You There? 6616002Sroot */ 6626002Sroot case AYT: 66317583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 66417583Ssam nfrontp += 9; 6656002Sroot break; 6666002Sroot 6676002Sroot /* 66827185Sminshall * Abort Output 66927185Sminshall */ 67027185Sminshall case AO: { 67127185Sminshall struct ltchars tmpltc; 67227185Sminshall 67327185Sminshall ptyflush(); /* half-hearted */ 67427185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 67527185Sminshall if (tmpltc.t_flushc != '\377') { 67627185Sminshall *pfrontp++ = tmpltc.t_flushc; 67727185Sminshall } 67827260Sminshall netclear(); /* clear buffer back */ 67927185Sminshall *nfrontp++ = IAC; 68027185Sminshall *nfrontp++ = DM; 68127187Sminshall neturg = nfrontp-1; /* off by one XXX */ 68227185Sminshall break; 68327185Sminshall } 68427185Sminshall 68527185Sminshall /* 6866002Sroot * Erase Character and 6876002Sroot * Erase Line 6886002Sroot */ 6896002Sroot case EC: 69027185Sminshall case EL: { 69127185Sminshall struct sgttyb b; 69227185Sminshall char ch; 6936002Sroot 69427185Sminshall ptyflush(); /* half-hearted */ 69527185Sminshall ioctl(pty, TIOCGETP, &b); 69627185Sminshall ch = (c == EC) ? 69727185Sminshall b.sg_erase : b.sg_kill; 69827185Sminshall if (ch != '\377') { 69927185Sminshall *pfrontp++ = ch; 70027185Sminshall } 70127185Sminshall break; 70227185Sminshall } 70327185Sminshall 7046002Sroot /* 7056002Sroot * Check for urgent data... 7066002Sroot */ 7076002Sroot case DM: 70827185Sminshall SYNCHing = stilloob(net); 70927185Sminshall settimer(gotDM); 7106002Sroot break; 7116002Sroot 71227185Sminshall 7136002Sroot /* 7146002Sroot * Begin option subnegotiation... 7156002Sroot */ 7166002Sroot case SB: 71727649Sminshall state = TS_SB; 7186002Sroot continue; 7196002Sroot 7206002Sroot case WILL: 72127188Sminshall state = TS_WILL; 72227188Sminshall continue; 72327188Sminshall 7246002Sroot case WONT: 72527188Sminshall state = TS_WONT; 72627188Sminshall continue; 72727188Sminshall 7286002Sroot case DO: 72927188Sminshall state = TS_DO; 73027188Sminshall continue; 73127188Sminshall 7326002Sroot case DONT: 73327188Sminshall state = TS_DONT; 7346002Sroot continue; 7356002Sroot 7366002Sroot case IAC: 7376002Sroot *pfrontp++ = c; 7386002Sroot break; 7396002Sroot } 7406002Sroot state = TS_DATA; 7416002Sroot break; 7426002Sroot 74327649Sminshall case TS_SB: 74427649Sminshall if (c == IAC) { 74527649Sminshall state = TS_SE; 74627649Sminshall } else { 74727649Sminshall SB_ACCUM(c); 74827649Sminshall } 7496002Sroot break; 7506002Sroot 75127649Sminshall case TS_SE: 75227649Sminshall if (c != SE) { 75327649Sminshall if (c != IAC) { 75427649Sminshall SB_ACCUM(IAC); 75527649Sminshall } 75627649Sminshall SB_ACCUM(c); 75727649Sminshall state = TS_SB; 75827649Sminshall } else { 75927649Sminshall SB_TERM(); 76027649Sminshall suboption(); /* handle sub-option */ 76127649Sminshall state = TS_DATA; 76227649Sminshall } 7636002Sroot break; 7646002Sroot 7656002Sroot case TS_WILL: 766*27983Sminshall if (hisopts[c] != OPT_YES) 7676002Sroot willoption(c); 7686002Sroot state = TS_DATA; 7696002Sroot continue; 7706002Sroot 7716002Sroot case TS_WONT: 772*27983Sminshall if (hisopts[c] != OPT_NO) 7736002Sroot wontoption(c); 7746002Sroot state = TS_DATA; 7756002Sroot continue; 7766002Sroot 7776002Sroot case TS_DO: 778*27983Sminshall if (myopts[c] != OPT_YES) 7796002Sroot dooption(c); 7806002Sroot state = TS_DATA; 7816002Sroot continue; 7826002Sroot 7836002Sroot case TS_DONT: 784*27983Sminshall if (myopts[c] != OPT_NO) { 78527649Sminshall dontoption(c); 7866002Sroot } 7876002Sroot state = TS_DATA; 7886002Sroot continue; 7896002Sroot 7906002Sroot default: 79127898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 7929218Ssam printf("telnetd: panic state=%d\n", state); 7936002Sroot exit(1); 7946002Sroot } 7956002Sroot } 7966002Sroot } 7976002Sroot 7986002Sroot willoption(option) 7996002Sroot int option; 8006002Sroot { 8016002Sroot char *fmt; 8026002Sroot 8036002Sroot switch (option) { 8046002Sroot 8056002Sroot case TELOPT_BINARY: 8066002Sroot mode(RAW, 0); 80727188Sminshall fmt = doopt; 80827188Sminshall break; 8096002Sroot 8106002Sroot case TELOPT_ECHO: 81127649Sminshall not42 = 0; /* looks like a 4.2 system */ 81227649Sminshall /* 81327649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 81427649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 81527649Sminshall * Kludge upon kludge! 81627649Sminshall */ 817*27983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 81827649Sminshall dooption(TELOPT_ECHO); 81927649Sminshall } 82027649Sminshall fmt = dont; 82127188Sminshall break; 8226002Sroot 82327649Sminshall case TELOPT_TTYPE: 824*27983Sminshall settimer(ttypeopt); 825*27983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 826*27983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 827*27983Sminshall return; 828*27983Sminshall } 829*27983Sminshall fmt = doopt; 830*27983Sminshall break; 831*27983Sminshall 8326002Sroot case TELOPT_SGA: 8336002Sroot fmt = doopt; 8346002Sroot break; 8356002Sroot 8366002Sroot case TELOPT_TM: 8376002Sroot fmt = dont; 8386002Sroot break; 8396002Sroot 8406002Sroot default: 8416002Sroot fmt = dont; 8426002Sroot break; 8436002Sroot } 84427188Sminshall if (fmt == doopt) { 845*27983Sminshall hisopts[option] = OPT_YES; 84627188Sminshall } else { 847*27983Sminshall hisopts[option] = OPT_NO; 84827188Sminshall } 8496023Ssam sprintf(nfrontp, fmt, option); 8508379Ssam nfrontp += sizeof (dont) - 2; 8516002Sroot } 8526002Sroot 8536002Sroot wontoption(option) 8546002Sroot int option; 8556002Sroot { 8566002Sroot char *fmt; 8576002Sroot 8586002Sroot switch (option) { 8596002Sroot case TELOPT_ECHO: 86027649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 86127188Sminshall break; 8626002Sroot 8636002Sroot case TELOPT_BINARY: 8646002Sroot mode(0, RAW); 8656002Sroot break; 8666002Sroot } 86727188Sminshall fmt = dont; 868*27983Sminshall hisopts[option] = OPT_NO; 8696002Sroot sprintf(nfrontp, fmt, option); 8708379Ssam nfrontp += sizeof (doopt) - 2; 8716002Sroot } 8726002Sroot 8736002Sroot dooption(option) 8746002Sroot int option; 8756002Sroot { 8766002Sroot char *fmt; 8776002Sroot 8786002Sroot switch (option) { 8796002Sroot 8806002Sroot case TELOPT_TM: 8816002Sroot fmt = wont; 8826002Sroot break; 8836002Sroot 8846002Sroot case TELOPT_ECHO: 8856002Sroot mode(ECHO|CRMOD, 0); 88627188Sminshall fmt = will; 88727188Sminshall break; 8886002Sroot 8896002Sroot case TELOPT_BINARY: 8906002Sroot mode(RAW, 0); 89127188Sminshall fmt = will; 89227188Sminshall break; 8936002Sroot 8946002Sroot case TELOPT_SGA: 8956002Sroot fmt = will; 8966002Sroot break; 8976002Sroot 8986002Sroot default: 8996002Sroot fmt = wont; 9006002Sroot break; 9016002Sroot } 90227188Sminshall if (fmt == will) { 903*27983Sminshall myopts[option] = OPT_YES; 90427188Sminshall } else { 905*27983Sminshall myopts[option] = OPT_NO; 90627188Sminshall } 9076002Sroot sprintf(nfrontp, fmt, option); 9088379Ssam nfrontp += sizeof (doopt) - 2; 9096002Sroot } 9106002Sroot 91127649Sminshall 91227649Sminshall dontoption(option) 91327649Sminshall int option; 91427649Sminshall { 91527649Sminshall char *fmt; 91627649Sminshall 91727649Sminshall switch (option) { 91827649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 91927649Sminshall mode(0, ECHO|CRMOD); 92027649Sminshall fmt = wont; 92127649Sminshall break; 922*27983Sminshall 923*27983Sminshall case TELOPT_TTYPE: 924*27983Sminshall fmt = wont; 925*27983Sminshall settimer(ttypeopt); 926*27983Sminshall break; 927*27983Sminshall 92827649Sminshall default: 92927649Sminshall fmt = wont; 93027649Sminshall break; 93127649Sminshall } 932*27983Sminshall 93327649Sminshall if (fmt = wont) { 934*27983Sminshall myopts[option] = OPT_NO; 93527649Sminshall } else { 936*27983Sminshall myopts[option] = OPT_YES; 93727649Sminshall } 93827649Sminshall sprintf(nfrontp, fmt, option); 93927649Sminshall nfrontp += sizeof (wont) - 2; 94027649Sminshall } 94127649Sminshall 94227649Sminshall /* 94327649Sminshall * suboption() 94427649Sminshall * 94527649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 94627649Sminshall * side. 94727649Sminshall * 94827649Sminshall * Currently we recognize: 94927649Sminshall * 950*27983Sminshall * Terminal type is 95127649Sminshall */ 95227649Sminshall 95327649Sminshall suboption() 95427649Sminshall { 955*27983Sminshall switch (SB_GET()) { 956*27983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 957*27983Sminshall static char terminalname[5+41] = "TERM="; 958*27983Sminshall 959*27983Sminshall settimer(ttypesubopt); 960*27983Sminshall 961*27983Sminshall if (SB_GET() != TELQUAL_IS) { 962*27983Sminshall return; /* ??? XXX but, this is the most robust */ 963*27983Sminshall } 964*27983Sminshall 965*27983Sminshall terminaltype = terminalname+strlen(terminalname); 966*27983Sminshall 967*27983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 968*27983Sminshall !SB_EOF()) { 969*27983Sminshall register int c; 970*27983Sminshall 971*27983Sminshall c = SB_GET(); 972*27983Sminshall if (isupper(c)) { 973*27983Sminshall c = tolower(c); 974*27983Sminshall } 975*27983Sminshall *terminaltype++ = c; /* accumulate name */ 976*27983Sminshall } 977*27983Sminshall *terminaltype = 0; 978*27983Sminshall terminaltype = terminalname; 979*27983Sminshall break; 980*27983Sminshall } 981*27983Sminshall 98227649Sminshall default: 98327649Sminshall ; 98427649Sminshall } 98527649Sminshall } 98627649Sminshall 9876002Sroot mode(on, off) 9886002Sroot int on, off; 9896002Sroot { 9906002Sroot struct sgttyb b; 9916002Sroot 9926002Sroot ptyflush(); 9936002Sroot ioctl(pty, TIOCGETP, &b); 9946002Sroot b.sg_flags |= on; 9956002Sroot b.sg_flags &= ~off; 9966002Sroot ioctl(pty, TIOCSETP, &b); 9976002Sroot } 9986002Sroot 9996002Sroot /* 10006002Sroot * Send interrupt to process on other side of pty. 10016002Sroot * If it is in raw mode, just write NULL; 10026002Sroot * otherwise, write intr char. 10036002Sroot */ 10046002Sroot interrupt() 10056002Sroot { 10066002Sroot struct sgttyb b; 10076002Sroot struct tchars tchars; 10086002Sroot 10096002Sroot ptyflush(); /* half-hearted */ 10106002Sroot ioctl(pty, TIOCGETP, &b); 10116002Sroot if (b.sg_flags & RAW) { 10126002Sroot *pfrontp++ = '\0'; 10136002Sroot return; 10146002Sroot } 10156002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10166002Sroot '\177' : tchars.t_intrc; 10176002Sroot } 10186002Sroot 101927229Sminshall /* 102027229Sminshall * Send quit to process on other side of pty. 102127229Sminshall * If it is in raw mode, just write NULL; 102227229Sminshall * otherwise, write quit char. 102327229Sminshall */ 102427229Sminshall sendbrk() 102527229Sminshall { 102627229Sminshall struct sgttyb b; 102727229Sminshall struct tchars tchars; 102827229Sminshall 102927229Sminshall ptyflush(); /* half-hearted */ 103027229Sminshall ioctl(pty, TIOCGETP, &b); 103127229Sminshall if (b.sg_flags & RAW) { 103227229Sminshall *pfrontp++ = '\0'; 103327229Sminshall return; 103427229Sminshall } 103527229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 103627229Sminshall '\034' : tchars.t_quitc; 103727229Sminshall } 103827229Sminshall 10396002Sroot ptyflush() 10406002Sroot { 10416002Sroot int n; 10426002Sroot 10436002Sroot if ((n = pfrontp - pbackp) > 0) 10446002Sroot n = write(pty, pbackp, n); 10458346Ssam if (n < 0) 10468346Ssam return; 10476002Sroot pbackp += n; 10486002Sroot if (pbackp == pfrontp) 10496002Sroot pbackp = pfrontp = ptyobuf; 10506002Sroot } 105127260Sminshall 105227260Sminshall /* 105327260Sminshall * nextitem() 105427260Sminshall * 105527260Sminshall * Return the address of the next "item" in the TELNET data 105627260Sminshall * stream. This will be the address of the next character if 105727260Sminshall * the current address is a user data character, or it will 105827260Sminshall * be the address of the character following the TELNET command 105927260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 106027260Sminshall * character. 106127260Sminshall */ 10626002Sroot 106327260Sminshall char * 106427260Sminshall nextitem(current) 106527260Sminshall char *current; 10666002Sroot { 106727260Sminshall if ((*current&0xff) != IAC) { 106827260Sminshall return current+1; 106927260Sminshall } 107027260Sminshall switch (*(current+1)&0xff) { 107127260Sminshall case DO: 107227260Sminshall case DONT: 107327260Sminshall case WILL: 107427260Sminshall case WONT: 107527260Sminshall return current+3; 107627260Sminshall case SB: /* loop forever looking for the SE */ 107727260Sminshall { 107827260Sminshall register char *look = current+2; 10796002Sroot 108027260Sminshall for (;;) { 108127260Sminshall if ((*look++&0xff) == IAC) { 108227260Sminshall if ((*look++&0xff) == SE) { 108327260Sminshall return look; 108427260Sminshall } 108527260Sminshall } 108627260Sminshall } 10878346Ssam } 108827260Sminshall default: 108927260Sminshall return current+2; 109027260Sminshall } 10916002Sroot } 10926002Sroot 109327185Sminshall 109427185Sminshall /* 109527260Sminshall * netclear() 109627260Sminshall * 109727260Sminshall * We are about to do a TELNET SYNCH operation. Clear 109827260Sminshall * the path to the network. 109927260Sminshall * 110027260Sminshall * Things are a bit tricky since we may have sent the first 110127260Sminshall * byte or so of a previous TELNET command into the network. 110227260Sminshall * So, we have to scan the network buffer from the beginning 110327260Sminshall * until we are up to where we want to be. 110427260Sminshall * 110527260Sminshall * A side effect of what we do, just to keep things 110627260Sminshall * simple, is to clear the urgent data pointer. The principal 110727260Sminshall * caller should be setting the urgent data pointer AFTER calling 110827260Sminshall * us in any case. 110927260Sminshall */ 111027260Sminshall 111127260Sminshall netclear() 111227260Sminshall { 111327260Sminshall register char *thisitem, *next; 111427260Sminshall char *good; 111527260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 111627260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 111727260Sminshall 111827260Sminshall thisitem = netobuf; 111927260Sminshall 112027260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 112127260Sminshall thisitem = next; 112227260Sminshall } 112327260Sminshall 112427260Sminshall /* Now, thisitem is first before/at boundary. */ 112527260Sminshall 112627260Sminshall good = netobuf; /* where the good bytes go */ 112727260Sminshall 112827260Sminshall while (nfrontp > thisitem) { 112927260Sminshall if (wewant(thisitem)) { 113027260Sminshall int length; 113127260Sminshall 113227260Sminshall next = thisitem; 113327260Sminshall do { 113427260Sminshall next = nextitem(next); 113527260Sminshall } while (wewant(next) && (nfrontp > next)); 113627260Sminshall length = next-thisitem; 113727260Sminshall bcopy(thisitem, good, length); 113827260Sminshall good += length; 113927260Sminshall thisitem = next; 114027260Sminshall } else { 114127260Sminshall thisitem = nextitem(thisitem); 114227260Sminshall } 114327260Sminshall } 114427260Sminshall 114527260Sminshall nbackp = netobuf; 114627260Sminshall nfrontp = good; /* next byte to be sent */ 114727260Sminshall neturg = 0; 114827260Sminshall } 114927260Sminshall 115027260Sminshall /* 115127185Sminshall * netflush 115227185Sminshall * Send as much data as possible to the network, 115327185Sminshall * handling requests for urgent data. 115427185Sminshall */ 115527185Sminshall 115627185Sminshall 115727185Sminshall netflush() 115827185Sminshall { 115927185Sminshall int n; 116027185Sminshall 116127185Sminshall if ((n = nfrontp - nbackp) > 0) { 116227649Sminshall /* 116327649Sminshall * if no urgent data, or if the other side appears to be an 116427649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 116527649Sminshall * write the entire buffer in non-OOB mode. 116627649Sminshall */ 116727649Sminshall if ((neturg == 0) || (not42 == 0)) { 116827185Sminshall n = write(net, nbackp, n); /* normal write */ 116927185Sminshall } else { 117027185Sminshall n = neturg - nbackp; 117127185Sminshall /* 117227185Sminshall * In 4.2 (and 4.3) systems, there is some question about 117327185Sminshall * what byte in a sendOOB operation is the "OOB" data. 117427185Sminshall * To make ourselves compatible, we only send ONE byte 117527185Sminshall * out of band, the one WE THINK should be OOB (though 117627185Sminshall * we really have more the TCP philosophy of urgent data 117727185Sminshall * rather than the Unix philosophy of OOB data). 117827185Sminshall */ 117927185Sminshall if (n > 1) { 118027185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 118127185Sminshall } else { 118227185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 118327185Sminshall } 118427185Sminshall } 118527185Sminshall } 118627185Sminshall if (n < 0) { 118727185Sminshall if (errno == EWOULDBLOCK) 118827185Sminshall return; 118927185Sminshall /* should blow this guy away... */ 119027185Sminshall return; 119127185Sminshall } 119227185Sminshall nbackp += n; 119327185Sminshall if (nbackp >= neturg) { 119427185Sminshall neturg = 0; 119527185Sminshall } 119627185Sminshall if (nbackp == nfrontp) { 119727185Sminshall nbackp = nfrontp = netobuf; 119827185Sminshall } 119927185Sminshall } 120027185Sminshall 12016002Sroot cleanup() 12026002Sroot { 12036002Sroot 12046002Sroot rmut(); 120510008Ssam vhangup(); /* XXX */ 120610191Ssam shutdown(net, 2); 12076002Sroot exit(1); 12086002Sroot } 12096002Sroot 12106002Sroot #include <utmp.h> 12116002Sroot 12126002Sroot struct utmp wtmp; 12136002Sroot char wtmpf[] = "/usr/adm/wtmp"; 121423567Sbloom char utmpf[] = "/etc/utmp"; 121523567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 121623567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12176002Sroot 12186002Sroot rmut() 12196002Sroot { 12206002Sroot register f; 12216002Sroot int found = 0; 122223567Sbloom struct utmp *u, *utmp; 122323567Sbloom int nutmp; 122423567Sbloom struct stat statbf; 12256002Sroot 122623567Sbloom f = open(utmpf, O_RDWR); 12276002Sroot if (f >= 0) { 122823567Sbloom fstat(f, &statbf); 122923567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 123023567Sbloom if (!utmp) 123123567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 123223567Sbloom if (statbf.st_size && utmp) { 123323567Sbloom nutmp = read(f, utmp, statbf.st_size); 123423567Sbloom nutmp /= sizeof(struct utmp); 123523567Sbloom 123623567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 123723567Sbloom if (SCMPN(u->ut_line, line+5) || 123823567Sbloom u->ut_name[0]==0) 123923567Sbloom continue; 124023567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 124123567Sbloom SCPYN(u->ut_name, ""); 124223567Sbloom SCPYN(u->ut_host, ""); 124323567Sbloom time(&u->ut_time); 124423567Sbloom write(f, (char *)u, sizeof(wtmp)); 124523567Sbloom found++; 124623567Sbloom } 12476002Sroot } 12486002Sroot close(f); 12496002Sroot } 12506002Sroot if (found) { 125117583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12526002Sroot if (f >= 0) { 12536002Sroot SCPYN(wtmp.ut_line, line+5); 12546002Sroot SCPYN(wtmp.ut_name, ""); 125512683Ssam SCPYN(wtmp.ut_host, ""); 12566002Sroot time(&wtmp.ut_time); 125723567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 12586002Sroot close(f); 12596002Sroot } 12606002Sroot } 12616002Sroot chmod(line, 0666); 12626002Sroot chown(line, 0, 0); 12636002Sroot line[strlen("/dev/")] = 'p'; 12646002Sroot chmod(line, 0666); 12656002Sroot chown(line, 0, 0); 12666002Sroot } 1267