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*33271Sminshall static char sccsid[] = "@(#)telnetd.c 5.25 (Berkeley) 01/05/88"; 1521182Sdist #endif not lint 1621182Sdist 176002Sroot /* 1827898Skarels * Telnet server. 196002Sroot */ 2027898Skarels #include <sys/param.h> 219218Ssam #include <sys/socket.h> 2213608Ssam #include <sys/wait.h> 2317583Ssam #include <sys/file.h> 2420188Skarels #include <sys/stat.h> 2527185Sminshall #include <sys/time.h> 269218Ssam 279218Ssam #include <netinet/in.h> 289218Ssam 2912216Ssam #include <arpa/telnet.h> 3012216Ssam 316002Sroot #include <stdio.h> 326002Sroot #include <signal.h> 336002Sroot #include <errno.h> 346002Sroot #include <sgtty.h> 358346Ssam #include <netdb.h> 3617187Sralph #include <syslog.h> 3727649Sminshall #include <ctype.h> 389218Ssam 3927983Sminshall #define OPT_NO 0 /* won't do this option */ 4027983Sminshall #define OPT_YES 1 /* will do this option */ 4127983Sminshall #define OPT_YES_BUT_ALWAYS_LOOK 2 4227983Sminshall #define OPT_NO_BUT_ALWAYS_LOOK 3 436002Sroot char hisopts[256]; 446002Sroot char myopts[256]; 456002Sroot 466002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 476002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 486002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 496002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 506002Sroot 516002Sroot /* 526002Sroot * I/O data buffers, pointers, and counters. 536002Sroot */ 546002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 5527649Sminshall 566002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 5727649Sminshall 586002Sroot char netibuf[BUFSIZ], *netip = netibuf; 5927649Sminshall #define NIACCUM(c) { *netip++ = c; \ 6027649Sminshall ncc++; \ 6127649Sminshall } 6227649Sminshall 636388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 6427185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 6527649Sminshall /* the remote system seems to NOT be an old 4.2 */ 6627649Sminshall int not42 = 1; 6727649Sminshall 68*33271Sminshall #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r" 6927649Sminshall 7027983Sminshall /* buffer for sub-options */ 7127983Sminshall char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 7227649Sminshall #define SB_CLEAR() subpointer = subbuffer; 7327983Sminshall #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 7427649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 7527649Sminshall *subpointer++ = (c); \ 7627649Sminshall } 7727983Sminshall #define SB_GET() ((*subpointer++)&0xff) 7827983Sminshall #define SB_EOF() (subpointer >= subend) 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 */ 9927983Sminshall ttypeopt, /* ttype will/won't received */ 10027983Sminshall ttypesubopt, /* ttype subopt is received */ 10127983Sminshall getterminal, /* time started to get terminal information */ 10227185Sminshall gotDM; /* when did we last see a data mark */ 10327185Sminshall } clocks; 10427185Sminshall 10527983Sminshall #define settimer(x) (clocks.x = ++clocks.system) 10627983Sminshall #define sequenceIs(x,y) (clocks.x < clocks.y) 10727185Sminshall 1086002Sroot main(argc, argv) 1096002Sroot char *argv[]; 1106002Sroot { 11116371Skarels struct sockaddr_in from; 11217156Ssam int on = 1, fromlen; 1136002Sroot 11427185Sminshall #if defined(DEBUG) 11527185Sminshall { 11627185Sminshall int s, ns, foo; 11727185Sminshall struct servent *sp; 11827185Sminshall static struct sockaddr_in sin = { AF_INET }; 11927185Sminshall 12027185Sminshall sp = getservbyname("telnet", "tcp"); 12127185Sminshall if (sp == 0) { 12227185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 12327185Sminshall exit(1); 12427185Sminshall } 12527185Sminshall sin.sin_port = sp->s_port; 12627185Sminshall argc--, argv++; 12727185Sminshall if (argc > 0) { 12827185Sminshall sin.sin_port = atoi(*argv); 12927185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 13027185Sminshall } 13127185Sminshall 13227185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 13327185Sminshall if (s < 0) { 13427185Sminshall perror("telnetd: socket");; 13527185Sminshall exit(1); 13627185Sminshall } 13727185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 13827185Sminshall perror("bind"); 13927185Sminshall exit(1); 14027185Sminshall } 14127185Sminshall if (listen(s, 1) < 0) { 14227185Sminshall perror("listen"); 14327185Sminshall exit(1); 14427185Sminshall } 14527185Sminshall foo = sizeof sin; 14627185Sminshall ns = accept(s, &sin, &foo); 14727185Sminshall if (ns < 0) { 14827185Sminshall perror("accept"); 14927185Sminshall exit(1); 15027185Sminshall } 15127185Sminshall dup2(ns, 0); 15227185Sminshall close(s); 15327185Sminshall } 15427185Sminshall #endif /* defined(DEBUG) */ 15524855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 15616371Skarels fromlen = sizeof (from); 15716371Skarels if (getpeername(0, &from, &fromlen) < 0) { 15816371Skarels fprintf(stderr, "%s: ", argv[0]); 15916371Skarels perror("getpeername"); 16016371Skarels _exit(1); 1618346Ssam } 16217156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 16317187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 16410418Ssam } 16516371Skarels doit(0, &from); 1666002Sroot } 1676002Sroot 16827983Sminshall char *terminaltype = 0; 16927983Sminshall char *envinit[2]; 17027983Sminshall int cleanup(); 17127649Sminshall 17227649Sminshall /* 17327983Sminshall * ttloop 17427649Sminshall * 17527983Sminshall * A small subroutine to flush the network output buffer, get some data 17627983Sminshall * from the network, and pass it through the telnet state machine. We 17727983Sminshall * also flush the pty input buffer (by dropping its data) if it becomes 17827983Sminshall * too full. 17927983Sminshall */ 18027983Sminshall 18127983Sminshall void 18227983Sminshall ttloop() 18327983Sminshall { 18427983Sminshall if (nfrontp-nbackp) { 18527983Sminshall netflush(); 18627983Sminshall } 18727983Sminshall ncc = read(net, netibuf, sizeof netibuf); 18827983Sminshall if (ncc < 0) { 18927983Sminshall syslog(LOG_INFO, "ttloop: read: %m\n"); 19028044Sminshall exit(1); 19128044Sminshall } else if (ncc == 0) { 19228044Sminshall syslog(LOG_INFO, "ttloop: peer died: %m\n"); 19328044Sminshall exit(1); 19427983Sminshall } 19527983Sminshall netip = netibuf; 19627983Sminshall telrcv(); /* state machine */ 19727983Sminshall if (ncc > 0) { 19827983Sminshall pfrontp = pbackp = ptyobuf; 19927983Sminshall telrcv(); 20027983Sminshall } 20127983Sminshall } 20227983Sminshall 20327983Sminshall /* 20427983Sminshall * getterminaltype 20527649Sminshall * 20627983Sminshall * Ask the other end to send along its terminal type. 20727983Sminshall * Output is the variable terminaltype filled in. 20827649Sminshall */ 20927649Sminshall 21027983Sminshall void 21127983Sminshall getterminaltype() 21227649Sminshall { 21327983Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 21427649Sminshall 21527983Sminshall settimer(getterminal); 21627983Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 21727983Sminshall nfrontp += sizeof sbuf; 21828044Sminshall hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; 21927983Sminshall while (sequenceIs(ttypeopt, getterminal)) { 22027983Sminshall ttloop(); 22127649Sminshall } 22227983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES) { 22327983Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 22427983Sminshall 22527983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 22627983Sminshall nfrontp += sizeof sbbuf; 22727983Sminshall while (sequenceIs(ttypesubopt, getterminal)) { 22827983Sminshall ttloop(); 22927983Sminshall } 23027983Sminshall } 23127649Sminshall } 23227649Sminshall 2336002Sroot /* 2346002Sroot * Get a pty, scan input lines. 2356002Sroot */ 23612683Ssam doit(f, who) 23712683Ssam int f; 23812683Ssam struct sockaddr_in *who; 2396002Sroot { 24020188Skarels char *host, *inet_ntoa(); 24117583Ssam int i, p, t; 2426002Sroot struct sgttyb b; 24312683Ssam struct hostent *hp; 24427649Sminshall int c; 2456002Sroot 24620188Skarels for (c = 'p'; c <= 's'; c++) { 24720188Skarels struct stat stb; 24820188Skarels 24920188Skarels line = "/dev/ptyXX"; 25020188Skarels line[strlen("/dev/pty")] = c; 25120188Skarels line[strlen("/dev/ptyp")] = '0'; 25220188Skarels if (stat(line, &stb) < 0) 25320188Skarels break; 25417583Ssam for (i = 0; i < 16; i++) { 25520188Skarels line[strlen("/dev/ptyp")] = "0123456789abcdef"[i]; 25620188Skarels p = open(line, 2); 25717583Ssam if (p > 0) 25817583Ssam goto gotpty; 25917583Ssam } 2606002Sroot } 2619244Ssam fatal(f, "All network ports in use"); 2629244Ssam /*NOTREACHED*/ 2636002Sroot gotpty: 2646002Sroot dup2(f, 0); 26520188Skarels line[strlen("/dev/")] = 't'; 26617583Ssam t = open("/dev/tty", O_RDWR); 2676002Sroot if (t >= 0) { 2686002Sroot ioctl(t, TIOCNOTTY, 0); 2696002Sroot close(t); 2706002Sroot } 27120188Skarels t = open(line, O_RDWR); 2729244Ssam if (t < 0) 27320188Skarels fatalperror(f, line, errno); 2746002Sroot ioctl(t, TIOCGETP, &b); 2756388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 2766002Sroot ioctl(t, TIOCSETP, &b); 2776388Ssam ioctl(p, TIOCGETP, &b); 2788379Ssam b.sg_flags &= ~ECHO; 2796388Ssam ioctl(p, TIOCSETP, &b); 28012683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 28112683Ssam who->sin_family); 28212683Ssam if (hp) 28312683Ssam host = hp->h_name; 28412683Ssam else 28517444Sralph host = inet_ntoa(who->sin_addr); 28627983Sminshall 28727983Sminshall net = f; 28827983Sminshall pty = p; 28927983Sminshall 29027983Sminshall /* 29127983Sminshall * get terminal type. 29227983Sminshall */ 29327983Sminshall getterminaltype(); 29427983Sminshall 2959244Ssam if ((i = fork()) < 0) 2969244Ssam fatalperror(f, "fork", errno); 2976002Sroot if (i) 2986002Sroot telnet(f, p); 2996002Sroot close(f); 3006002Sroot close(p); 3016002Sroot dup2(t, 0); 3026002Sroot dup2(t, 1); 3036002Sroot dup2(t, 2); 3046002Sroot close(t); 30527983Sminshall envinit[0] = terminaltype; 30627983Sminshall envinit[1] = 0; 30713799Ssam environ = envinit; 30827649Sminshall /* 30927649Sminshall * -h : pass on name of host. 31027983Sminshall * WARNING: -h is accepted by login if and only if 31127983Sminshall * getuid() == 0. 31227649Sminshall * -p : don't clobber the environment (so terminal type stays set). 31327649Sminshall */ 31427649Sminshall execl("/bin/login", "login", "-h", host, 31527983Sminshall terminaltype ? "-p" : 0, 0); 3169244Ssam fatalperror(f, "/bin/login", errno); 3179244Ssam /*NOTREACHED*/ 3189244Ssam } 3199244Ssam 3209244Ssam fatal(f, msg) 3219244Ssam int f; 3229244Ssam char *msg; 3239244Ssam { 3249244Ssam char buf[BUFSIZ]; 3259244Ssam 32617583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3279244Ssam (void) write(f, buf, strlen(buf)); 3286002Sroot exit(1); 3296002Sroot } 3306002Sroot 3319244Ssam fatalperror(f, msg, errno) 3329244Ssam int f; 3339244Ssam char *msg; 3349244Ssam int errno; 3359244Ssam { 3369244Ssam char buf[BUFSIZ]; 3379244Ssam extern char *sys_errlist[]; 3389244Ssam 33917583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3409244Ssam fatal(f, buf); 3419244Ssam } 3429244Ssam 34327185Sminshall 3446002Sroot /* 34527185Sminshall * Check a descriptor to see if out of band data exists on it. 34627185Sminshall */ 34727185Sminshall 34827185Sminshall 34927185Sminshall stilloob(s) 35027185Sminshall int s; /* socket number */ 35127185Sminshall { 35227185Sminshall static struct timeval timeout = { 0 }; 35327185Sminshall fd_set excepts; 35427185Sminshall int value; 35527185Sminshall 35627185Sminshall do { 35727185Sminshall FD_ZERO(&excepts); 35827185Sminshall FD_SET(s, &excepts); 35927185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 36027898Skarels } while ((value == -1) && (errno == EINTR)); 36127185Sminshall 36227185Sminshall if (value < 0) { 36327185Sminshall fatalperror(pty, "select", errno); 36427185Sminshall } 36527185Sminshall if (FD_ISSET(s, &excepts)) { 36627185Sminshall return 1; 36727185Sminshall } else { 36827185Sminshall return 0; 36927185Sminshall } 37027185Sminshall } 37127185Sminshall 37227185Sminshall /* 3736002Sroot * Main loop. Select from pty and network, and 3746002Sroot * hand data to telnet receiver finite state machine. 3756002Sroot */ 3766002Sroot telnet(f, p) 3776002Sroot { 3786002Sroot int on = 1; 37927898Skarels char hostname[MAXHOSTNAMELEN]; 380*33271Sminshall #define TABBUFSIZ 512 381*33271Sminshall char defent[TABBUFSIZ]; 382*33271Sminshall char defstrs[TABBUFSIZ]; 383*33271Sminshall #undef TABBUFSIZ 384*33271Sminshall char *HE; 385*33271Sminshall char *HN; 386*33271Sminshall char *IM; 3876002Sroot 3886002Sroot ioctl(f, FIONBIO, &on); 3896002Sroot ioctl(p, FIONBIO, &on); 39033267Sminshall ioctl(p, TIOCPKT, &on); 39127649Sminshall #if defined(SO_OOBINLINE) 39227649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 39327649Sminshall #endif /* defined(SO_OOBINLINE) */ 3946002Sroot signal(SIGTSTP, SIG_IGN); 39532400Sminshall /* 39632400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 39732400Sminshall * in ttioctl() in /sys/tty.c. 39832400Sminshall */ 39932400Sminshall signal(SIGTTOU, SIG_IGN); 40013028Ssam signal(SIGCHLD, cleanup); 40126083Slepreau setpgrp(0, 0); 4026002Sroot 4038379Ssam /* 40427185Sminshall * Request to do remote echo and to suppress go ahead. 4058379Ssam */ 40627983Sminshall if (!myopts[TELOPT_ECHO]) { 40727983Sminshall dooption(TELOPT_ECHO); 40827983Sminshall } 40927983Sminshall if (!myopts[TELOPT_SGA]) { 41027983Sminshall dooption(TELOPT_SGA); 41127983Sminshall } 41212713Ssam /* 41327649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 41427649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 41527649Sminshall * 41627649Sminshall * To find out, we send out a "DO ECHO". If the remote system 41727649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 41827649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 41927649Sminshall * WE, the server, sends it; it does NOT mean that the client will 42027649Sminshall * echo the terminal input). 42127649Sminshall */ 42232452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 42327649Sminshall nfrontp += sizeof doopt-2; 42427983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 42527649Sminshall 42627649Sminshall /* 42712713Ssam * Show banner that getty never gave. 42827797Sminshall * 429*33271Sminshall * We put the banner in the pty input buffer. This way, it 430*33271Sminshall * gets carriage return null processing, etc., just like all 431*33271Sminshall * other pty --> client data. 43212713Ssam */ 43327797Sminshall 43412713Ssam gethostname(hostname, sizeof (hostname)); 435*33271Sminshall if (getent(defent, "default") == 1) { 436*33271Sminshall char *getstr(); 437*33271Sminshall char *p=defstrs; 43827649Sminshall 439*33271Sminshall HE = getstr("he", &p); 440*33271Sminshall HN = getstr("hn", &p); 441*33271Sminshall IM = getstr("im", &p); 442*33271Sminshall if (HN && *HN) 443*33271Sminshall strcpy(hostname, HN); 444*33271Sminshall edithost(HE, hostname); 445*33271Sminshall if (IM && *IM) 446*33271Sminshall putf(IM, ptyibuf+1, p); 447*33271Sminshall } else { 448*33271Sminshall sprintf(ptyibuf+1, BANNER, hostname); 449*33271Sminshall } 45027797Sminshall 451*33271Sminshall ptyip = ptyibuf+1; /* Prime the pump */ 452*33271Sminshall pcc = strlen(ptyip); /* ditto */ 453*33271Sminshall 45433267Sminshall /* Clear ptybuf[0] - where the packet information is received */ 45533267Sminshall ptyibuf[0] = 0; 456*33271Sminshall 45727649Sminshall /* 45827649Sminshall * Call telrcv() once to pick up anything received during 45927649Sminshall * terminal type negotiation. 46027649Sminshall */ 46127649Sminshall telrcv(); 46227649Sminshall 4636002Sroot for (;;) { 46427185Sminshall fd_set ibits, obits, xbits; 4656002Sroot register int c; 4666002Sroot 46727185Sminshall if (ncc < 0 && pcc < 0) 46827185Sminshall break; 46927185Sminshall 47027185Sminshall FD_ZERO(&ibits); 47127185Sminshall FD_ZERO(&obits); 47227185Sminshall FD_ZERO(&xbits); 4736002Sroot /* 4746002Sroot * Never look for input if there's still 4756002Sroot * stuff in the corresponding output buffer 4766002Sroot */ 47727185Sminshall if (nfrontp - nbackp || pcc > 0) { 47827185Sminshall FD_SET(f, &obits); 47933267Sminshall FD_SET(p, &xbits); 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)) { 51327649Sminshall #if !defined(SO_OOBINLINE) 51427185Sminshall /* 51527898Skarels * In 4.2 (and 4.3 beta) 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)); 55627983Sminshall if (sequenceIs(didnetreceive, 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); 56727649Sminshall #else /* !defined(SO_OOBINLINE)) */ 56827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 56927649Sminshall #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 */ 58333267Sminshall if (FD_ISSET(p, &xbits)) { 58433267Sminshall if (read(p, ptyibuf, 1) != 1) { 58533267Sminshall break; 58633267Sminshall } 58733267Sminshall } 58827185Sminshall if (FD_ISSET(p, &ibits)) { 5896002Sroot pcc = read(p, ptyibuf, BUFSIZ); 5906002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 5916002Sroot pcc = 0; 5926002Sroot else { 5936002Sroot if (pcc <= 0) 5946002Sroot break; 59533267Sminshall /* Skip past "packet" */ 59633267Sminshall pcc--; 59733267Sminshall ptyip = ptyibuf+1; 5986002Sroot } 5996002Sroot } 60033267Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 60133267Sminshall netclear(); /* clear buffer back */ 60233267Sminshall *nfrontp++ = IAC; 60333267Sminshall *nfrontp++ = DM; 60433267Sminshall neturg = nfrontp-1; /* off by one XXX */ 60533267Sminshall ptyibuf[0] = 0; 60633267Sminshall } 6076002Sroot 6086002Sroot while (pcc > 0) { 6096002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6106002Sroot break; 6116002Sroot c = *ptyip++ & 0377, pcc--; 6126002Sroot if (c == IAC) 6136002Sroot *nfrontp++ = c; 6146002Sroot *nfrontp++ = c; 61531940Sbostic /* Don't do CR-NUL if we are in binary mode */ 61631940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 61727020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 61827020Sminshall *nfrontp++ = *ptyip++ & 0377; 61927020Sminshall pcc--; 62027020Sminshall } else 62127020Sminshall *nfrontp++ = '\0'; 62227020Sminshall } 6236002Sroot } 62427185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6256002Sroot netflush(); 6266002Sroot if (ncc > 0) 6276002Sroot telrcv(); 62827185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6296002Sroot ptyflush(); 6306002Sroot } 6316002Sroot cleanup(); 6326002Sroot } 6336002Sroot 6346002Sroot /* 6356002Sroot * State for recv fsm 6366002Sroot */ 6376002Sroot #define TS_DATA 0 /* base state */ 6386002Sroot #define TS_IAC 1 /* look for double IAC's */ 6396002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 64027649Sminshall #define TS_SB 3 /* throw away begin's... */ 64127649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6426002Sroot #define TS_WILL 5 /* will option negotiation */ 6436002Sroot #define TS_WONT 6 /* wont " */ 6446002Sroot #define TS_DO 7 /* do " */ 6456002Sroot #define TS_DONT 8 /* dont " */ 6466002Sroot 6476002Sroot telrcv() 6486002Sroot { 6496002Sroot register int c; 6506002Sroot static int state = TS_DATA; 6516002Sroot 6526002Sroot while (ncc > 0) { 6536002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6546002Sroot return; 6556002Sroot c = *netip++ & 0377, ncc--; 6566002Sroot switch (state) { 6576002Sroot 65826090Sminshall case TS_CR: 65926090Sminshall state = TS_DATA; 66032097Sminshall /* Strip off \n or \0 after a \r */ 66126499Sminshall if ((c == 0) || (c == '\n')) { 66226090Sminshall break; 66326499Sminshall } 66426090Sminshall /* FALL THROUGH */ 66526090Sminshall 6666002Sroot case TS_DATA: 6676002Sroot if (c == IAC) { 6686002Sroot state = TS_IAC; 6696002Sroot break; 6706002Sroot } 6716002Sroot if (inter > 0) 6726002Sroot break; 67327020Sminshall /* 67432097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 67532097Sminshall * Many client implementations send \r\n when 67632097Sminshall * the user hits the CarriageReturn key. 67732097Sminshall * 67832097Sminshall * We USED to map \r\n ==> \n, since \r\n says 67927020Sminshall * that we want to be in column 1 of the next 68027020Sminshall * printable line, and \n is the standard 68127020Sminshall * unix way of saying that (\r is only good 68227020Sminshall * if CRMOD is set, which it normally is). 68327020Sminshall */ 68431940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 68532097Sminshall state = TS_CR; 68626499Sminshall } 68726499Sminshall *pfrontp++ = c; 6886002Sroot break; 6896002Sroot 6906002Sroot case TS_IAC: 6916002Sroot switch (c) { 6926002Sroot 6936002Sroot /* 6946002Sroot * Send the process on the pty side an 6956002Sroot * interrupt. Do this with a NULL or 6966002Sroot * interrupt char; depending on the tty mode. 6976002Sroot */ 6986002Sroot case IP: 6996002Sroot interrupt(); 7006002Sroot break; 7016002Sroot 70227229Sminshall case BREAK: 70327229Sminshall sendbrk(); 70427229Sminshall break; 70527229Sminshall 7066002Sroot /* 7076002Sroot * Are You There? 7086002Sroot */ 7096002Sroot case AYT: 71017583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 71117583Ssam nfrontp += 9; 7126002Sroot break; 7136002Sroot 7146002Sroot /* 71527185Sminshall * Abort Output 71627185Sminshall */ 71727185Sminshall case AO: { 71827185Sminshall struct ltchars tmpltc; 71927185Sminshall 72027185Sminshall ptyflush(); /* half-hearted */ 72127185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 72227185Sminshall if (tmpltc.t_flushc != '\377') { 72327185Sminshall *pfrontp++ = tmpltc.t_flushc; 72427185Sminshall } 72527260Sminshall netclear(); /* clear buffer back */ 72627185Sminshall *nfrontp++ = IAC; 72727185Sminshall *nfrontp++ = DM; 72827187Sminshall neturg = nfrontp-1; /* off by one XXX */ 72927185Sminshall break; 73027185Sminshall } 73127185Sminshall 73227185Sminshall /* 7336002Sroot * Erase Character and 7346002Sroot * Erase Line 7356002Sroot */ 7366002Sroot case EC: 73727185Sminshall case EL: { 73827185Sminshall struct sgttyb b; 73927185Sminshall char ch; 7406002Sroot 74127185Sminshall ptyflush(); /* half-hearted */ 74227185Sminshall ioctl(pty, TIOCGETP, &b); 74327185Sminshall ch = (c == EC) ? 74427185Sminshall b.sg_erase : b.sg_kill; 74527185Sminshall if (ch != '\377') { 74627185Sminshall *pfrontp++ = ch; 74727185Sminshall } 74827185Sminshall break; 74927185Sminshall } 75027185Sminshall 7516002Sroot /* 7526002Sroot * Check for urgent data... 7536002Sroot */ 7546002Sroot case DM: 75527185Sminshall SYNCHing = stilloob(net); 75627185Sminshall settimer(gotDM); 7576002Sroot break; 7586002Sroot 75927185Sminshall 7606002Sroot /* 7616002Sroot * Begin option subnegotiation... 7626002Sroot */ 7636002Sroot case SB: 76427649Sminshall state = TS_SB; 7656002Sroot continue; 7666002Sroot 7676002Sroot case WILL: 76827188Sminshall state = TS_WILL; 76927188Sminshall continue; 77027188Sminshall 7716002Sroot case WONT: 77227188Sminshall state = TS_WONT; 77327188Sminshall continue; 77427188Sminshall 7756002Sroot case DO: 77627188Sminshall state = TS_DO; 77727188Sminshall continue; 77827188Sminshall 7796002Sroot case DONT: 78027188Sminshall state = TS_DONT; 7816002Sroot continue; 7826002Sroot 7836002Sroot case IAC: 7846002Sroot *pfrontp++ = c; 7856002Sroot break; 7866002Sroot } 7876002Sroot state = TS_DATA; 7886002Sroot break; 7896002Sroot 79027649Sminshall case TS_SB: 79127649Sminshall if (c == IAC) { 79227649Sminshall state = TS_SE; 79327649Sminshall } else { 79427649Sminshall SB_ACCUM(c); 79527649Sminshall } 7966002Sroot break; 7976002Sroot 79827649Sminshall case TS_SE: 79927649Sminshall if (c != SE) { 80027649Sminshall if (c != IAC) { 80127649Sminshall SB_ACCUM(IAC); 80227649Sminshall } 80327649Sminshall SB_ACCUM(c); 80427649Sminshall state = TS_SB; 80527649Sminshall } else { 80627649Sminshall SB_TERM(); 80727649Sminshall suboption(); /* handle sub-option */ 80827649Sminshall state = TS_DATA; 80927649Sminshall } 8106002Sroot break; 8116002Sroot 8126002Sroot case TS_WILL: 81327983Sminshall if (hisopts[c] != OPT_YES) 8146002Sroot willoption(c); 8156002Sroot state = TS_DATA; 8166002Sroot continue; 8176002Sroot 8186002Sroot case TS_WONT: 81927983Sminshall if (hisopts[c] != OPT_NO) 8206002Sroot wontoption(c); 8216002Sroot state = TS_DATA; 8226002Sroot continue; 8236002Sroot 8246002Sroot case TS_DO: 82527983Sminshall if (myopts[c] != OPT_YES) 8266002Sroot dooption(c); 8276002Sroot state = TS_DATA; 8286002Sroot continue; 8296002Sroot 8306002Sroot case TS_DONT: 83127983Sminshall if (myopts[c] != OPT_NO) { 83227649Sminshall dontoption(c); 8336002Sroot } 8346002Sroot state = TS_DATA; 8356002Sroot continue; 8366002Sroot 8376002Sroot default: 83827898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8399218Ssam printf("telnetd: panic state=%d\n", state); 8406002Sroot exit(1); 8416002Sroot } 8426002Sroot } 8436002Sroot } 8446002Sroot 8456002Sroot willoption(option) 8466002Sroot int option; 8476002Sroot { 8486002Sroot char *fmt; 8496002Sroot 8506002Sroot switch (option) { 8516002Sroot 8526002Sroot case TELOPT_BINARY: 8536002Sroot mode(RAW, 0); 85427188Sminshall fmt = doopt; 85527188Sminshall break; 8566002Sroot 8576002Sroot case TELOPT_ECHO: 85827649Sminshall not42 = 0; /* looks like a 4.2 system */ 85927649Sminshall /* 86027649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 86127649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 86227649Sminshall * Kludge upon kludge! 86327649Sminshall */ 86427983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 86527649Sminshall dooption(TELOPT_ECHO); 86627649Sminshall } 86727649Sminshall fmt = dont; 86827188Sminshall break; 8696002Sroot 87027649Sminshall case TELOPT_TTYPE: 87127983Sminshall settimer(ttypeopt); 87227983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 87327983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 87427983Sminshall return; 87527983Sminshall } 87627983Sminshall fmt = doopt; 87727983Sminshall break; 87827983Sminshall 8796002Sroot case TELOPT_SGA: 8806002Sroot fmt = doopt; 8816002Sroot break; 8826002Sroot 8836002Sroot case TELOPT_TM: 8846002Sroot fmt = dont; 8856002Sroot break; 8866002Sroot 8876002Sroot default: 8886002Sroot fmt = dont; 8896002Sroot break; 8906002Sroot } 89127188Sminshall if (fmt == doopt) { 89227983Sminshall hisopts[option] = OPT_YES; 89327188Sminshall } else { 89427983Sminshall hisopts[option] = OPT_NO; 89527188Sminshall } 89632452Sbostic (void) sprintf(nfrontp, fmt, option); 8978379Ssam nfrontp += sizeof (dont) - 2; 8986002Sroot } 8996002Sroot 9006002Sroot wontoption(option) 9016002Sroot int option; 9026002Sroot { 9036002Sroot char *fmt; 9046002Sroot 9056002Sroot switch (option) { 9066002Sroot case TELOPT_ECHO: 90727649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 90827188Sminshall break; 9096002Sroot 9106002Sroot case TELOPT_BINARY: 9116002Sroot mode(0, RAW); 9126002Sroot break; 91328044Sminshall 91428044Sminshall case TELOPT_TTYPE: 91528044Sminshall settimer(ttypeopt); 91628044Sminshall break; 9176002Sroot } 91828044Sminshall 91927188Sminshall fmt = dont; 92027983Sminshall hisopts[option] = OPT_NO; 92132452Sbostic (void) sprintf(nfrontp, fmt, option); 9228379Ssam nfrontp += sizeof (doopt) - 2; 9236002Sroot } 9246002Sroot 9256002Sroot dooption(option) 9266002Sroot int option; 9276002Sroot { 9286002Sroot char *fmt; 9296002Sroot 9306002Sroot switch (option) { 9316002Sroot 9326002Sroot case TELOPT_TM: 9336002Sroot fmt = wont; 9346002Sroot break; 9356002Sroot 9366002Sroot case TELOPT_ECHO: 9376002Sroot mode(ECHO|CRMOD, 0); 93827188Sminshall fmt = will; 93927188Sminshall break; 9406002Sroot 9416002Sroot case TELOPT_BINARY: 9426002Sroot mode(RAW, 0); 94327188Sminshall fmt = will; 94427188Sminshall break; 9456002Sroot 9466002Sroot case TELOPT_SGA: 9476002Sroot fmt = will; 9486002Sroot break; 9496002Sroot 9506002Sroot default: 9516002Sroot fmt = wont; 9526002Sroot break; 9536002Sroot } 95427188Sminshall if (fmt == will) { 95527983Sminshall myopts[option] = OPT_YES; 95627188Sminshall } else { 95727983Sminshall myopts[option] = OPT_NO; 95827188Sminshall } 95932452Sbostic (void) sprintf(nfrontp, fmt, option); 9608379Ssam nfrontp += sizeof (doopt) - 2; 9616002Sroot } 9626002Sroot 96327649Sminshall 96427649Sminshall dontoption(option) 96527649Sminshall int option; 96627649Sminshall { 96727649Sminshall char *fmt; 96827649Sminshall 96927649Sminshall switch (option) { 97027649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 97132401Sminshall mode(0, ECHO); 97227649Sminshall fmt = wont; 97327649Sminshall break; 97427983Sminshall 97527649Sminshall default: 97627649Sminshall fmt = wont; 97727649Sminshall break; 97827649Sminshall } 97927983Sminshall 98027649Sminshall if (fmt = wont) { 98127983Sminshall myopts[option] = OPT_NO; 98227649Sminshall } else { 98327983Sminshall myopts[option] = OPT_YES; 98427649Sminshall } 98532452Sbostic (void) sprintf(nfrontp, fmt, option); 98627649Sminshall nfrontp += sizeof (wont) - 2; 98727649Sminshall } 98827649Sminshall 98927649Sminshall /* 99027649Sminshall * suboption() 99127649Sminshall * 99227649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 99327649Sminshall * side. 99427649Sminshall * 99527649Sminshall * Currently we recognize: 99627649Sminshall * 99727983Sminshall * Terminal type is 99827649Sminshall */ 99927649Sminshall 100027649Sminshall suboption() 100127649Sminshall { 100227983Sminshall switch (SB_GET()) { 100327983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 100427983Sminshall static char terminalname[5+41] = "TERM="; 100527983Sminshall 100627983Sminshall settimer(ttypesubopt); 100727983Sminshall 100827983Sminshall if (SB_GET() != TELQUAL_IS) { 100927983Sminshall return; /* ??? XXX but, this is the most robust */ 101027983Sminshall } 101127983Sminshall 101227983Sminshall terminaltype = terminalname+strlen(terminalname); 101327983Sminshall 101427983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 101527983Sminshall !SB_EOF()) { 101627983Sminshall register int c; 101727983Sminshall 101827983Sminshall c = SB_GET(); 101927983Sminshall if (isupper(c)) { 102027983Sminshall c = tolower(c); 102127983Sminshall } 102227983Sminshall *terminaltype++ = c; /* accumulate name */ 102327983Sminshall } 102427983Sminshall *terminaltype = 0; 102527983Sminshall terminaltype = terminalname; 102627983Sminshall break; 102727983Sminshall } 102827983Sminshall 102927649Sminshall default: 103027649Sminshall ; 103127649Sminshall } 103227649Sminshall } 103327649Sminshall 10346002Sroot mode(on, off) 10356002Sroot int on, off; 10366002Sroot { 10376002Sroot struct sgttyb b; 10386002Sroot 10396002Sroot ptyflush(); 10406002Sroot ioctl(pty, TIOCGETP, &b); 10416002Sroot b.sg_flags |= on; 10426002Sroot b.sg_flags &= ~off; 10436002Sroot ioctl(pty, TIOCSETP, &b); 10446002Sroot } 10456002Sroot 10466002Sroot /* 10476002Sroot * Send interrupt to process on other side of pty. 10486002Sroot * If it is in raw mode, just write NULL; 10496002Sroot * otherwise, write intr char. 10506002Sroot */ 10516002Sroot interrupt() 10526002Sroot { 10536002Sroot struct sgttyb b; 10546002Sroot struct tchars tchars; 10556002Sroot 10566002Sroot ptyflush(); /* half-hearted */ 10576002Sroot ioctl(pty, TIOCGETP, &b); 10586002Sroot if (b.sg_flags & RAW) { 10596002Sroot *pfrontp++ = '\0'; 10606002Sroot return; 10616002Sroot } 10626002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10636002Sroot '\177' : tchars.t_intrc; 10646002Sroot } 10656002Sroot 106627229Sminshall /* 106727229Sminshall * Send quit to process on other side of pty. 106827229Sminshall * If it is in raw mode, just write NULL; 106927229Sminshall * otherwise, write quit char. 107027229Sminshall */ 107127229Sminshall sendbrk() 107227229Sminshall { 107327229Sminshall struct sgttyb b; 107427229Sminshall struct tchars tchars; 107527229Sminshall 107627229Sminshall ptyflush(); /* half-hearted */ 107727229Sminshall ioctl(pty, TIOCGETP, &b); 107827229Sminshall if (b.sg_flags & RAW) { 107927229Sminshall *pfrontp++ = '\0'; 108027229Sminshall return; 108127229Sminshall } 108227229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 108327229Sminshall '\034' : tchars.t_quitc; 108427229Sminshall } 108527229Sminshall 10866002Sroot ptyflush() 10876002Sroot { 10886002Sroot int n; 10896002Sroot 10906002Sroot if ((n = pfrontp - pbackp) > 0) 10916002Sroot n = write(pty, pbackp, n); 10928346Ssam if (n < 0) 10938346Ssam return; 10946002Sroot pbackp += n; 10956002Sroot if (pbackp == pfrontp) 10966002Sroot pbackp = pfrontp = ptyobuf; 10976002Sroot } 109827260Sminshall 109927260Sminshall /* 110027260Sminshall * nextitem() 110127260Sminshall * 110227260Sminshall * Return the address of the next "item" in the TELNET data 110327260Sminshall * stream. This will be the address of the next character if 110427260Sminshall * the current address is a user data character, or it will 110527260Sminshall * be the address of the character following the TELNET command 110627260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 110727260Sminshall * character. 110827260Sminshall */ 11096002Sroot 111027260Sminshall char * 111127260Sminshall nextitem(current) 111227260Sminshall char *current; 11136002Sroot { 111427260Sminshall if ((*current&0xff) != IAC) { 111527260Sminshall return current+1; 111627260Sminshall } 111727260Sminshall switch (*(current+1)&0xff) { 111827260Sminshall case DO: 111927260Sminshall case DONT: 112027260Sminshall case WILL: 112127260Sminshall case WONT: 112227260Sminshall return current+3; 112327260Sminshall case SB: /* loop forever looking for the SE */ 112427260Sminshall { 112527260Sminshall register char *look = current+2; 11266002Sroot 112727260Sminshall for (;;) { 112827260Sminshall if ((*look++&0xff) == IAC) { 112927260Sminshall if ((*look++&0xff) == SE) { 113027260Sminshall return look; 113127260Sminshall } 113227260Sminshall } 113327260Sminshall } 11348346Ssam } 113527260Sminshall default: 113627260Sminshall return current+2; 113727260Sminshall } 11386002Sroot } 11396002Sroot 114027185Sminshall 114127185Sminshall /* 114227260Sminshall * netclear() 114327260Sminshall * 114427260Sminshall * We are about to do a TELNET SYNCH operation. Clear 114527260Sminshall * the path to the network. 114627260Sminshall * 114727260Sminshall * Things are a bit tricky since we may have sent the first 114827260Sminshall * byte or so of a previous TELNET command into the network. 114927260Sminshall * So, we have to scan the network buffer from the beginning 115027260Sminshall * until we are up to where we want to be. 115127260Sminshall * 115227260Sminshall * A side effect of what we do, just to keep things 115327260Sminshall * simple, is to clear the urgent data pointer. The principal 115427260Sminshall * caller should be setting the urgent data pointer AFTER calling 115527260Sminshall * us in any case. 115627260Sminshall */ 115727260Sminshall 115827260Sminshall netclear() 115927260Sminshall { 116027260Sminshall register char *thisitem, *next; 116127260Sminshall char *good; 116227260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 116327260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 116427260Sminshall 116527260Sminshall thisitem = netobuf; 116627260Sminshall 116727260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 116827260Sminshall thisitem = next; 116927260Sminshall } 117027260Sminshall 117127260Sminshall /* Now, thisitem is first before/at boundary. */ 117227260Sminshall 117327260Sminshall good = netobuf; /* where the good bytes go */ 117427260Sminshall 117527260Sminshall while (nfrontp > thisitem) { 117627260Sminshall if (wewant(thisitem)) { 117727260Sminshall int length; 117827260Sminshall 117927260Sminshall next = thisitem; 118027260Sminshall do { 118127260Sminshall next = nextitem(next); 118227260Sminshall } while (wewant(next) && (nfrontp > next)); 118327260Sminshall length = next-thisitem; 118427260Sminshall bcopy(thisitem, good, length); 118527260Sminshall good += length; 118627260Sminshall thisitem = next; 118727260Sminshall } else { 118827260Sminshall thisitem = nextitem(thisitem); 118927260Sminshall } 119027260Sminshall } 119127260Sminshall 119227260Sminshall nbackp = netobuf; 119327260Sminshall nfrontp = good; /* next byte to be sent */ 119427260Sminshall neturg = 0; 119527260Sminshall } 119627260Sminshall 119727260Sminshall /* 119827185Sminshall * netflush 119927185Sminshall * Send as much data as possible to the network, 120027185Sminshall * handling requests for urgent data. 120127185Sminshall */ 120227185Sminshall 120327185Sminshall 120427185Sminshall netflush() 120527185Sminshall { 120627185Sminshall int n; 120727185Sminshall 120827185Sminshall if ((n = nfrontp - nbackp) > 0) { 120927649Sminshall /* 121027649Sminshall * if no urgent data, or if the other side appears to be an 121127649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 121227649Sminshall * write the entire buffer in non-OOB mode. 121327649Sminshall */ 121427649Sminshall if ((neturg == 0) || (not42 == 0)) { 121527185Sminshall n = write(net, nbackp, n); /* normal write */ 121627185Sminshall } else { 121727185Sminshall n = neturg - nbackp; 121827185Sminshall /* 121927185Sminshall * In 4.2 (and 4.3) systems, there is some question about 122027185Sminshall * what byte in a sendOOB operation is the "OOB" data. 122127185Sminshall * To make ourselves compatible, we only send ONE byte 122227185Sminshall * out of band, the one WE THINK should be OOB (though 122327185Sminshall * we really have more the TCP philosophy of urgent data 122427185Sminshall * rather than the Unix philosophy of OOB data). 122527185Sminshall */ 122627185Sminshall if (n > 1) { 122727185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 122827185Sminshall } else { 122927185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 123027185Sminshall } 123127185Sminshall } 123227185Sminshall } 123327185Sminshall if (n < 0) { 123427185Sminshall if (errno == EWOULDBLOCK) 123527185Sminshall return; 123627185Sminshall /* should blow this guy away... */ 123727185Sminshall return; 123827185Sminshall } 123927185Sminshall nbackp += n; 124027185Sminshall if (nbackp >= neturg) { 124127185Sminshall neturg = 0; 124227185Sminshall } 124327185Sminshall if (nbackp == nfrontp) { 124427185Sminshall nbackp = nfrontp = netobuf; 124527185Sminshall } 124627185Sminshall } 124727185Sminshall 12486002Sroot cleanup() 12496002Sroot { 12506002Sroot 12516002Sroot rmut(); 125210008Ssam vhangup(); /* XXX */ 125310191Ssam shutdown(net, 2); 12546002Sroot exit(1); 12556002Sroot } 12566002Sroot 12576002Sroot #include <utmp.h> 12586002Sroot 12596002Sroot struct utmp wtmp; 12606002Sroot char wtmpf[] = "/usr/adm/wtmp"; 126123567Sbloom char utmpf[] = "/etc/utmp"; 126223567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 126323567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12646002Sroot 12656002Sroot rmut() 12666002Sroot { 12676002Sroot register f; 12686002Sroot int found = 0; 126923567Sbloom struct utmp *u, *utmp; 127023567Sbloom int nutmp; 127123567Sbloom struct stat statbf; 12726002Sroot 127323567Sbloom f = open(utmpf, O_RDWR); 12746002Sroot if (f >= 0) { 127523567Sbloom fstat(f, &statbf); 127623567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 127723567Sbloom if (!utmp) 127823567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 127923567Sbloom if (statbf.st_size && utmp) { 128023567Sbloom nutmp = read(f, utmp, statbf.st_size); 128123567Sbloom nutmp /= sizeof(struct utmp); 128223567Sbloom 128323567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 128423567Sbloom if (SCMPN(u->ut_line, line+5) || 128523567Sbloom u->ut_name[0]==0) 128623567Sbloom continue; 128723567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 128823567Sbloom SCPYN(u->ut_name, ""); 128923567Sbloom SCPYN(u->ut_host, ""); 129023567Sbloom time(&u->ut_time); 129123567Sbloom write(f, (char *)u, sizeof(wtmp)); 129223567Sbloom found++; 129323567Sbloom } 12946002Sroot } 12956002Sroot close(f); 12966002Sroot } 12976002Sroot if (found) { 129817583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 12996002Sroot if (f >= 0) { 13006002Sroot SCPYN(wtmp.ut_line, line+5); 13016002Sroot SCPYN(wtmp.ut_name, ""); 130212683Ssam SCPYN(wtmp.ut_host, ""); 13036002Sroot time(&wtmp.ut_time); 130423567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 13056002Sroot close(f); 13066002Sroot } 13076002Sroot } 13086002Sroot chmod(line, 0666); 13096002Sroot chown(line, 0, 0); 13106002Sroot line[strlen("/dev/")] = 'p'; 13116002Sroot chmod(line, 0666); 13126002Sroot chown(line, 0, 0); 13136002Sroot } 1314*33271Sminshall 1315*33271Sminshall char editedhost[32]; 1316*33271Sminshall 1317*33271Sminshall edithost(pat, host) 1318*33271Sminshall register char *pat; 1319*33271Sminshall register char *host; 1320*33271Sminshall { 1321*33271Sminshall register char *res = editedhost; 1322*33271Sminshall 1323*33271Sminshall if (!pat) 1324*33271Sminshall pat = ""; 1325*33271Sminshall while (*pat) { 1326*33271Sminshall switch (*pat) { 1327*33271Sminshall 1328*33271Sminshall case '#': 1329*33271Sminshall if (*host) 1330*33271Sminshall host++; 1331*33271Sminshall break; 1332*33271Sminshall 1333*33271Sminshall case '@': 1334*33271Sminshall if (*host) 1335*33271Sminshall *res++ = *host++; 1336*33271Sminshall break; 1337*33271Sminshall 1338*33271Sminshall default: 1339*33271Sminshall *res++ = *pat; 1340*33271Sminshall break; 1341*33271Sminshall 1342*33271Sminshall } 1343*33271Sminshall if (res == &editedhost[sizeof editedhost - 1]) { 1344*33271Sminshall *res = '\0'; 1345*33271Sminshall return; 1346*33271Sminshall } 1347*33271Sminshall pat++; 1348*33271Sminshall } 1349*33271Sminshall if (*host) 1350*33271Sminshall strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); 1351*33271Sminshall else 1352*33271Sminshall *res = '\0'; 1353*33271Sminshall editedhost[sizeof editedhost - 1] = '\0'; 1354*33271Sminshall } 1355*33271Sminshall 1356*33271Sminshall static char *putlocation; 1357*33271Sminshall 1358*33271Sminshall puts(s) 1359*33271Sminshall register char *s; 1360*33271Sminshall { 1361*33271Sminshall 1362*33271Sminshall while (*s) 1363*33271Sminshall putchr(*s++); 1364*33271Sminshall } 1365*33271Sminshall 1366*33271Sminshall putchr(cc) 1367*33271Sminshall { 1368*33271Sminshall *putlocation++ = cc; 1369*33271Sminshall } 1370*33271Sminshall 1371*33271Sminshall putf(cp, where, tty) 1372*33271Sminshall register char *cp; 1373*33271Sminshall char *where; 1374*33271Sminshall int tty; 1375*33271Sminshall { 1376*33271Sminshall char *slash; 1377*33271Sminshall char datebuffer[60]; 1378*33271Sminshall extern char *rindex(); 1379*33271Sminshall 1380*33271Sminshall putlocation = where; 1381*33271Sminshall 1382*33271Sminshall while (*cp) { 1383*33271Sminshall if (*cp != '%') { 1384*33271Sminshall putchr(*cp++); 1385*33271Sminshall continue; 1386*33271Sminshall } 1387*33271Sminshall switch (*++cp) { 1388*33271Sminshall 1389*33271Sminshall case 't': 1390*33271Sminshall slash = rindex(line, '/'); 1391*33271Sminshall if (slash == (char *) 0) 1392*33271Sminshall puts(line); 1393*33271Sminshall else 1394*33271Sminshall puts(&slash[1]); 1395*33271Sminshall break; 1396*33271Sminshall 1397*33271Sminshall case 'h': 1398*33271Sminshall puts(editedhost); 1399*33271Sminshall break; 1400*33271Sminshall 1401*33271Sminshall case 'd': 1402*33271Sminshall get_date(datebuffer); 1403*33271Sminshall puts(datebuffer); 1404*33271Sminshall break; 1405*33271Sminshall 1406*33271Sminshall case '%': 1407*33271Sminshall putchr('%'); 1408*33271Sminshall break; 1409*33271Sminshall } 1410*33271Sminshall cp++; 1411*33271Sminshall } 1412*33271Sminshall } 1413