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 634778Sbostic * provided that the above copyright notice and this paragraph are 734778Sbostic * duplicated in all such forms and that any documentation, 834778Sbostic * advertising materials, and other materials related to such 934778Sbostic * distribution and use acknowledge that the software was developed 1034778Sbostic * by the University of California, Berkeley. The name of the 1134778Sbostic * University may not be used to endorse or promote products derived 1234778Sbostic * from this software without specific prior written permission. 1334778Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1434778Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1534778Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1621182Sdist */ 1721182Sdist 186295Sroot #ifndef lint 1921182Sdist char copyright[] = 2033687Sbostic "@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\ 2121182Sdist All rights reserved.\n"; 2233687Sbostic #endif /* not lint */ 236295Sroot 2421182Sdist #ifndef lint 25*37210Sminshall static char sccsid[] = "@(#)telnetd.c 5.32 (Berkeley) 03/18/89"; 2633687Sbostic #endif /* not lint */ 2721182Sdist 286002Sroot /* 2927898Skarels * Telnet server. 306002Sroot */ 3127898Skarels #include <sys/param.h> 329218Ssam #include <sys/socket.h> 3313608Ssam #include <sys/wait.h> 3417583Ssam #include <sys/file.h> 3520188Skarels #include <sys/stat.h> 3627185Sminshall #include <sys/time.h> 379218Ssam 389218Ssam #include <netinet/in.h> 399218Ssam 4012216Ssam #include <arpa/telnet.h> 4112216Ssam 426002Sroot #include <stdio.h> 436002Sroot #include <signal.h> 446002Sroot #include <errno.h> 456002Sroot #include <sgtty.h> 468346Ssam #include <netdb.h> 4717187Sralph #include <syslog.h> 4827649Sminshall #include <ctype.h> 499218Ssam 5027983Sminshall #define OPT_NO 0 /* won't do this option */ 5127983Sminshall #define OPT_YES 1 /* will do this option */ 5227983Sminshall #define OPT_YES_BUT_ALWAYS_LOOK 2 5327983Sminshall #define OPT_NO_BUT_ALWAYS_LOOK 3 546002Sroot char hisopts[256]; 556002Sroot char myopts[256]; 566002Sroot 576002Sroot char doopt[] = { IAC, DO, '%', 'c', 0 }; 586002Sroot char dont[] = { IAC, DONT, '%', 'c', 0 }; 596002Sroot char will[] = { IAC, WILL, '%', 'c', 0 }; 606002Sroot char wont[] = { IAC, WONT, '%', 'c', 0 }; 616002Sroot 626002Sroot /* 636002Sroot * I/O data buffers, pointers, and counters. 646002Sroot */ 656002Sroot char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 6627649Sminshall 676002Sroot char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; 6827649Sminshall 696002Sroot char netibuf[BUFSIZ], *netip = netibuf; 7027649Sminshall #define NIACCUM(c) { *netip++ = c; \ 7127649Sminshall ncc++; \ 7227649Sminshall } 7327649Sminshall 746388Ssam char netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf; 7527185Sminshall char *neturg = 0; /* one past last bye of urgent data */ 7627649Sminshall /* the remote system seems to NOT be an old 4.2 */ 7727649Sminshall int not42 = 1; 7827649Sminshall 7933271Sminshall #define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r" 8027649Sminshall 8127983Sminshall /* buffer for sub-options */ 8227983Sminshall char subbuffer[100], *subpointer= subbuffer, *subend= subbuffer; 8327649Sminshall #define SB_CLEAR() subpointer = subbuffer; 8427983Sminshall #define SB_TERM() { subend = subpointer; SB_CLEAR(); } 8527649Sminshall #define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \ 8627649Sminshall *subpointer++ = (c); \ 8727649Sminshall } 8827983Sminshall #define SB_GET() ((*subpointer++)&0xff) 8927983Sminshall #define SB_EOF() (subpointer >= subend) 9027649Sminshall 916002Sroot int pcc, ncc; 926002Sroot 936002Sroot int pty, net; 946002Sroot int inter; 9513799Ssam extern char **environ; 966002Sroot extern int errno; 9720188Skarels char *line; 9827185Sminshall int SYNCHing = 0; /* we are in TELNET SYNCH mode */ 9927185Sminshall /* 10027185Sminshall * The following are some clocks used to decide how to interpret 10127185Sminshall * the relationship between various variables. 10227185Sminshall */ 1036002Sroot 10427185Sminshall struct { 10527185Sminshall int 10627185Sminshall system, /* what the current time is */ 10727185Sminshall echotoggle, /* last time user entered echo character */ 10827185Sminshall modenegotiated, /* last time operating mode negotiated */ 10927185Sminshall didnetreceive, /* last time we read data from network */ 11027983Sminshall ttypeopt, /* ttype will/won't received */ 11127983Sminshall ttypesubopt, /* ttype subopt is received */ 11227983Sminshall getterminal, /* time started to get terminal information */ 11327185Sminshall gotDM; /* when did we last see a data mark */ 11427185Sminshall } clocks; 11527185Sminshall 11627983Sminshall #define settimer(x) (clocks.x = ++clocks.system) 11727983Sminshall #define sequenceIs(x,y) (clocks.x < clocks.y) 11827185Sminshall 1196002Sroot main(argc, argv) 1206002Sroot char *argv[]; 1216002Sroot { 12216371Skarels struct sockaddr_in from; 12317156Ssam int on = 1, fromlen; 1246002Sroot 125*37210Sminshall if ((argc > 1) && (strcmp(argv[1], "-debug") == 0)) { 12627185Sminshall int s, ns, foo; 12727185Sminshall struct servent *sp; 12827185Sminshall static struct sockaddr_in sin = { AF_INET }; 12927185Sminshall 13027185Sminshall argc--, argv++; 131*37210Sminshall if (argc > 1) { 132*37210Sminshall sin.sin_port = atoi(argv[1]); 13327185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 134*37210Sminshall } else { 135*37210Sminshall sp = getservbyname("telnet", "tcp"); 136*37210Sminshall if (sp == 0) { 137*37210Sminshall fprintf(stderr, 138*37210Sminshall "telnetd: tcp/telnet: unknown service\n"); 139*37210Sminshall exit(1); 140*37210Sminshall } 141*37210Sminshall sin.sin_port = sp->s_port; 14227185Sminshall } 14327185Sminshall 14427185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 14527185Sminshall if (s < 0) { 14627185Sminshall perror("telnetd: socket");; 14727185Sminshall exit(1); 14827185Sminshall } 14927185Sminshall if (bind(s, &sin, sizeof sin) < 0) { 15027185Sminshall perror("bind"); 15127185Sminshall exit(1); 15227185Sminshall } 15327185Sminshall if (listen(s, 1) < 0) { 15427185Sminshall perror("listen"); 15527185Sminshall exit(1); 15627185Sminshall } 15727185Sminshall foo = sizeof sin; 15827185Sminshall ns = accept(s, &sin, &foo); 15927185Sminshall if (ns < 0) { 16027185Sminshall perror("accept"); 16127185Sminshall exit(1); 16227185Sminshall } 16327185Sminshall dup2(ns, 0); 16427185Sminshall close(s); 16527185Sminshall } 16624855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 16716371Skarels fromlen = sizeof (from); 16816371Skarels if (getpeername(0, &from, &fromlen) < 0) { 16916371Skarels fprintf(stderr, "%s: ", argv[0]); 17016371Skarels perror("getpeername"); 17116371Skarels _exit(1); 1728346Ssam } 17317156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 17417187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 17510418Ssam } 17616371Skarels doit(0, &from); 1776002Sroot } 1786002Sroot 17927983Sminshall char *terminaltype = 0; 18027983Sminshall char *envinit[2]; 18127983Sminshall int cleanup(); 18227649Sminshall 18327649Sminshall /* 18427983Sminshall * ttloop 18527649Sminshall * 18627983Sminshall * A small subroutine to flush the network output buffer, get some data 18727983Sminshall * from the network, and pass it through the telnet state machine. We 18827983Sminshall * also flush the pty input buffer (by dropping its data) if it becomes 18927983Sminshall * too full. 19027983Sminshall */ 19127983Sminshall 19227983Sminshall void 19327983Sminshall ttloop() 19427983Sminshall { 19527983Sminshall if (nfrontp-nbackp) { 19627983Sminshall netflush(); 19727983Sminshall } 19827983Sminshall ncc = read(net, netibuf, sizeof netibuf); 19927983Sminshall if (ncc < 0) { 20027983Sminshall syslog(LOG_INFO, "ttloop: read: %m\n"); 20128044Sminshall exit(1); 20228044Sminshall } else if (ncc == 0) { 20328044Sminshall syslog(LOG_INFO, "ttloop: peer died: %m\n"); 20428044Sminshall exit(1); 20527983Sminshall } 20627983Sminshall netip = netibuf; 20727983Sminshall telrcv(); /* state machine */ 20827983Sminshall if (ncc > 0) { 20927983Sminshall pfrontp = pbackp = ptyobuf; 21027983Sminshall telrcv(); 21127983Sminshall } 21227983Sminshall } 21327983Sminshall 21427983Sminshall /* 215*37210Sminshall * getterminalspeed 216*37210Sminshall * 217*37210Sminshall * Ask the other end to send along its terminal speed. 218*37210Sminshall * subopt does the rest. Interlocked so it can't happen during 219*37210Sminshall * getterminaltype. 220*37210Sminshall */ 221*37210Sminshall 222*37210Sminshall void 223*37210Sminshall getterminalspeed() 224*37210Sminshall { 225*37210Sminshall static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 226*37210Sminshall 227*37210Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 228*37210Sminshall nfrontp += sizeof sbuf; 229*37210Sminshall } 230*37210Sminshall 231*37210Sminshall /* 23227983Sminshall * getterminaltype 23327649Sminshall * 23427983Sminshall * Ask the other end to send along its terminal type. 23527983Sminshall * Output is the variable terminaltype filled in. 23627649Sminshall */ 23727649Sminshall 23827983Sminshall void 23927983Sminshall getterminaltype() 24027649Sminshall { 24127983Sminshall static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; 24227649Sminshall 24327983Sminshall settimer(getterminal); 24427983Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 24527983Sminshall nfrontp += sizeof sbuf; 24628044Sminshall hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; 24727983Sminshall while (sequenceIs(ttypeopt, getterminal)) { 24827983Sminshall ttloop(); 24927649Sminshall } 25027983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES) { 25127983Sminshall static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 25227983Sminshall 25327983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 25427983Sminshall nfrontp += sizeof sbbuf; 25527983Sminshall while (sequenceIs(ttypesubopt, getterminal)) { 25627983Sminshall ttloop(); 25727983Sminshall } 25827983Sminshall } 25927649Sminshall } 26027649Sminshall 2616002Sroot /* 2626002Sroot * Get a pty, scan input lines. 2636002Sroot */ 26412683Ssam doit(f, who) 26512683Ssam int f; 26612683Ssam struct sockaddr_in *who; 2676002Sroot { 26820188Skarels char *host, *inet_ntoa(); 26917583Ssam int i, p, t; 2706002Sroot struct sgttyb b; 27112683Ssam struct hostent *hp; 27227649Sminshall int c; 2736002Sroot 27420188Skarels for (c = 'p'; c <= 's'; c++) { 27520188Skarels struct stat stb; 27620188Skarels 27720188Skarels line = "/dev/ptyXX"; 27820188Skarels line[strlen("/dev/pty")] = c; 27920188Skarels line[strlen("/dev/ptyp")] = '0'; 28020188Skarels if (stat(line, &stb) < 0) 28120188Skarels break; 28217583Ssam for (i = 0; i < 16; i++) { 28334424Sbostic line[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; 28434424Sbostic p = open(line, O_RDWR); 28517583Ssam if (p > 0) 28617583Ssam goto gotpty; 28717583Ssam } 2886002Sroot } 2899244Ssam fatal(f, "All network ports in use"); 2909244Ssam /*NOTREACHED*/ 2916002Sroot gotpty: 2926002Sroot dup2(f, 0); 29320188Skarels line[strlen("/dev/")] = 't'; 29417583Ssam t = open("/dev/tty", O_RDWR); 2956002Sroot if (t >= 0) { 2966002Sroot ioctl(t, TIOCNOTTY, 0); 2976002Sroot close(t); 2986002Sroot } 29920188Skarels t = open(line, O_RDWR); 3009244Ssam if (t < 0) 30134424Sbostic fatalperror(f, line); 30234424Sbostic if (fchmod(t, 0)) 30334424Sbostic fatalperror(f, line); 30434424Sbostic (void)signal(SIGHUP, SIG_IGN); 30534424Sbostic vhangup(); 30634424Sbostic (void)signal(SIGHUP, SIG_DFL); 30734424Sbostic t = open(line, O_RDWR); 30834424Sbostic if (t < 0) 30934424Sbostic fatalperror(f, line); 3106002Sroot ioctl(t, TIOCGETP, &b); 3116388Ssam b.sg_flags = CRMOD|XTABS|ANYP; 3126002Sroot ioctl(t, TIOCSETP, &b); 3136388Ssam ioctl(p, TIOCGETP, &b); 3148379Ssam b.sg_flags &= ~ECHO; 315*37210Sminshall b.sg_ospeed = b.sg_ispeed = B9600; 3166388Ssam ioctl(p, TIOCSETP, &b); 31712683Ssam hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), 31812683Ssam who->sin_family); 31912683Ssam if (hp) 32012683Ssam host = hp->h_name; 32112683Ssam else 32217444Sralph host = inet_ntoa(who->sin_addr); 32327983Sminshall 32427983Sminshall net = f; 32527983Sminshall pty = p; 32627983Sminshall 32727983Sminshall /* 328*37210Sminshall * get terminal type and size. 32927983Sminshall */ 33027983Sminshall getterminaltype(); 33127983Sminshall 3329244Ssam if ((i = fork()) < 0) 33334424Sbostic fatalperror(f, "fork"); 3346002Sroot if (i) 3356002Sroot telnet(f, p); 3366002Sroot close(f); 3376002Sroot close(p); 3386002Sroot dup2(t, 0); 3396002Sroot dup2(t, 1); 3406002Sroot dup2(t, 2); 3416002Sroot close(t); 34227983Sminshall envinit[0] = terminaltype; 34327983Sminshall envinit[1] = 0; 34413799Ssam environ = envinit; 34527649Sminshall /* 34627649Sminshall * -h : pass on name of host. 34727983Sminshall * WARNING: -h is accepted by login if and only if 34827983Sminshall * getuid() == 0. 34927649Sminshall * -p : don't clobber the environment (so terminal type stays set). 35027649Sminshall */ 35127649Sminshall execl("/bin/login", "login", "-h", host, 35227983Sminshall terminaltype ? "-p" : 0, 0); 35336878Skarels syslog(LOG_ERR, "/bin/login: %m\n"); 35436878Skarels fatalperror(2, "/bin/login"); 3559244Ssam /*NOTREACHED*/ 3569244Ssam } 3579244Ssam 3589244Ssam fatal(f, msg) 3599244Ssam int f; 3609244Ssam char *msg; 3619244Ssam { 3629244Ssam char buf[BUFSIZ]; 3639244Ssam 36417583Ssam (void) sprintf(buf, "telnetd: %s.\r\n", msg); 3659244Ssam (void) write(f, buf, strlen(buf)); 3666002Sroot exit(1); 3676002Sroot } 3686002Sroot 36934424Sbostic fatalperror(f, msg) 3709244Ssam int f; 3719244Ssam char *msg; 3729244Ssam { 3739244Ssam char buf[BUFSIZ]; 3749244Ssam extern char *sys_errlist[]; 3759244Ssam 37617583Ssam (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); 3779244Ssam fatal(f, buf); 3789244Ssam } 3799244Ssam 38027185Sminshall 3816002Sroot /* 38227185Sminshall * Check a descriptor to see if out of band data exists on it. 38327185Sminshall */ 38427185Sminshall 38527185Sminshall 38627185Sminshall stilloob(s) 38727185Sminshall int s; /* socket number */ 38827185Sminshall { 38927185Sminshall static struct timeval timeout = { 0 }; 39027185Sminshall fd_set excepts; 39127185Sminshall int value; 39227185Sminshall 39327185Sminshall do { 39427185Sminshall FD_ZERO(&excepts); 39527185Sminshall FD_SET(s, &excepts); 39627185Sminshall value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout); 39727898Skarels } while ((value == -1) && (errno == EINTR)); 39827185Sminshall 39927185Sminshall if (value < 0) { 40034424Sbostic fatalperror(pty, "select"); 40127185Sminshall } 40227185Sminshall if (FD_ISSET(s, &excepts)) { 40327185Sminshall return 1; 40427185Sminshall } else { 40527185Sminshall return 0; 40627185Sminshall } 40727185Sminshall } 40827185Sminshall 40927185Sminshall /* 4106002Sroot * Main loop. Select from pty and network, and 4116002Sroot * hand data to telnet receiver finite state machine. 4126002Sroot */ 4136002Sroot telnet(f, p) 4146002Sroot { 4156002Sroot int on = 1; 41627898Skarels char hostname[MAXHOSTNAMELEN]; 41733271Sminshall #define TABBUFSIZ 512 41833271Sminshall char defent[TABBUFSIZ]; 41933271Sminshall char defstrs[TABBUFSIZ]; 42033271Sminshall #undef TABBUFSIZ 42133271Sminshall char *HE; 42233271Sminshall char *HN; 42333271Sminshall char *IM; 4246002Sroot 4256002Sroot ioctl(f, FIONBIO, &on); 4266002Sroot ioctl(p, FIONBIO, &on); 42733267Sminshall ioctl(p, TIOCPKT, &on); 42827649Sminshall #if defined(SO_OOBINLINE) 42927649Sminshall setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 43027649Sminshall #endif /* defined(SO_OOBINLINE) */ 4316002Sroot signal(SIGTSTP, SIG_IGN); 43232400Sminshall /* 43332400Sminshall * Ignoring SIGTTOU keeps the kernel from blocking us 43432400Sminshall * in ttioctl() in /sys/tty.c. 43532400Sminshall */ 43632400Sminshall signal(SIGTTOU, SIG_IGN); 43713028Ssam signal(SIGCHLD, cleanup); 43826083Slepreau setpgrp(0, 0); 4396002Sroot 4408379Ssam /* 44127185Sminshall * Request to do remote echo and to suppress go ahead. 4428379Ssam */ 44327983Sminshall if (!myopts[TELOPT_ECHO]) { 44427983Sminshall dooption(TELOPT_ECHO); 44527983Sminshall } 44627983Sminshall if (!myopts[TELOPT_SGA]) { 44727983Sminshall dooption(TELOPT_SGA); 44827983Sminshall } 449*37210Sminshall if (!hisopts[TELOPT_NAWS]) { 450*37210Sminshall willoption(TELOPT_NAWS); 451*37210Sminshall hisopts[TELOPT_NAWS] = OPT_NO; 452*37210Sminshall } 453*37210Sminshall if (!hisopts[TELOPT_TSPEED]) { 454*37210Sminshall willoption(TELOPT_TSPEED); 455*37210Sminshall hisopts[TELOPT_TSPEED] = OPT_NO; 456*37210Sminshall } 457*37210Sminshall if (!hisopts[TELOPT_LFLOW]) { 458*37210Sminshall willoption(TELOPT_LFLOW); 459*37210Sminshall hisopts[TELOPT_LFLOW] = OPT_NO; 460*37210Sminshall } 461*37210Sminshall 46212713Ssam /* 46327649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 46427649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 46527649Sminshall * 46627649Sminshall * To find out, we send out a "DO ECHO". If the remote system 46727649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 46827649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 46927649Sminshall * WE, the server, sends it; it does NOT mean that the client will 47027649Sminshall * echo the terminal input). 47127649Sminshall */ 47232452Sbostic (void) sprintf(nfrontp, doopt, TELOPT_ECHO); 47327649Sminshall nfrontp += sizeof doopt-2; 47427983Sminshall hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK; 47527649Sminshall 47627649Sminshall /* 47712713Ssam * Show banner that getty never gave. 47827797Sminshall * 47933271Sminshall * We put the banner in the pty input buffer. This way, it 48033271Sminshall * gets carriage return null processing, etc., just like all 48133271Sminshall * other pty --> client data. 48212713Ssam */ 48327797Sminshall 48412713Ssam gethostname(hostname, sizeof (hostname)); 48533271Sminshall if (getent(defent, "default") == 1) { 48633271Sminshall char *getstr(); 48733271Sminshall char *p=defstrs; 48827649Sminshall 48933271Sminshall HE = getstr("he", &p); 49033271Sminshall HN = getstr("hn", &p); 49133271Sminshall IM = getstr("im", &p); 49233271Sminshall if (HN && *HN) 49333271Sminshall strcpy(hostname, HN); 49433271Sminshall edithost(HE, hostname); 49533271Sminshall if (IM && *IM) 49634424Sbostic putf(IM, ptyibuf+1); 49733271Sminshall } else { 49833271Sminshall sprintf(ptyibuf+1, BANNER, hostname); 49933271Sminshall } 50027797Sminshall 50133271Sminshall ptyip = ptyibuf+1; /* Prime the pump */ 50233271Sminshall pcc = strlen(ptyip); /* ditto */ 50333271Sminshall 50433267Sminshall /* Clear ptybuf[0] - where the packet information is received */ 50533267Sminshall ptyibuf[0] = 0; 50633271Sminshall 50727649Sminshall /* 50827649Sminshall * Call telrcv() once to pick up anything received during 50927649Sminshall * terminal type negotiation. 51027649Sminshall */ 51127649Sminshall telrcv(); 51227649Sminshall 5136002Sroot for (;;) { 51427185Sminshall fd_set ibits, obits, xbits; 5156002Sroot register int c; 5166002Sroot 51727185Sminshall if (ncc < 0 && pcc < 0) 51827185Sminshall break; 51927185Sminshall 52027185Sminshall FD_ZERO(&ibits); 52127185Sminshall FD_ZERO(&obits); 52227185Sminshall FD_ZERO(&xbits); 5236002Sroot /* 5246002Sroot * Never look for input if there's still 5256002Sroot * stuff in the corresponding output buffer 5266002Sroot */ 52727185Sminshall if (nfrontp - nbackp || pcc > 0) { 52827185Sminshall FD_SET(f, &obits); 52927185Sminshall } else { 53027185Sminshall FD_SET(p, &ibits); 53127185Sminshall } 53227185Sminshall if (pfrontp - pbackp || ncc > 0) { 53327185Sminshall FD_SET(p, &obits); 53427185Sminshall } else { 53527185Sminshall FD_SET(f, &ibits); 53627185Sminshall } 53727185Sminshall if (!SYNCHing) { 53827185Sminshall FD_SET(f, &xbits); 53927185Sminshall } 540*37210Sminshall FD_SET(p, &xbits); 54127185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 54227185Sminshall (struct timeval *)0)) < 1) { 54327185Sminshall if (c == -1) { 54427185Sminshall if (errno == EINTR) { 54527185Sminshall continue; 54627185Sminshall } 54727185Sminshall } 5486002Sroot sleep(5); 5496002Sroot continue; 5506002Sroot } 5516002Sroot 5526002Sroot /* 55327185Sminshall * Any urgent data? 55427185Sminshall */ 55527185Sminshall if (FD_ISSET(net, &xbits)) { 55627185Sminshall SYNCHing = 1; 55727185Sminshall } 55827185Sminshall 55927185Sminshall /* 5606002Sroot * Something to read from the network... 5616002Sroot */ 56227185Sminshall if (FD_ISSET(net, &ibits)) { 56327649Sminshall #if !defined(SO_OOBINLINE) 56427185Sminshall /* 56527898Skarels * In 4.2 (and 4.3 beta) systems, the 56627185Sminshall * OOB indication and data handling in the kernel 56727185Sminshall * is such that if two separate TCP Urgent requests 56827185Sminshall * come in, one byte of TCP data will be overlaid. 56927185Sminshall * This is fatal for Telnet, but we try to live 57027185Sminshall * with it. 57127185Sminshall * 57227185Sminshall * In addition, in 4.2 (and...), a special protocol 57327185Sminshall * is needed to pick up the TCP Urgent data in 57427185Sminshall * the correct sequence. 57527185Sminshall * 57627185Sminshall * What we do is: if we think we are in urgent 57727185Sminshall * mode, we look to see if we are "at the mark". 57827185Sminshall * If we are, we do an OOB receive. If we run 57927185Sminshall * this twice, we will do the OOB receive twice, 58027185Sminshall * but the second will fail, since the second 58127185Sminshall * time we were "at the mark", but there wasn't 58227185Sminshall * any data there (the kernel doesn't reset 58327185Sminshall * "at the mark" until we do a normal read). 58427185Sminshall * Once we've read the OOB data, we go ahead 58527185Sminshall * and do normal reads. 58627185Sminshall * 58727185Sminshall * There is also another problem, which is that 58827185Sminshall * since the OOB byte we read doesn't put us 58927185Sminshall * out of OOB state, and since that byte is most 59027185Sminshall * likely the TELNET DM (data mark), we would 59127185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 59227185Sminshall * So, clocks to the rescue. If we've "just" 59327185Sminshall * received a DM, then we test for the 59427185Sminshall * presence of OOB data when the receive OOB 59527185Sminshall * fails (and AFTER we did the normal mode read 59627185Sminshall * to clear "at the mark"). 59727185Sminshall */ 59827185Sminshall if (SYNCHing) { 59927185Sminshall int atmark; 60027185Sminshall 60127185Sminshall ioctl(net, SIOCATMARK, (char *)&atmark); 60227185Sminshall if (atmark) { 60327185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 60427185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 60527185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 60627983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 60727185Sminshall SYNCHing = stilloob(net); 60827185Sminshall } 60927185Sminshall } 61027185Sminshall } else { 61127185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 6126002Sroot } 61327185Sminshall } else { 61427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 61527185Sminshall } 61627185Sminshall settimer(didnetreceive); 61727649Sminshall #else /* !defined(SO_OOBINLINE)) */ 61827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 61927649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 62027185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 62127185Sminshall ncc = 0; 62227185Sminshall else { 62327185Sminshall if (ncc <= 0) { 62427185Sminshall break; 62527185Sminshall } 62627185Sminshall netip = netibuf; 62727185Sminshall } 6286002Sroot } 6296002Sroot 6306002Sroot /* 6316002Sroot * Something to read from the pty... 6326002Sroot */ 633*37210Sminshall if (FD_ISSET(p, &ibits) || FD_ISSET(p, &xbits)) { 6346002Sroot pcc = read(p, ptyibuf, BUFSIZ); 6356002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 6366002Sroot pcc = 0; 6376002Sroot else { 6386002Sroot if (pcc <= 0) 6396002Sroot break; 640*37210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 641*37210Sminshall netclear(); /* clear buffer back */ 642*37210Sminshall *nfrontp++ = IAC; 643*37210Sminshall *nfrontp++ = DM; 644*37210Sminshall neturg = nfrontp-1; /* off by one XXX */ 645*37210Sminshall } 646*37210Sminshall if (hisopts[TELOPT_LFLOW] && 647*37210Sminshall (ptyibuf[0] & 648*37210Sminshall (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 649*37210Sminshall sprintf(nfrontp,"%c%c%c%c%c%c", 650*37210Sminshall IAC, SB, TELOPT_LFLOW, 651*37210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 652*37210Sminshall IAC, SE); 653*37210Sminshall nfrontp += 6; 654*37210Sminshall } 65533267Sminshall pcc--; 65633267Sminshall ptyip = ptyibuf+1; 657*37210Sminshall } 6586002Sroot } 6596002Sroot 6606002Sroot while (pcc > 0) { 6616002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6626002Sroot break; 6636002Sroot c = *ptyip++ & 0377, pcc--; 6646002Sroot if (c == IAC) 6656002Sroot *nfrontp++ = c; 6666002Sroot *nfrontp++ = c; 66731940Sbostic /* Don't do CR-NUL if we are in binary mode */ 66831940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 66927020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 67027020Sminshall *nfrontp++ = *ptyip++ & 0377; 67127020Sminshall pcc--; 67227020Sminshall } else 67327020Sminshall *nfrontp++ = '\0'; 67427020Sminshall } 6756002Sroot } 67627185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 6776002Sroot netflush(); 6786002Sroot if (ncc > 0) 6796002Sroot telrcv(); 68027185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 6816002Sroot ptyflush(); 6826002Sroot } 6836002Sroot cleanup(); 6846002Sroot } 6856002Sroot 6866002Sroot /* 6876002Sroot * State for recv fsm 6886002Sroot */ 6896002Sroot #define TS_DATA 0 /* base state */ 6906002Sroot #define TS_IAC 1 /* look for double IAC's */ 6916002Sroot #define TS_CR 2 /* CR-LF ->'s CR */ 69227649Sminshall #define TS_SB 3 /* throw away begin's... */ 69327649Sminshall #define TS_SE 4 /* ...end's (suboption negotiation) */ 6946002Sroot #define TS_WILL 5 /* will option negotiation */ 6956002Sroot #define TS_WONT 6 /* wont " */ 6966002Sroot #define TS_DO 7 /* do " */ 6976002Sroot #define TS_DONT 8 /* dont " */ 6986002Sroot 6996002Sroot telrcv() 7006002Sroot { 7016002Sroot register int c; 7026002Sroot static int state = TS_DATA; 7036002Sroot 7046002Sroot while (ncc > 0) { 7056002Sroot if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) 7066002Sroot return; 7076002Sroot c = *netip++ & 0377, ncc--; 7086002Sroot switch (state) { 7096002Sroot 71026090Sminshall case TS_CR: 71126090Sminshall state = TS_DATA; 71232097Sminshall /* Strip off \n or \0 after a \r */ 71326499Sminshall if ((c == 0) || (c == '\n')) { 71426090Sminshall break; 71526499Sminshall } 71626090Sminshall /* FALL THROUGH */ 71726090Sminshall 7186002Sroot case TS_DATA: 7196002Sroot if (c == IAC) { 7206002Sroot state = TS_IAC; 7216002Sroot break; 7226002Sroot } 7236002Sroot if (inter > 0) 7246002Sroot break; 72527020Sminshall /* 72632097Sminshall * We now map \r\n ==> \r for pragmatic reasons. 72732097Sminshall * Many client implementations send \r\n when 72832097Sminshall * the user hits the CarriageReturn key. 72932097Sminshall * 73032097Sminshall * We USED to map \r\n ==> \n, since \r\n says 73127020Sminshall * that we want to be in column 1 of the next 73227020Sminshall * printable line, and \n is the standard 73327020Sminshall * unix way of saying that (\r is only good 73427020Sminshall * if CRMOD is set, which it normally is). 73527020Sminshall */ 73631940Sbostic if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) { 73732097Sminshall state = TS_CR; 73826499Sminshall } 73926499Sminshall *pfrontp++ = c; 7406002Sroot break; 7416002Sroot 7426002Sroot case TS_IAC: 7436002Sroot switch (c) { 7446002Sroot 7456002Sroot /* 7466002Sroot * Send the process on the pty side an 7476002Sroot * interrupt. Do this with a NULL or 7486002Sroot * interrupt char; depending on the tty mode. 7496002Sroot */ 7506002Sroot case IP: 7516002Sroot interrupt(); 7526002Sroot break; 7536002Sroot 75427229Sminshall case BREAK: 75527229Sminshall sendbrk(); 75627229Sminshall break; 75727229Sminshall 7586002Sroot /* 7596002Sroot * Are You There? 7606002Sroot */ 7616002Sroot case AYT: 76217583Ssam strcpy(nfrontp, "\r\n[Yes]\r\n"); 76317583Ssam nfrontp += 9; 7646002Sroot break; 7656002Sroot 7666002Sroot /* 76727185Sminshall * Abort Output 76827185Sminshall */ 76927185Sminshall case AO: { 77027185Sminshall struct ltchars tmpltc; 77127185Sminshall 77227185Sminshall ptyflush(); /* half-hearted */ 77327185Sminshall ioctl(pty, TIOCGLTC, &tmpltc); 77427185Sminshall if (tmpltc.t_flushc != '\377') { 77527185Sminshall *pfrontp++ = tmpltc.t_flushc; 77627185Sminshall } 77727260Sminshall netclear(); /* clear buffer back */ 77827185Sminshall *nfrontp++ = IAC; 77927185Sminshall *nfrontp++ = DM; 78027187Sminshall neturg = nfrontp-1; /* off by one XXX */ 78127185Sminshall break; 78227185Sminshall } 78327185Sminshall 78427185Sminshall /* 7856002Sroot * Erase Character and 7866002Sroot * Erase Line 7876002Sroot */ 7886002Sroot case EC: 78927185Sminshall case EL: { 79027185Sminshall struct sgttyb b; 79127185Sminshall char ch; 7926002Sroot 79327185Sminshall ptyflush(); /* half-hearted */ 79427185Sminshall ioctl(pty, TIOCGETP, &b); 79527185Sminshall ch = (c == EC) ? 79627185Sminshall b.sg_erase : b.sg_kill; 79727185Sminshall if (ch != '\377') { 79827185Sminshall *pfrontp++ = ch; 79927185Sminshall } 80027185Sminshall break; 80127185Sminshall } 80227185Sminshall 8036002Sroot /* 8046002Sroot * Check for urgent data... 8056002Sroot */ 8066002Sroot case DM: 80727185Sminshall SYNCHing = stilloob(net); 80827185Sminshall settimer(gotDM); 8096002Sroot break; 8106002Sroot 81127185Sminshall 8126002Sroot /* 8136002Sroot * Begin option subnegotiation... 8146002Sroot */ 8156002Sroot case SB: 81627649Sminshall state = TS_SB; 817*37210Sminshall SB_CLEAR(); 8186002Sroot continue; 8196002Sroot 8206002Sroot case WILL: 82127188Sminshall state = TS_WILL; 82227188Sminshall continue; 82327188Sminshall 8246002Sroot case WONT: 82527188Sminshall state = TS_WONT; 82627188Sminshall continue; 82727188Sminshall 8286002Sroot case DO: 82927188Sminshall state = TS_DO; 83027188Sminshall continue; 83127188Sminshall 8326002Sroot case DONT: 83327188Sminshall state = TS_DONT; 8346002Sroot continue; 8356002Sroot 8366002Sroot case IAC: 8376002Sroot *pfrontp++ = c; 8386002Sroot break; 8396002Sroot } 8406002Sroot state = TS_DATA; 8416002Sroot break; 8426002Sroot 84327649Sminshall case TS_SB: 84427649Sminshall if (c == IAC) { 84527649Sminshall state = TS_SE; 84627649Sminshall } else { 84727649Sminshall SB_ACCUM(c); 84827649Sminshall } 8496002Sroot break; 8506002Sroot 85127649Sminshall case TS_SE: 85227649Sminshall if (c != SE) { 85327649Sminshall if (c != IAC) { 85427649Sminshall SB_ACCUM(IAC); 85527649Sminshall } 85627649Sminshall SB_ACCUM(c); 85727649Sminshall state = TS_SB; 85827649Sminshall } else { 85927649Sminshall SB_TERM(); 86027649Sminshall suboption(); /* handle sub-option */ 86127649Sminshall state = TS_DATA; 86227649Sminshall } 8636002Sroot break; 8646002Sroot 8656002Sroot case TS_WILL: 86627983Sminshall if (hisopts[c] != OPT_YES) 8676002Sroot willoption(c); 8686002Sroot state = TS_DATA; 869*37210Sminshall if (c == TELOPT_TSPEED) 870*37210Sminshall getterminalspeed(); 8716002Sroot continue; 8726002Sroot 8736002Sroot case TS_WONT: 87427983Sminshall if (hisopts[c] != OPT_NO) 8756002Sroot wontoption(c); 8766002Sroot state = TS_DATA; 8776002Sroot continue; 8786002Sroot 8796002Sroot case TS_DO: 88027983Sminshall if (myopts[c] != OPT_YES) 8816002Sroot dooption(c); 8826002Sroot state = TS_DATA; 8836002Sroot continue; 8846002Sroot 8856002Sroot case TS_DONT: 88627983Sminshall if (myopts[c] != OPT_NO) { 88727649Sminshall dontoption(c); 8886002Sroot } 8896002Sroot state = TS_DATA; 8906002Sroot continue; 8916002Sroot 8926002Sroot default: 89327898Skarels syslog(LOG_ERR, "telnetd: panic state=%d\n", state); 8949218Ssam printf("telnetd: panic state=%d\n", state); 8956002Sroot exit(1); 8966002Sroot } 8976002Sroot } 8986002Sroot } 8996002Sroot 9006002Sroot willoption(option) 9016002Sroot int option; 9026002Sroot { 9036002Sroot char *fmt; 9046002Sroot 9056002Sroot switch (option) { 9066002Sroot 9076002Sroot case TELOPT_BINARY: 9086002Sroot mode(RAW, 0); 90927188Sminshall fmt = doopt; 91027188Sminshall break; 9116002Sroot 9126002Sroot case TELOPT_ECHO: 91327649Sminshall not42 = 0; /* looks like a 4.2 system */ 91427649Sminshall /* 91527649Sminshall * Now, in a 4.2 system, to break them out of ECHOing 91627649Sminshall * (to the terminal) mode, we need to send a "WILL ECHO". 91727649Sminshall * Kludge upon kludge! 91827649Sminshall */ 91927983Sminshall if (myopts[TELOPT_ECHO] == OPT_YES) { 92027649Sminshall dooption(TELOPT_ECHO); 92127649Sminshall } 92227649Sminshall fmt = dont; 92327188Sminshall break; 9246002Sroot 92527649Sminshall case TELOPT_TTYPE: 92627983Sminshall settimer(ttypeopt); 92727983Sminshall if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) { 92827983Sminshall hisopts[TELOPT_TTYPE] = OPT_YES; 92927983Sminshall return; 93027983Sminshall } 93127983Sminshall fmt = doopt; 93227983Sminshall break; 93327983Sminshall 934*37210Sminshall case TELOPT_NAWS: 935*37210Sminshall case TELOPT_TSPEED: 936*37210Sminshall case TELOPT_LFLOW: 9376002Sroot case TELOPT_SGA: 9386002Sroot fmt = doopt; 9396002Sroot break; 9406002Sroot 9416002Sroot case TELOPT_TM: 9426002Sroot fmt = dont; 9436002Sroot break; 9446002Sroot 9456002Sroot default: 9466002Sroot fmt = dont; 9476002Sroot break; 9486002Sroot } 94927188Sminshall if (fmt == doopt) { 95027983Sminshall hisopts[option] = OPT_YES; 95127188Sminshall } else { 95227983Sminshall hisopts[option] = OPT_NO; 95327188Sminshall } 95432452Sbostic (void) sprintf(nfrontp, fmt, option); 9558379Ssam nfrontp += sizeof (dont) - 2; 9566002Sroot } 9576002Sroot 9586002Sroot wontoption(option) 9596002Sroot int option; 9606002Sroot { 9616002Sroot char *fmt; 9626002Sroot 9636002Sroot switch (option) { 9646002Sroot case TELOPT_ECHO: 96527649Sminshall not42 = 1; /* doesn't seem to be a 4.2 system */ 96627188Sminshall break; 9676002Sroot 9686002Sroot case TELOPT_BINARY: 9696002Sroot mode(0, RAW); 9706002Sroot break; 97128044Sminshall 97228044Sminshall case TELOPT_TTYPE: 97328044Sminshall settimer(ttypeopt); 97428044Sminshall break; 9756002Sroot } 97628044Sminshall 97727188Sminshall fmt = dont; 97827983Sminshall hisopts[option] = OPT_NO; 97932452Sbostic (void) sprintf(nfrontp, fmt, option); 9808379Ssam nfrontp += sizeof (doopt) - 2; 9816002Sroot } 9826002Sroot 9836002Sroot dooption(option) 9846002Sroot int option; 9856002Sroot { 9866002Sroot char *fmt; 9876002Sroot 9886002Sroot switch (option) { 9896002Sroot 9906002Sroot case TELOPT_TM: 9916002Sroot fmt = wont; 9926002Sroot break; 9936002Sroot 9946002Sroot case TELOPT_ECHO: 9956002Sroot mode(ECHO|CRMOD, 0); 99627188Sminshall fmt = will; 99727188Sminshall break; 9986002Sroot 9996002Sroot case TELOPT_BINARY: 10006002Sroot mode(RAW, 0); 100127188Sminshall fmt = will; 100227188Sminshall break; 10036002Sroot 10046002Sroot case TELOPT_SGA: 10056002Sroot fmt = will; 10066002Sroot break; 10076002Sroot 10086002Sroot default: 10096002Sroot fmt = wont; 10106002Sroot break; 10116002Sroot } 101227188Sminshall if (fmt == will) { 101327983Sminshall myopts[option] = OPT_YES; 101427188Sminshall } else { 101527983Sminshall myopts[option] = OPT_NO; 101627188Sminshall } 101732452Sbostic (void) sprintf(nfrontp, fmt, option); 10188379Ssam nfrontp += sizeof (doopt) - 2; 10196002Sroot } 10206002Sroot 102127649Sminshall 102227649Sminshall dontoption(option) 102327649Sminshall int option; 102427649Sminshall { 102527649Sminshall char *fmt; 102627649Sminshall 102727649Sminshall switch (option) { 102827649Sminshall case TELOPT_ECHO: /* we should stop echoing */ 102932401Sminshall mode(0, ECHO); 103027649Sminshall fmt = wont; 103127649Sminshall break; 103227983Sminshall 103327649Sminshall default: 103427649Sminshall fmt = wont; 103527649Sminshall break; 103627649Sminshall } 103727983Sminshall 103827649Sminshall if (fmt = wont) { 103927983Sminshall myopts[option] = OPT_NO; 104027649Sminshall } else { 104127983Sminshall myopts[option] = OPT_YES; 104227649Sminshall } 104332452Sbostic (void) sprintf(nfrontp, fmt, option); 104427649Sminshall nfrontp += sizeof (wont) - 2; 104527649Sminshall } 104627649Sminshall 1047*37210Sminshall char *ttyspeeds[] = { 1048*37210Sminshall "0", "50", "75", "110", "134", "150", "200", "300", 1049*37210Sminshall "600", "1200", "1800", "2400", "4800", "9600", "19200", "38400" }; 1050*37210Sminshall #define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0] 1051*37210Sminshall 1052*37210Sminshall string2speed(s) 1053*37210Sminshall char *s; 1054*37210Sminshall { 1055*37210Sminshall int i; 1056*37210Sminshall 1057*37210Sminshall for (i = 0; i < NUMSPEEDS; i++) 1058*37210Sminshall if (strcmp(s, ttyspeeds[i]) == 0) 1059*37210Sminshall return(i); 1060*37210Sminshall 1061*37210Sminshall return(0); 1062*37210Sminshall } 1063*37210Sminshall 106427649Sminshall /* 106527649Sminshall * suboption() 106627649Sminshall * 106727649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 106827649Sminshall * side. 106927649Sminshall * 107027649Sminshall * Currently we recognize: 107127649Sminshall * 107227983Sminshall * Terminal type is 1073*37210Sminshall * Terminal size 1074*37210Sminshall * Terminal speed is 107527649Sminshall */ 107627649Sminshall 107727649Sminshall suboption() 107827649Sminshall { 107927983Sminshall switch (SB_GET()) { 108027983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 108127983Sminshall static char terminalname[5+41] = "TERM="; 108227983Sminshall 108327983Sminshall settimer(ttypesubopt); 108427983Sminshall 108527983Sminshall if (SB_GET() != TELQUAL_IS) { 108627983Sminshall return; /* ??? XXX but, this is the most robust */ 108727983Sminshall } 108827983Sminshall 108927983Sminshall terminaltype = terminalname+strlen(terminalname); 109027983Sminshall 109127983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 109227983Sminshall !SB_EOF()) { 109327983Sminshall register int c; 109427983Sminshall 109527983Sminshall c = SB_GET(); 109627983Sminshall if (isupper(c)) { 109727983Sminshall c = tolower(c); 109827983Sminshall } 109927983Sminshall *terminaltype++ = c; /* accumulate name */ 110027983Sminshall } 110127983Sminshall *terminaltype = 0; 110227983Sminshall terminaltype = terminalname; 110327983Sminshall break; 110427983Sminshall } 1105*37210Sminshall case TELOPT_NAWS: { 1106*37210Sminshall struct winsize win; 1107*37210Sminshall char c; 110827983Sminshall 1109*37210Sminshall #define SB_GETCHAR(c) \ 1110*37210Sminshall { if ((c = SB_GET()) == IAC && SB_GET() != IAC) return; } 1111*37210Sminshall 1112*37210Sminshall ioctl(pty, TIOCGWINSZ, &win); 1113*37210Sminshall settimer(ttypesubopt); 1114*37210Sminshall 1115*37210Sminshall syslog(LOG_INFO, "%x %x %x %x", 1116*37210Sminshall subpointer[0],subpointer[1],subpointer[2],subpointer[3]); 1117*37210Sminshall SB_GETCHAR(c); 1118*37210Sminshall win.ws_col = c << 8; 1119*37210Sminshall SB_GETCHAR(c); 1120*37210Sminshall win.ws_col |= c; 1121*37210Sminshall SB_GETCHAR(c); 1122*37210Sminshall win.ws_row = c << 8; 1123*37210Sminshall SB_GETCHAR(c); 1124*37210Sminshall win.ws_row |= c; 1125*37210Sminshall syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row); 1126*37210Sminshall ioctl(pty, TIOCSWINSZ, &win); 1127*37210Sminshall break; 1128*37210Sminshall } 1129*37210Sminshall case TELOPT_TSPEED: { 1130*37210Sminshall char speeds[41],*cp=speeds; 1131*37210Sminshall struct sgttyb b; 1132*37210Sminshall int ispeed,ospeed; 1133*37210Sminshall char *ispeeds,*ospeeds; 1134*37210Sminshall 1135*37210Sminshall if (SB_GET() != TELQUAL_IS) { 1136*37210Sminshall return; /* ??? XXX but, this is the most robust */ 1137*37210Sminshall } 1138*37210Sminshall 1139*37210Sminshall ispeeds = NULL; 1140*37210Sminshall ospeeds = speeds; 1141*37210Sminshall ispeed = 0; 1142*37210Sminshall ospeed = 0; 1143*37210Sminshall while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) { 1144*37210Sminshall register int c; 1145*37210Sminshall 1146*37210Sminshall c = SB_GET(); 1147*37210Sminshall if (c == ',') { 1148*37210Sminshall c = 0; 1149*37210Sminshall ispeeds = cp+1; 1150*37210Sminshall } 1151*37210Sminshall *cp++ = c; /* accumulate name */ 1152*37210Sminshall } 1153*37210Sminshall *cp = 0; 1154*37210Sminshall 1155*37210Sminshall if (ispeeds) 1156*37210Sminshall ispeed = string2speed(ispeeds); 1157*37210Sminshall if (ospeeds) 1158*37210Sminshall ospeed = string2speed(ospeeds); 1159*37210Sminshall 1160*37210Sminshall if (ispeed && ospeed) { 1161*37210Sminshall ioctl(pty, TIOCGETP, &b); 1162*37210Sminshall b.sg_ospeed = ospeed; 1163*37210Sminshall b.sg_ispeed = ispeed; 1164*37210Sminshall ioctl(pty, TIOCSETP, &b); 1165*37210Sminshall } 1166*37210Sminshall 1167*37210Sminshall break; 1168*37210Sminshall } 116927649Sminshall default: 117027649Sminshall ; 117127649Sminshall } 117227649Sminshall } 117327649Sminshall 11746002Sroot mode(on, off) 11756002Sroot int on, off; 11766002Sroot { 11776002Sroot struct sgttyb b; 11786002Sroot 11796002Sroot ptyflush(); 11806002Sroot ioctl(pty, TIOCGETP, &b); 11816002Sroot b.sg_flags |= on; 11826002Sroot b.sg_flags &= ~off; 11836002Sroot ioctl(pty, TIOCSETP, &b); 11846002Sroot } 11856002Sroot 11866002Sroot /* 11876002Sroot * Send interrupt to process on other side of pty. 11886002Sroot * If it is in raw mode, just write NULL; 11896002Sroot * otherwise, write intr char. 11906002Sroot */ 11916002Sroot interrupt() 11926002Sroot { 11936002Sroot struct sgttyb b; 11946002Sroot struct tchars tchars; 11956002Sroot 11966002Sroot ptyflush(); /* half-hearted */ 11976002Sroot ioctl(pty, TIOCGETP, &b); 11986002Sroot if (b.sg_flags & RAW) { 11996002Sroot *pfrontp++ = '\0'; 12006002Sroot return; 12016002Sroot } 12026002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 12036002Sroot '\177' : tchars.t_intrc; 12046002Sroot } 12056002Sroot 120627229Sminshall /* 120727229Sminshall * Send quit to process on other side of pty. 120827229Sminshall * If it is in raw mode, just write NULL; 120927229Sminshall * otherwise, write quit char. 121027229Sminshall */ 121127229Sminshall sendbrk() 121227229Sminshall { 121327229Sminshall struct sgttyb b; 121427229Sminshall struct tchars tchars; 121527229Sminshall 121627229Sminshall ptyflush(); /* half-hearted */ 121727229Sminshall ioctl(pty, TIOCGETP, &b); 121827229Sminshall if (b.sg_flags & RAW) { 121927229Sminshall *pfrontp++ = '\0'; 122027229Sminshall return; 122127229Sminshall } 122227229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 122327229Sminshall '\034' : tchars.t_quitc; 122427229Sminshall } 122527229Sminshall 12266002Sroot ptyflush() 12276002Sroot { 12286002Sroot int n; 12296002Sroot 12306002Sroot if ((n = pfrontp - pbackp) > 0) 12316002Sroot n = write(pty, pbackp, n); 12328346Ssam if (n < 0) 12338346Ssam return; 12346002Sroot pbackp += n; 12356002Sroot if (pbackp == pfrontp) 12366002Sroot pbackp = pfrontp = ptyobuf; 12376002Sroot } 123827260Sminshall 123927260Sminshall /* 124027260Sminshall * nextitem() 124127260Sminshall * 124227260Sminshall * Return the address of the next "item" in the TELNET data 124327260Sminshall * stream. This will be the address of the next character if 124427260Sminshall * the current address is a user data character, or it will 124527260Sminshall * be the address of the character following the TELNET command 124627260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 124727260Sminshall * character. 124827260Sminshall */ 12496002Sroot 125027260Sminshall char * 125127260Sminshall nextitem(current) 125227260Sminshall char *current; 12536002Sroot { 125427260Sminshall if ((*current&0xff) != IAC) { 125527260Sminshall return current+1; 125627260Sminshall } 125727260Sminshall switch (*(current+1)&0xff) { 125827260Sminshall case DO: 125927260Sminshall case DONT: 126027260Sminshall case WILL: 126127260Sminshall case WONT: 126227260Sminshall return current+3; 126327260Sminshall case SB: /* loop forever looking for the SE */ 126427260Sminshall { 126527260Sminshall register char *look = current+2; 12666002Sroot 126727260Sminshall for (;;) { 126827260Sminshall if ((*look++&0xff) == IAC) { 126927260Sminshall if ((*look++&0xff) == SE) { 127027260Sminshall return look; 127127260Sminshall } 127227260Sminshall } 127327260Sminshall } 12748346Ssam } 127527260Sminshall default: 127627260Sminshall return current+2; 127727260Sminshall } 12786002Sroot } 12796002Sroot 128027185Sminshall 128127185Sminshall /* 128227260Sminshall * netclear() 128327260Sminshall * 128427260Sminshall * We are about to do a TELNET SYNCH operation. Clear 128527260Sminshall * the path to the network. 128627260Sminshall * 128727260Sminshall * Things are a bit tricky since we may have sent the first 128827260Sminshall * byte or so of a previous TELNET command into the network. 128927260Sminshall * So, we have to scan the network buffer from the beginning 129027260Sminshall * until we are up to where we want to be. 129127260Sminshall * 129227260Sminshall * A side effect of what we do, just to keep things 129327260Sminshall * simple, is to clear the urgent data pointer. The principal 129427260Sminshall * caller should be setting the urgent data pointer AFTER calling 129527260Sminshall * us in any case. 129627260Sminshall */ 129727260Sminshall 129827260Sminshall netclear() 129927260Sminshall { 130027260Sminshall register char *thisitem, *next; 130127260Sminshall char *good; 130227260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 130327260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 130427260Sminshall 130527260Sminshall thisitem = netobuf; 130627260Sminshall 130727260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 130827260Sminshall thisitem = next; 130927260Sminshall } 131027260Sminshall 131127260Sminshall /* Now, thisitem is first before/at boundary. */ 131227260Sminshall 131327260Sminshall good = netobuf; /* where the good bytes go */ 131427260Sminshall 131527260Sminshall while (nfrontp > thisitem) { 131627260Sminshall if (wewant(thisitem)) { 131727260Sminshall int length; 131827260Sminshall 131927260Sminshall next = thisitem; 132027260Sminshall do { 132127260Sminshall next = nextitem(next); 132227260Sminshall } while (wewant(next) && (nfrontp > next)); 132327260Sminshall length = next-thisitem; 132427260Sminshall bcopy(thisitem, good, length); 132527260Sminshall good += length; 132627260Sminshall thisitem = next; 132727260Sminshall } else { 132827260Sminshall thisitem = nextitem(thisitem); 132927260Sminshall } 133027260Sminshall } 133127260Sminshall 133227260Sminshall nbackp = netobuf; 133327260Sminshall nfrontp = good; /* next byte to be sent */ 133427260Sminshall neturg = 0; 133527260Sminshall } 133627260Sminshall 133727260Sminshall /* 133827185Sminshall * netflush 133927185Sminshall * Send as much data as possible to the network, 134027185Sminshall * handling requests for urgent data. 134127185Sminshall */ 134227185Sminshall 134327185Sminshall 134427185Sminshall netflush() 134527185Sminshall { 134627185Sminshall int n; 134727185Sminshall 134827185Sminshall if ((n = nfrontp - nbackp) > 0) { 134927649Sminshall /* 135027649Sminshall * if no urgent data, or if the other side appears to be an 135127649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 135227649Sminshall * write the entire buffer in non-OOB mode. 135327649Sminshall */ 135427649Sminshall if ((neturg == 0) || (not42 == 0)) { 135527185Sminshall n = write(net, nbackp, n); /* normal write */ 135627185Sminshall } else { 135727185Sminshall n = neturg - nbackp; 135827185Sminshall /* 135927185Sminshall * In 4.2 (and 4.3) systems, there is some question about 136027185Sminshall * what byte in a sendOOB operation is the "OOB" data. 136127185Sminshall * To make ourselves compatible, we only send ONE byte 136227185Sminshall * out of band, the one WE THINK should be OOB (though 136327185Sminshall * we really have more the TCP philosophy of urgent data 136427185Sminshall * rather than the Unix philosophy of OOB data). 136527185Sminshall */ 136627185Sminshall if (n > 1) { 136727185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 136827185Sminshall } else { 136927185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 137027185Sminshall } 137127185Sminshall } 137227185Sminshall } 137327185Sminshall if (n < 0) { 137427185Sminshall if (errno == EWOULDBLOCK) 137527185Sminshall return; 137627185Sminshall /* should blow this guy away... */ 137727185Sminshall return; 137827185Sminshall } 137927185Sminshall nbackp += n; 138027185Sminshall if (nbackp >= neturg) { 138127185Sminshall neturg = 0; 138227185Sminshall } 138327185Sminshall if (nbackp == nfrontp) { 138427185Sminshall nbackp = nfrontp = netobuf; 138527185Sminshall } 138627185Sminshall } 138727185Sminshall 13886002Sroot cleanup() 13896002Sroot { 139035443Sbostic char *p; 13916002Sroot 139235443Sbostic p = line + sizeof("/dev/") - 1; 139335443Sbostic if (logout(p)) 139435443Sbostic logwtmp(p, "", ""); 139535443Sbostic (void)chmod(line, 0666); 139635443Sbostic (void)chown(line, 0, 0); 139735443Sbostic *p = 'p'; 139835443Sbostic (void)chmod(line, 0666); 139935443Sbostic (void)chown(line, 0, 0); 140010191Ssam shutdown(net, 2); 14016002Sroot exit(1); 14026002Sroot } 14036002Sroot 140433271Sminshall char editedhost[32]; 140533271Sminshall 140633271Sminshall edithost(pat, host) 140733271Sminshall register char *pat; 140833271Sminshall register char *host; 140933271Sminshall { 141033271Sminshall register char *res = editedhost; 141133271Sminshall 141233271Sminshall if (!pat) 141333271Sminshall pat = ""; 141433271Sminshall while (*pat) { 141533271Sminshall switch (*pat) { 141633271Sminshall 141733271Sminshall case '#': 141833271Sminshall if (*host) 141933271Sminshall host++; 142033271Sminshall break; 142133271Sminshall 142233271Sminshall case '@': 142333271Sminshall if (*host) 142433271Sminshall *res++ = *host++; 142533271Sminshall break; 142633271Sminshall 142733271Sminshall default: 142833271Sminshall *res++ = *pat; 142933271Sminshall break; 143033271Sminshall 143133271Sminshall } 143233271Sminshall if (res == &editedhost[sizeof editedhost - 1]) { 143333271Sminshall *res = '\0'; 143433271Sminshall return; 143533271Sminshall } 143633271Sminshall pat++; 143733271Sminshall } 143833271Sminshall if (*host) 143933271Sminshall strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); 144033271Sminshall else 144133271Sminshall *res = '\0'; 144233271Sminshall editedhost[sizeof editedhost - 1] = '\0'; 144333271Sminshall } 144433271Sminshall 144533271Sminshall static char *putlocation; 144633271Sminshall 144733271Sminshall puts(s) 144833271Sminshall register char *s; 144933271Sminshall { 145033271Sminshall 145133271Sminshall while (*s) 145233271Sminshall putchr(*s++); 145333271Sminshall } 145433271Sminshall 145533271Sminshall putchr(cc) 145633271Sminshall { 145733271Sminshall *putlocation++ = cc; 145833271Sminshall } 145933271Sminshall 146034424Sbostic putf(cp, where) 146133271Sminshall register char *cp; 146233271Sminshall char *where; 146333271Sminshall { 146433271Sminshall char *slash; 146533271Sminshall char datebuffer[60]; 146633271Sminshall extern char *rindex(); 146733271Sminshall 146833271Sminshall putlocation = where; 146933271Sminshall 147033271Sminshall while (*cp) { 147133271Sminshall if (*cp != '%') { 147233271Sminshall putchr(*cp++); 147333271Sminshall continue; 147433271Sminshall } 147533271Sminshall switch (*++cp) { 147633271Sminshall 147733271Sminshall case 't': 147833271Sminshall slash = rindex(line, '/'); 147933271Sminshall if (slash == (char *) 0) 148033271Sminshall puts(line); 148133271Sminshall else 148233271Sminshall puts(&slash[1]); 148333271Sminshall break; 148433271Sminshall 148533271Sminshall case 'h': 148633271Sminshall puts(editedhost); 148733271Sminshall break; 148833271Sminshall 148933271Sminshall case 'd': 149033271Sminshall get_date(datebuffer); 149133271Sminshall puts(datebuffer); 149233271Sminshall break; 149333271Sminshall 149433271Sminshall case '%': 149533271Sminshall putchr('%'); 149633271Sminshall break; 149733271Sminshall } 149833271Sminshall cp++; 149933271Sminshall } 150033271Sminshall } 1501