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*27649Sminshall static char sccsid[] = "@(#)telnetd.c 5.14 (Berkeley) 04/30/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> 37*27649Sminshall #include <ctype.h> 389218Ssam 3913798Ssam #define BELL '\07' 4023567Sbloom #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r%s" 416002Sroot 42*27649Sminshall #define OPT_DONT 0 /* don't do this option */ 43*27649Sminshall #define OPT_WONT 0 /* won't do this option */ 44*27649Sminshall #define OPT_DO 1 /* do this option */ 45*27649Sminshall #define OPT_WILL 1 /* will do this option */ 46*27649Sminshall #define OPT_ALWAYS_LOOK 2 /* special case for echo */ 476002Sroot char hisopts[256]; 486002Sroot char myopts[256]; 496002Sroot 506002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 516002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 526002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 536002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 546002Sroot 556002Sroot /* 566002Sroot * I/O data buffers, pointers, and counters. 576002Sroot */ 586002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 59*27649Sminshall 606002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 61*27649Sminshall 626002Sroot char netibuf[BUFSIZ], *netip = netibuf; 63*27649Sminshall #define NIACCUM(c) { *netip++ = c; \ 64*27649Sminshall ncc++; \ 65*27649Sminshall } 66*27649Sminshall 676388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 6827185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 69*27649Sminshall /* the remote system seems to NOT be an old 4.2 */ 70*27649Sminshall int not42 = 1; 71*27649Sminshall 72*27649Sminshall 73*27649Sminshall char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */ 74*27649Sminshall #define SB_CLEAR() subpointer = subbuffer; 75*27649Sminshall #define SB_TERM() subend = subpointer; 76*27649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 77*27649Sminshall *subpointer++ = (c); \ 78*27649Sminshall } 79*27649Sminshall 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 164*27649Sminshall 165*27649Sminshall /* 166*27649Sminshall * Get() 167*27649Sminshall * 168*27649Sminshall * Return next character from file descriptor. 169*27649Sminshall * 170*27649Sminshall * This is not meant to be very efficient, since it is only 171*27649Sminshall * run during startup. 172*27649Sminshall */ 173*27649Sminshall 174*27649Sminshall Get(f) 175*27649Sminshall int f; /* the file descriptor */ 176*27649Sminshall { 177*27649Sminshall char input; 178*27649Sminshall 179*27649Sminshall if (read(f, &input, 1) != 1) { 180*27649Sminshall syslog(LOG_ERR, "read: %m\n"); 181*27649Sminshall exit(1); 182*27649Sminshall } 183*27649Sminshall return input&0xff; 184*27649Sminshall } 185*27649Sminshall 186*27649Sminshall char *terminaltype; 187*27649Sminshall 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; 201*27649Sminshall int c; 202*27649Sminshall int gotterminaltype = 0; 2036002Sroot 204*27649Sminshall /* 205*27649Sminshall * Try to get a terminal type from the foreign host. 206*27649Sminshall */ 207*27649Sminshall 208*27649Sminshall { 209*27649Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 210*27649Sminshall 211*27649Sminshall terminaltype = 0; 212*27649Sminshall if (write(f, sbuf, sizeof sbuf) == -1) { 213*27649Sminshall syslog(LOG_ERR, "write sbuf: %m\n"); 214*27649Sminshall exit(1); 215*27649Sminshall } 216*27649Sminshall for (;;) { /* ugly, but we are VERY early */ 217*27649Sminshall while ((c = Get(f)) != IAC) { 218*27649Sminshall NIACCUM(c); 219*27649Sminshall } 220*27649Sminshall if ((c = Get(f)) == WILL) { 221*27649Sminshall if ((c = Get(f)) == TELOPT_TTYPE) { 222*27649Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, 223*27649Sminshall TELQUAL_SEND, IAC, SE }; 224*27649Sminshall if (write(f, sbbuf, sizeof sbbuf) == -1) { 225*27649Sminshall syslog(LOG_ERR, "write sbbuf: %m\n"); 226*27649Sminshall exit(1); 227*27649Sminshall } 228*27649Sminshall break; 229*27649Sminshall } else { 230*27649Sminshall NIACCUM(IAC); 231*27649Sminshall NIACCUM(WILL); 232*27649Sminshall NIACCUM(c); 233*27649Sminshall } 234*27649Sminshall } else if (c == WONT) { 235*27649Sminshall if ((c = Get(f)) == TELOPT_TTYPE) { 236*27649Sminshall terminaltype = "TERM=network"; 237*27649Sminshall break; 238*27649Sminshall } else { 239*27649Sminshall NIACCUM(IAC); 240*27649Sminshall NIACCUM(WONT); 241*27649Sminshall NIACCUM(c); 242*27649Sminshall } 243*27649Sminshall } else { 244*27649Sminshall NIACCUM(IAC); 245*27649Sminshall NIACCUM(c); 246*27649Sminshall } 247*27649Sminshall } 248*27649Sminshall if (!terminaltype) { 249*27649Sminshall for (;;) { 250*27649Sminshall while ((c = Get(f)) != IAC) { 251*27649Sminshall NIACCUM(c); 252*27649Sminshall } 253*27649Sminshall if ((c = Get(f)) != SB) { 254*27649Sminshall NIACCUM(IAC); 255*27649Sminshall NIACCUM(c); 256*27649Sminshall } else if ((c = Get(f)) != TELOPT_TTYPE) { 257*27649Sminshall NIACCUM(IAC); 258*27649Sminshall NIACCUM(SB); 259*27649Sminshall NIACCUM(c); 260*27649Sminshall } else if ((c = Get(f)) != TELQUAL_IS) { 261*27649Sminshall NIACCUM(IAC); 262*27649Sminshall NIACCUM(SB); 263*27649Sminshall NIACCUM(TELOPT_TTYPE); 264*27649Sminshall NIACCUM(c); 265*27649Sminshall } else { /* Yaaaay! */ 266*27649Sminshall static char terminalname[5+41] = "TERM="; 267*27649Sminshall 268*27649Sminshall terminaltype = terminalname+strlen(terminalname); 269*27649Sminshall 270*27649Sminshall while (terminaltype < 271*27649Sminshall (terminalname + sizeof terminalname-1)) { 272*27649Sminshall if ((c = Get(f)) == IAC) { 273*27649Sminshall if ((c = Get(f)) == SE) { 274*27649Sminshall break; /* done */ 275*27649Sminshall } else { 276*27649Sminshall *terminaltype++ = IAC; /* ? */ 277*27649Sminshall if (isupper(c)) { 278*27649Sminshall c = tolower(c); 279*27649Sminshall } 280*27649Sminshall *terminaltype++ = c; 281*27649Sminshall } 282*27649Sminshall } else { 283*27649Sminshall if (isupper(c)) { 284*27649Sminshall c = tolower(c); 285*27649Sminshall } 286*27649Sminshall *terminaltype++ = c; /* accumulate name */ 287*27649Sminshall } 288*27649Sminshall } 289*27649Sminshall *terminaltype = 0; 290*27649Sminshall terminaltype = terminalname; 291*27649Sminshall gotterminaltype = 1; 292*27649Sminshall break; 293*27649Sminshall } 294*27649Sminshall } 295*27649Sminshall } 296*27649Sminshall envinit[0] = terminaltype; 297*27649Sminshall envinit[1] = 0; 298*27649Sminshall } 299*27649Sminshall 30020188Skarels for (c = 'p'; c <= 's'; c++) { 30120188Skarels struct stat stb; 30220188Skarels 30320188Skarels line = "/dev/ptyXX"; 30420188Skarels line[strlen("/dev/pty")] = c; 30520188Skarels line[strlen("/dev/ptyp")] = '0'; 30620188Skarels if (stat(line, &stb) < 0) 30720188Skarels break; 30817583Ssam for (i = 0; i < 16; i++) { 30920188Skarels line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 31020188Skarels p = open(line, 2); 31117583Ssam if (p > 0) 31217583Ssam goto gotpty; 31317583Ssam } 3146002Sroot } 3159244Ssam fatal(f, "All network ports in use"); 3169244Ssam /*NOTREACHED*/ 3176002Sroot gotpty: 3186002Sroot dup2(f, 0); 31920188Skarels line[strlen("/dev/")] = 't'; 32017583Ssam t = open("/dev/tty", O_RDWR); 3216002Sroot if (t >= 0) { 3226002Sroot ioctl(t, TIOCNOTTY, 0); 3236002Sroot close(t); 3246002Sroot } 32520188Skarels t = open(line, O_RDWR); 3269244Ssam if (t < 0) 32720188Skarels fatalperror(f, line, errno); 3286002Sroot ioctl(t, TIOCGETP, &b); 3296388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 3306002Sroot ioctl(t, TIOCSETP, &b); 3316388Ssam ioctl(p, TIOCGETP, &b); 3328379Ssam b.sg_flags &= ~ECHO; 3336388Ssam ioctl(p, TIOCSETP, &b); 33412683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 33512683Ssam who->sin_family); 33612683Ssam if (hp) 33712683Ssam host = hp->h_name; 33812683Ssam else 33917444Sralph host = inet_ntoa(who->sin_addr); 3409244Ssam if ((i = fork()) < 0) 3419244Ssam fatalperror(f, "fork", errno); 3426002Sroot if (i) 3436002Sroot telnet(f, p); 3446002Sroot close(f); 3456002Sroot close(p); 3466002Sroot dup2(t, 0); 3476002Sroot dup2(t, 1); 3486002Sroot dup2(t, 2); 3496002Sroot close(t); 35013799Ssam environ = envinit; 351*27649Sminshall /* 352*27649Sminshall * -h : pass on name of host. 353*27649Sminshall * -p : don't clobber the environment (so terminal type stays set). 354*27649Sminshall */ 355*27649Sminshall execl("/bin/login", "login", "-h", host, 356*27649Sminshall gotterminaltype ? "-p" : 0, 0); 3579244Ssam fatalperror(f, "/bin/login", errno); 3589244Ssam /*NOTREACHED*/ 3599244Ssam } 3609244Ssam 3619244Ssam fatal(f, msg) 3629244Ssam int f; 3639244Ssam char *msg; 3649244Ssam { 3659244Ssam char buf[BUFSIZ]; 3669244Ssam 36717583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3689244Ssam (void) write(f, buf, strlen(buf)); 3696002Sroot exit(1); 3706002Sroot } 3716002Sroot 3729244Ssam fatalperror(f, msg, errno) 3739244Ssam int f; 3749244Ssam char *msg; 3759244Ssam int errno; 3769244Ssam { 3779244Ssam char buf[BUFSIZ]; 3789244Ssam extern char *sys_errlist[]; 3799244Ssam 38017583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3819244Ssam fatal(f, buf); 3829244Ssam } 3839244Ssam 38427185Sminshall 3856002Sroot /* 38627185Sminshall * Check a descriptor to see if out of band data exists on it. 38727185Sminshall */ 38827185Sminshall 38927185Sminshall 39027185Sminshall stilloob(s) 39127185Sminshall int s; /* socket number */ 39227185Sminshall { 39327185Sminshall static struct timeval timeout = { 0 }; 39427185Sminshall fd_set excepts; 39527185Sminshall int value; 39627185Sminshall 39727185Sminshall do { 39827185Sminshall FD_ZERO(&excepts); 39927185Sminshall FD_SET(s, &excepts); 40027185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 40127185Sminshall } while ((value == -1) && (errno = EINTR)); 40227185Sminshall 40327185Sminshall if (value < 0) { 40427185Sminshall fatalperror(pty, "select", errno); 40527185Sminshall } 40627185Sminshall if (FD_ISSET(s, &excepts)) { 40727185Sminshall return 1; 40827185Sminshall } else { 40927185Sminshall return 0; 41027185Sminshall } 41127185Sminshall } 41227185Sminshall 41327185Sminshall /* 4146002Sroot * Main loop. Select from pty and network, and 4156002Sroot * hand data to telnet receiver finite state machine. 4166002Sroot */ 4176002Sroot telnet(f, p) 4186002Sroot { 4196002Sroot int on = 1; 42012713Ssam char hostname[32]; 4216002Sroot 4226002Sroot net = f, pty = p; 4236002Sroot ioctl(f, FIONBIO, &on); 4246002Sroot ioctl(p, FIONBIO, &on); 425*27649Sminshall #if defined(SO_OOBINLINE) 426*27649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 427*27649Sminshall #endif /* defined(SO_OOBINLINE) */ 4286002Sroot signal(SIGTSTP, SIG_IGN); 42913028Ssam signal(SIGCHLD, cleanup); 43026083Slepreau setpgrp(0, 0); 4316002Sroot 4328379Ssam /* 43327185Sminshall * Request to do remote echo and to suppress go ahead. 4348379Ssam */ 4358379Ssam dooption(TELOPT_ECHO); 43627185Sminshall dooption(TELOPT_SGA); 43712713Ssam /* 438*27649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 439*27649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 440*27649Sminshall * 441*27649Sminshall * To find out, we send out a "DO ECHO". If the remote system 442*27649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 443*27649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 444*27649Sminshall * WE, the server, sends it; it does NOT mean that the client will 445*27649Sminshall * echo the terminal input). 446*27649Sminshall */ 447*27649Sminshall sprintf(nfrontp, doopt, TELOPT_ECHO); 448*27649Sminshall nfrontp += sizeof doopt-2; 449*27649Sminshall hisopts[TELOPT_ECHO] = OPT_ALWAYS_LOOK; 450*27649Sminshall 451*27649Sminshall /* 45212713Ssam * Show banner that getty never gave. 45312713Ssam */ 45412713Ssam gethostname(hostname, sizeof (hostname)); 45512713Ssam sprintf(nfrontp, BANNER, hostname, ""); 45612713Ssam nfrontp += strlen(nfrontp); 457*27649Sminshall 458*27649Sminshall /* 459*27649Sminshall * Call telrcv() once to pick up anything received during 460*27649Sminshall * terminal type negotiation. 461*27649Sminshall */ 462*27649Sminshall telrcv(); 463*27649Sminshall 4646002Sroot for (;;) { 46527185Sminshall fd_set ibits, obits, xbits; 4666002Sroot register int c; 4676002Sroot 46827185Sminshall if (ncc < 0 && pcc < 0) 46927185Sminshall break; 47027185Sminshall 47127185Sminshall FD_ZERO(&ibits); 47227185Sminshall FD_ZERO(&obits); 47327185Sminshall FD_ZERO(&xbits); 4746002Sroot /* 4756002Sroot * Never look for input if there's still 4766002Sroot * stuff in the corresponding output buffer 4776002Sroot */ 47827185Sminshall if (nfrontp - nbackp || pcc > 0) { 47927185Sminshall FD_SET(f, &obits); 48027185Sminshall } else { 48127185Sminshall FD_SET(p, &ibits); 48227185Sminshall } 48327185Sminshall if (pfrontp - pbackp || ncc > 0) { 48427185Sminshall FD_SET(p, &obits); 48527185Sminshall } else { 48627185Sminshall FD_SET(f, &ibits); 48727185Sminshall } 48827185Sminshall if (!SYNCHing) { 48927185Sminshall FD_SET(f, &xbits); 49027185Sminshall } 49127185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 49227185Sminshall (struct timeval *)0)) < 1) { 49327185Sminshall if (c == -1) { 49427185Sminshall if (errno == EINTR) { 49527185Sminshall continue; 49627185Sminshall } 49727185Sminshall } 4986002Sroot sleep(5); 4996002Sroot continue; 5006002Sroot } 5016002Sroot 5026002Sroot /* 50327185Sminshall * Any urgent data? 50427185Sminshall */ 50527185Sminshall if (FD_ISSET(net, &xbits)) { 50627185Sminshall SYNCHing = 1; 50727185Sminshall } 50827185Sminshall 50927185Sminshall /* 5106002Sroot * Something to read from the network... 5116002Sroot */ 51227185Sminshall if (FD_ISSET(net, &ibits)) { 513*27649Sminshall #if !defined(SO_OOBINLINE) 51427185Sminshall /* 51527185Sminshall * In 4.2 (and some early 4.3) systems, the 51627185Sminshall * OOB indication and data handling in the kernel 51727185Sminshall * is such that if two separate TCP Urgent requests 51827185Sminshall * come in, one byte of TCP data will be overlaid. 51927185Sminshall * This is fatal for Telnet, but we try to live 52027185Sminshall * with it. 52127185Sminshall * 52227185Sminshall * In addition, in 4.2 (and...), a special protocol 52327185Sminshall * is needed to pick up the TCP Urgent data in 52427185Sminshall * the correct sequence. 52527185Sminshall * 52627185Sminshall * What we do is: if we think we are in urgent 52727185Sminshall * mode, we look to see if we are "at the mark". 52827185Sminshall * If we are, we do an OOB receive. If we run 52927185Sminshall * this twice, we will do the OOB receive twice, 53027185Sminshall * but the second will fail, since the second 53127185Sminshall * time we were "at the mark", but there wasn't 53227185Sminshall * any data there (the kernel doesn't reset 53327185Sminshall * "at the mark" until we do a normal read). 53427185Sminshall * Once we've read the OOB data, we go ahead 53527185Sminshall * and do normal reads. 53627185Sminshall * 53727185Sminshall * There is also another problem, which is that 53827185Sminshall * since the OOB byte we read doesn't put us 53927185Sminshall * out of OOB state, and since that byte is most 54027185Sminshall * likely the TELNET DM (data mark), we would 54127185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 54227185Sminshall * So, clocks to the rescue. If we've "just" 54327185Sminshall * received a DM, then we test for the 54427185Sminshall * presence of OOB data when the receive OOB 54527185Sminshall * fails (and AFTER we did the normal mode read 54627185Sminshall * to clear "at the mark"). 54727185Sminshall */ 54827185Sminshall if (SYNCHing) { 54927185Sminshall int atmark; 55027185Sminshall 55127185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 55227185Sminshall if (atmark) { 55327185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 55427185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 55527185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 55627185Sminshall if (clocks.didnetreceive < clocks.gotDM) { 55727185Sminshall SYNCHing = stilloob(net); 55827185Sminshall } 55927185Sminshall } 56027185Sminshall } else { 56127185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5626002Sroot } 56327185Sminshall } else { 56427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 56527185Sminshall } 56627185Sminshall settimer(didnetreceive); 567*27649Sminshall #else /* !defined(SO_OOBINLINE)) */ 56827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 569*27649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 57027185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 57127185Sminshall ncc = 0; 57227185Sminshall else { 57327185Sminshall if (ncc <= 0) { 57427185Sminshall break; 57527185Sminshall } 57627185Sminshall netip = netibuf; 57727185Sminshall } 5786002Sroot } 5796002Sroot 5806002Sroot /* 5816002Sroot * Something to read from the pty... 5826002Sroot */ 58327185Sminshall if (FD_ISSET(p, &ibits)) { 5846002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5856002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5866002Sroot pcc = 0; 5876002Sroot else { 5886002Sroot if (pcc <= 0) 5896002Sroot break; 5906002Sroot ptyip = ptyibuf; 5916002Sroot } 5926002Sroot } 5936002Sroot 5946002Sroot while (pcc > 0) { 5956002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 5966002Sroot break; 5976002Sroot c = *ptyip++ & 0377, pcc--; 5986002Sroot if (c == IAC) 5996002Sroot *nfrontp++ = c; 6006002Sroot *nfrontp++ = c; 60127020Sminshall if (c == '\r') { 60227020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 60327020Sminshall *nfrontp++ = *ptyip++ & 0377; 60427020Sminshall pcc--; 60527020Sminshall } else 60627020Sminshall *nfrontp++ = '\0'; 60727020Sminshall } 6086002Sroot } 60927185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6106002Sroot netflush(); 6116002Sroot if (ncc > 0) 6126002Sroot telrcv(); 61327185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6146002Sroot ptyflush(); 6156002Sroot } 6166002Sroot cleanup(); 6176002Sroot } 6186002Sroot 6196002Sroot /* 6206002Sroot * State for recv fsm 6216002Sroot */ 6226002Sroot #define TS_DATA 0 /* base state */ 6236002Sroot #define TS_IAC 1 /* look for double IAC's */ 6246002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 625*27649Sminshall #define TS_SB 3 /* throw away begin's... */ 626*27649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6276002Sroot #define TS_WILL 5 /* will option negotiation */ 6286002Sroot #define TS_WONT 6 /* wont " */ 6296002Sroot #define TS_DO 7 /* do " */ 6306002Sroot #define TS_DONT 8 /* dont " */ 6316002Sroot 6326002Sroot telrcv() 6336002Sroot { 6346002Sroot register int c; 6356002Sroot static int state = TS_DATA; 6366002Sroot 6376002Sroot while (ncc > 0) { 6386002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6396002Sroot return; 6406002Sroot c = *netip++ & 0377, ncc--; 6416002Sroot switch (state) { 6426002Sroot 64326090Sminshall case TS_CR: 64426090Sminshall state = TS_DATA; 64526499Sminshall if ((c == 0) || (c == '\n')) { 64626090Sminshall break; 64726499Sminshall } 64826090Sminshall /* FALL THROUGH */ 64926090Sminshall 6506002Sroot case TS_DATA: 6516002Sroot if (c == IAC) { 6526002Sroot state = TS_IAC; 6536002Sroot break; 6546002Sroot } 6556002Sroot if (inter > 0) 6566002Sroot break; 65727020Sminshall /* 65827020Sminshall * We map \r\n ==> \n, since \r\n says 65927020Sminshall * that we want to be in column 1 of the next 66027020Sminshall * printable line, and \n is the standard 66127020Sminshall * unix way of saying that (\r is only good 66227020Sminshall * if CRMOD is set, which it normally is). 66327020Sminshall */ 664*27649Sminshall if ((myopts[TELOPT_BINARY] == OPT_DONT) && c == '\r') { 66527020Sminshall if ((ncc > 0) && ('\n' == *netip)) { 66627020Sminshall netip++; ncc--; 66727020Sminshall c = '\n'; 66827020Sminshall } else { 66927020Sminshall state = TS_CR; 67027020Sminshall } 67126499Sminshall } 67226499Sminshall *pfrontp++ = c; 6736002Sroot break; 6746002Sroot 6756002Sroot case TS_IAC: 6766002Sroot switch (c) { 6776002Sroot 6786002Sroot /* 6796002Sroot * Send the process on the pty side an 6806002Sroot * interrupt. Do this with a NULL or 6816002Sroot * interrupt char; depending on the tty mode. 6826002Sroot */ 6836002Sroot case IP: 6846002Sroot interrupt(); 6856002Sroot break; 6866002Sroot 68727229Sminshall case BREAK: 68827229Sminshall sendbrk(); 68927229Sminshall break; 69027229Sminshall 6916002Sroot /* 6926002Sroot * Are You There? 6936002Sroot */ 6946002Sroot case AYT: 69517583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 69617583Ssam nfrontp += 9; 6976002Sroot break; 6986002Sroot 6996002Sroot /* 70027185Sminshall * Abort Output 70127185Sminshall */ 70227185Sminshall case AO: { 70327185Sminshall struct ltchars tmpltc; 70427185Sminshall 70527185Sminshall ptyflush(); /* half-hearted */ 70627185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 70727185Sminshall if (tmpltc.t_flushc != '\377') { 70827185Sminshall *pfrontp++ = tmpltc.t_flushc; 70927185Sminshall } 71027260Sminshall netclear(); /* clear buffer back */ 71127185Sminshall *nfrontp++ = IAC; 71227185Sminshall *nfrontp++ = DM; 71327187Sminshall neturg = nfrontp-1; /* off by one XXX */ 71427185Sminshall break; 71527185Sminshall } 71627185Sminshall 71727185Sminshall /* 7186002Sroot * Erase Character and 7196002Sroot * Erase Line 7206002Sroot */ 7216002Sroot case EC: 72227185Sminshall case EL: { 72327185Sminshall struct sgttyb b; 72427185Sminshall char ch; 7256002Sroot 72627185Sminshall ptyflush(); /* half-hearted */ 72727185Sminshall ioctl(pty, TIOCGETP, &b); 72827185Sminshall ch = (c == EC) ? 72927185Sminshall b.sg_erase : b.sg_kill; 73027185Sminshall if (ch != '\377') { 73127185Sminshall *pfrontp++ = ch; 73227185Sminshall } 73327185Sminshall break; 73427185Sminshall } 73527185Sminshall 7366002Sroot /* 7376002Sroot * Check for urgent data... 7386002Sroot */ 7396002Sroot case DM: 74027185Sminshall SYNCHing = stilloob(net); 74127185Sminshall settimer(gotDM); 7426002Sroot break; 7436002Sroot 74427185Sminshall 7456002Sroot /* 7466002Sroot * Begin option subnegotiation... 7476002Sroot */ 7486002Sroot case SB: 749*27649Sminshall state = TS_SB; 7506002Sroot continue; 7516002Sroot 7526002Sroot case WILL: 75327188Sminshall state = TS_WILL; 75427188Sminshall continue; 75527188Sminshall 7566002Sroot case WONT: 75727188Sminshall state = TS_WONT; 75827188Sminshall continue; 75927188Sminshall 7606002Sroot case DO: 76127188Sminshall state = TS_DO; 76227188Sminshall continue; 76327188Sminshall 7646002Sroot case DONT: 76527188Sminshall state = TS_DONT; 7666002Sroot continue; 7676002Sroot 7686002Sroot case IAC: 7696002Sroot *pfrontp++ = c; 7706002Sroot break; 7716002Sroot } 7726002Sroot state = TS_DATA; 7736002Sroot break; 7746002Sroot 775*27649Sminshall case TS_SB: 776*27649Sminshall if (c == IAC) { 777*27649Sminshall state = TS_SE; 778*27649Sminshall } else { 779*27649Sminshall SB_ACCUM(c); 780*27649Sminshall } 7816002Sroot break; 7826002Sroot 783*27649Sminshall case TS_SE: 784*27649Sminshall if (c != SE) { 785*27649Sminshall if (c != IAC) { 786*27649Sminshall SB_ACCUM(IAC); 787*27649Sminshall } 788*27649Sminshall SB_ACCUM(c); 789*27649Sminshall state = TS_SB; 790*27649Sminshall } else { 791*27649Sminshall SB_TERM(); 792*27649Sminshall suboption(); /* handle sub-option */ 793*27649Sminshall state = TS_DATA; 794*27649Sminshall } 7956002Sroot break; 7966002Sroot 7976002Sroot case TS_WILL: 798*27649Sminshall if (hisopts[c] != OPT_WILL) 7996002Sroot willoption(c); 8006002Sroot state = TS_DATA; 8016002Sroot continue; 8026002Sroot 8036002Sroot case TS_WONT: 804*27649Sminshall if (hisopts[c] != OPT_WONT) 8056002Sroot wontoption(c); 8066002Sroot state = TS_DATA; 8076002Sroot continue; 8086002Sroot 8096002Sroot case TS_DO: 810*27649Sminshall if (myopts[c] != OPT_DO) 8116002Sroot dooption(c); 8126002Sroot state = TS_DATA; 8136002Sroot continue; 8146002Sroot 8156002Sroot case TS_DONT: 816*27649Sminshall if (myopts[c] != OPT_DONT) { 817*27649Sminshall dontoption(c); 8186002Sroot } 8196002Sroot state = TS_DATA; 8206002Sroot continue; 8216002Sroot 8226002Sroot default: 8239218Ssam printf("telnetd: panic state=%d\n", state); 8246002Sroot exit(1); 8256002Sroot } 8266002Sroot } 8276002Sroot } 8286002Sroot 8296002Sroot willoption(option) 8306002Sroot int option; 8316002Sroot { 8326002Sroot char *fmt; 8336002Sroot 8346002Sroot switch (option) { 8356002Sroot 8366002Sroot case TELOPT_BINARY: 8376002Sroot mode(RAW, 0); 83827188Sminshall fmt = doopt; 83927188Sminshall break; 8406002Sroot 8416002Sroot case TELOPT_ECHO: 842*27649Sminshall not42 = 0; /* looks like a 4.2 system */ 843*27649Sminshall /* 844*27649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 845*27649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 846*27649Sminshall * Kludge upon kludge! 847*27649Sminshall */ 848*27649Sminshall if (myopts[TELOPT_ECHO] == OPT_DO) { 849*27649Sminshall dooption(TELOPT_ECHO); 850*27649Sminshall } 851*27649Sminshall fmt = dont; 85227188Sminshall break; 8536002Sroot 854*27649Sminshall case TELOPT_TTYPE: 8556002Sroot case TELOPT_SGA: 8566002Sroot fmt = doopt; 8576002Sroot break; 8586002Sroot 8596002Sroot case TELOPT_TM: 8606002Sroot fmt = dont; 8616002Sroot break; 8626002Sroot 8636002Sroot default: 8646002Sroot fmt = dont; 8656002Sroot break; 8666002Sroot } 86727188Sminshall if (fmt == doopt) { 868*27649Sminshall hisopts[option] = OPT_WILL; 86927188Sminshall } else { 870*27649Sminshall hisopts[option] = OPT_WONT; 87127188Sminshall } 8726023Ssam sprintf(nfrontp, fmt, option); 8738379Ssam nfrontp += sizeof (dont) - 2; 8746002Sroot } 8756002Sroot 8766002Sroot wontoption(option) 8776002Sroot int option; 8786002Sroot { 8796002Sroot char *fmt; 8806002Sroot 8816002Sroot switch (option) { 8826002Sroot case TELOPT_ECHO: 883*27649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 88427188Sminshall break; 8856002Sroot 8866002Sroot case TELOPT_BINARY: 8876002Sroot mode(0, RAW); 8886002Sroot break; 8896002Sroot } 89027188Sminshall fmt = dont; 891*27649Sminshall hisopts[option] = OPT_WONT; 8926002Sroot sprintf(nfrontp, fmt, option); 8938379Ssam nfrontp += sizeof (doopt) - 2; 8946002Sroot } 8956002Sroot 8966002Sroot dooption(option) 8976002Sroot int option; 8986002Sroot { 8996002Sroot char *fmt; 9006002Sroot 9016002Sroot switch (option) { 9026002Sroot 9036002Sroot case TELOPT_TM: 9046002Sroot fmt = wont; 9056002Sroot break; 9066002Sroot 9076002Sroot case TELOPT_ECHO: 9086002Sroot mode(ECHO|CRMOD, 0); 90927188Sminshall fmt = will; 91027188Sminshall break; 9116002Sroot 9126002Sroot case TELOPT_BINARY: 9136002Sroot mode(RAW, 0); 91427188Sminshall fmt = will; 91527188Sminshall break; 9166002Sroot 9176002Sroot case TELOPT_SGA: 9186002Sroot fmt = will; 9196002Sroot break; 9206002Sroot 9216002Sroot default: 9226002Sroot fmt = wont; 9236002Sroot break; 9246002Sroot } 92527188Sminshall if (fmt == will) { 926*27649Sminshall myopts[option] = OPT_DO; 92727188Sminshall } else { 928*27649Sminshall myopts[option] = OPT_DONT; 92927188Sminshall } 9306002Sroot sprintf(nfrontp, fmt, option); 9318379Ssam nfrontp += sizeof (doopt) - 2; 9326002Sroot } 9336002Sroot 934*27649Sminshall 935*27649Sminshall dontoption(option) 936*27649Sminshall int option; 937*27649Sminshall { 938*27649Sminshall char *fmt; 939*27649Sminshall 940*27649Sminshall switch (option) { 941*27649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 942*27649Sminshall mode(0, ECHO|CRMOD); 943*27649Sminshall fmt = wont; 944*27649Sminshall break; 945*27649Sminshall default: 946*27649Sminshall fmt = wont; 947*27649Sminshall break; 948*27649Sminshall } 949*27649Sminshall if (fmt = wont) { 950*27649Sminshall myopts[option] = OPT_DONT; 951*27649Sminshall } else { 952*27649Sminshall myopts[option] = OPT_DO; 953*27649Sminshall } 954*27649Sminshall sprintf(nfrontp, fmt, option); 955*27649Sminshall nfrontp += sizeof (wont) - 2; 956*27649Sminshall } 957*27649Sminshall 958*27649Sminshall /* 959*27649Sminshall * suboption() 960*27649Sminshall * 961*27649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 962*27649Sminshall * side. 963*27649Sminshall * 964*27649Sminshall * Currently we recognize: 965*27649Sminshall * 966*27649Sminshall * (nothing - we only do terminal type at start-up time) 967*27649Sminshall */ 968*27649Sminshall 969*27649Sminshall suboption() 970*27649Sminshall { 971*27649Sminshall switch (subbuffer[0]&0xff) { 972*27649Sminshall default: 973*27649Sminshall ; 974*27649Sminshall } 975*27649Sminshall } 976*27649Sminshall 9776002Sroot mode(on, off) 9786002Sroot int on, off; 9796002Sroot { 9806002Sroot struct sgttyb b; 9816002Sroot 9826002Sroot ptyflush(); 9836002Sroot ioctl(pty, TIOCGETP, &b); 9846002Sroot b.sg_flags |= on; 9856002Sroot b.sg_flags &= ~off; 9866002Sroot ioctl(pty, TIOCSETP, &b); 9876002Sroot } 9886002Sroot 9896002Sroot /* 9906002Sroot * Send interrupt to process on other side of pty. 9916002Sroot * If it is in raw mode, just write NULL; 9926002Sroot * otherwise, write intr char. 9936002Sroot */ 9946002Sroot interrupt() 9956002Sroot { 9966002Sroot struct sgttyb b; 9976002Sroot struct tchars tchars; 9986002Sroot 9996002Sroot ptyflush(); /* half-hearted */ 10006002Sroot ioctl(pty, TIOCGETP, &b); 10016002Sroot if (b.sg_flags & RAW) { 10026002Sroot *pfrontp++ = '\0'; 10036002Sroot return; 10046002Sroot } 10056002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10066002Sroot '\177' : tchars.t_intrc; 10076002Sroot } 10086002Sroot 100927229Sminshall /* 101027229Sminshall * Send quit to process on other side of pty. 101127229Sminshall * If it is in raw mode, just write NULL; 101227229Sminshall * otherwise, write quit char. 101327229Sminshall */ 101427229Sminshall sendbrk() 101527229Sminshall { 101627229Sminshall struct sgttyb b; 101727229Sminshall struct tchars tchars; 101827229Sminshall 101927229Sminshall ptyflush(); /* half-hearted */ 102027229Sminshall ioctl(pty, TIOCGETP, &b); 102127229Sminshall if (b.sg_flags & RAW) { 102227229Sminshall *pfrontp++ = '\0'; 102327229Sminshall return; 102427229Sminshall } 102527229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 102627229Sminshall '\034' : tchars.t_quitc; 102727229Sminshall } 102827229Sminshall 10296002Sroot ptyflush() 10306002Sroot { 10316002Sroot int n; 10326002Sroot 10336002Sroot if ((n = pfrontp - pbackp) > 0) 10346002Sroot n = write(pty, pbackp, n); 10358346Ssam if (n < 0) 10368346Ssam return; 10376002Sroot pbackp += n; 10386002Sroot if (pbackp == pfrontp) 10396002Sroot pbackp = pfrontp = ptyobuf; 10406002Sroot } 104127260Sminshall 104227260Sminshall /* 104327260Sminshall * nextitem() 104427260Sminshall * 104527260Sminshall * Return the address of the next "item" in the TELNET data 104627260Sminshall * stream. This will be the address of the next character if 104727260Sminshall * the current address is a user data character, or it will 104827260Sminshall * be the address of the character following the TELNET command 104927260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 105027260Sminshall * character. 105127260Sminshall */ 10526002Sroot 105327260Sminshall char * 105427260Sminshall nextitem(current) 105527260Sminshall char *current; 10566002Sroot { 105727260Sminshall if ((*current&0xff) != IAC) { 105827260Sminshall return current+1; 105927260Sminshall } 106027260Sminshall switch (*(current+1)&0xff) { 106127260Sminshall case DO: 106227260Sminshall case DONT: 106327260Sminshall case WILL: 106427260Sminshall case WONT: 106527260Sminshall return current+3; 106627260Sminshall case SB: /* loop forever looking for the SE */ 106727260Sminshall { 106827260Sminshall register char *look = current+2; 10696002Sroot 107027260Sminshall for (;;) { 107127260Sminshall if ((*look++&0xff) == IAC) { 107227260Sminshall if ((*look++&0xff) == SE) { 107327260Sminshall return look; 107427260Sminshall } 107527260Sminshall } 107627260Sminshall } 10778346Ssam } 107827260Sminshall default: 107927260Sminshall return current+2; 108027260Sminshall } 10816002Sroot } 10826002Sroot 108327185Sminshall 108427185Sminshall /* 108527260Sminshall * netclear() 108627260Sminshall * 108727260Sminshall * We are about to do a TELNET SYNCH operation. Clear 108827260Sminshall * the path to the network. 108927260Sminshall * 109027260Sminshall * Things are a bit tricky since we may have sent the first 109127260Sminshall * byte or so of a previous TELNET command into the network. 109227260Sminshall * So, we have to scan the network buffer from the beginning 109327260Sminshall * until we are up to where we want to be. 109427260Sminshall * 109527260Sminshall * A side effect of what we do, just to keep things 109627260Sminshall * simple, is to clear the urgent data pointer. The principal 109727260Sminshall * caller should be setting the urgent data pointer AFTER calling 109827260Sminshall * us in any case. 109927260Sminshall */ 110027260Sminshall 110127260Sminshall netclear() 110227260Sminshall { 110327260Sminshall register char *thisitem, *next; 110427260Sminshall char *good; 110527260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 110627260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 110727260Sminshall 110827260Sminshall thisitem = netobuf; 110927260Sminshall 111027260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 111127260Sminshall thisitem = next; 111227260Sminshall } 111327260Sminshall 111427260Sminshall /* Now, thisitem is first before/at boundary. */ 111527260Sminshall 111627260Sminshall good = netobuf; /* where the good bytes go */ 111727260Sminshall 111827260Sminshall while (nfrontp > thisitem) { 111927260Sminshall if (wewant(thisitem)) { 112027260Sminshall int length; 112127260Sminshall 112227260Sminshall next = thisitem; 112327260Sminshall do { 112427260Sminshall next = nextitem(next); 112527260Sminshall } while (wewant(next) && (nfrontp > next)); 112627260Sminshall length = next-thisitem; 112727260Sminshall bcopy(thisitem, good, length); 112827260Sminshall good += length; 112927260Sminshall thisitem = next; 113027260Sminshall } else { 113127260Sminshall thisitem = nextitem(thisitem); 113227260Sminshall } 113327260Sminshall } 113427260Sminshall 113527260Sminshall nbackp = netobuf; 113627260Sminshall nfrontp = good; /* next byte to be sent */ 113727260Sminshall neturg = 0; 113827260Sminshall } 113927260Sminshall 114027260Sminshall /* 114127185Sminshall * netflush 114227185Sminshall * Send as much data as possible to the network, 114327185Sminshall * handling requests for urgent data. 114427185Sminshall */ 114527185Sminshall 114627185Sminshall 114727185Sminshall netflush() 114827185Sminshall { 114927185Sminshall int n; 115027185Sminshall 115127185Sminshall if ((n = nfrontp - nbackp) > 0) { 1152*27649Sminshall /* 1153*27649Sminshall * if no urgent data, or if the other side appears to be an 1154*27649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 1155*27649Sminshall * write the entire buffer in non-OOB mode. 1156*27649Sminshall */ 1157*27649Sminshall if ((neturg == 0) || (not42 == 0)) { 115827185Sminshall n = write(net, nbackp, n); /* normal write */ 115927185Sminshall } else { 116027185Sminshall n = neturg - nbackp; 116127185Sminshall /* 116227185Sminshall * In 4.2 (and 4.3) systems, there is some question about 116327185Sminshall * what byte in a sendOOB operation is the "OOB" data. 116427185Sminshall * To make ourselves compatible, we only send ONE byte 116527185Sminshall * out of band, the one WE THINK should be OOB (though 116627185Sminshall * we really have more the TCP philosophy of urgent data 116727185Sminshall * rather than the Unix philosophy of OOB data). 116827185Sminshall */ 116927185Sminshall if (n > 1) { 117027185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 117127185Sminshall } else { 117227185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 117327185Sminshall } 117427185Sminshall } 117527185Sminshall } 117627185Sminshall if (n < 0) { 117727185Sminshall if (errno == EWOULDBLOCK) 117827185Sminshall return; 117927185Sminshall /* should blow this guy away... */ 118027185Sminshall return; 118127185Sminshall } 118227185Sminshall nbackp += n; 118327185Sminshall if (nbackp >= neturg) { 118427185Sminshall neturg = 0; 118527185Sminshall } 118627185Sminshall if (nbackp == nfrontp) { 118727185Sminshall nbackp = nfrontp = netobuf; 118827185Sminshall } 118927185Sminshall } 119027185Sminshall 11916002Sroot cleanup() 11926002Sroot { 11936002Sroot 11946002Sroot rmut(); 119510008Ssam vhangup(); /* XXX */ 119610191Ssam shutdown(net, 2); 11976002Sroot exit(1); 11986002Sroot } 11996002Sroot 12006002Sroot #include <utmp.h> 12016002Sroot 12026002Sroot struct utmp wtmp; 12036002Sroot char wtmpf[] = "/usr/adm/wtmp"; 120423567Sbloom char utmpf[] = "/etc/utmp"; 120523567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 120623567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12076002Sroot 12086002Sroot rmut() 12096002Sroot { 12106002Sroot register f; 12116002Sroot int found = 0; 121223567Sbloom struct utmp *u, *utmp; 121323567Sbloom int nutmp; 121423567Sbloom struct stat statbf; 12156002Sroot 121623567Sbloom f = open(utmpf, O_RDWR); 12176002Sroot if (f >= 0) { 121823567Sbloom fstat(f, &statbf); 121923567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 122023567Sbloom if (!utmp) 122123567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 122223567Sbloom if (statbf.st_size && utmp) { 122323567Sbloom nutmp = read(f, utmp, statbf.st_size); 122423567Sbloom nutmp /= sizeof(struct utmp); 122523567Sbloom 122623567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 122723567Sbloom if (SCMPN(u->ut_line, line+5) || 122823567Sbloom u->ut_name[0]==0) 122923567Sbloom continue; 123023567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 123123567Sbloom SCPYN(u->ut_name, ""); 123223567Sbloom SCPYN(u->ut_host, ""); 123323567Sbloom time(&u->ut_time); 123423567Sbloom write(f, (char *)u, sizeof(wtmp)); 123523567Sbloom found++; 123623567Sbloom } 12376002Sroot } 12386002Sroot close(f); 12396002Sroot } 12406002Sroot if (found) { 124117583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12426002Sroot if (f >= 0) { 12436002Sroot SCPYN(wtmp.ut_line, line+5); 12446002Sroot SCPYN(wtmp.ut_name, ""); 124512683Ssam SCPYN(wtmp.ut_host, ""); 12466002Sroot time(&wtmp.ut_time); 124723567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 12486002Sroot close(f); 12496002Sroot } 12506002Sroot } 12516002Sroot chmod(line, 0666); 12526002Sroot chown(line, 0, 0); 12536002Sroot line[strlen("/dev/")] = 'p'; 12546002Sroot chmod(line, 0666); 12556002Sroot chown(line, 0, 0); 12566002Sroot } 1257