121182Sdist /* 2*38904Sborman * Copyright (c) 1989 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[] = 20*38904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\ 2121182Sdist All rights reserved.\n"; 2233687Sbostic #endif /* not lint */ 236295Sroot 2421182Sdist #ifndef lint 25*38904Sborman static char sccsid[] = "@(#)telnetd.c 5.38 (Berkeley) 09/01/89"; 2633687Sbostic #endif /* not lint */ 2721182Sdist 28*38904Sborman #include "telnetd.h" 29*38904Sborman 306002Sroot /* 31*38904Sborman * I/O data buffers, 32*38904Sborman * pointers, and counters. 336002Sroot */ 34*38904Sborman char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 35*38904Sborman char ptyibuf2[BUFSIZ]; 369218Ssam 37*38904Sborman #ifdef CRAY 38*38904Sborman int hostinfo = 1; /* do we print login banner? */ 39*38904Sborman #endif 409218Ssam 41*38904Sborman #ifdef CRAY 42*38904Sborman extern int newmap; /* nonzero if \n maps to ^M^J */ 43*38904Sborman int lowpty = 0, highpty = 128; /* low, high pty numbers */ 44*38904Sborman #endif /* CRAY */ 4512216Ssam 46*38904Sborman int debug = 0; 47*38904Sborman char *progname; 489218Ssam 49*38904Sborman main(argc, argv) 50*38904Sborman char *argv[]; 51*38904Sborman { 52*38904Sborman struct sockaddr_in from; 53*38904Sborman int on = 1, fromlen; 546002Sroot 55*38904Sborman pfrontp = pbackp = ptyobuf; 56*38904Sborman netip = netibuf; 57*38904Sborman nfrontp = nbackp = netobuf; 586002Sroot 59*38904Sborman progname = *argv; 60*38904Sborman top: 61*38904Sborman argc--, argv++; 6227649Sminshall 63*38904Sborman if (argc > 0 && strcmp(*argv, "-debug") == 0) { 64*38904Sborman debug++; 65*38904Sborman goto top; 66*38904Sborman } 6727649Sminshall 68*38904Sborman #ifdef LINEMODE 69*38904Sborman if (argc > 0 && !strcmp(*argv, "-l")) { 70*38904Sborman alwayslinemode = 1; 71*38904Sborman goto top; 72*38904Sborman } 73*38904Sborman #endif /* LINEMODE */ 7427649Sminshall 75*38904Sborman #ifdef CRAY 76*38904Sborman if (argc > 0 && !strcmp(*argv, "-h")) { 77*38904Sborman hostinfo = 0; 78*38904Sborman goto top; 79*38904Sborman } 8027649Sminshall 81*38904Sborman if (argc > 0 && !strncmp(*argv, "-r", 2)) { 82*38904Sborman char *strchr(); 83*38904Sborman char *c; 8427649Sminshall 85*38904Sborman *argv += 2; 86*38904Sborman if (**argv == '\0' && argc) 87*38904Sborman argv++, argc--; 88*38904Sborman c = strchr(*argv, '-'); 89*38904Sborman if (c) { 90*38904Sborman *c++ = '\0'; 91*38904Sborman highpty = atoi(c); 92*38904Sborman } else 93*38904Sborman highpty = -1; 94*38904Sborman lowpty = atoi(*argv); 95*38904Sborman if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) { 96*38904Sborman usage: 97*38904Sborman fprintf(stderr, "Usage: telnetd [-debug] [-h] "); 98*38904Sborman # ifdef NEWINIT 99*38904Sborman fprintf(stderr, "[-Iinitid] "); 100*38904Sborman # endif /* NEWINIT */ 101*38904Sborman fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n"); 102*38904Sborman exit(1); 103*38904Sborman } 104*38904Sborman goto top; 105*38904Sborman } 106*38904Sborman # ifdef NEWINIT 107*38904Sborman if (argc > 0 && !strncmp(*argv, "-I", 2)) { 108*38904Sborman extern char *gen_id; 10927649Sminshall 110*38904Sborman *argv += 2; 111*38904Sborman if (**argv == '\0') { 112*38904Sborman if (argc < 2) 113*38904Sborman goto usage; 114*38904Sborman argv++, argc--; 115*38904Sborman if (**argv == '\0') 116*38904Sborman goto usage; 117*38904Sborman } 118*38904Sborman gen_id = *argv; 119*38904Sborman goto top; 120*38904Sborman } 121*38904Sborman # endif /* NEWINIT */ 122*38904Sborman #endif /* CRAY */ 1236002Sroot 124*38904Sborman if (debug) { 12527185Sminshall int s, ns, foo; 12627185Sminshall struct servent *sp; 12727185Sminshall static struct sockaddr_in sin = { AF_INET }; 12827185Sminshall 129*38904Sborman if (argc > 0) { 130*38904Sborman if (sp = getservbyname(*argv, "tcp")) { 131*38904Sborman sin.sin_port = sp->s_port; 132*38904Sborman } else { 133*38904Sborman sin.sin_port = atoi(*argv); 134*38904Sborman if ((int)sin.sin_port <= 0) { 135*38904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 136*38904Sborman exit(1); 137*38904Sborman } 138*38904Sborman sin.sin_port = htons((u_short)sin.sin_port); 139*38904Sborman } 14037210Sminshall } else { 14137210Sminshall sp = getservbyname("telnet", "tcp"); 14237210Sminshall if (sp == 0) { 14337210Sminshall fprintf(stderr, 14437210Sminshall "telnetd: tcp/telnet: unknown service\n"); 145*38904Sborman exit(1); 14637210Sminshall } 14737210Sminshall sin.sin_port = sp->s_port; 14827185Sminshall } 14927185Sminshall 15027185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 15127185Sminshall if (s < 0) { 15227185Sminshall perror("telnetd: socket");; 15327185Sminshall exit(1); 15427185Sminshall } 155*38904Sborman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 156*38904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 15727185Sminshall perror("bind"); 15827185Sminshall exit(1); 15927185Sminshall } 16027185Sminshall if (listen(s, 1) < 0) { 16127185Sminshall perror("listen"); 16227185Sminshall exit(1); 16327185Sminshall } 16427185Sminshall foo = sizeof sin; 165*38904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo); 16627185Sminshall if (ns < 0) { 16727185Sminshall perror("accept"); 16827185Sminshall exit(1); 16927185Sminshall } 170*38904Sborman (void) dup2(ns, 0); 171*38904Sborman (void) close(ns); 172*38904Sborman (void) close(s); 17327185Sminshall } 174*38904Sborman 17524855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 17616371Skarels fromlen = sizeof (from); 177*38904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 178*38904Sborman fprintf(stderr, "%s: ", progname); 17916371Skarels perror("getpeername"); 18016371Skarels _exit(1); 1818346Ssam } 18217156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 18317187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 18410418Ssam } 185*38904Sborman net = 0; 186*38904Sborman doit(&from); 187*38904Sborman /* NOTREACHED */ 188*38904Sborman } /* end of main */ 1896002Sroot 19027983Sminshall int cleanup(); 19127649Sminshall 19227649Sminshall /* 19327983Sminshall * getterminaltype 19427649Sminshall * 195*38904Sborman * Ask the other end to send along its terminal type and speed. 19627983Sminshall * Output is the variable terminaltype filled in. 19727649Sminshall */ 198*38904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 19927983Sminshall void 20027983Sminshall getterminaltype() 20127649Sminshall { 202*38904Sborman void ttloop(); 20327649Sminshall 204*38904Sborman settimer(baseline); 205*38904Sborman willoption(TELOPT_TTYPE, 1); 206*38904Sborman willoption(TELOPT_TSPEED, 1); 207*38904Sborman while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) || 208*38904Sborman (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) { 20927983Sminshall ttloop(); 21027649Sminshall } 211*38904Sborman if (hisopts[TELOPT_TSPEED] == OPT_YES) { 212*38904Sborman static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 21327983Sminshall 21427983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 21527983Sminshall nfrontp += sizeof sbbuf; 216*38904Sborman } 217*38904Sborman if (hisopts[TELOPT_TTYPE] == OPT_YES) { 218*38904Sborman 219*38904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 220*38904Sborman nfrontp += sizeof ttytype_sbbuf; 221*38904Sborman } 222*38904Sborman if (hisopts[TELOPT_TSPEED] == OPT_YES) { 223*38904Sborman while (sequenceIs(tspeedsubopt, baseline)) 22427983Sminshall ttloop(); 225*38904Sborman } 226*38904Sborman if (hisopts[TELOPT_TTYPE] == OPT_YES) { 227*38904Sborman char first[256], last[256]; 228*38904Sborman 229*38904Sborman while (sequenceIs(ttypesubopt, baseline)) 230*38904Sborman ttloop(); 231*38904Sborman 232*38904Sborman if (!terminaltypeok(&terminaltype[5])) { 233*38904Sborman (void) strncpy(first, terminaltype, sizeof(first)); 234*38904Sborman for(;;) { 235*38904Sborman /* 236*38904Sborman * Save the unknown name, and request the next name. 237*38904Sborman */ 238*38904Sborman (void) strncpy(last, terminaltype, sizeof(last)); 239*38904Sborman _gettermname(); 240*38904Sborman if (terminaltypeok(&terminaltype[5])) 241*38904Sborman break; 242*38904Sborman if (strncmp(last, terminaltype, sizeof(last)) == 0) { 243*38904Sborman /* 244*38904Sborman * We've hit the end. If this is the same as 245*38904Sborman * the first name, just go with it. 246*38904Sborman */ 247*38904Sborman if (strncmp(first, terminaltype, sizeof(first) == 0)) 248*38904Sborman break; 249*38904Sborman /* 250*38904Sborman * Get the terminal name one more type, so that 251*38904Sborman * RFC1091 compliant telnets will cycle back to 252*38904Sborman * the start of the list. 253*38904Sborman */ 254*38904Sborman _gettermname(); 255*38904Sborman if (strncmp(first, terminaltype, sizeof(first) != 0)) 256*38904Sborman (void) strncpy(terminaltype, first, sizeof(first)); 257*38904Sborman break; 258*38904Sborman } 259*38904Sborman } 26027983Sminshall } 26127983Sminshall } 262*38904Sborman } /* end of getterminaltype */ 263*38904Sborman 264*38904Sborman _gettermname() 265*38904Sborman { 266*38904Sborman settimer(baseline); 267*38904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 268*38904Sborman nfrontp += sizeof ttytype_sbbuf; 269*38904Sborman while (sequenceIs(ttypesubopt, baseline)) 270*38904Sborman ttloop(); 27127649Sminshall } 27227649Sminshall 273*38904Sborman terminaltypeok(s) 274*38904Sborman char *s; 275*38904Sborman { 276*38904Sborman char buf[1024]; 277*38904Sborman 278*38904Sborman if (terminaltype == NULL) 279*38904Sborman return(1); 280*38904Sborman 281*38904Sborman /* 282*38904Sborman * tgetent() will return 1 if the type is known, and 283*38904Sborman * 0 if it is not known. If it returns -1, it couldn't 284*38904Sborman * open the database. But if we can't open the database, 285*38904Sborman * it won't help to say we failed, because we won't be 286*38904Sborman * able to verify anything else. So, we treat -1 like 1. 287*38904Sborman */ 288*38904Sborman if (tgetent(buf, s) == 0) 289*38904Sborman return(0); 290*38904Sborman return(1); 291*38904Sborman } 292*38904Sborman 2936002Sroot /* 2946002Sroot * Get a pty, scan input lines. 2956002Sroot */ 296*38904Sborman doit(who) 29712683Ssam struct sockaddr_in *who; 2986002Sroot { 29920188Skarels char *host, *inet_ntoa(); 300*38904Sborman int t; 30112683Ssam struct hostent *hp; 3026002Sroot 303*38904Sborman /* 304*38904Sborman * Find an available pty to use. 305*38904Sborman */ 306*38904Sborman pty = getpty(); 307*38904Sborman if (pty < 0) 308*38904Sborman fatal(net, "All network ports in use"); 30920188Skarels 310*38904Sborman t = getptyslave(); 311*38904Sborman 312*38904Sborman /* get name of connected client */ 313*38904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 31412683Ssam who->sin_family); 31512683Ssam if (hp) 31612683Ssam host = hp->h_name; 31712683Ssam else 31817444Sralph host = inet_ntoa(who->sin_addr); 31927983Sminshall 32027983Sminshall /* 321*38904Sborman * get terminal type. 32227983Sminshall */ 32327983Sminshall getterminaltype(); 324*38904Sborman if (terminaltype == NULL) 325*38904Sborman terminaltype = "TERM=network"; 32627983Sminshall 32727649Sminshall /* 328*38904Sborman * Start up the login process on the slave side of the terminal 32927649Sminshall */ 330*38904Sborman startslave(t, host); 331*38904Sborman 332*38904Sborman telnet(net, pty); /* begin server processing */ 3339244Ssam /*NOTREACHED*/ 334*38904Sborman } /* end of doit */ 3359244Ssam 336*38904Sborman #ifndef MAXHOSTNAMELEN 337*38904Sborman #define MAXHOSTNAMELEN 64 338*38904Sborman #endif MAXHOSTNAMELEN 3396002Sroot /* 3406002Sroot * Main loop. Select from pty and network, and 3416002Sroot * hand data to telnet receiver finite state machine. 3426002Sroot */ 3436002Sroot telnet(f, p) 344*38904Sborman int f, p; 3456002Sroot { 3466002Sroot int on = 1; 34727898Skarels char hostname[MAXHOSTNAMELEN]; 348*38904Sborman #ifdef CRAY2 349*38904Sborman int termstat(); 350*38904Sborman int interrupt(), sendbrk(); 351*38904Sborman #endif 35233271Sminshall #define TABBUFSIZ 512 35333271Sminshall char defent[TABBUFSIZ]; 35433271Sminshall char defstrs[TABBUFSIZ]; 35533271Sminshall #undef TABBUFSIZ 35633271Sminshall char *HE; 35733271Sminshall char *HN; 35833271Sminshall char *IM; 359*38904Sborman void netflush(); 360*38904Sborman 36132400Sminshall /* 362*38904Sborman * Initialize the slc mapping table. 36332400Sminshall */ 364*38904Sborman get_slc_defaults(); 3656002Sroot 3668379Ssam /* 367*38904Sborman * Do some tests where it is desireable to wait for a response. 368*38904Sborman * Rather than doing them slowly, one at a time, do them all 369*38904Sborman * at once. 3708379Ssam */ 371*38904Sborman if (!myopts[TELOPT_ECHO]) 372*38904Sborman dooption(TELOPT_ECHO); 373*38904Sborman if (!myopts[TELOPT_SGA]) 374*38904Sborman dooption(TELOPT_SGA); 37512713Ssam /* 37627649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 37727649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 37827649Sminshall * 37927649Sminshall * To find out, we send out a "DO ECHO". If the remote system 38027649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 38127649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 38227649Sminshall * WE, the server, sends it; it does NOT mean that the client will 38327649Sminshall * echo the terminal input). 38427649Sminshall */ 385*38904Sborman willoption(TELOPT_ECHO, 1); 38627649Sminshall 387*38904Sborman #ifdef LINEMODE 388*38904Sborman if (hisopts[TELOPT_LINEMODE] == OPT_NO) { 389*38904Sborman /* Query the peer for linemode support by trying to negotiate 390*38904Sborman * the linemode option. 391*38904Sborman */ 392*38904Sborman linemode = 1; 393*38904Sborman editmode = 0; 394*38904Sborman willoption(TELOPT_LINEMODE, 1); /* send do linemode */ 395*38904Sborman } 396*38904Sborman #endif /* LINEMODE */ 397*38904Sborman 39827649Sminshall /* 399*38904Sborman * Send along a couple of other options that we wish to negotiate. 400*38904Sborman */ 401*38904Sborman willoption(TELOPT_NAWS, 1); 402*38904Sborman dooption(TELOPT_STATUS, 1); 403*38904Sborman flowmode = 1; /* default flow control state */ 404*38904Sborman willoption(TELOPT_LFLOW, 1); 405*38904Sborman 406*38904Sborman /* 407*38904Sborman * Spin, waiting for a response from the DO ECHO. However, 408*38904Sborman * some REALLY DUMB telnets out there might not respond 409*38904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 410*38904Sborman * telnets so far seem to respond with WONT for a DO that 411*38904Sborman * they don't understand...) because by the time we get the 412*38904Sborman * response, it will already have processed the DO ECHO. 413*38904Sborman * Kludge upon kludge. 414*38904Sborman */ 415*38904Sborman while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS]) 416*38904Sborman ttloop(); 417*38904Sborman 418*38904Sborman /* 419*38904Sborman * Turn on packet mode, and default to line at at time mode. 420*38904Sborman */ 421*38904Sborman (void) ioctl(p, TIOCPKT, (char *)&on); 422*38904Sborman #ifdef LINEMODE 423*38904Sborman tty_setlinemode(1); 424*38904Sborman 425*38904Sborman # ifdef KLUDGELINEMODE 426*38904Sborman /* 427*38904Sborman * Continuing line mode support. If client does not support 428*38904Sborman * real linemode, attempt to negotiate kludge linemode by sending 429*38904Sborman * the do timing mark sequence. 430*38904Sborman */ 431*38904Sborman if (lmodetype < REAL_LINEMODE) 432*38904Sborman willoption(TELOPT_TM, 1); 433*38904Sborman # endif /* KLUDGELINEMODE */ 434*38904Sborman #endif /* LINEMODE */ 435*38904Sborman 436*38904Sborman /* 437*38904Sborman * Call telrcv() once to pick up anything received during 438*38904Sborman * terminal type negotiation, 4.2/4.3 determination, and 439*38904Sborman * linemode negotiation. 440*38904Sborman */ 441*38904Sborman telrcv(); 442*38904Sborman 443*38904Sborman (void) ioctl(f, FIONBIO, (char *)&on); 444*38904Sborman (void) ioctl(p, FIONBIO, (char *)&on); 445*38904Sborman #ifdef CRAY2 446*38904Sborman init_termdriver(f, p, interrupt, sendbrk); 447*38904Sborman #endif 448*38904Sborman 449*38904Sborman #if defined(SO_OOBINLINE) 450*38904Sborman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 451*38904Sborman #endif /* defined(SO_OOBINLINE) */ 452*38904Sborman 453*38904Sborman #ifdef SIGTSTP 454*38904Sborman (void) signal(SIGTSTP, SIG_IGN); 455*38904Sborman #endif 456*38904Sborman #ifdef SIGTTOU 457*38904Sborman /* 458*38904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us 459*38904Sborman * in ttioct() in /sys/tty.c. 460*38904Sborman */ 461*38904Sborman (void) signal(SIGTTOU, SIG_IGN); 462*38904Sborman #endif 463*38904Sborman 464*38904Sborman (void) signal(SIGCHLD, cleanup); 465*38904Sborman 466*38904Sborman #if defined(CRAY2) 467*38904Sborman /* 468*38904Sborman * Cray-2 will send a signal when pty modes are changed by slave 469*38904Sborman * side. Set up signal handler now. 470*38904Sborman */ 471*38904Sborman if ((int)signal(SIGUSR1, termstat) < 0) 472*38904Sborman perror("signal"); 473*38904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 474*38904Sborman perror("ioctl:TCSIGME"); 475*38904Sborman /* 476*38904Sborman * Make processing loop check terminal characteristics early on. 477*38904Sborman */ 478*38904Sborman termstat(); 479*38904Sborman #endif 480*38904Sborman 481*38904Sborman (void) setpgrp(0, 0); 482*38904Sborman 483*38904Sborman /* 48412713Ssam * Show banner that getty never gave. 48527797Sminshall * 48633271Sminshall * We put the banner in the pty input buffer. This way, it 48733271Sminshall * gets carriage return null processing, etc., just like all 48833271Sminshall * other pty --> client data. 48912713Ssam */ 49027797Sminshall 491*38904Sborman (void) gethostname(hostname, sizeof (hostname)); 492*38904Sborman 49333271Sminshall if (getent(defent, "default") == 1) { 49433271Sminshall char *getstr(); 495*38904Sborman char *cp=defstrs; 49627649Sminshall 497*38904Sborman HE = getstr("he", &cp); 498*38904Sborman HN = getstr("hn", &cp); 499*38904Sborman IM = getstr("im", &cp); 50033271Sminshall if (HN && *HN) 501*38904Sborman (void) strcpy(hostname, HN); 502*38904Sborman if (IM == 0) 503*38904Sborman IM = ""; 50433271Sminshall } else { 505*38904Sborman #ifdef CRAY 506*38904Sborman if (hostinfo == 0) 507*38904Sborman IM = 0; 508*38904Sborman else 509*38904Sborman #endif 510*38904Sborman IM = DEFAULT_IM; 511*38904Sborman HE = 0; 51233271Sminshall } 513*38904Sborman edithost(HE, hostname); 514*38904Sborman if (IM && *IM) 515*38904Sborman putf(IM, ptyibuf2); 51627797Sminshall 517*38904Sborman if (pcc) 518*38904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1); 519*38904Sborman ptyip = ptyibuf2; 520*38904Sborman pcc = strlen(ptyip); 52133271Sminshall 5226002Sroot for (;;) { 52327185Sminshall fd_set ibits, obits, xbits; 5246002Sroot register int c; 5256002Sroot 52627185Sminshall if (ncc < 0 && pcc < 0) 52727185Sminshall break; 52827185Sminshall 529*38904Sborman #ifdef CRAY2 530*38904Sborman if (needtermstat) 531*38904Sborman _termstat(); 532*38904Sborman #endif /* CRAY2 */ 53327185Sminshall FD_ZERO(&ibits); 53427185Sminshall FD_ZERO(&obits); 53527185Sminshall FD_ZERO(&xbits); 5366002Sroot /* 5376002Sroot * Never look for input if there's still 5386002Sroot * stuff in the corresponding output buffer 5396002Sroot */ 54027185Sminshall if (nfrontp - nbackp || pcc > 0) { 54127185Sminshall FD_SET(f, &obits); 54227185Sminshall } else { 54327185Sminshall FD_SET(p, &ibits); 54427185Sminshall } 54527185Sminshall if (pfrontp - pbackp || ncc > 0) { 54627185Sminshall FD_SET(p, &obits); 54727185Sminshall } else { 54827185Sminshall FD_SET(f, &ibits); 54927185Sminshall } 55027185Sminshall if (!SYNCHing) { 55127185Sminshall FD_SET(f, &xbits); 55227185Sminshall } 55327185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 55427185Sminshall (struct timeval *)0)) < 1) { 55527185Sminshall if (c == -1) { 55627185Sminshall if (errno == EINTR) { 55727185Sminshall continue; 55827185Sminshall } 55927185Sminshall } 5606002Sroot sleep(5); 5616002Sroot continue; 5626002Sroot } 5636002Sroot 5646002Sroot /* 56527185Sminshall * Any urgent data? 56627185Sminshall */ 56727185Sminshall if (FD_ISSET(net, &xbits)) { 56827185Sminshall SYNCHing = 1; 56927185Sminshall } 57027185Sminshall 57127185Sminshall /* 5726002Sroot * Something to read from the network... 5736002Sroot */ 57427185Sminshall if (FD_ISSET(net, &ibits)) { 57527649Sminshall #if !defined(SO_OOBINLINE) 57627185Sminshall /* 57727898Skarels * In 4.2 (and 4.3 beta) systems, the 57827185Sminshall * OOB indication and data handling in the kernel 57927185Sminshall * is such that if two separate TCP Urgent requests 58027185Sminshall * come in, one byte of TCP data will be overlaid. 58127185Sminshall * This is fatal for Telnet, but we try to live 58227185Sminshall * with it. 58327185Sminshall * 58427185Sminshall * In addition, in 4.2 (and...), a special protocol 58527185Sminshall * is needed to pick up the TCP Urgent data in 58627185Sminshall * the correct sequence. 58727185Sminshall * 58827185Sminshall * What we do is: if we think we are in urgent 58927185Sminshall * mode, we look to see if we are "at the mark". 59027185Sminshall * If we are, we do an OOB receive. If we run 59127185Sminshall * this twice, we will do the OOB receive twice, 59227185Sminshall * but the second will fail, since the second 59327185Sminshall * time we were "at the mark", but there wasn't 59427185Sminshall * any data there (the kernel doesn't reset 59527185Sminshall * "at the mark" until we do a normal read). 59627185Sminshall * Once we've read the OOB data, we go ahead 59727185Sminshall * and do normal reads. 59827185Sminshall * 59927185Sminshall * There is also another problem, which is that 60027185Sminshall * since the OOB byte we read doesn't put us 60127185Sminshall * out of OOB state, and since that byte is most 60227185Sminshall * likely the TELNET DM (data mark), we would 60327185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 60427185Sminshall * So, clocks to the rescue. If we've "just" 60527185Sminshall * received a DM, then we test for the 60627185Sminshall * presence of OOB data when the receive OOB 60727185Sminshall * fails (and AFTER we did the normal mode read 60827185Sminshall * to clear "at the mark"). 60927185Sminshall */ 61027185Sminshall if (SYNCHing) { 61127185Sminshall int atmark; 61227185Sminshall 613*38904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 61427185Sminshall if (atmark) { 61527185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 61627185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 61727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 61827983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 61927185Sminshall SYNCHing = stilloob(net); 62027185Sminshall } 62127185Sminshall } 62227185Sminshall } else { 62327185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 6246002Sroot } 62527185Sminshall } else { 62627185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 62727185Sminshall } 62827185Sminshall settimer(didnetreceive); 62927649Sminshall #else /* !defined(SO_OOBINLINE)) */ 63027185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 63127649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 63227185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 63327185Sminshall ncc = 0; 63427185Sminshall else { 63527185Sminshall if (ncc <= 0) { 63627185Sminshall break; 63727185Sminshall } 63827185Sminshall netip = netibuf; 63927185Sminshall } 6406002Sroot } 6416002Sroot 6426002Sroot /* 6436002Sroot * Something to read from the pty... 6446002Sroot */ 645*38904Sborman if (FD_ISSET(p, &ibits)) { 6466002Sroot pcc = read(p, ptyibuf, BUFSIZ); 6476002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 6486002Sroot pcc = 0; 6496002Sroot else { 6506002Sroot if (pcc <= 0) 6516002Sroot break; 652*38904Sborman #ifndef CRAY2 653*38904Sborman #ifdef LINEMODE 654*38904Sborman /* 655*38904Sborman * If ioctl from pty, pass it through net 656*38904Sborman */ 657*38904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) { 658*38904Sborman copy_termbuf(ptyibuf+1, pcc-1); 659*38904Sborman localstat(); 660*38904Sborman pcc = 1; 661*38904Sborman } 662*38904Sborman #endif LINEMODE 66337210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 664*38904Sborman netclear(); /* clear buffer back */ 66537210Sminshall *nfrontp++ = IAC; 66637210Sminshall *nfrontp++ = DM; 66737210Sminshall neturg = nfrontp-1; /* off by one XXX */ 66837210Sminshall } 66937210Sminshall if (hisopts[TELOPT_LFLOW] && 67037210Sminshall (ptyibuf[0] & 671*38904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 672*38904Sborman (void) sprintf(nfrontp, "%c%c%c%c%c%c", 67337210Sminshall IAC, SB, TELOPT_LFLOW, 67437210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 67537210Sminshall IAC, SE); 67637210Sminshall nfrontp += 6; 67737210Sminshall } 67833267Sminshall pcc--; 67933267Sminshall ptyip = ptyibuf+1; 680*38904Sborman #else /* CRAY2 */ 681*38904Sborman if (!uselinemode) { 682*38904Sborman pcc = term_output(ptyibuf, ptyibuf2, 683*38904Sborman pcc, BUFSIZ); 684*38904Sborman ptyip = ptyibuf2; 685*38904Sborman } else 686*38904Sborman ptyip = ptyibuf; 687*38904Sborman #endif /* CRAY2 */ 688*38904Sborman } 6896002Sroot } 6906002Sroot 6916002Sroot while (pcc > 0) { 6926002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 6936002Sroot break; 6946002Sroot c = *ptyip++ & 0377, pcc--; 6956002Sroot if (c == IAC) 6966002Sroot *nfrontp++ = c; 697*38904Sborman #ifdef CRAY2 698*38904Sborman else if (c == '\n' && 699*38904Sborman myopts[TELOPT_BINARY] == OPT_NO && newmap) 700*38904Sborman *nfrontp++ = '\r'; 701*38904Sborman #endif /* CRAY2 */ 7026002Sroot *nfrontp++ = c; 70331940Sbostic if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { 70427020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 70527020Sminshall *nfrontp++ = *ptyip++ & 0377; 70627020Sminshall pcc--; 70727020Sminshall } else 70827020Sminshall *nfrontp++ = '\0'; 70927020Sminshall } 7106002Sroot } 71127185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 7126002Sroot netflush(); 7136002Sroot if (ncc > 0) 7146002Sroot telrcv(); 71527185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 7166002Sroot ptyflush(); 7176002Sroot } 7186002Sroot cleanup(); 719*38904Sborman } /* end of telnet */ 7206002Sroot 721*38904Sborman #ifndef TCSIG 722*38904Sborman # ifdef TIOCSIG 723*38904Sborman # define TCSIG TIOCSIG 724*38904Sborman # endif 725*38904Sborman #endif 7266002Sroot 72737212Sminshall /* 7286002Sroot * Send interrupt to process on other side of pty. 7296002Sroot * If it is in raw mode, just write NULL; 7306002Sroot * otherwise, write intr char. 7316002Sroot */ 7326002Sroot interrupt() 7336002Sroot { 734*38904Sborman ptyflush(); /* half-hearted */ 7356002Sroot 736*38904Sborman #ifdef TCSIG 737*38904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT); 738*38904Sborman #else /* TCSIG */ 739*38904Sborman init_termbuf(); 740*38904Sborman *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177'; 741*38904Sborman #endif /* TCSIG */ 7426002Sroot } 7436002Sroot 74427229Sminshall /* 74527229Sminshall * Send quit to process on other side of pty. 74627229Sminshall * If it is in raw mode, just write NULL; 74727229Sminshall * otherwise, write quit char. 74827229Sminshall */ 74927229Sminshall sendbrk() 75027229Sminshall { 75127229Sminshall ptyflush(); /* half-hearted */ 752*38904Sborman #ifdef TCSIG 753*38904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 754*38904Sborman #else /* TCSIG */ 755*38904Sborman init_termbuf(); 756*38904Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034'; 757*38904Sborman #endif /* TCSIG */ 75827229Sminshall } 75927229Sminshall 760*38904Sborman sendsusp() 7616002Sroot { 762*38904Sborman #ifdef SIGTSTP 763*38904Sborman ptyflush(); /* half-hearted */ 764*38904Sborman # ifdef TCSIG 765*38904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 766*38904Sborman # else /* TCSIG */ 767*38904Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032'; 768*38904Sborman # endif /* TCSIG */ 769*38904Sborman #endif /* SIGTSTP */ 7706002Sroot } 7716002Sroot 772*38904Sborman doeof() 7736002Sroot { 774*38904Sborman init_termbuf(); 7756002Sroot 776*38904Sborman *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004'; 7776002Sroot } 778