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*37212Sminshall static char sccsid[] = "@(#)telnetd.c 5.33 (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 12537210Sminshall 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++; 13137210Sminshall if (argc > 1) { 13237210Sminshall sin.sin_port = atoi(argv[1]); 13327185Sminshall sin.sin_port = htons((u_short)sin.sin_port); 13437210Sminshall } else { 13537210Sminshall sp = getservbyname("telnet", "tcp"); 13637210Sminshall if (sp == 0) { 13737210Sminshall fprintf(stderr, 13837210Sminshall "telnetd: tcp/telnet: unknown service\n"); 13937210Sminshall exit(1); 14037210Sminshall } 14137210Sminshall 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 /* 21537210Sminshall * getterminalspeed 21637210Sminshall * 21737210Sminshall * Ask the other end to send along its terminal speed. 21837210Sminshall * subopt does the rest. Interlocked so it can't happen during 21937210Sminshall * getterminaltype. 22037210Sminshall */ 22137210Sminshall 22237210Sminshall void 22337210Sminshall getterminalspeed() 22437210Sminshall { 22537210Sminshall static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 22637210Sminshall 22737210Sminshall bcopy(sbuf, nfrontp, sizeof sbuf); 22837210Sminshall nfrontp += sizeof sbuf; 22937210Sminshall } 23037210Sminshall 23137210Sminshall /* 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; 31537210Sminshall 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 /* 32837210Sminshall * 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 } 44937210Sminshall if (!hisopts[TELOPT_NAWS]) { 45037210Sminshall willoption(TELOPT_NAWS); 45137210Sminshall hisopts[TELOPT_NAWS] = OPT_NO; 45237210Sminshall } 45337210Sminshall if (!hisopts[TELOPT_TSPEED]) { 45437210Sminshall willoption(TELOPT_TSPEED); 45537210Sminshall hisopts[TELOPT_TSPEED] = OPT_NO; 45637210Sminshall } 45737210Sminshall if (!hisopts[TELOPT_LFLOW]) { 45837210Sminshall willoption(TELOPT_LFLOW); 45937210Sminshall hisopts[TELOPT_LFLOW] = OPT_NO; 46037210Sminshall } 46137210Sminshall 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 } 54037210Sminshall 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 */ 63337210Sminshall 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; 64037210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 64137210Sminshall netclear(); /* clear buffer back */ 64237210Sminshall *nfrontp++ = IAC; 64337210Sminshall *nfrontp++ = DM; 64437210Sminshall neturg = nfrontp-1; /* off by one XXX */ 64537210Sminshall } 64637210Sminshall if (hisopts[TELOPT_LFLOW] && 64737210Sminshall (ptyibuf[0] & 64837210Sminshall (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 64937210Sminshall sprintf(nfrontp,"%c%c%c%c%c%c", 65037210Sminshall IAC, SB, TELOPT_LFLOW, 65137210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 65237210Sminshall IAC, SE); 65337210Sminshall nfrontp += 6; 65437210Sminshall } 65533267Sminshall pcc--; 65633267Sminshall ptyip = ptyibuf+1; 65737210Sminshall } 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; 81737210Sminshall 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; 86937210Sminshall if (c == TELOPT_TSPEED) 87037210Sminshall 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 93437210Sminshall case TELOPT_NAWS: 93537210Sminshall case TELOPT_TSPEED: 93637210Sminshall 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*37212Sminshall /* 1048*37212Sminshall * Given a string, assign the "best" speed which we support. 1049*37212Sminshall * 1050*37212Sminshall * "Best" is defined as rounding up, unless what is presented is 1051*37212Sminshall * higher than the highest. 1052*37212Sminshall */ 105337210Sminshall 105437210Sminshall string2speed(s) 105537210Sminshall char *s; 105637210Sminshall { 1057*37212Sminshall /* 1058*37212Sminshall * The order here is important. The index of each speed needs to 1059*37212Sminshall * correspond with the sgtty structure value for that speed. 1060*37212Sminshall * 1061*37212Sminshall * Additionally, the search algorithm assumes the table is in 1062*37212Sminshall * ascending sequence. 1063*37212Sminshall */ 1064*37212Sminshall static int ttyspeeds[] = { 1065*37212Sminshall 0, 50, 75, 110, 134, 150, 200, 300, 1066*37212Sminshall 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 }; 1067*37212Sminshall #define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0] 1068*37212Sminshall int i; 1069*37212Sminshall int theirspeed = atoi(s); 107037210Sminshall 1071*37212Sminshall for (i = 0; i < NUMSPEEDS; i++) { 1072*37212Sminshall if (ttyspeeds[i] == theirspeed) { /* Exact match */ 1073*37212Sminshall return(i); 1074*37212Sminshall } else if (ttyspeeds[i] > theirspeed) { 1075*37212Sminshall if (i > 0) { 1076*37212Sminshall return i-1; 1077*37212Sminshall } 1078*37212Sminshall } 1079*37212Sminshall } 1080*37212Sminshall /* Their number is greater than any of our numbers */ 1081*37212Sminshall return(NUMSPEEDS-1); 108237210Sminshall } 108337210Sminshall 108427649Sminshall /* 108527649Sminshall * suboption() 108627649Sminshall * 108727649Sminshall * Look at the sub-option buffer, and try to be helpful to the other 108827649Sminshall * side. 108927649Sminshall * 109027649Sminshall * Currently we recognize: 109127649Sminshall * 109227983Sminshall * Terminal type is 109337210Sminshall * Terminal size 109437210Sminshall * Terminal speed is 109527649Sminshall */ 109627649Sminshall 109727649Sminshall suboption() 109827649Sminshall { 109927983Sminshall switch (SB_GET()) { 110027983Sminshall case TELOPT_TTYPE: { /* Yaaaay! */ 110127983Sminshall static char terminalname[5+41] = "TERM="; 110227983Sminshall 110327983Sminshall settimer(ttypesubopt); 110427983Sminshall 110527983Sminshall if (SB_GET() != TELQUAL_IS) { 110627983Sminshall return; /* ??? XXX but, this is the most robust */ 110727983Sminshall } 110827983Sminshall 110927983Sminshall terminaltype = terminalname+strlen(terminalname); 111027983Sminshall 111127983Sminshall while ((terminaltype < (terminalname + sizeof terminalname-1)) && 111227983Sminshall !SB_EOF()) { 111327983Sminshall register int c; 111427983Sminshall 111527983Sminshall c = SB_GET(); 111627983Sminshall if (isupper(c)) { 111727983Sminshall c = tolower(c); 111827983Sminshall } 111927983Sminshall *terminaltype++ = c; /* accumulate name */ 112027983Sminshall } 112127983Sminshall *terminaltype = 0; 112227983Sminshall terminaltype = terminalname; 112327983Sminshall break; 112427983Sminshall } 112537210Sminshall case TELOPT_NAWS: { 112637210Sminshall struct winsize win; 112737210Sminshall char c; 112827983Sminshall 112937210Sminshall #define SB_GETCHAR(c) \ 113037210Sminshall { if ((c = SB_GET()) == IAC && SB_GET() != IAC) return; } 113137210Sminshall 113237210Sminshall ioctl(pty, TIOCGWINSZ, &win); 113337210Sminshall settimer(ttypesubopt); 113437210Sminshall 113537210Sminshall syslog(LOG_INFO, "%x %x %x %x", 113637210Sminshall subpointer[0],subpointer[1],subpointer[2],subpointer[3]); 113737210Sminshall SB_GETCHAR(c); 113837210Sminshall win.ws_col = c << 8; 113937210Sminshall SB_GETCHAR(c); 114037210Sminshall win.ws_col |= c; 114137210Sminshall SB_GETCHAR(c); 114237210Sminshall win.ws_row = c << 8; 114337210Sminshall SB_GETCHAR(c); 114437210Sminshall win.ws_row |= c; 114537210Sminshall syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row); 114637210Sminshall ioctl(pty, TIOCSWINSZ, &win); 114737210Sminshall break; 114837210Sminshall } 114937210Sminshall case TELOPT_TSPEED: { 115037210Sminshall char speeds[41],*cp=speeds; 115137210Sminshall struct sgttyb b; 115237210Sminshall int ispeed,ospeed; 115337210Sminshall char *ispeeds,*ospeeds; 115437210Sminshall 115537210Sminshall if (SB_GET() != TELQUAL_IS) { 115637210Sminshall return; /* ??? XXX but, this is the most robust */ 115737210Sminshall } 115837210Sminshall 115937210Sminshall ispeeds = NULL; 116037210Sminshall ospeeds = speeds; 116137210Sminshall ispeed = 0; 116237210Sminshall ospeed = 0; 116337210Sminshall while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) { 116437210Sminshall register int c; 116537210Sminshall 116637210Sminshall c = SB_GET(); 116737210Sminshall if (c == ',') { 116837210Sminshall c = 0; 116937210Sminshall ispeeds = cp+1; 117037210Sminshall } 117137210Sminshall *cp++ = c; /* accumulate name */ 117237210Sminshall } 117337210Sminshall *cp = 0; 117437210Sminshall 117537210Sminshall if (ispeeds) 117637210Sminshall ispeed = string2speed(ispeeds); 117737210Sminshall if (ospeeds) 117837210Sminshall ospeed = string2speed(ospeeds); 117937210Sminshall 118037210Sminshall if (ispeed && ospeed) { 118137210Sminshall ioctl(pty, TIOCGETP, &b); 118237210Sminshall b.sg_ospeed = ospeed; 118337210Sminshall b.sg_ispeed = ispeed; 118437210Sminshall ioctl(pty, TIOCSETP, &b); 118537210Sminshall } 118637210Sminshall 118737210Sminshall break; 118837210Sminshall } 118927649Sminshall default: 119027649Sminshall ; 119127649Sminshall } 119227649Sminshall } 119327649Sminshall 11946002Sroot mode(on, off) 11956002Sroot int on, off; 11966002Sroot { 11976002Sroot struct sgttyb b; 11986002Sroot 11996002Sroot ptyflush(); 12006002Sroot ioctl(pty, TIOCGETP, &b); 12016002Sroot b.sg_flags |= on; 12026002Sroot b.sg_flags &= ~off; 12036002Sroot ioctl(pty, TIOCSETP, &b); 12046002Sroot } 12056002Sroot 12066002Sroot /* 12076002Sroot * Send interrupt to process on other side of pty. 12086002Sroot * If it is in raw mode, just write NULL; 12096002Sroot * otherwise, write intr char. 12106002Sroot */ 12116002Sroot interrupt() 12126002Sroot { 12136002Sroot struct sgttyb b; 12146002Sroot struct tchars tchars; 12156002Sroot 12166002Sroot ptyflush(); /* half-hearted */ 12176002Sroot ioctl(pty, TIOCGETP, &b); 12186002Sroot if (b.sg_flags & RAW) { 12196002Sroot *pfrontp++ = '\0'; 12206002Sroot return; 12216002Sroot } 12226002Sroot *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 12236002Sroot '\177' : tchars.t_intrc; 12246002Sroot } 12256002Sroot 122627229Sminshall /* 122727229Sminshall * Send quit to process on other side of pty. 122827229Sminshall * If it is in raw mode, just write NULL; 122927229Sminshall * otherwise, write quit char. 123027229Sminshall */ 123127229Sminshall sendbrk() 123227229Sminshall { 123327229Sminshall struct sgttyb b; 123427229Sminshall struct tchars tchars; 123527229Sminshall 123627229Sminshall ptyflush(); /* half-hearted */ 123727229Sminshall ioctl(pty, TIOCGETP, &b); 123827229Sminshall if (b.sg_flags & RAW) { 123927229Sminshall *pfrontp++ = '\0'; 124027229Sminshall return; 124127229Sminshall } 124227229Sminshall *pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ? 124327229Sminshall '\034' : tchars.t_quitc; 124427229Sminshall } 124527229Sminshall 12466002Sroot ptyflush() 12476002Sroot { 12486002Sroot int n; 12496002Sroot 12506002Sroot if ((n = pfrontp - pbackp) > 0) 12516002Sroot n = write(pty, pbackp, n); 12528346Ssam if (n < 0) 12538346Ssam return; 12546002Sroot pbackp += n; 12556002Sroot if (pbackp == pfrontp) 12566002Sroot pbackp = pfrontp = ptyobuf; 12576002Sroot } 125827260Sminshall 125927260Sminshall /* 126027260Sminshall * nextitem() 126127260Sminshall * 126227260Sminshall * Return the address of the next "item" in the TELNET data 126327260Sminshall * stream. This will be the address of the next character if 126427260Sminshall * the current address is a user data character, or it will 126527260Sminshall * be the address of the character following the TELNET command 126627260Sminshall * if the current address is a TELNET IAC ("I Am a Command") 126727260Sminshall * character. 126827260Sminshall */ 12696002Sroot 127027260Sminshall char * 127127260Sminshall nextitem(current) 127227260Sminshall char *current; 12736002Sroot { 127427260Sminshall if ((*current&0xff) != IAC) { 127527260Sminshall return current+1; 127627260Sminshall } 127727260Sminshall switch (*(current+1)&0xff) { 127827260Sminshall case DO: 127927260Sminshall case DONT: 128027260Sminshall case WILL: 128127260Sminshall case WONT: 128227260Sminshall return current+3; 128327260Sminshall case SB: /* loop forever looking for the SE */ 128427260Sminshall { 128527260Sminshall register char *look = current+2; 12866002Sroot 128727260Sminshall for (;;) { 128827260Sminshall if ((*look++&0xff) == IAC) { 128927260Sminshall if ((*look++&0xff) == SE) { 129027260Sminshall return look; 129127260Sminshall } 129227260Sminshall } 129327260Sminshall } 12948346Ssam } 129527260Sminshall default: 129627260Sminshall return current+2; 129727260Sminshall } 12986002Sroot } 12996002Sroot 130027185Sminshall 130127185Sminshall /* 130227260Sminshall * netclear() 130327260Sminshall * 130427260Sminshall * We are about to do a TELNET SYNCH operation. Clear 130527260Sminshall * the path to the network. 130627260Sminshall * 130727260Sminshall * Things are a bit tricky since we may have sent the first 130827260Sminshall * byte or so of a previous TELNET command into the network. 130927260Sminshall * So, we have to scan the network buffer from the beginning 131027260Sminshall * until we are up to where we want to be. 131127260Sminshall * 131227260Sminshall * A side effect of what we do, just to keep things 131327260Sminshall * simple, is to clear the urgent data pointer. The principal 131427260Sminshall * caller should be setting the urgent data pointer AFTER calling 131527260Sminshall * us in any case. 131627260Sminshall */ 131727260Sminshall 131827260Sminshall netclear() 131927260Sminshall { 132027260Sminshall register char *thisitem, *next; 132127260Sminshall char *good; 132227260Sminshall #define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ 132327260Sminshall ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) 132427260Sminshall 132527260Sminshall thisitem = netobuf; 132627260Sminshall 132727260Sminshall while ((next = nextitem(thisitem)) <= nbackp) { 132827260Sminshall thisitem = next; 132927260Sminshall } 133027260Sminshall 133127260Sminshall /* Now, thisitem is first before/at boundary. */ 133227260Sminshall 133327260Sminshall good = netobuf; /* where the good bytes go */ 133427260Sminshall 133527260Sminshall while (nfrontp > thisitem) { 133627260Sminshall if (wewant(thisitem)) { 133727260Sminshall int length; 133827260Sminshall 133927260Sminshall next = thisitem; 134027260Sminshall do { 134127260Sminshall next = nextitem(next); 134227260Sminshall } while (wewant(next) && (nfrontp > next)); 134327260Sminshall length = next-thisitem; 134427260Sminshall bcopy(thisitem, good, length); 134527260Sminshall good += length; 134627260Sminshall thisitem = next; 134727260Sminshall } else { 134827260Sminshall thisitem = nextitem(thisitem); 134927260Sminshall } 135027260Sminshall } 135127260Sminshall 135227260Sminshall nbackp = netobuf; 135327260Sminshall nfrontp = good; /* next byte to be sent */ 135427260Sminshall neturg = 0; 135527260Sminshall } 135627260Sminshall 135727260Sminshall /* 135827185Sminshall * netflush 135927185Sminshall * Send as much data as possible to the network, 136027185Sminshall * handling requests for urgent data. 136127185Sminshall */ 136227185Sminshall 136327185Sminshall 136427185Sminshall netflush() 136527185Sminshall { 136627185Sminshall int n; 136727185Sminshall 136827185Sminshall if ((n = nfrontp - nbackp) > 0) { 136927649Sminshall /* 137027649Sminshall * if no urgent data, or if the other side appears to be an 137127649Sminshall * old 4.2 client (and thus unable to survive TCP urgent data), 137227649Sminshall * write the entire buffer in non-OOB mode. 137327649Sminshall */ 137427649Sminshall if ((neturg == 0) || (not42 == 0)) { 137527185Sminshall n = write(net, nbackp, n); /* normal write */ 137627185Sminshall } else { 137727185Sminshall n = neturg - nbackp; 137827185Sminshall /* 137927185Sminshall * In 4.2 (and 4.3) systems, there is some question about 138027185Sminshall * what byte in a sendOOB operation is the "OOB" data. 138127185Sminshall * To make ourselves compatible, we only send ONE byte 138227185Sminshall * out of band, the one WE THINK should be OOB (though 138327185Sminshall * we really have more the TCP philosophy of urgent data 138427185Sminshall * rather than the Unix philosophy of OOB data). 138527185Sminshall */ 138627185Sminshall if (n > 1) { 138727185Sminshall n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ 138827185Sminshall } else { 138927185Sminshall n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ 139027185Sminshall } 139127185Sminshall } 139227185Sminshall } 139327185Sminshall if (n < 0) { 139427185Sminshall if (errno == EWOULDBLOCK) 139527185Sminshall return; 139627185Sminshall /* should blow this guy away... */ 139727185Sminshall return; 139827185Sminshall } 139927185Sminshall nbackp += n; 140027185Sminshall if (nbackp >= neturg) { 140127185Sminshall neturg = 0; 140227185Sminshall } 140327185Sminshall if (nbackp == nfrontp) { 140427185Sminshall nbackp = nfrontp = netobuf; 140527185Sminshall } 140627185Sminshall } 140727185Sminshall 14086002Sroot cleanup() 14096002Sroot { 141035443Sbostic char *p; 14116002Sroot 141235443Sbostic p = line + sizeof("/dev/") - 1; 141335443Sbostic if (logout(p)) 141435443Sbostic logwtmp(p, "", ""); 141535443Sbostic (void)chmod(line, 0666); 141635443Sbostic (void)chown(line, 0, 0); 141735443Sbostic *p = 'p'; 141835443Sbostic (void)chmod(line, 0666); 141935443Sbostic (void)chown(line, 0, 0); 142010191Ssam shutdown(net, 2); 14216002Sroot exit(1); 14226002Sroot } 14236002Sroot 142433271Sminshall char editedhost[32]; 142533271Sminshall 142633271Sminshall edithost(pat, host) 142733271Sminshall register char *pat; 142833271Sminshall register char *host; 142933271Sminshall { 143033271Sminshall register char *res = editedhost; 143133271Sminshall 143233271Sminshall if (!pat) 143333271Sminshall pat = ""; 143433271Sminshall while (*pat) { 143533271Sminshall switch (*pat) { 143633271Sminshall 143733271Sminshall case '#': 143833271Sminshall if (*host) 143933271Sminshall host++; 144033271Sminshall break; 144133271Sminshall 144233271Sminshall case '@': 144333271Sminshall if (*host) 144433271Sminshall *res++ = *host++; 144533271Sminshall break; 144633271Sminshall 144733271Sminshall default: 144833271Sminshall *res++ = *pat; 144933271Sminshall break; 145033271Sminshall 145133271Sminshall } 145233271Sminshall if (res == &editedhost[sizeof editedhost - 1]) { 145333271Sminshall *res = '\0'; 145433271Sminshall return; 145533271Sminshall } 145633271Sminshall pat++; 145733271Sminshall } 145833271Sminshall if (*host) 145933271Sminshall strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); 146033271Sminshall else 146133271Sminshall *res = '\0'; 146233271Sminshall editedhost[sizeof editedhost - 1] = '\0'; 146333271Sminshall } 146433271Sminshall 146533271Sminshall static char *putlocation; 146633271Sminshall 146733271Sminshall puts(s) 146833271Sminshall register char *s; 146933271Sminshall { 147033271Sminshall 147133271Sminshall while (*s) 147233271Sminshall putchr(*s++); 147333271Sminshall } 147433271Sminshall 147533271Sminshall putchr(cc) 147633271Sminshall { 147733271Sminshall *putlocation++ = cc; 147833271Sminshall } 147933271Sminshall 148034424Sbostic putf(cp, where) 148133271Sminshall register char *cp; 148233271Sminshall char *where; 148333271Sminshall { 148433271Sminshall char *slash; 148533271Sminshall char datebuffer[60]; 148633271Sminshall extern char *rindex(); 148733271Sminshall 148833271Sminshall putlocation = where; 148933271Sminshall 149033271Sminshall while (*cp) { 149133271Sminshall if (*cp != '%') { 149233271Sminshall putchr(*cp++); 149333271Sminshall continue; 149433271Sminshall } 149533271Sminshall switch (*++cp) { 149633271Sminshall 149733271Sminshall case 't': 149833271Sminshall slash = rindex(line, '/'); 149933271Sminshall if (slash == (char *) 0) 150033271Sminshall puts(line); 150133271Sminshall else 150233271Sminshall puts(&slash[1]); 150333271Sminshall break; 150433271Sminshall 150533271Sminshall case 'h': 150633271Sminshall puts(editedhost); 150733271Sminshall break; 150833271Sminshall 150933271Sminshall case 'd': 151033271Sminshall get_date(datebuffer); 151133271Sminshall puts(datebuffer); 151233271Sminshall break; 151333271Sminshall 151433271Sminshall case '%': 151533271Sminshall putchr('%'); 151633271Sminshall break; 151733271Sminshall } 151833271Sminshall cp++; 151933271Sminshall } 152033271Sminshall } 1521