121182Sdist /* 233687Sbostic * Copyright (c) 1983, 1986 Regents of the University of California. 333687Sbostic * All rights reserved. 433687Sbostic * 533687Sbostic * Redistribution and use in source and binary forms are permitted 633687Sbostic * provided that this notice is preserved and that due credit is given 733687Sbostic * to the University of California at Berkeley. The name of the University 833687Sbostic * may not be used to endorse or promote products derived from this 933687Sbostic * software without specific prior written permission. This software 1033687Sbostic * is provided ``as is'' without express or implied warranty. 1121182Sdist */ 1221182Sdist 136295Sroot #ifndef lint 1421182Sdist char copyright[] = 1533687Sbostic "@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\ 1621182Sdist All rights reserved.\n"; 1733687Sbostic #endif /* not lint */ 186295Sroot 1921182Sdist #ifndef lint 20*34424Sbostic static char sccsid[] = "@(#)telnetd.c 5.27 (Berkeley) 05/22/88"; 2133687Sbostic #endif /* not lint */ 2221182Sdist 236002Sroot /* 2427898Skarels * Telnet server. 256002Sroot */ 2627898Skarels #include <sys/param.h> 279218Ssam #include <sys/socket.h> 2813608Ssam #include <sys/wait.h> 2917583Ssam #include <sys/file.h> 3020188Skarels #include <sys/stat.h> 3127185Sminshall #include <sys/time.h> 329218Ssam 339218Ssam #include <netinet/in.h> 349218Ssam 3512216Ssam #include <arpa/telnet.h> 3612216Ssam 376002Sroot #include <stdio.h> 386002Sroot #include <signal.h> 396002Sroot #include <errno.h> 406002Sroot #include <sgtty.h> 418346Ssam #include <netdb.h> 4217187Sralph #include <syslog.h> 4327649Sminshall #include <ctype.h> 449218Ssam 4527983Sminshall #define OPT_NO 0 /* won't do this option */ 4627983Sminshall #define OPT_YES 1 /* will do this option */ 4727983Sminshall #define OPT_YES_BUT_ALWAYS_LOOK 2 4827983Sminshall #define OPT_NO_BUT_ALWAYS_LOOK 3 496002Sroot char hisopts[256]; 506002Sroot char myopts[256]; 516002Sroot 526002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 536002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 546002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 556002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 566002Sroot 576002Sroot /* 586002Sroot * I/O data buffers, pointers, and counters. 596002Sroot */ 606002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 6127649Sminshall 626002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 6327649Sminshall 646002Sroot char netibuf[BUFSIZ], *netip = netibuf; 6527649Sminshall #define NIACCUM(c) { *netip++ = c; \ 6627649Sminshall ncc++; \ 6727649Sminshall } 6827649Sminshall 696388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 7027185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 7127649Sminshall /* the remote system seems to NOT be an old 4.2 */ 7227649Sminshall int not42 = 1; 7327649Sminshall 7433271Sminshall #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r" 7527649Sminshall 7627983Sminshall /* buffer for sub-options */ 7727983Sminshall char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 7827649Sminshall #define SB_CLEAR() subpointer = subbuffer; 7927983Sminshall #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 8027649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 8127649Sminshall *subpointer++ = (c); \ 8227649Sminshall } 8327983Sminshall #define SB_GET() ((*subpointer++)&0xff) 8427983Sminshall #define SB_EOF() (subpointer >= subend) 8527649Sminshall 866002Sroot int pcc, ncc; 876002Sroot 886002Sroot int pty, net; 896002Sroot int inter; 9013799Ssam extern char **environ; 916002Sroot extern int errno; 9220188Skarels char *line; 9327185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 9427185Sminshall /* 9527185Sminshall * The following are some clocks used to decide how to interpret 9627185Sminshall * the relationship between various variables. 9727185Sminshall */ 986002Sroot 9927185Sminshall struct { 10027185Sminshall int 10127185Sminshall system, /* what the current time is */ 10227185Sminshall echotoggle, /* last time user entered echo character */ 10327185Sminshall modenegotiated, /* last time operating mode negotiated */ 10427185Sminshall didnetreceive, /* last time we read data from network */ 10527983Sminshall ttypeopt, /* ttype will/won't received */ 10627983Sminshall ttypesubopt, /* ttype subopt is received */ 10727983Sminshall getterminal, /* time started to get terminal information */ 10827185Sminshall gotDM; /* when did we last see a data mark */ 10927185Sminshall } clocks; 11027185Sminshall 11127983Sminshall #define settimer(x) (clocks.x = ++clocks.system) 11227983Sminshall #define sequenceIs(x,y) (clocks.x < clocks.y) 11327185Sminshall 1146002Sroot main(argc, argv) 1156002Sroot char *argv[]; 1166002Sroot { 11716371Skarels struct sockaddr_in from; 11817156Ssam int on = 1, fromlen; 1196002Sroot 12027185Sminshall #if defined(DEBUG) 12127185Sminshall { 12227185Sminshall int s, ns, foo; 12327185Sminshall struct servent *sp; 12427185Sminshall static struct sockaddr_in sin = { AF_INET }; 12527185Sminshall 12627185Sminshall sp = getservbyname("telnet", "tcp"); 12727185Sminshall if (sp == 0) { 12827185Sminshall fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 12927185Sminshall exit(1); 13027185Sminshall } 13127185Sminshall sin.sin_port = sp->s_port; 13227185Sminshall argc--, argv++; 13327185Sminshall if (argc > 0) { 13427185Sminshall sin.sin_port = atoi(*argv); 13527185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 13627185Sminshall } 13727185Sminshall 13827185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 13927185Sminshall if (s < 0) { 14027185Sminshall perror("telnetd: socket");; 14127185Sminshall exit(1); 14227185Sminshall } 14327185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 14427185Sminshall perror("bind"); 14527185Sminshall exit(1); 14627185Sminshall } 14727185Sminshall if (listen(s, 1) < 0) { 14827185Sminshall perror("listen"); 14927185Sminshall exit(1); 15027185Sminshall } 15127185Sminshall foo = sizeof sin; 15227185Sminshall ns = accept(s, &sin, &foo); 15327185Sminshall if (ns < 0) { 15427185Sminshall perror("accept"); 15527185Sminshall exit(1); 15627185Sminshall } 15727185Sminshall dup2(ns, 0); 15827185Sminshall close(s); 15927185Sminshall } 16027185Sminshall #endif /* defined(DEBUG) */ 16124855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 16216371Skarels fromlen = sizeof (from); 16316371Skarels if (getpeername(0, &from, &fromlen) < 0) { 16416371Skarels fprintf(stderr, "%s: ", argv[0]); 16516371Skarels perror("getpeername"); 16616371Skarels _exit(1); 1678346Ssam } 16817156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 16917187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 17010418Ssam } 17116371Skarels doit(0, &from); 1726002Sroot } 1736002Sroot 17427983Sminshall char *terminaltype = 0; 17527983Sminshall char *envinit[2]; 17627983Sminshall int cleanup(); 17727649Sminshall 17827649Sminshall /* 17927983Sminshall * ttloop 18027649Sminshall * 18127983Sminshall * A small subroutine to flush the network output buffer, get some data 18227983Sminshall * from the network, and pass it through the telnet state machine. We 18327983Sminshall * also flush the pty input buffer (by dropping its data) if it becomes 18427983Sminshall * too full. 18527983Sminshall */ 18627983Sminshall 18727983Sminshall void 18827983Sminshall ttloop() 18927983Sminshall { 19027983Sminshall if (nfrontp-nbackp) { 19127983Sminshall netflush(); 19227983Sminshall } 19327983Sminshall ncc = read(net, netibuf, sizeof netibuf); 19427983Sminshall if (ncc < 0) { 19527983Sminshall syslog(LOG_INFO, "ttloop: read: %m\n"); 19628044Sminshall exit(1); 19728044Sminshall } else if (ncc == 0) { 19828044Sminshall syslog(LOG_INFO, "ttloop: peer died: %m\n"); 19928044Sminshall exit(1); 20027983Sminshall } 20127983Sminshall netip = netibuf; 20227983Sminshall telrcv(); /* state machine */ 20327983Sminshall if (ncc > 0) { 20427983Sminshall pfrontp = pbackp = ptyobuf; 20527983Sminshall telrcv(); 20627983Sminshall } 20727983Sminshall } 20827983Sminshall 20927983Sminshall /* 21027983Sminshall * getterminaltype 21127649Sminshall * 21227983Sminshall * Ask the other end to send along its terminal type. 21327983Sminshall * Output is the variable terminaltype filled in. 21427649Sminshall */ 21527649Sminshall 21627983Sminshall void 21727983Sminshall getterminaltype() 21827649Sminshall { 21927983Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 22027649Sminshall 22127983Sminshall settimer(getterminal); 22227983Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 22327983Sminshall nfrontp += sizeof sbuf; 22428044Sminshall hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; 22527983Sminshall while (sequenceIs(ttypeopt, getterminal)) { 22627983Sminshall ttloop(); 22727649Sminshall } 22827983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES) { 22927983Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 23027983Sminshall 23127983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 23227983Sminshall nfrontp += sizeof sbbuf; 23327983Sminshall while (sequenceIs(ttypesubopt, getterminal)) { 23427983Sminshall ttloop(); 23527983Sminshall } 23627983Sminshall } 23727649Sminshall } 23827649Sminshall 2396002Sroot /* 2406002Sroot * Get a pty, scan input lines. 2416002Sroot */ 24212683Ssam doit(f, who) 24312683Ssam int f; 24412683Ssam struct sockaddr_in *who; 2456002Sroot { 24620188Skarels char *host, *inet_ntoa(); 24717583Ssam int i, p, t; 2486002Sroot struct sgttyb b; 24912683Ssam struct hostent *hp; 25027649Sminshall int c; 2516002Sroot 25220188Skarels for (c = 'p'; c <= 's'; c++) { 25320188Skarels struct stat stb; 25420188Skarels 25520188Skarels line = "/dev/ptyXX"; 25620188Skarels line[strlen("/dev/pty")] = c; 25720188Skarels line[strlen("/dev/ptyp")] = '0'; 25820188Skarels if (stat(line, &stb) < 0) 25920188Skarels break; 26017583Ssam for (i = 0; i < 16; i++) { 261*34424Sbostic line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; 262*34424Sbostic p = open(line, O_RDWR); 26317583Ssam if (p > 0) 26417583Ssam goto gotpty; 26517583Ssam } 2666002Sroot } 2679244Ssam fatal(f, "All network ports in use"); 2689244Ssam /*NOTREACHED*/ 2696002Sroot gotpty: 2706002Sroot dup2(f, 0); 27120188Skarels line[strlen("/dev/")] = 't'; 27217583Ssam t = open("/dev/tty", O_RDWR); 2736002Sroot if (t >= 0) { 2746002Sroot ioctl(t, TIOCNOTTY, 0); 2756002Sroot close(t); 2766002Sroot } 27720188Skarels t = open(line, O_RDWR); 2789244Ssam if (t < 0) 279*34424Sbostic fatalperror(f, line); 280*34424Sbostic if (fchmod(t, 0)) 281*34424Sbostic fatalperror(f, line); 282*34424Sbostic (void)signal(SIGHUP, SIG_IGN); 283*34424Sbostic vhangup(); 284*34424Sbostic (void)signal(SIGHUP, SIG_DFL); 285*34424Sbostic t = open(line, O_RDWR); 286*34424Sbostic if (t < 0) 287*34424Sbostic fatalperror(f, line); 2886002Sroot ioctl(t, TIOCGETP, &b); 2896388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 2906002Sroot ioctl(t, TIOCSETP, &b); 2916388Ssam ioctl(p, TIOCGETP, &b); 2928379Ssam b.sg_flags &= ~ECHO; 2936388Ssam ioctl(p, TIOCSETP, &b); 29412683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 29512683Ssam who->sin_family); 29612683Ssam if (hp) 29712683Ssam host = hp->h_name; 29812683Ssam else 29917444Sralph host = inet_ntoa(who->sin_addr); 30027983Sminshall 30127983Sminshall net = f; 30227983Sminshall pty = p; 30327983Sminshall 30427983Sminshall /* 30527983Sminshall * get terminal type. 30627983Sminshall */ 30727983Sminshall getterminaltype(); 30827983Sminshall 3099244Ssam if ((i = fork()) < 0) 310*34424Sbostic fatalperror(f, "fork"); 3116002Sroot if (i) 3126002Sroot telnet(f, p); 3136002Sroot close(f); 3146002Sroot close(p); 3156002Sroot dup2(t, 0); 3166002Sroot dup2(t, 1); 3176002Sroot dup2(t, 2); 3186002Sroot close(t); 31927983Sminshall envinit[0] = terminaltype; 32027983Sminshall envinit[1] = 0; 32113799Ssam environ = envinit; 32227649Sminshall /* 32327649Sminshall * -h : pass on name of host. 32427983Sminshall * WARNING: -h is accepted by login if and only if 32527983Sminshall * getuid() == 0. 32627649Sminshall * -p : don't clobber the environment (so terminal type stays set). 32727649Sminshall */ 32827649Sminshall execl("/bin/login", "login", "-h", host, 32927983Sminshall terminaltype ? "-p" : 0, 0); 330*34424Sbostic fatalperror(f, "/bin/login"); 3319244Ssam /*NOTREACHED*/ 3329244Ssam } 3339244Ssam 3349244Ssam fatal(f, msg) 3359244Ssam int f; 3369244Ssam char *msg; 3379244Ssam { 3389244Ssam char buf[BUFSIZ]; 3399244Ssam 34017583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3419244Ssam (void) write(f, buf, strlen(buf)); 3426002Sroot exit(1); 3436002Sroot } 3446002Sroot 345*34424Sbostic fatalperror(f, msg) 3469244Ssam int f; 3479244Ssam char *msg; 3489244Ssam { 3499244Ssam char buf[BUFSIZ]; 3509244Ssam extern char *sys_errlist[]; 3519244Ssam 35217583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3539244Ssam fatal(f, buf); 3549244Ssam } 3559244Ssam 35627185Sminshall 3576002Sroot /* 35827185Sminshall * Check a descriptor to see if out of band data exists on it. 35927185Sminshall */ 36027185Sminshall 36127185Sminshall 36227185Sminshall stilloob(s) 36327185Sminshall int s; /* socket number */ 36427185Sminshall { 36527185Sminshall static struct timeval timeout = { 0 }; 36627185Sminshall fd_set excepts; 36727185Sminshall int value; 36827185Sminshall 36927185Sminshall do { 37027185Sminshall FD_ZERO(&excepts); 37127185Sminshall FD_SET(s, &excepts); 37227185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 37327898Skarels } while ((value == -1) && (errno == EINTR)); 37427185Sminshall 37527185Sminshall if (value < 0) { 376*34424Sbostic fatalperror(pty, "select"); 37727185Sminshall } 37827185Sminshall if (FD_ISSET(s, &excepts)) { 37927185Sminshall return 1; 38027185Sminshall } else { 38127185Sminshall return 0; 38227185Sminshall } 38327185Sminshall } 38427185Sminshall 38527185Sminshall /* 3866002Sroot * Main loop. Select from pty and network, and 3876002Sroot * hand data to telnet receiver finite state machine. 3886002Sroot */ 3896002Sroot telnet(f, p) 3906002Sroot { 3916002Sroot int on = 1; 39227898Skarels char hostname[MAXHOSTNAMELEN]; 39333271Sminshall #define TABBUFSIZ 512 39433271Sminshall char defent[TABBUFSIZ]; 39533271Sminshall char defstrs[TABBUFSIZ]; 39633271Sminshall #undef TABBUFSIZ 39733271Sminshall char *HE; 39833271Sminshall char *HN; 39933271Sminshall char *IM; 4006002Sroot 4016002Sroot ioctl(f, FIONBIO, &on); 4026002Sroot ioctl(p, FIONBIO, &on); 40333267Sminshall ioctl(p, TIOCPKT, &on); 40427649Sminshall #if defined(SO_OOBINLINE) 40527649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 40627649Sminshall #endif /* defined(SO_OOBINLINE) */ 4076002Sroot signal(SIGTSTP, SIG_IGN); 40832400Sminshall /* 40932400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 41032400Sminshall * in ttioctl() in /sys/tty.c. 41132400Sminshall */ 41232400Sminshall signal(SIGTTOU, SIG_IGN); 41313028Ssam signal(SIGCHLD, cleanup); 41426083Slepreau setpgrp(0, 0); 4156002Sroot 4168379Ssam /* 41727185Sminshall * Request to do remote echo and to suppress go ahead. 4188379Ssam */ 41927983Sminshall if (!myopts[TELOPT_ECHO]) { 42027983Sminshall dooption(TELOPT_ECHO); 42127983Sminshall } 42227983Sminshall if (!myopts[TELOPT_SGA]) { 42327983Sminshall dooption(TELOPT_SGA); 42427983Sminshall } 42512713Ssam /* 42627649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 42727649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 42827649Sminshall * 42927649Sminshall * To find out, we send out a "DO ECHO". If the remote system 43027649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 43127649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 43227649Sminshall * WE, the server, sends it; it does NOT mean that the client will 43327649Sminshall * echo the terminal input). 43427649Sminshall */ 43532452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 43627649Sminshall nfrontp += sizeof doopt-2; 43727983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 43827649Sminshall 43927649Sminshall /* 44012713Ssam * Show banner that getty never gave. 44127797Sminshall * 44233271Sminshall * We put the banner in the pty input buffer. This way, it 44333271Sminshall * gets carriage return null processing, etc., just like all 44433271Sminshall * other pty --> client data. 44512713Ssam */ 44627797Sminshall 44712713Ssam gethostname(hostname, sizeof (hostname)); 44833271Sminshall if (getent(defent, "default") == 1) { 44933271Sminshall char *getstr(); 45033271Sminshall char *p=defstrs; 45127649Sminshall 45233271Sminshall HE = getstr("he", &p); 45333271Sminshall HN = getstr("hn", &p); 45433271Sminshall IM = getstr("im", &p); 45533271Sminshall if (HN && *HN) 45633271Sminshall strcpy(hostname, HN); 45733271Sminshall edithost(HE, hostname); 45833271Sminshall if (IM && *IM) 459*34424Sbostic putf(IM, ptyibuf+1); 46033271Sminshall } else { 46133271Sminshall sprintf(ptyibuf+1, BANNER, hostname); 46233271Sminshall } 46327797Sminshall 46433271Sminshall ptyip = ptyibuf+1; /* Prime the pump */ 46533271Sminshall pcc = strlen(ptyip); /* ditto */ 46633271Sminshall 46733267Sminshall /* Clear ptybuf[0] - where the packet information is received */ 46833267Sminshall ptyibuf[0] = 0; 46933271Sminshall 47027649Sminshall /* 47127649Sminshall * Call telrcv() once to pick up anything received during 47227649Sminshall * terminal type negotiation. 47327649Sminshall */ 47427649Sminshall telrcv(); 47527649Sminshall 4766002Sroot for (;;) { 47727185Sminshall fd_set ibits, obits, xbits; 4786002Sroot register int c; 4796002Sroot 48027185Sminshall if (ncc < 0 && pcc < 0) 48127185Sminshall break; 48227185Sminshall 48327185Sminshall FD_ZERO(&ibits); 48427185Sminshall FD_ZERO(&obits); 48527185Sminshall FD_ZERO(&xbits); 4866002Sroot /* 4876002Sroot * Never look for input if there's still 4886002Sroot * stuff in the corresponding output buffer 4896002Sroot */ 49027185Sminshall if (nfrontp - nbackp || pcc > 0) { 49127185Sminshall FD_SET(f, &obits); 49233267Sminshall FD_SET(p, &xbits); 49327185Sminshall } else { 49427185Sminshall FD_SET(p, &ibits); 49527185Sminshall } 49627185Sminshall if (pfrontp - pbackp || ncc > 0) { 49727185Sminshall FD_SET(p, &obits); 49827185Sminshall } else { 49927185Sminshall FD_SET(f, &ibits); 50027185Sminshall } 50127185Sminshall if (!SYNCHing) { 50227185Sminshall FD_SET(f, &xbits); 50327185Sminshall } 50427185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 50527185Sminshall (struct timeval *)0)) < 1) { 50627185Sminshall if (c == -1) { 50727185Sminshall if (errno == EINTR) { 50827185Sminshall continue; 50927185Sminshall } 51027185Sminshall } 5116002Sroot sleep(5); 5126002Sroot continue; 5136002Sroot } 5146002Sroot 5156002Sroot /* 51627185Sminshall * Any urgent data? 51727185Sminshall */ 51827185Sminshall if (FD_ISSET(net, &xbits)) { 51927185Sminshall SYNCHing = 1; 52027185Sminshall } 52127185Sminshall 52227185Sminshall /* 5236002Sroot * Something to read from the network... 5246002Sroot */ 52527185Sminshall if (FD_ISSET(net, &ibits)) { 52627649Sminshall #if !defined(SO_OOBINLINE) 52727185Sminshall /* 52827898Skarels * In 4.2 (and 4.3 beta) systems, the 52927185Sminshall * OOB indication and data handling in the kernel 53027185Sminshall * is such that if two separate TCP Urgent requests 53127185Sminshall * come in, one byte of TCP data will be overlaid. 53227185Sminshall * This is fatal for Telnet, but we try to live 53327185Sminshall * with it. 53427185Sminshall * 53527185Sminshall * In addition, in 4.2 (and...), a special protocol 53627185Sminshall * is needed to pick up the TCP Urgent data in 53727185Sminshall * the correct sequence. 53827185Sminshall * 53927185Sminshall * What we do is: if we think we are in urgent 54027185Sminshall * mode, we look to see if we are "at the mark". 54127185Sminshall * If we are, we do an OOB receive. If we run 54227185Sminshall * this twice, we will do the OOB receive twice, 54327185Sminshall * but the second will fail, since the second 54427185Sminshall * time we were "at the mark", but there wasn't 54527185Sminshall * any data there (the kernel doesn't reset 54627185Sminshall * "at the mark" until we do a normal read). 54727185Sminshall * Once we've read the OOB data, we go ahead 54827185Sminshall * and do normal reads. 54927185Sminshall * 55027185Sminshall * There is also another problem, which is that 55127185Sminshall * since the OOB byte we read doesn't put us 55227185Sminshall * out of OOB state, and since that byte is most 55327185Sminshall * likely the TELNET DM (data mark), we would 55427185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 55527185Sminshall * So, clocks to the rescue. If we've "just" 55627185Sminshall * received a DM, then we test for the 55727185Sminshall * presence of OOB data when the receive OOB 55827185Sminshall * fails (and AFTER we did the normal mode read 55927185Sminshall * to clear "at the mark"). 56027185Sminshall */ 56127185Sminshall if (SYNCHing) { 56227185Sminshall int atmark; 56327185Sminshall 56427185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 56527185Sminshall if (atmark) { 56627185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 56727185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 56827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 56927983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 57027185Sminshall SYNCHing = stilloob(net); 57127185Sminshall } 57227185Sminshall } 57327185Sminshall } else { 57427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 5756002Sroot } 57627185Sminshall } else { 57727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 57827185Sminshall } 57927185Sminshall settimer(didnetreceive); 58027649Sminshall #else /* !defined(SO_OOBINLINE)) */ 58127185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 58227649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 58327185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 58427185Sminshall ncc = 0; 58527185Sminshall else { 58627185Sminshall if (ncc <= 0) { 58727185Sminshall break; 58827185Sminshall } 58927185Sminshall netip = netibuf; 59027185Sminshall } 5916002Sroot } 5926002Sroot 5936002Sroot /* 5946002Sroot * Something to read from the pty... 5956002Sroot */ 59633267Sminshall if (FD_ISSET(p, &xbits)) { 59733267Sminshall if (read(p, ptyibuf, 1) != 1) { 59833267Sminshall break; 59933267Sminshall } 60033267Sminshall } 60127185Sminshall if (FD_ISSET(p, &ibits)) { 6026002Sroot pcc = read(p, ptyibuf, BUFSIZ); 6036002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 6046002Sroot pcc = 0; 6056002Sroot else { 6066002Sroot if (pcc <= 0) 6076002Sroot break; 60833267Sminshall /* Skip past "packet" */ 60933267Sminshall pcc--; 61033267Sminshall ptyip = ptyibuf+1; 6116002Sroot } 6126002Sroot } 61333267Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 61433267Sminshall netclear(); /* clear buffer back */ 61533267Sminshall *nfrontp++ = IAC; 61633267Sminshall *nfrontp++ = DM; 61733267Sminshall neturg = nfrontp-1; /* off by one XXX */ 61833267Sminshall ptyibuf[0] = 0; 61933267Sminshall } 6206002Sroot 6216002Sroot while (pcc > 0) { 6226002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6236002Sroot break; 6246002Sroot c = *ptyip++ & 0377, pcc--; 6256002Sroot if (c == IAC) 6266002Sroot *nfrontp++ = c; 6276002Sroot *nfrontp++ = c; 62831940Sbostic /* Don't do CR-NUL if we are in binary mode */ 62931940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 63027020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 63127020Sminshall *nfrontp++ = *ptyip++ & 0377; 63227020Sminshall pcc--; 63327020Sminshall } else 63427020Sminshall *nfrontp++ = '\0'; 63527020Sminshall } 6366002Sroot } 63727185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6386002Sroot netflush(); 6396002Sroot if (ncc > 0) 6406002Sroot telrcv(); 64127185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6426002Sroot ptyflush(); 6436002Sroot } 6446002Sroot cleanup(); 6456002Sroot } 6466002Sroot 6476002Sroot /* 6486002Sroot * State for recv fsm 6496002Sroot */ 6506002Sroot #define TS_DATA 0 /* base state */ 6516002Sroot #define TS_IAC 1 /* look for double IAC's */ 6526002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 65327649Sminshall #define TS_SB 3 /* throw away begin's... */ 65427649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6556002Sroot #define TS_WILL 5 /* will option negotiation */ 6566002Sroot #define TS_WONT 6 /* wont " */ 6576002Sroot #define TS_DO 7 /* do " */ 6586002Sroot #define TS_DONT 8 /* dont " */ 6596002Sroot 6606002Sroot telrcv() 6616002Sroot { 6626002Sroot register int c; 6636002Sroot static int state = TS_DATA; 6646002Sroot 6656002Sroot while (ncc > 0) { 6666002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 6676002Sroot return; 6686002Sroot c = *netip++ & 0377, ncc--; 6696002Sroot switch (state) { 6706002Sroot 67126090Sminshall case TS_CR: 67226090Sminshall state = TS_DATA; 67332097Sminshall /* Strip off \n or \0 after a \r */ 67426499Sminshall if ((c == 0) || (c == '\n')) { 67526090Sminshall break; 67626499Sminshall } 67726090Sminshall /* FALL THROUGH */ 67826090Sminshall 6796002Sroot case TS_DATA: 6806002Sroot if (c == IAC) { 6816002Sroot state = TS_IAC; 6826002Sroot break; 6836002Sroot } 6846002Sroot if (inter > 0) 6856002Sroot break; 68627020Sminshall /* 68732097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 68832097Sminshall * Many client implementations send \r\n when 68932097Sminshall * the user hits the CarriageReturn key. 69032097Sminshall * 69132097Sminshall * We USED to map \r\n ==> \n, since \r\n says 69227020Sminshall * that we want to be in column 1 of the next 69327020Sminshall * printable line, and \n is the standard 69427020Sminshall * unix way of saying that (\r is only good 69527020Sminshall * if CRMOD is set, which it normally is). 69627020Sminshall */ 69731940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 69832097Sminshall state = TS_CR; 69926499Sminshall } 70026499Sminshall *pfrontp++ = c; 7016002Sroot break; 7026002Sroot 7036002Sroot case TS_IAC: 7046002Sroot switch (c) { 7056002Sroot 7066002Sroot /* 7076002Sroot * Send the process on the pty side an 7086002Sroot * interrupt. Do this with a NULL or 7096002Sroot * interrupt char; depending on the tty mode. 7106002Sroot */ 7116002Sroot case IP: 7126002Sroot interrupt(); 7136002Sroot break; 7146002Sroot 71527229Sminshall case BREAK: 71627229Sminshall sendbrk(); 71727229Sminshall break; 71827229Sminshall 7196002Sroot /* 7206002Sroot * Are You There? 7216002Sroot */ 7226002Sroot case AYT: 72317583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 72417583Ssam nfrontp += 9; 7256002Sroot break; 7266002Sroot 7276002Sroot /* 72827185Sminshall * Abort Output 72927185Sminshall */ 73027185Sminshall case AO: { 73127185Sminshall struct ltchars tmpltc; 73227185Sminshall 73327185Sminshall ptyflush(); /* half-hearted */ 73427185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 73527185Sminshall if (tmpltc.t_flushc != '\377') { 73627185Sminshall *pfrontp++ = tmpltc.t_flushc; 73727185Sminshall } 73827260Sminshall netclear(); /* clear buffer back */ 73927185Sminshall *nfrontp++ = IAC; 74027185Sminshall *nfrontp++ = DM; 74127187Sminshall neturg = nfrontp-1; /* off by one XXX */ 74227185Sminshall break; 74327185Sminshall } 74427185Sminshall 74527185Sminshall /* 7466002Sroot * Erase Character and 7476002Sroot * Erase Line 7486002Sroot */ 7496002Sroot case EC: 75027185Sminshall case EL: { 75127185Sminshall struct sgttyb b; 75227185Sminshall char ch; 7536002Sroot 75427185Sminshall ptyflush(); /* half-hearted */ 75527185Sminshall ioctl(pty, TIOCGETP, &b); 75627185Sminshall ch = (c == EC) ? 75727185Sminshall b.sg_erase : b.sg_kill; 75827185Sminshall if (ch != '\377') { 75927185Sminshall *pfrontp++ = ch; 76027185Sminshall } 76127185Sminshall break; 76227185Sminshall } 76327185Sminshall 7646002Sroot /* 7656002Sroot * Check for urgent data... 7666002Sroot */ 7676002Sroot case DM: 76827185Sminshall SYNCHing = stilloob(net); 76927185Sminshall settimer(gotDM); 7706002Sroot break; 7716002Sroot 77227185Sminshall 7736002Sroot /* 7746002Sroot * Begin option subnegotiation... 7756002Sroot */ 7766002Sroot case SB: 77727649Sminshall state = TS_SB; 7786002Sroot continue; 7796002Sroot 7806002Sroot case WILL: 78127188Sminshall state = TS_WILL; 78227188Sminshall continue; 78327188Sminshall 7846002Sroot case WONT: 78527188Sminshall state = TS_WONT; 78627188Sminshall continue; 78727188Sminshall 7886002Sroot case DO: 78927188Sminshall state = TS_DO; 79027188Sminshall continue; 79127188Sminshall 7926002Sroot case DONT: 79327188Sminshall state = TS_DONT; 7946002Sroot continue; 7956002Sroot 7966002Sroot case IAC: 7976002Sroot *pfrontp++ = c; 7986002Sroot break; 7996002Sroot } 8006002Sroot state = TS_DATA; 8016002Sroot break; 8026002Sroot 80327649Sminshall case TS_SB: 80427649Sminshall if (c == IAC) { 80527649Sminshall state = TS_SE; 80627649Sminshall } else { 80727649Sminshall SB_ACCUM(c); 80827649Sminshall } 8096002Sroot break; 8106002Sroot 81127649Sminshall case TS_SE: 81227649Sminshall if (c != SE) { 81327649Sminshall if (c != IAC) { 81427649Sminshall SB_ACCUM(IAC); 81527649Sminshall } 81627649Sminshall SB_ACCUM(c); 81727649Sminshall state = TS_SB; 81827649Sminshall } else { 81927649Sminshall SB_TERM(); 82027649Sminshall suboption(); /* handle sub-option */ 82127649Sminshall state = TS_DATA; 82227649Sminshall } 8236002Sroot break; 8246002Sroot 8256002Sroot case TS_WILL: 82627983Sminshall if (hisopts[c] != OPT_YES) 8276002Sroot willoption(c); 8286002Sroot state = TS_DATA; 8296002Sroot continue; 8306002Sroot 8316002Sroot case TS_WONT: 83227983Sminshall if (hisopts[c] != OPT_NO) 8336002Sroot wontoption(c); 8346002Sroot state = TS_DATA; 8356002Sroot continue; 8366002Sroot 8376002Sroot case TS_DO: 83827983Sminshall if (myopts[c] != OPT_YES) 8396002Sroot dooption(c); 8406002Sroot state = TS_DATA; 8416002Sroot continue; 8426002Sroot 8436002Sroot case TS_DONT: 84427983Sminshall if (myopts[c] != OPT_NO) { 84527649Sminshall dontoption(c); 8466002Sroot } 8476002Sroot state = TS_DATA; 8486002Sroot continue; 8496002Sroot 8506002Sroot default: 85127898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8529218Ssam printf("telnetd: panic state=%d\n", state); 8536002Sroot exit(1); 8546002Sroot } 8556002Sroot } 8566002Sroot } 8576002Sroot 8586002Sroot willoption(option) 8596002Sroot int option; 8606002Sroot { 8616002Sroot char *fmt; 8626002Sroot 8636002Sroot switch (option) { 8646002Sroot 8656002Sroot case TELOPT_BINARY: 8666002Sroot mode(RAW, 0); 86727188Sminshall fmt = doopt; 86827188Sminshall break; 8696002Sroot 8706002Sroot case TELOPT_ECHO: 87127649Sminshall not42 = 0; /* looks like a 4.2 system */ 87227649Sminshall /* 87327649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 87427649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 87527649Sminshall * Kludge upon kludge! 87627649Sminshall */ 87727983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 87827649Sminshall dooption(TELOPT_ECHO); 87927649Sminshall } 88027649Sminshall fmt = dont; 88127188Sminshall break; 8826002Sroot 88327649Sminshall case TELOPT_TTYPE: 88427983Sminshall settimer(ttypeopt); 88527983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 88627983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 88727983Sminshall return; 88827983Sminshall } 88927983Sminshall fmt = doopt; 89027983Sminshall break; 89127983Sminshall 8926002Sroot case TELOPT_SGA: 8936002Sroot fmt = doopt; 8946002Sroot break; 8956002Sroot 8966002Sroot case TELOPT_TM: 8976002Sroot fmt = dont; 8986002Sroot break; 8996002Sroot 9006002Sroot default: 9016002Sroot fmt = dont; 9026002Sroot break; 9036002Sroot } 90427188Sminshall if (fmt == doopt) { 90527983Sminshall hisopts[option] = OPT_YES; 90627188Sminshall } else { 90727983Sminshall hisopts[option] = OPT_NO; 90827188Sminshall } 90932452Sbostic (void) sprintf(nfrontp, fmt, option); 9108379Ssam nfrontp += sizeof (dont) - 2; 9116002Sroot } 9126002Sroot 9136002Sroot wontoption(option) 9146002Sroot int option; 9156002Sroot { 9166002Sroot char *fmt; 9176002Sroot 9186002Sroot switch (option) { 9196002Sroot case TELOPT_ECHO: 92027649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 92127188Sminshall break; 9226002Sroot 9236002Sroot case TELOPT_BINARY: 9246002Sroot mode(0, RAW); 9256002Sroot break; 92628044Sminshall 92728044Sminshall case TELOPT_TTYPE: 92828044Sminshall settimer(ttypeopt); 92928044Sminshall break; 9306002Sroot } 93128044Sminshall 93227188Sminshall fmt = dont; 93327983Sminshall hisopts[option] = OPT_NO; 93432452Sbostic (void) sprintf(nfrontp, fmt, option); 9358379Ssam nfrontp += sizeof (doopt) - 2; 9366002Sroot } 9376002Sroot 9386002Sroot dooption(option) 9396002Sroot int option; 9406002Sroot { 9416002Sroot char *fmt; 9426002Sroot 9436002Sroot switch (option) { 9446002Sroot 9456002Sroot case TELOPT_TM: 9466002Sroot fmt = wont; 9476002Sroot break; 9486002Sroot 9496002Sroot case TELOPT_ECHO: 9506002Sroot mode(ECHO|CRMOD, 0); 95127188Sminshall fmt = will; 95227188Sminshall break; 9536002Sroot 9546002Sroot case TELOPT_BINARY: 9556002Sroot mode(RAW, 0); 95627188Sminshall fmt = will; 95727188Sminshall break; 9586002Sroot 9596002Sroot case TELOPT_SGA: 9606002Sroot fmt = will; 9616002Sroot break; 9626002Sroot 9636002Sroot default: 9646002Sroot fmt = wont; 9656002Sroot break; 9666002Sroot } 96727188Sminshall if (fmt == will) { 96827983Sminshall myopts[option] = OPT_YES; 96927188Sminshall } else { 97027983Sminshall myopts[option] = OPT_NO; 97127188Sminshall } 97232452Sbostic (void) sprintf(nfrontp, fmt, option); 9738379Ssam nfrontp += sizeof (doopt) - 2; 9746002Sroot } 9756002Sroot 97627649Sminshall 97727649Sminshall dontoption(option) 97827649Sminshall int option; 97927649Sminshall { 98027649Sminshall char *fmt; 98127649Sminshall 98227649Sminshall switch (option) { 98327649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 98432401Sminshall mode(0, ECHO); 98527649Sminshall fmt = wont; 98627649Sminshall break; 98727983Sminshall 98827649Sminshall default: 98927649Sminshall fmt = wont; 99027649Sminshall break; 99127649Sminshall } 99227983Sminshall 99327649Sminshall if (fmt = wont) { 99427983Sminshall myopts[option] = OPT_NO; 99527649Sminshall } else { 99627983Sminshall myopts[option] = OPT_YES; 99727649Sminshall } 99832452Sbostic (void) sprintf(nfrontp, fmt, option); 99927649Sminshall nfrontp += sizeof (wont) - 2; 100027649Sminshall } 100127649Sminshall 100227649Sminshall /* 100327649Sminshall * suboption() 100427649Sminshall * 100527649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 100627649Sminshall * side. 100727649Sminshall * 100827649Sminshall * Currently we recognize: 100927649Sminshall * 101027983Sminshall * Terminal type is 101127649Sminshall */ 101227649Sminshall 101327649Sminshall suboption() 101427649Sminshall { 101527983Sminshall switch (SB_GET()) { 101627983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 101727983Sminshall static char terminalname[5+41] = "TERM="; 101827983Sminshall 101927983Sminshall settimer(ttypesubopt); 102027983Sminshall 102127983Sminshall if (SB_GET() != TELQUAL_IS) { 102227983Sminshall return; /* ??? XXX but, this is the most robust */ 102327983Sminshall } 102427983Sminshall 102527983Sminshall terminaltype = terminalname+strlen(terminalname); 102627983Sminshall 102727983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 102827983Sminshall !SB_EOF()) { 102927983Sminshall register int c; 103027983Sminshall 103127983Sminshall c = SB_GET(); 103227983Sminshall if (isupper(c)) { 103327983Sminshall c = tolower(c); 103427983Sminshall } 103527983Sminshall *terminaltype++ = c; /* accumulate name */ 103627983Sminshall } 103727983Sminshall *terminaltype = 0; 103827983Sminshall terminaltype = terminalname; 103927983Sminshall break; 104027983Sminshall } 104127983Sminshall 104227649Sminshall default: 104327649Sminshall ; 104427649Sminshall } 104527649Sminshall } 104627649Sminshall 10476002Sroot mode(on, off) 10486002Sroot int on, off; 10496002Sroot { 10506002Sroot struct sgttyb b; 10516002Sroot 10526002Sroot ptyflush(); 10536002Sroot ioctl(pty, TIOCGETP, &b); 10546002Sroot b.sg_flags |= on; 10556002Sroot b.sg_flags &= ~off; 10566002Sroot ioctl(pty, TIOCSETP, &b); 10576002Sroot } 10586002Sroot 10596002Sroot /* 10606002Sroot * Send interrupt to process on other side of pty. 10616002Sroot * If it is in raw mode, just write NULL; 10626002Sroot * otherwise, write intr char. 10636002Sroot */ 10646002Sroot interrupt() 10656002Sroot { 10666002Sroot struct sgttyb b; 10676002Sroot struct tchars tchars; 10686002Sroot 10696002Sroot ptyflush(); /* half-hearted */ 10706002Sroot ioctl(pty, TIOCGETP, &b); 10716002Sroot if (b.sg_flags & RAW) { 10726002Sroot *pfrontp++ = '\0'; 10736002Sroot return; 10746002Sroot } 10756002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 10766002Sroot '\177' : tchars.t_intrc; 10776002Sroot } 10786002Sroot 107927229Sminshall /* 108027229Sminshall * Send quit to process on other side of pty. 108127229Sminshall * If it is in raw mode, just write NULL; 108227229Sminshall * otherwise, write quit char. 108327229Sminshall */ 108427229Sminshall sendbrk() 108527229Sminshall { 108627229Sminshall struct sgttyb b; 108727229Sminshall struct tchars tchars; 108827229Sminshall 108927229Sminshall ptyflush(); /* half-hearted */ 109027229Sminshall ioctl(pty, TIOCGETP, &b); 109127229Sminshall if (b.sg_flags & RAW) { 109227229Sminshall *pfrontp++ = '\0'; 109327229Sminshall return; 109427229Sminshall } 109527229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 109627229Sminshall '\034' : tchars.t_quitc; 109727229Sminshall } 109827229Sminshall 10996002Sroot ptyflush() 11006002Sroot { 11016002Sroot int n; 11026002Sroot 11036002Sroot if ((n = pfrontp - pbackp) > 0) 11046002Sroot n = write(pty, pbackp, n); 11058346Ssam if (n < 0) 11068346Ssam return; 11076002Sroot pbackp += n; 11086002Sroot if (pbackp == pfrontp) 11096002Sroot pbackp = pfrontp = ptyobuf; 11106002Sroot } 111127260Sminshall 111227260Sminshall /* 111327260Sminshall * nextitem() 111427260Sminshall * 111527260Sminshall * Return the address of the next "item" in the TELNET data 111627260Sminshall * stream. This will be the address of the next character if 111727260Sminshall * the current address is a user data character, or it will 111827260Sminshall * be the address of the character following the TELNET command 111927260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 112027260Sminshall * character. 112127260Sminshall */ 11226002Sroot 112327260Sminshall char * 112427260Sminshall nextitem(current) 112527260Sminshall char *current; 11266002Sroot { 112727260Sminshall if ((*current&0xff) != IAC) { 112827260Sminshall return current+1; 112927260Sminshall } 113027260Sminshall switch (*(current+1)&0xff) { 113127260Sminshall case DO: 113227260Sminshall case DONT: 113327260Sminshall case WILL: 113427260Sminshall case WONT: 113527260Sminshall return current+3; 113627260Sminshall case SB: /* loop forever looking for the SE */ 113727260Sminshall { 113827260Sminshall register char *look = current+2; 11396002Sroot 114027260Sminshall for (;;) { 114127260Sminshall if ((*look++&0xff) == IAC) { 114227260Sminshall if ((*look++&0xff) == SE) { 114327260Sminshall return look; 114427260Sminshall } 114527260Sminshall } 114627260Sminshall } 11478346Ssam } 114827260Sminshall default: 114927260Sminshall return current+2; 115027260Sminshall } 11516002Sroot } 11526002Sroot 115327185Sminshall 115427185Sminshall /* 115527260Sminshall * netclear() 115627260Sminshall * 115727260Sminshall * We are about to do a TELNET SYNCH operation. Clear 115827260Sminshall * the path to the network. 115927260Sminshall * 116027260Sminshall * Things are a bit tricky since we may have sent the first 116127260Sminshall * byte or so of a previous TELNET command into the network. 116227260Sminshall * So, we have to scan the network buffer from the beginning 116327260Sminshall * until we are up to where we want to be. 116427260Sminshall * 116527260Sminshall * A side effect of what we do, just to keep things 116627260Sminshall * simple, is to clear the urgent data pointer. The principal 116727260Sminshall * caller should be setting the urgent data pointer AFTER calling 116827260Sminshall * us in any case. 116927260Sminshall */ 117027260Sminshall 117127260Sminshall netclear() 117227260Sminshall { 117327260Sminshall register char *thisitem, *next; 117427260Sminshall char *good; 117527260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 117627260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 117727260Sminshall 117827260Sminshall thisitem = netobuf; 117927260Sminshall 118027260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 118127260Sminshall thisitem = next; 118227260Sminshall } 118327260Sminshall 118427260Sminshall /* Now, thisitem is first before/at boundary. */ 118527260Sminshall 118627260Sminshall good = netobuf; /* where the good bytes go */ 118727260Sminshall 118827260Sminshall while (nfrontp > thisitem) { 118927260Sminshall if (wewant(thisitem)) { 119027260Sminshall int length; 119127260Sminshall 119227260Sminshall next = thisitem; 119327260Sminshall do { 119427260Sminshall next = nextitem(next); 119527260Sminshall } while (wewant(next) && (nfrontp > next)); 119627260Sminshall length = next-thisitem; 119727260Sminshall bcopy(thisitem, good, length); 119827260Sminshall good += length; 119927260Sminshall thisitem = next; 120027260Sminshall } else { 120127260Sminshall thisitem = nextitem(thisitem); 120227260Sminshall } 120327260Sminshall } 120427260Sminshall 120527260Sminshall nbackp = netobuf; 120627260Sminshall nfrontp = good; /* next byte to be sent */ 120727260Sminshall neturg = 0; 120827260Sminshall } 120927260Sminshall 121027260Sminshall /* 121127185Sminshall * netflush 121227185Sminshall * Send as much data as possible to the network, 121327185Sminshall * handling requests for urgent data. 121427185Sminshall */ 121527185Sminshall 121627185Sminshall 121727185Sminshall netflush() 121827185Sminshall { 121927185Sminshall int n; 122027185Sminshall 122127185Sminshall if ((n = nfrontp - nbackp) > 0) { 122227649Sminshall /* 122327649Sminshall * if no urgent data, or if the other side appears to be an 122427649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 122527649Sminshall * write the entire buffer in non-OOB mode. 122627649Sminshall */ 122727649Sminshall if ((neturg == 0) || (not42 == 0)) { 122827185Sminshall n = write(net, nbackp, n); /* normal write */ 122927185Sminshall } else { 123027185Sminshall n = neturg - nbackp; 123127185Sminshall /* 123227185Sminshall * In 4.2 (and 4.3) systems, there is some question about 123327185Sminshall * what byte in a sendOOB operation is the "OOB" data. 123427185Sminshall * To make ourselves compatible, we only send ONE byte 123527185Sminshall * out of band, the one WE THINK should be OOB (though 123627185Sminshall * we really have more the TCP philosophy of urgent data 123727185Sminshall * rather than the Unix philosophy of OOB data). 123827185Sminshall */ 123927185Sminshall if (n > 1) { 124027185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 124127185Sminshall } else { 124227185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 124327185Sminshall } 124427185Sminshall } 124527185Sminshall } 124627185Sminshall if (n < 0) { 124727185Sminshall if (errno == EWOULDBLOCK) 124827185Sminshall return; 124927185Sminshall /* should blow this guy away... */ 125027185Sminshall return; 125127185Sminshall } 125227185Sminshall nbackp += n; 125327185Sminshall if (nbackp >= neturg) { 125427185Sminshall neturg = 0; 125527185Sminshall } 125627185Sminshall if (nbackp == nfrontp) { 125727185Sminshall nbackp = nfrontp = netobuf; 125827185Sminshall } 125927185Sminshall } 126027185Sminshall 12616002Sroot cleanup() 12626002Sroot { 12636002Sroot 12646002Sroot rmut(); 126510008Ssam vhangup(); /* XXX */ 126610191Ssam shutdown(net, 2); 12676002Sroot exit(1); 12686002Sroot } 12696002Sroot 12706002Sroot #include <utmp.h> 12716002Sroot 12726002Sroot struct utmp wtmp; 12736002Sroot char wtmpf[] = "/usr/adm/wtmp"; 127423567Sbloom char utmpf[] = "/etc/utmp"; 127523567Sbloom #define SCPYN(a, b) strncpy(a, b, sizeof(a)) 127623567Sbloom #define SCMPN(a, b) strncmp(a, b, sizeof(a)) 12776002Sroot 12786002Sroot rmut() 12796002Sroot { 12806002Sroot register f; 12816002Sroot int found = 0; 128223567Sbloom struct utmp *u, *utmp; 128323567Sbloom int nutmp; 128423567Sbloom struct stat statbf; 12856002Sroot 128623567Sbloom f = open(utmpf, O_RDWR); 12876002Sroot if (f >= 0) { 128823567Sbloom fstat(f, &statbf); 128923567Sbloom utmp = (struct utmp *)malloc(statbf.st_size); 129023567Sbloom if (!utmp) 129123567Sbloom syslog(LOG_ERR, "utmp malloc failed"); 129223567Sbloom if (statbf.st_size && utmp) { 129323567Sbloom nutmp = read(f, utmp, statbf.st_size); 129423567Sbloom nutmp /= sizeof(struct utmp); 129523567Sbloom 129623567Sbloom for (u = utmp ; u < &utmp[nutmp] ; u++) { 129723567Sbloom if (SCMPN(u->ut_line, line+5) || 129823567Sbloom u->ut_name[0]==0) 129923567Sbloom continue; 130023567Sbloom lseek(f, ((long)u)-((long)utmp), L_SET); 130123567Sbloom SCPYN(u->ut_name, ""); 130223567Sbloom SCPYN(u->ut_host, ""); 130323567Sbloom time(&u->ut_time); 130423567Sbloom write(f, (char *)u, sizeof(wtmp)); 130523567Sbloom found++; 130623567Sbloom } 13076002Sroot } 13086002Sroot close(f); 13096002Sroot } 13106002Sroot if (found) { 131117583Ssam f = open(wtmpf, O_WRONLY|O_APPEND); 13126002Sroot if (f >= 0) { 13136002Sroot SCPYN(wtmp.ut_line, line+5); 13146002Sroot SCPYN(wtmp.ut_name, ""); 131512683Ssam SCPYN(wtmp.ut_host, ""); 13166002Sroot time(&wtmp.ut_time); 131723567Sbloom write(f, (char *)&wtmp, sizeof(wtmp)); 13186002Sroot close(f); 13196002Sroot } 13206002Sroot } 13216002Sroot chmod(line, 0666); 13226002Sroot chown(line, 0, 0); 13236002Sroot line[strlen("/dev/")] = 'p'; 13246002Sroot chmod(line, 0666); 13256002Sroot chown(line, 0, 0); 13266002Sroot } 132733271Sminshall 132833271Sminshall char editedhost[32]; 132933271Sminshall 133033271Sminshall edithost(pat, host) 133133271Sminshall register char *pat; 133233271Sminshall register char *host; 133333271Sminshall { 133433271Sminshall register char *res = editedhost; 133533271Sminshall 133633271Sminshall if (!pat) 133733271Sminshall pat = ""; 133833271Sminshall while (*pat) { 133933271Sminshall switch (*pat) { 134033271Sminshall 134133271Sminshall case '#': 134233271Sminshall if (*host) 134333271Sminshall host++; 134433271Sminshall break; 134533271Sminshall 134633271Sminshall case '@': 134733271Sminshall if (*host) 134833271Sminshall *res++ = *host++; 134933271Sminshall break; 135033271Sminshall 135133271Sminshall default: 135233271Sminshall *res++ = *pat; 135333271Sminshall break; 135433271Sminshall 135533271Sminshall } 135633271Sminshall if (res == &editedhost[sizeof editedhost - 1]) { 135733271Sminshall *res = '\0'; 135833271Sminshall return; 135933271Sminshall } 136033271Sminshall pat++; 136133271Sminshall } 136233271Sminshall if (*host) 136333271Sminshall strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); 136433271Sminshall else 136533271Sminshall *res = '\0'; 136633271Sminshall editedhost[sizeof editedhost - 1] = '\0'; 136733271Sminshall } 136833271Sminshall 136933271Sminshall static char *putlocation; 137033271Sminshall 137133271Sminshall puts(s) 137233271Sminshall register char *s; 137333271Sminshall { 137433271Sminshall 137533271Sminshall while (*s) 137633271Sminshall putchr(*s++); 137733271Sminshall } 137833271Sminshall 137933271Sminshall putchr(cc) 138033271Sminshall { 138133271Sminshall *putlocation++ = cc; 138233271Sminshall } 138333271Sminshall 1384*34424Sbostic putf(cp, where) 138533271Sminshall register char *cp; 138633271Sminshall char *where; 138733271Sminshall { 138833271Sminshall char *slash; 138933271Sminshall char datebuffer[60]; 139033271Sminshall extern char *rindex(); 139133271Sminshall 139233271Sminshall putlocation = where; 139333271Sminshall 139433271Sminshall while (*cp) { 139533271Sminshall if (*cp != '%') { 139633271Sminshall putchr(*cp++); 139733271Sminshall continue; 139833271Sminshall } 139933271Sminshall switch (*++cp) { 140033271Sminshall 140133271Sminshall case 't': 140233271Sminshall slash = rindex(line, '/'); 140333271Sminshall if (slash == (char *) 0) 140433271Sminshall puts(line); 140533271Sminshall else 140633271Sminshall puts(&slash[1]); 140733271Sminshall break; 140833271Sminshall 140933271Sminshall case 'h': 141033271Sminshall puts(editedhost); 141133271Sminshall break; 141233271Sminshall 141333271Sminshall case 'd': 141433271Sminshall get_date(datebuffer); 141533271Sminshall puts(datebuffer); 141633271Sminshall break; 141733271Sminshall 141833271Sminshall case '%': 141933271Sminshall putchr('%'); 142033271Sminshall break; 142133271Sminshall } 142233271Sminshall cp++; 142333271Sminshall } 142433271Sminshall } 1425