121182Sdist /* 221182Sdist * Copyright (c) 1983 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*27797Sminshall static char sccsid[] = "@(#)telnetd.c 5.15 (Berkeley) 05/07/86"; 1521182Sdist #endif not lint 1621182Sdist 176002Sroot /* 186002Sroot * Stripped-down telnet server. 196002Sroot */ 209218Ssam #include <sys/types.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 3927649Sminshall #define OPT_DONT 0 /* don't do this option */ 4027649Sminshall #define OPT_WONT 0 /* won't do this option */ 4127649Sminshall #define OPT_DO 1 /* do this option */ 4227649Sminshall #define OPT_WILL 1 /* will do this option */ 4327649Sminshall #define OPT_ALWAYS_LOOK 2 /* special case for echo */ 446002Sroot char hisopts[256]; 456002Sroot char myopts[256]; 466002Sroot 476002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 486002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 496002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 506002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 516002Sroot 526002Sroot /* 536002Sroot * I/O data buffers, pointers, and counters. 546002Sroot */ 556002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 5627649Sminshall 576002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 5827649Sminshall 596002Sroot char netibuf[BUFSIZ], *netip = netibuf; 6027649Sminshall #define NIACCUM(c) { *netip++ = c; \ 6127649Sminshall ncc++; \ 6227649Sminshall } 6327649Sminshall 646388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 6527185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 6627649Sminshall /* the remote system seems to NOT be an old 4.2 */ 6727649Sminshall int not42 = 1; 6827649Sminshall 6927649Sminshall 70*27797Sminshall char BANNER1[] = "\r\n\r\n4.3 BSD UNIX (", 71*27797Sminshall BANNER2[] = ")\r\n\r\0\r\n\r\0"; 72*27797Sminshall 7327649Sminshall char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */ 7427649Sminshall #define SB_CLEAR() subpointer = subbuffer; 7527649Sminshall #define SB_TERM() subend = subpointer; 7627649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 7727649Sminshall *subpointer++ = (c); \ 7827649Sminshall } 7927649Sminshall 806002Sroot int pcc, ncc; 816002Sroot 826002Sroot int pty, net; 836002Sroot int inter; 8413799Ssam extern char **environ; 856002Sroot extern int errno; 8620188Skarels char *line; 8727185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 8827185Sminshall /* 8927185Sminshall * The following are some clocks used to decide how to interpret 9027185Sminshall * the relationship between various variables. 9127185Sminshall */ 926002Sroot 9327185Sminshall struct { 9427185Sminshall int 9527185Sminshall system, /* what the current time is */ 9627185Sminshall echotoggle, /* last time user entered echo character */ 9727185Sminshall modenegotiated, /* last time operating mode negotiated */ 9827185Sminshall didnetreceive, /* last time we read data from network */ 9927185Sminshall gotDM; /* when did we last see a data mark */ 10027185Sminshall } clocks; 10127185Sminshall 10227185Sminshall #define settimer(x) clocks.x = clocks.system++ 10327185Sminshall 1046002Sroot main(argc, argv) 1056002Sroot char *argv[]; 1066002Sroot { 10716371Skarels struct sockaddr_in from; 10817156Ssam int on = 1, fromlen; 1096002Sroot 11027185Sminshall #if defined(DEBUG) 11127185Sminshall { 11227185Sminshall int s, ns, foo; 11327185Sminshall struct servent *sp; 11427185Sminshall static struct sockaddr_in sin = { AF_INET }; 11527185Sminshall 11627185Sminshall sp = getservbyname("telnet", "tcp"); 11727185Sminshall if (sp == 0) { 11827185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 11927185Sminshall exit(1); 12027185Sminshall } 12127185Sminshall sin.sin_port = sp->s_port; 12227185Sminshall argc--, argv++; 12327185Sminshall if (argc > 0) { 12427185Sminshall sin.sin_port = atoi(*argv); 12527185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 12627185Sminshall } 12727185Sminshall 12827185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 12927185Sminshall if (s < 0) { 13027185Sminshall perror("telnetd: socket");; 13127185Sminshall exit(1); 13227185Sminshall } 13327185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 13427185Sminshall perror("bind"); 13527185Sminshall exit(1); 13627185Sminshall } 13727185Sminshall if (listen(s, 1) < 0) { 13827185Sminshall perror("listen"); 13927185Sminshall exit(1); 14027185Sminshall } 14127185Sminshall foo = sizeof sin; 14227185Sminshall ns = accept(s, &sin, &foo); 14327185Sminshall if (ns < 0) { 14427185Sminshall perror("accept"); 14527185Sminshall exit(1); 14627185Sminshall } 14727185Sminshall dup2(ns, 0); 14827185Sminshall close(s); 14927185Sminshall } 15027185Sminshall #endif /* defined(DEBUG) */ 15124855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 15216371Skarels fromlen = sizeof (from); 15316371Skarels if (getpeername(0, &from, &fromlen) < 0) { 15416371Skarels fprintf(stderr, "%s: ", argv[0]); 15516371Skarels perror("getpeername"); 15616371Skarels _exit(1); 1578346Ssam } 15817156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 15917187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 16010418Ssam } 16116371Skarels doit(0, &from); 1626002Sroot } 1636002Sroot 16427649Sminshall 16527649Sminshall /* 16627649Sminshall * Get() 16727649Sminshall * 16827649Sminshall * Return next character from file descriptor. 16927649Sminshall * 17027649Sminshall * This is not meant to be very efficient, since it is only 17127649Sminshall * run during startup. 17227649Sminshall */ 17327649Sminshall 17427649Sminshall Get(f) 17527649Sminshall int f; /* the file descriptor */ 17627649Sminshall { 17727649Sminshall char input; 17827649Sminshall 17927649Sminshall if (read(f, &input, 1) != 1) { 18027649Sminshall syslog(LOG_ERR, "read: %m\n"); 18127649Sminshall exit(1); 18227649Sminshall } 18327649Sminshall return input&0xff; 18427649Sminshall } 18527649Sminshall 18627649Sminshall char *terminaltype; 18727649Sminshall char *envinit[2]; 1886002Sroot int cleanup(); 1896002Sroot 1906002Sroot /* 1916002Sroot * Get a pty, scan input lines. 1926002Sroot */ 19312683Ssam doit(f, who) 19412683Ssam int f; 19512683Ssam struct sockaddr_in *who; 1966002Sroot { 19720188Skarels char *host, *inet_ntoa(); 19817583Ssam int i, p, t; 1996002Sroot struct sgttyb b; 20012683Ssam struct hostent *hp; 20127649Sminshall int c; 20227649Sminshall int gotterminaltype = 0; 2036002Sroot 20427649Sminshall /* 20527649Sminshall * Try to get a terminal type from the foreign host. 20627649Sminshall */ 20727649Sminshall 20827649Sminshall { 20927649Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 21027649Sminshall 21127649Sminshall terminaltype = 0; 21227649Sminshall if (write(f, sbuf, sizeof sbuf) == -1) { 21327649Sminshall syslog(LOG_ERR, "write sbuf: %m\n"); 21427649Sminshall exit(1); 21527649Sminshall } 21627649Sminshall for (;;) { /* ugly, but we are VERY early */ 21727649Sminshall while ((c = Get(f)) != IAC) { 21827649Sminshall NIACCUM(c); 21927649Sminshall } 22027649Sminshall if ((c = Get(f)) == WILL) { 22127649Sminshall if ((c = Get(f)) == TELOPT_TTYPE) { 22227649Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, 22327649Sminshall TELQUAL_SEND, IAC, SE }; 22427649Sminshall if (write(f, sbbuf, sizeof sbbuf) == -1) { 22527649Sminshall syslog(LOG_ERR, "write sbbuf: %m\n"); 22627649Sminshall exit(1); 22727649Sminshall } 22827649Sminshall break; 22927649Sminshall } else { 23027649Sminshall NIACCUM(IAC); 23127649Sminshall NIACCUM(WILL); 23227649Sminshall NIACCUM(c); 23327649Sminshall } 23427649Sminshall } else if (c == WONT) { 23527649Sminshall if ((c = Get(f)) == TELOPT_TTYPE) { 23627649Sminshall terminaltype = "TERM=network"; 23727649Sminshall break; 23827649Sminshall } else { 23927649Sminshall NIACCUM(IAC); 24027649Sminshall NIACCUM(WONT); 24127649Sminshall NIACCUM(c); 24227649Sminshall } 24327649Sminshall } else { 24427649Sminshall NIACCUM(IAC); 24527649Sminshall NIACCUM(c); 24627649Sminshall } 24727649Sminshall } 24827649Sminshall if (!terminaltype) { 24927649Sminshall for (;;) { 25027649Sminshall while ((c = Get(f)) != IAC) { 25127649Sminshall NIACCUM(c); 25227649Sminshall } 25327649Sminshall if ((c = Get(f)) != SB) { 25427649Sminshall NIACCUM(IAC); 25527649Sminshall NIACCUM(c); 25627649Sminshall } else if ((c = Get(f)) != TELOPT_TTYPE) { 25727649Sminshall NIACCUM(IAC); 25827649Sminshall NIACCUM(SB); 25927649Sminshall NIACCUM(c); 26027649Sminshall } else if ((c = Get(f)) != TELQUAL_IS) { 26127649Sminshall NIACCUM(IAC); 26227649Sminshall NIACCUM(SB); 26327649Sminshall NIACCUM(TELOPT_TTYPE); 26427649Sminshall NIACCUM(c); 26527649Sminshall } else { /* Yaaaay! */ 26627649Sminshall static char terminalname[5+41] = "TERM="; 26727649Sminshall 26827649Sminshall terminaltype = terminalname+strlen(terminalname); 26927649Sminshall 27027649Sminshall while (terminaltype < 27127649Sminshall (terminalname + sizeof terminalname-1)) { 27227649Sminshall if ((c = Get(f)) == IAC) { 27327649Sminshall if ((c = Get(f)) == SE) { 27427649Sminshall break; /* done */ 27527649Sminshall } else { 27627649Sminshall *terminaltype++ = IAC; /* ? */ 27727649Sminshall if (isupper(c)) { 27827649Sminshall c = tolower(c); 27927649Sminshall } 28027649Sminshall *terminaltype++ = c; 28127649Sminshall } 28227649Sminshall } else { 28327649Sminshall if (isupper(c)) { 28427649Sminshall c = tolower(c); 28527649Sminshall } 28627649Sminshall *terminaltype++ = c; /* accumulate name */ 28727649Sminshall } 28827649Sminshall } 28927649Sminshall *terminaltype = 0; 29027649Sminshall terminaltype = terminalname; 29127649Sminshall gotterminaltype = 1; 29227649Sminshall break; 29327649Sminshall } 29427649Sminshall } 29527649Sminshall } 29627649Sminshall envinit[0] = terminaltype; 29727649Sminshall envinit[1] = 0; 29827649Sminshall } 299*27797Sminshall netip = netibuf; 30027649Sminshall 30120188Skarels for (c = 'p'; c <= 's'; c++) { 30220188Skarels struct stat stb; 30320188Skarels 30420188Skarels line = "/dev/ptyXX"; 30520188Skarels line[strlen("/dev/pty")] = c; 30620188Skarels line[strlen("/dev/ptyp")] = '0'; 30720188Skarels if (stat(line, &stb) < 0) 30820188Skarels break; 30917583Ssam for (i = 0; i < 16; i++) { 31020188Skarels line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 31120188Skarels p = open(line, 2); 31217583Ssam if (p > 0) 31317583Ssam goto gotpty; 31417583Ssam } 3156002Sroot } 3169244Ssam fatal(f, "All network ports in use"); 3179244Ssam /*NOTREACHED*/ 3186002Sroot gotpty: 3196002Sroot dup2(f, 0); 32020188Skarels line[strlen("/dev/")] = 't'; 32117583Ssam t = open("/dev/tty", O_RDWR); 3226002Sroot if (t >= 0) { 3236002Sroot ioctl(t, TIOCNOTTY, 0); 3246002Sroot close(t); 3256002Sroot } 32620188Skarels t = open(line, O_RDWR); 3279244Ssam if (t < 0) 32820188Skarels fatalperror(f, line, errno); 3296002Sroot ioctl(t, TIOCGETP, &b); 3306388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 3316002Sroot ioctl(t, TIOCSETP, &b); 3326388Ssam ioctl(p, TIOCGETP, &b); 3338379Ssam b.sg_flags &= ~ECHO; 3346388Ssam ioctl(p, TIOCSETP, &b); 33512683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 33612683Ssam who->sin_family); 33712683Ssam if (hp) 33812683Ssam host = hp->h_name; 33912683Ssam else 34017444Sralph host = inet_ntoa(who->sin_addr); 3419244Ssam if ((i = fork()) < 0) 3429244Ssam fatalperror(f, "fork", errno); 3436002Sroot if (i) 3446002Sroot telnet(f, p); 3456002Sroot close(f); 3466002Sroot close(p); 3476002Sroot dup2(t, 0); 3486002Sroot dup2(t, 1); 3496002Sroot dup2(t, 2); 3506002Sroot close(t); 35113799Ssam environ = envinit; 35227649Sminshall /* 35327649Sminshall * -h : pass on name of host. 35427649Sminshall * -p : don't clobber the environment (so terminal type stays set). 35527649Sminshall */ 35627649Sminshall execl("/bin/login", "login", "-h", host, 35727649Sminshall gotterminaltype ? "-p" : 0, 0); 3589244Ssam fatalperror(f, "/bin/login", errno); 3599244Ssam /*NOTREACHED*/ 3609244Ssam } 3619244Ssam 3629244Ssam fatal(f, msg) 3639244Ssam int f; 3649244Ssam char *msg; 3659244Ssam { 3669244Ssam char buf[BUFSIZ]; 3679244Ssam 36817583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3699244Ssam (void) write(f, buf, strlen(buf)); 3706002Sroot exit(1); 3716002Sroot } 3726002Sroot 3739244Ssam fatalperror(f, msg, errno) 3749244Ssam int f; 3759244Ssam char *msg; 3769244Ssam int errno; 3779244Ssam { 3789244Ssam char buf[BUFSIZ]; 3799244Ssam extern char *sys_errlist[]; 3809244Ssam 38117583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3829244Ssam fatal(f, buf); 3839244Ssam } 3849244Ssam 38527185Sminshall 3866002Sroot /* 38727185Sminshall * Check a descriptor to see if out of band data exists on it. 38827185Sminshall */ 38927185Sminshall 39027185Sminshall 39127185Sminshall stilloob(s) 39227185Sminshall int s; /* socket number */ 39327185Sminshall { 39427185Sminshall static struct timeval timeout = { 0 }; 39527185Sminshall fd_set excepts; 39627185Sminshall int value; 39727185Sminshall 39827185Sminshall do { 39927185Sminshall FD_ZERO(&excepts); 40027185Sminshall FD_SET(s, &excepts); 40127185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 40227185Sminshall } while ((value == -1) && (errno = EINTR)); 40327185Sminshall 40427185Sminshall if (value < 0) { 40527185Sminshall fatalperror(pty, "select", errno); 40627185Sminshall } 40727185Sminshall if (FD_ISSET(s, &excepts)) { 40827185Sminshall return 1; 40927185Sminshall } else { 41027185Sminshall return 0; 41127185Sminshall } 41227185Sminshall } 41327185Sminshall 41427185Sminshall /* 4156002Sroot * Main loop. Select from pty and network, and 4166002Sroot * hand data to telnet receiver finite state machine. 4176002Sroot */ 4186002Sroot telnet(f, p) 4196002Sroot { 4206002Sroot int on = 1; 42112713Ssam char hostname[32]; 4226002Sroot 4236002Sroot net = f, pty = p; 4246002Sroot ioctl(f, FIONBIO, &on); 4256002Sroot ioctl(p, FIONBIO, &on); 42627649Sminshall #if defined(SO_OOBINLINE) 42727649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 42827649Sminshall #endif /* defined(SO_OOBINLINE) */ 4296002Sroot signal(SIGTSTP, SIG_IGN); 43013028Ssam signal(SIGCHLD, cleanup); 43126083Slepreau setpgrp(0, 0); 4326002Sroot 4338379Ssam /* 43427185Sminshall * Request to do remote echo and to suppress go ahead. 4358379Ssam */ 4368379Ssam dooption(TELOPT_ECHO); 43727185Sminshall dooption(TELOPT_SGA); 43812713Ssam /* 43927649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 44027649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 44127649Sminshall * 44227649Sminshall * To find out, we send out a "DO ECHO". If the remote system 44327649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 44427649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 44527649Sminshall * WE, the server, sends it; it does NOT mean that the client will 44627649Sminshall * echo the terminal input). 44727649Sminshall */ 44827649Sminshall sprintf(nfrontp, doopt, TELOPT_ECHO); 44927649Sminshall nfrontp += sizeof doopt-2; 45027649Sminshall hisopts[TELOPT_ECHO] = OPT_ALWAYS_LOOK; 45127649Sminshall 45227649Sminshall /* 45312713Ssam * Show banner that getty never gave. 454*27797Sminshall * 455*27797Sminshall * The banner includes some null's (for TELNET CR disambiguation), 456*27797Sminshall * so we have to be somewhat complicated. 45712713Ssam */ 458*27797Sminshall 45912713Ssam gethostname(hostname, sizeof (hostname)); 46027649Sminshall 461*27797Sminshall bcopy(BANNER1, nfrontp, sizeof BANNER1 -1); 462*27797Sminshall nfrontp += sizeof BANNER1 - 1; 463*27797Sminshall bcopy(hostname, nfrontp, strlen(hostname)); 464*27797Sminshall nfrontp += strlen(hostname); 465*27797Sminshall bcopy(BANNER2, nfrontp, sizeof BANNER2 -1); 466*27797Sminshall nfrontp += sizeof BANNER2 - 1; 467*27797Sminshall 46827649Sminshall /* 46927649Sminshall * Call telrcv() once to pick up anything received during 47027649Sminshall * terminal type negotiation. 47127649Sminshall */ 47227649Sminshall telrcv(); 47327649Sminshall 4746002Sroot for (;;) { 47527185Sminshall fd_set ibits, obits, xbits; 4766002Sroot register int c; 4776002Sroot 47827185Sminshall if (ncc < 0 && pcc < 0) 47927185Sminshall break; 48027185Sminshall 48127185Sminshall FD_ZERO(&ibits); 48227185Sminshall FD_ZERO(&obits); 48327185Sminshall FD_ZERO(&xbits); 4846002Sroot /* 4856002Sroot * Never look for input if there's still 4866002Sroot * stuff in the corresponding output buffer 4876002Sroot */ 48827185Sminshall if (nfrontp - nbackp || pcc > 0) { 48927185Sminshall FD_SET(f, &obits); 49027185Sminshall } else { 49127185Sminshall FD_SET(p, &ibits); 49227185Sminshall } 49327185Sminshall if (pfrontp - pbackp || ncc > 0) { 49427185Sminshall FD_SET(p, &obits); 49527185Sminshall } else { 49627185Sminshall FD_SET(f, &ibits); 49727185Sminshall } 49827185Sminshall if (!SYNCHing) { 49927185Sminshall FD_SET(f, &xbits); 50027185Sminshall } 50127185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 50227185Sminshall (struct timeval *)0)) < 1) { 50327185Sminshall if (c == -1) { 50427185Sminshall if (errno == EINTR) { 50527185Sminshall continue; 50627185Sminshall } 50727185Sminshall } 5086002Sroot sleep(5); 5096002Sroot continue; 5106002Sroot } 5116002Sroot 5126002Sroot /* 51327185Sminshall * Any urgent data? 51427185Sminshall */ 51527185Sminshall if (FD_ISSET(net, &xbits)) { 51627185Sminshall SYNCHing = 1; 51727185Sminshall } 51827185Sminshall 51927185Sminshall /* 5206002Sroot * Something to read from the network... 5216002Sroot */ 52227185Sminshall if (FD_ISSET(net, &ibits)) { 52327649Sminshall #if !defined(SO_OOBINLINE) 52427185Sminshall /* 52527185Sminshall * In 4.2 (and some early 4.3) systems, the 52627185Sminshall * OOB indication and data handling in the kernel 52727185Sminshall * is such that if two separate TCP Urgent requests 52827185Sminshall * come in, one byte of TCP data will be overlaid. 52927185Sminshall * This is fatal for Telnet, but we try to live 53027185Sminshall * with it. 53127185Sminshall * 53227185Sminshall * In addition, in 4.2 (and...), a special protocol 53327185Sminshall * is needed to pick up the TCP Urgent data in 53427185Sminshall * the correct sequence. 53527185Sminshall * 53627185Sminshall * What we do is: if we think we are in urgent 53727185Sminshall * mode, we look to see if we are "at the mark". 53827185Sminshall * If we are, we do an OOB receive. If we run 53927185Sminshall * this twice, we will do the OOB receive twice, 54027185Sminshall * but the second will fail, since the second 54127185Sminshall * time we were "at the mark", but there wasn't 54227185Sminshall * any data there (the kernel doesn't reset 54327185Sminshall * "at the mark" until we do a normal read). 54427185Sminshall * Once we've read the OOB data, we go ahead 54527185Sminshall * and do normal reads. 54627185Sminshall * 54727185Sminshall * There is also another problem, which is that 54827185Sminshall * since the OOB byte we read doesn't put us 54927185Sminshall * out of OOB state, and since that byte is most 55027185Sminshall * likely the TELNET DM (data mark), we would 55127185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 55227185Sminshall * So, clocks to the rescue. If we've "just" 55327185Sminshall * received a DM, then we test for the 55427185Sminshall * presence of OOB data when the receive OOB 55527185Sminshall * fails (and AFTER we did the normal mode read 55627185Sminshall * to clear "at the mark"). 55727185Sminshall */ 55827185Sminshall if (SYNCHing) { 55927185Sminshall int atmark; 56027185Sminshall 56127185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 56227185Sminshall if (atmark) { 56327185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 56427185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 56527185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 56627185Sminshall if (clocks.didnetreceive < clocks.gotDM) { 56727185Sminshall SYNCHing = stilloob(net); 56827185Sminshall } 56927185Sminshall } 57027185Sminshall } else { 57127185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5726002Sroot } 57327185Sminshall } else { 57427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 57527185Sminshall } 57627185Sminshall settimer(didnetreceive); 57727649Sminshall #else /* !defined(SO_OOBINLINE)) */ 57827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 57927649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 58027185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 58127185Sminshall ncc = 0; 58227185Sminshall else { 58327185Sminshall if (ncc <= 0) { 58427185Sminshall break; 58527185Sminshall } 58627185Sminshall netip = netibuf; 58727185Sminshall } 5886002Sroot } 5896002Sroot 5906002Sroot /* 5916002Sroot * Something to read from the pty... 5926002Sroot */ 59327185Sminshall if (FD_ISSET(p, &ibits)) { 5946002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5956002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5966002Sroot pcc = 0; 5976002Sroot else { 5986002Sroot if (pcc <= 0) 5996002Sroot break; 6006002Sroot ptyip = ptyibuf; 6016002Sroot } 6026002Sroot } 6036002Sroot 6046002Sroot while (pcc > 0) { 6056002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6066002Sroot break; 6076002Sroot c = *ptyip++ & 0377, pcc--; 6086002Sroot if (c == IAC) 6096002Sroot *nfrontp++ = c; 6106002Sroot *nfrontp++ = c; 61127020Sminshall if (c == '\r') { 61227020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 61327020Sminshall *nfrontp++ = *ptyip++ & 0377; 61427020Sminshall pcc--; 61527020Sminshall } else 61627020Sminshall *nfrontp++ = '\0'; 61727020Sminshall } 6186002Sroot } 61927185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6206002Sroot netflush(); 6216002Sroot if (ncc > 0) 6226002Sroot telrcv(); 62327185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6246002Sroot ptyflush(); 6256002Sroot } 6266002Sroot cleanup(); 6276002Sroot } 6286002Sroot 6296002Sroot /* 6306002Sroot * State for recv fsm 6316002Sroot */ 6326002Sroot #define TS_DATA 0 /* base state */ 6336002Sroot #define TS_IAC 1 /* look for double IAC's */ 6346002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 63527649Sminshall #define TS_SB 3 /* throw away begin's... */ 63627649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6376002Sroot #define TS_WILL 5 /* will option negotiation */ 6386002Sroot #define TS_WONT 6 /* wont " */ 6396002Sroot #define TS_DO 7 /* do " */ 6406002Sroot #define TS_DONT 8 /* dont " */ 6416002Sroot 6426002Sroot telrcv() 6436002Sroot { 6446002Sroot register int c; 6456002Sroot static int state = TS_DATA; 6466002Sroot 6476002Sroot while (ncc > 0) { 6486002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6496002Sroot return; 6506002Sroot c = *netip++ & 0377, ncc--; 6516002Sroot switch (state) { 6526002Sroot 65326090Sminshall case TS_CR: 65426090Sminshall state = TS_DATA; 65526499Sminshall if ((c == 0) || (c == '\n')) { 65626090Sminshall break; 65726499Sminshall } 65826090Sminshall /* FALL THROUGH */ 65926090Sminshall 6606002Sroot case TS_DATA: 6616002Sroot if (c == IAC) { 6626002Sroot state = TS_IAC; 6636002Sroot break; 6646002Sroot } 6656002Sroot if (inter > 0) 6666002Sroot break; 66727020Sminshall /* 66827020Sminshall * We map \r\n ==> \n, since \r\n says 66927020Sminshall * that we want to be in column 1 of the next 67027020Sminshall * printable line, and \n is the standard 67127020Sminshall * unix way of saying that (\r is only good 67227020Sminshall * if CRMOD is set, which it normally is). 67327020Sminshall */ 67427649Sminshall if ((myopts[TELOPT_BINARY] == OPT_DONT) && c == '\r') { 67527020Sminshall if ((ncc > 0) && ('\n' == *netip)) { 67627020Sminshall netip++; ncc--; 67727020Sminshall c = '\n'; 67827020Sminshall } else { 67927020Sminshall state = TS_CR; 68027020Sminshall } 68126499Sminshall } 68226499Sminshall *pfrontp++ = c; 6836002Sroot break; 6846002Sroot 6856002Sroot case TS_IAC: 6866002Sroot switch (c) { 6876002Sroot 6886002Sroot /* 6896002Sroot * Send the process on the pty side an 6906002Sroot * interrupt. Do this with a NULL or 6916002Sroot * interrupt char; depending on the tty mode. 6926002Sroot */ 6936002Sroot case IP: 6946002Sroot interrupt(); 6956002Sroot break; 6966002Sroot 69727229Sminshall case BREAK: 69827229Sminshall sendbrk(); 69927229Sminshall break; 70027229Sminshall 7016002Sroot /* 7026002Sroot * Are You There? 7036002Sroot */ 7046002Sroot case AYT: 70517583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 70617583Ssam nfrontp += 9; 7076002Sroot break; 7086002Sroot 7096002Sroot /* 71027185Sminshall * Abort Output 71127185Sminshall */ 71227185Sminshall case AO: { 71327185Sminshall struct ltchars tmpltc; 71427185Sminshall 71527185Sminshall ptyflush(); /* half-hearted */ 71627185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 71727185Sminshall if (tmpltc.t_flushc != '\377') { 71827185Sminshall *pfrontp++ = tmpltc.t_flushc; 71927185Sminshall } 72027260Sminshall netclear(); /* clear buffer back */ 72127185Sminshall *nfrontp++ = IAC; 72227185Sminshall *nfrontp++ = DM; 72327187Sminshall neturg = nfrontp-1; /* off by one XXX */ 72427185Sminshall break; 72527185Sminshall } 72627185Sminshall 72727185Sminshall /* 7286002Sroot * Erase Character and 7296002Sroot * Erase Line 7306002Sroot */ 7316002Sroot case EC: 73227185Sminshall case EL: { 73327185Sminshall struct sgttyb b; 73427185Sminshall char ch; 7356002Sroot 73627185Sminshall ptyflush(); /* half-hearted */ 73727185Sminshall ioctl(pty, TIOCGETP, &b); 73827185Sminshall ch = (c == EC) ? 73927185Sminshall b.sg_erase : b.sg_kill; 74027185Sminshall if (ch != '\377') { 74127185Sminshall *pfrontp++ = ch; 74227185Sminshall } 74327185Sminshall break; 74427185Sminshall } 74527185Sminshall 7466002Sroot /* 7476002Sroot * Check for urgent data... 7486002Sroot */ 7496002Sroot case DM: 75027185Sminshall SYNCHing = stilloob(net); 75127185Sminshall settimer(gotDM); 7526002Sroot break; 7536002Sroot 75427185Sminshall 7556002Sroot /* 7566002Sroot * Begin option subnegotiation... 7576002Sroot */ 7586002Sroot case SB: 75927649Sminshall state = TS_SB; 7606002Sroot continue; 7616002Sroot 7626002Sroot case WILL: 76327188Sminshall state = TS_WILL; 76427188Sminshall continue; 76527188Sminshall 7666002Sroot case WONT: 76727188Sminshall state = TS_WONT; 76827188Sminshall continue; 76927188Sminshall 7706002Sroot case DO: 77127188Sminshall state = TS_DO; 77227188Sminshall continue; 77327188Sminshall 7746002Sroot case DONT: 77527188Sminshall state = TS_DONT; 7766002Sroot continue; 7776002Sroot 7786002Sroot case IAC: 7796002Sroot *pfrontp++ = c; 7806002Sroot break; 7816002Sroot } 7826002Sroot state = TS_DATA; 7836002Sroot break; 7846002Sroot 78527649Sminshall case TS_SB: 78627649Sminshall if (c == IAC) { 78727649Sminshall state = TS_SE; 78827649Sminshall } else { 78927649Sminshall SB_ACCUM(c); 79027649Sminshall } 7916002Sroot break; 7926002Sroot 79327649Sminshall case TS_SE: 79427649Sminshall if (c != SE) { 79527649Sminshall if (c != IAC) { 79627649Sminshall SB_ACCUM(IAC); 79727649Sminshall } 79827649Sminshall SB_ACCUM(c); 79927649Sminshall state = TS_SB; 80027649Sminshall } else { 80127649Sminshall SB_TERM(); 80227649Sminshall suboption(); /* handle sub-option */ 80327649Sminshall state = TS_DATA; 80427649Sminshall } 8056002Sroot break; 8066002Sroot 8076002Sroot case TS_WILL: 80827649Sminshall if (hisopts[c] != OPT_WILL) 8096002Sroot willoption(c); 8106002Sroot state = TS_DATA; 8116002Sroot continue; 8126002Sroot 8136002Sroot case TS_WONT: 81427649Sminshall if (hisopts[c] != OPT_WONT) 8156002Sroot wontoption(c); 8166002Sroot state = TS_DATA; 8176002Sroot continue; 8186002Sroot 8196002Sroot case TS_DO: 82027649Sminshall if (myopts[c] != OPT_DO) 8216002Sroot dooption(c); 8226002Sroot state = TS_DATA; 8236002Sroot continue; 8246002Sroot 8256002Sroot case TS_DONT: 82627649Sminshall if (myopts[c] != OPT_DONT) { 82727649Sminshall dontoption(c); 8286002Sroot } 8296002Sroot state = TS_DATA; 8306002Sroot continue; 8316002Sroot 8326002Sroot default: 8339218Ssam printf("telnetd: panic state=%d\n", state); 8346002Sroot exit(1); 8356002Sroot } 8366002Sroot } 8376002Sroot } 8386002Sroot 8396002Sroot willoption(option) 8406002Sroot int option; 8416002Sroot { 8426002Sroot char *fmt; 8436002Sroot 8446002Sroot switch (option) { 8456002Sroot 8466002Sroot case TELOPT_BINARY: 8476002Sroot mode(RAW, 0); 84827188Sminshall fmt = doopt; 84927188Sminshall break; 8506002Sroot 8516002Sroot case TELOPT_ECHO: 85227649Sminshall not42 = 0; /* looks like a 4.2 system */ 85327649Sminshall /* 85427649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 85527649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 85627649Sminshall * Kludge upon kludge! 85727649Sminshall */ 85827649Sminshall if (myopts[TELOPT_ECHO] == OPT_DO) { 85927649Sminshall dooption(TELOPT_ECHO); 86027649Sminshall } 86127649Sminshall fmt = dont; 86227188Sminshall break; 8636002Sroot 86427649Sminshall case TELOPT_TTYPE: 8656002Sroot case TELOPT_SGA: 8666002Sroot fmt = doopt; 8676002Sroot break; 8686002Sroot 8696002Sroot case TELOPT_TM: 8706002Sroot fmt = dont; 8716002Sroot break; 8726002Sroot 8736002Sroot default: 8746002Sroot fmt = dont; 8756002Sroot break; 8766002Sroot } 87727188Sminshall if (fmt == doopt) { 87827649Sminshall hisopts[option] = OPT_WILL; 87927188Sminshall } else { 88027649Sminshall hisopts[option] = OPT_WONT; 88127188Sminshall } 8826023Ssam sprintf(nfrontp, fmt, option); 8838379Ssam nfrontp += sizeof (dont) - 2; 8846002Sroot } 8856002Sroot 8866002Sroot wontoption(option) 8876002Sroot int option; 8886002Sroot { 8896002Sroot char *fmt; 8906002Sroot 8916002Sroot switch (option) { 8926002Sroot case TELOPT_ECHO: 89327649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 89427188Sminshall break; 8956002Sroot 8966002Sroot case TELOPT_BINARY: 8976002Sroot mode(0, RAW); 8986002Sroot break; 8996002Sroot } 90027188Sminshall fmt = dont; 90127649Sminshall hisopts[option] = OPT_WONT; 9026002Sroot sprintf(nfrontp, fmt, option); 9038379Ssam nfrontp += sizeof (doopt) - 2; 9046002Sroot } 9056002Sroot 9066002Sroot dooption(option) 9076002Sroot int option; 9086002Sroot { 9096002Sroot char *fmt; 9106002Sroot 9116002Sroot switch (option) { 9126002Sroot 9136002Sroot case TELOPT_TM: 9146002Sroot fmt = wont; 9156002Sroot break; 9166002Sroot 9176002Sroot case TELOPT_ECHO: 9186002Sroot mode(ECHO|CRMOD, 0); 91927188Sminshall fmt = will; 92027188Sminshall break; 9216002Sroot 9226002Sroot case TELOPT_BINARY: 9236002Sroot mode(RAW, 0); 92427188Sminshall fmt = will; 92527188Sminshall break; 9266002Sroot 9276002Sroot case TELOPT_SGA: 9286002Sroot fmt = will; 9296002Sroot break; 9306002Sroot 9316002Sroot default: 9326002Sroot fmt = wont; 9336002Sroot break; 9346002Sroot } 93527188Sminshall if (fmt == will) { 93627649Sminshall myopts[option] = OPT_DO; 93727188Sminshall } else { 93827649Sminshall myopts[option] = OPT_DONT; 93927188Sminshall } 9406002Sroot sprintf(nfrontp, fmt, option); 9418379Ssam nfrontp += sizeof (doopt) - 2; 9426002Sroot } 9436002Sroot 94427649Sminshall 94527649Sminshall dontoption(option) 94627649Sminshall int option; 94727649Sminshall { 94827649Sminshall char *fmt; 94927649Sminshall 95027649Sminshall switch (option) { 95127649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 95227649Sminshall mode(0, ECHO|CRMOD); 95327649Sminshall fmt = wont; 95427649Sminshall break; 95527649Sminshall default: 95627649Sminshall fmt = wont; 95727649Sminshall break; 95827649Sminshall } 95927649Sminshall if (fmt = wont) { 96027649Sminshall myopts[option] = OPT_DONT; 96127649Sminshall } else { 96227649Sminshall myopts[option] = OPT_DO; 96327649Sminshall } 96427649Sminshall sprintf(nfrontp, fmt, option); 96527649Sminshall nfrontp += sizeof (wont) - 2; 96627649Sminshall } 96727649Sminshall 96827649Sminshall /* 96927649Sminshall * suboption() 97027649Sminshall * 97127649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 97227649Sminshall * side. 97327649Sminshall * 97427649Sminshall * Currently we recognize: 97527649Sminshall * 97627649Sminshall * (nothing - we only do terminal type at start-up time) 97727649Sminshall */ 97827649Sminshall 97927649Sminshall suboption() 98027649Sminshall { 98127649Sminshall switch (subbuffer[0]&0xff) { 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