1 /* 2 * Copyright (c) 1989 Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 char copyright[] = 10 "@(#) Copyright (c) 1989 Regents of the University of California.\n\ 11 All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)telnetd.c 5.47 (Berkeley) 09/14/90"; 16 #endif /* not lint */ 17 18 #include "telnetd.h" 19 #include "pathnames.h" 20 21 /* 22 * I/O data buffers, 23 * pointers, and counters. 24 */ 25 char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 26 char ptyibuf2[BUFSIZ]; 27 28 int hostinfo = 1; /* do we print login banner? */ 29 30 #ifdef CRAY 31 extern int newmap; /* nonzero if \n maps to ^M^J */ 32 int lowpty = 0, highpty; /* low, high pty numbers */ 33 #endif /* CRAY */ 34 35 int debug = 0; 36 char *progname; 37 38 #if defined(NEED_GETTOS) 39 struct tosent { 40 char *t_name; /* name */ 41 char **t_aliases; /* alias list */ 42 char *t_proto; /* protocol */ 43 int t_tos; /* Type Of Service bits */ 44 }; 45 46 struct tosent * 47 gettosbyname(name, proto) 48 char *name, *proto; 49 { 50 static struct tosent te; 51 static char *aliasp = 0; 52 53 te.t_name = name; 54 te.t_aliases = &aliasp; 55 te.t_proto = proto; 56 te.t_tos = 020; /* Low Delay bit */ 57 return(&te); 58 } 59 #endif 60 61 main(argc, argv) 62 char *argv[]; 63 { 64 struct sockaddr_in from; 65 int on = 1, fromlen; 66 #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 67 struct tosent *tp; 68 #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 69 70 pfrontp = pbackp = ptyobuf; 71 netip = netibuf; 72 nfrontp = nbackp = netobuf; 73 74 progname = *argv; 75 76 #ifdef CRAY 77 /* 78 * Get number of pty's before trying to process options, 79 * which may include changing pty range. 80 */ 81 highpty = getnpty(); 82 #endif /* CRAY */ 83 84 top: 85 argc--, argv++; 86 #ifdef convex 87 if (argc == 1 && !debug) 88 argc--; /* ignore the host/port name */ 89 #endif 90 91 if (argc > 0 && strcmp(*argv, "-debug") == 0) { 92 debug++; 93 goto top; 94 } 95 96 #ifdef LINEMODE 97 if (argc > 0 && !strcmp(*argv, "-l")) { 98 alwayslinemode = 1; 99 goto top; 100 } 101 #endif /* LINEMODE */ 102 103 if (argc > 0 && !strcmp(*argv, "-h")) { 104 hostinfo = 0; 105 goto top; 106 } 107 108 #ifdef CRAY 109 if (argc > 0 && !strncmp(*argv, "-r", 2)) { 110 char *strchr(); 111 char *c; 112 113 /* 114 * Allow the specification of alterations to the pty search 115 * range. It is legal to specify only one, and not change the 116 * other from its default. 117 */ 118 *argv += 2; 119 if (**argv == '\0' && argc) 120 argv++, argc--; 121 c = strchr(*argv, '-'); 122 if (c) { 123 *c++ = '\0'; 124 highpty = atoi(c); 125 } 126 if (**argv != '\0') 127 lowpty = atoi(*argv); 128 if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { 129 usage(); 130 /* NOT REACHED */ 131 } 132 goto top; 133 } 134 # ifdef NEWINIT 135 if (argc > 0 && !strncmp(*argv, "-I", 2)) { 136 extern char *gen_id; 137 138 *argv += 2; 139 if (**argv == '\0') { 140 if (argc < 2) { 141 usage(); 142 /* NOT REACHED */ 143 } 144 argv++, argc--; 145 if (**argv == '\0') { 146 usage(); 147 /* NOT REACHED */ 148 } 149 } 150 gen_id = *argv; 151 goto top; 152 } 153 # endif /* NEWINIT */ 154 #endif /* CRAY */ 155 156 #ifdef DIAGNOSTICS 157 /* 158 * Check for desired diagnostics capabilities. 159 */ 160 if (argc > 0 && !strncmp(*argv, "-D", 2)) { 161 *argv += 2; 162 if (**argv == '\0') { 163 if (argc < 2) { 164 usage(); 165 /* NOT REACHED */ 166 } 167 argv++, argc--; 168 if (**argv == '\0') { 169 usage(); 170 /* NOT REACHED */ 171 } 172 } 173 if (!strcmp(*argv, "report")) { 174 diagnostic |= TD_REPORT|TD_OPTIONS; 175 } else if (!strcmp(*argv, "exercise")) { 176 diagnostic |= TD_EXERCISE; 177 } else if (!strcmp(*argv, "netdata")) { 178 diagnostic |= TD_NETDATA; 179 } else if (!strcmp(*argv, "ptydata")) { 180 diagnostic |= TD_PTYDATA; 181 } else if (!strcmp(*argv, "options")) { 182 diagnostic |= TD_OPTIONS; 183 } else { 184 usage(); 185 /* NOT REACHED */ 186 } 187 goto top; 188 } 189 #endif /* DIAGNOSTICS */ 190 191 #ifdef BFTPDAEMON 192 /* 193 * Check for bftp daemon 194 */ 195 if (argc > 0 && !strncmp(*argv, "-B", 2)) { 196 bftpd++; 197 goto top; 198 } 199 #endif /* BFTPDAEMON */ 200 201 if (argc > 0 && **argv == '-') { 202 fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1); 203 usage(); 204 /* NOT REACHED */ 205 } 206 207 if (debug) { 208 int s, ns, foo; 209 struct servent *sp; 210 static struct sockaddr_in sin = { AF_INET }; 211 212 if (argc > 1) { 213 usage(); 214 /* NOT REACHED */ 215 } else if (argc == 1) { 216 if (sp = getservbyname(*argv, "tcp")) { 217 sin.sin_port = sp->s_port; 218 } else { 219 sin.sin_port = atoi(*argv); 220 if ((int)sin.sin_port <= 0) { 221 fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 222 usage(); 223 /* NOT REACHED */ 224 } 225 sin.sin_port = htons((u_short)sin.sin_port); 226 } 227 } else { 228 sp = getservbyname("telnet", "tcp"); 229 if (sp == 0) { 230 fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 231 exit(1); 232 } 233 sin.sin_port = sp->s_port; 234 } 235 236 s = socket(AF_INET, SOCK_STREAM, 0); 237 if (s < 0) { 238 perror("telnetd: socket");; 239 exit(1); 240 } 241 (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 242 if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 243 perror("bind"); 244 exit(1); 245 } 246 if (listen(s, 1) < 0) { 247 perror("listen"); 248 exit(1); 249 } 250 foo = sizeof sin; 251 ns = accept(s, (struct sockaddr *)&sin, &foo); 252 if (ns < 0) { 253 perror("accept"); 254 exit(1); 255 } 256 (void) dup2(ns, 0); 257 (void) close(ns); 258 (void) close(s); 259 } else if (argc > 0) { 260 usage(); 261 /* NOT REACHED */ 262 } 263 264 openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 265 fromlen = sizeof (from); 266 if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 267 fprintf(stderr, "%s: ", progname); 268 perror("getpeername"); 269 _exit(1); 270 } 271 if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 272 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 273 } 274 275 #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 276 if ((tp = gettosbyname("telnet", "tcp")) && 277 (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0)) 278 syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 279 #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 280 net = 0; 281 doit(&from); 282 /* NOTREACHED */ 283 } /* end of main */ 284 285 usage() 286 { 287 fprintf(stderr, "Usage: telnetd [-debug] [-h]"); 288 #ifdef NEWINIT 289 fprintf(stderr, " [-Iinitid]"); 290 #endif /* NEWINIT */ 291 #ifdef DIAGNOSTICS 292 fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]"); 293 #endif /* DIAGNOSTICS */ 294 #ifdef LINEMODE 295 fprintf(stderr, " [-l]"); 296 #endif 297 #ifdef CRAY 298 fprintf(stderr, " [-r[lowpty]-[highpty]]"); 299 #endif 300 #ifdef BFTPDAEMON 301 fprintf(stderr, " [-B]"); 302 #endif /* BFTPDAEMON */ 303 fprintf(stderr, " [port]\n"); 304 exit(1); 305 } 306 307 void cleanup(); 308 309 /* 310 * getterminaltype 311 * 312 * Ask the other end to send along its terminal type and speed. 313 * Output is the variable terminaltype filled in. 314 */ 315 static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 316 void 317 getterminaltype() 318 { 319 void ttloop(); 320 321 settimer(baseline); 322 send_do(TELOPT_TTYPE, 1); 323 send_do(TELOPT_TSPEED, 1); 324 send_do(TELOPT_XDISPLOC, 1); 325 send_do(TELOPT_ENVIRON, 1); 326 while (his_will_wont_is_changing(TELOPT_TTYPE) || 327 his_will_wont_is_changing(TELOPT_TSPEED) || 328 his_will_wont_is_changing(TELOPT_XDISPLOC) || 329 his_will_wont_is_changing(TELOPT_ENVIRON)) { 330 ttloop(); 331 } 332 if (his_state_is_will(TELOPT_TSPEED)) { 333 static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 334 335 bcopy(sbbuf, nfrontp, sizeof sbbuf); 336 nfrontp += sizeof sbbuf; 337 } 338 if (his_state_is_will(TELOPT_XDISPLOC)) { 339 static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; 340 341 bcopy(sbbuf, nfrontp, sizeof sbbuf); 342 nfrontp += sizeof sbbuf; 343 } 344 if (his_state_is_will(TELOPT_ENVIRON)) { 345 static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE }; 346 347 bcopy(sbbuf, nfrontp, sizeof sbbuf); 348 nfrontp += sizeof sbbuf; 349 } 350 if (his_state_is_will(TELOPT_TTYPE)) { 351 352 bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 353 nfrontp += sizeof ttytype_sbbuf; 354 } 355 if (his_state_is_will(TELOPT_TSPEED)) { 356 while (sequenceIs(tspeedsubopt, baseline)) 357 ttloop(); 358 } 359 if (his_state_is_will(TELOPT_XDISPLOC)) { 360 while (sequenceIs(xdisplocsubopt, baseline)) 361 ttloop(); 362 } 363 if (his_state_is_will(TELOPT_ENVIRON)) { 364 while (sequenceIs(environsubopt, baseline)) 365 ttloop(); 366 } 367 if (his_state_is_will(TELOPT_TTYPE)) { 368 char first[256], last[256]; 369 370 while (sequenceIs(ttypesubopt, baseline)) 371 ttloop(); 372 373 /* 374 * If the other side has already disabled the option, then 375 * we have to just go with what we (might) have already gotten. 376 */ 377 if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { 378 (void) strncpy(first, terminaltype, sizeof(first)); 379 for(;;) { 380 /* 381 * Save the unknown name, and request the next name. 382 */ 383 (void) strncpy(last, terminaltype, sizeof(last)); 384 _gettermname(); 385 if (terminaltypeok(terminaltype)) 386 break; 387 if ((strncmp(last, terminaltype, sizeof(last)) == 0) || 388 his_state_is_wont(TELOPT_TTYPE)) { 389 /* 390 * We've hit the end. If this is the same as 391 * the first name, just go with it. 392 */ 393 if (strncmp(first, terminaltype, sizeof(first)) == 0) 394 break; 395 /* 396 * Get the terminal name one more time, so that 397 * RFC1091 compliant telnets will cycle back to 398 * the start of the list. 399 */ 400 _gettermname(); 401 if (strncmp(first, terminaltype, sizeof(first)) != 0) 402 (void) strncpy(terminaltype, first, sizeof(first)); 403 break; 404 } 405 } 406 } 407 } 408 } /* end of getterminaltype */ 409 410 _gettermname() 411 { 412 /* 413 * If the client turned off the option, 414 * we can't send another request, so we 415 * just return. 416 */ 417 if (his_state_is_wont(TELOPT_TTYPE)) 418 return; 419 settimer(baseline); 420 bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 421 nfrontp += sizeof ttytype_sbbuf; 422 while (sequenceIs(ttypesubopt, baseline)) 423 ttloop(); 424 } 425 426 terminaltypeok(s) 427 char *s; 428 { 429 char buf[1024]; 430 431 if (terminaltype == NULL) 432 return(1); 433 434 /* 435 * tgetent() will return 1 if the type is known, and 436 * 0 if it is not known. If it returns -1, it couldn't 437 * open the database. But if we can't open the database, 438 * it won't help to say we failed, because we won't be 439 * able to verify anything else. So, we treat -1 like 1. 440 */ 441 if (tgetent(buf, s) == 0) 442 return(0); 443 return(1); 444 } 445 446 /* 447 * Get a pty, scan input lines. 448 */ 449 doit(who) 450 struct sockaddr_in *who; 451 { 452 char *host, *inet_ntoa(); 453 int t; 454 struct hostent *hp; 455 456 /* 457 * Find an available pty to use. 458 */ 459 #ifndef convex 460 pty = getpty(); 461 if (pty < 0) 462 fatal(net, "All network ports in use"); 463 #else 464 for (;;) { 465 char *lp; 466 extern char *line, *getpty(); 467 468 if ((lp = getpty()) == NULL) 469 fatal(net, "Out of ptys"); 470 471 if ((pty = open(lp, 2)) >= 0) { 472 strcpy(line,lp); 473 line[5] = 't'; 474 break; 475 } 476 } 477 #endif 478 479 /* get name of connected client */ 480 hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 481 who->sin_family); 482 if (hp) 483 host = hp->h_name; 484 else 485 host = inet_ntoa(who->sin_addr); 486 487 init_env(); 488 /* 489 * get terminal type. 490 */ 491 getterminaltype(); 492 setenv("TERM", terminaltype ? terminaltype : "network", 1); 493 494 /* 495 * Start up the login process on the slave side of the terminal 496 */ 497 #ifndef convex 498 startslave(host); 499 500 telnet(net, pty); /* begin server processing */ 501 #else 502 telnet(net, pty, host); 503 #endif 504 /*NOTREACHED*/ 505 } /* end of doit */ 506 507 #ifndef MAXHOSTNAMELEN 508 #define MAXHOSTNAMELEN 64 509 #endif MAXHOSTNAMELEN 510 /* 511 * Main loop. Select from pty and network, and 512 * hand data to telnet receiver finite state machine. 513 */ 514 #ifndef convex 515 telnet(f, p) 516 #else 517 telnet(f, p, host) 518 #endif 519 int f, p; 520 #ifdef convex 521 char *host; 522 #endif 523 { 524 int on = 1; 525 char hostname[MAXHOSTNAMELEN]; 526 #if defined(CRAY2) && defined(UNICOS5) 527 int termstat(); 528 int interrupt(), sendbrk(); 529 #endif 530 #define TABBUFSIZ 512 531 char defent[TABBUFSIZ]; 532 char defstrs[TABBUFSIZ]; 533 #undef TABBUFSIZ 534 char *HE; 535 char *HN; 536 char *IM; 537 void netflush(); 538 539 /* 540 * Initialize the slc mapping table. 541 */ 542 get_slc_defaults(); 543 544 /* 545 * Do some tests where it is desireable to wait for a response. 546 * Rather than doing them slowly, one at a time, do them all 547 * at once. 548 */ 549 if (my_state_is_wont(TELOPT_SGA)) 550 send_will(TELOPT_SGA, 1); 551 /* 552 * Is the client side a 4.2 (NOT 4.3) system? We need to know this 553 * because 4.2 clients are unable to deal with TCP urgent data. 554 * 555 * To find out, we send out a "DO ECHO". If the remote system 556 * answers "WILL ECHO" it is probably a 4.2 client, and we note 557 * that fact ("WILL ECHO" ==> that the client will echo what 558 * WE, the server, sends it; it does NOT mean that the client will 559 * echo the terminal input). 560 */ 561 send_do(TELOPT_ECHO, 1); 562 563 #ifdef LINEMODE 564 if (his_state_is_wont(TELOPT_LINEMODE)) { 565 /* Query the peer for linemode support by trying to negotiate 566 * the linemode option. 567 */ 568 linemode = 0; 569 editmode = 0; 570 send_do(TELOPT_LINEMODE, 1); /* send do linemode */ 571 } 572 #endif /* LINEMODE */ 573 574 /* 575 * Send along a couple of other options that we wish to negotiate. 576 */ 577 send_do(TELOPT_NAWS, 1); 578 send_will(TELOPT_STATUS, 1); 579 flowmode = 1; /* default flow control state */ 580 send_do(TELOPT_LFLOW, 1); 581 582 /* 583 * Spin, waiting for a response from the DO ECHO. However, 584 * some REALLY DUMB telnets out there might not respond 585 * to the DO ECHO. So, we spin looking for NAWS, (most dumb 586 * telnets so far seem to respond with WONT for a DO that 587 * they don't understand...) because by the time we get the 588 * response, it will already have processed the DO ECHO. 589 * Kludge upon kludge. 590 */ 591 while (his_will_wont_is_changing(TELOPT_NAWS)) 592 ttloop(); 593 594 /* 595 * But... 596 * The client might have sent a WILL NAWS as part of its 597 * startup code; if so, we'll be here before we get the 598 * response to the DO ECHO. We'll make the assumption 599 * that any implementation that understands about NAWS 600 * is a modern enough implementation that it will respond 601 * to our DO ECHO request; hence we'll do another spin 602 * waiting for the ECHO option to settle down, which is 603 * what we wanted to do in the first place... 604 */ 605 if (his_want_state_is_will(TELOPT_ECHO) && 606 his_state_is_will(TELOPT_NAWS)) { 607 while (his_will_wont_is_changing(TELOPT_ECHO)) 608 ttloop(); 609 } 610 /* 611 * On the off chance that the telnet client is broken and does not 612 * respond to the DO ECHO we sent, (after all, we did send the 613 * DO NAWS negotiation after the DO ECHO, and we won't get here 614 * until a response to the DO NAWS comes back) simulate the 615 * receipt of a will echo. This will also send a WONT ECHO 616 * to the client, since we assume that the client failed to 617 * respond because it believes that it is already in DO ECHO 618 * mode, which we do not want. 619 */ 620 if (his_want_state_is_will(TELOPT_ECHO)) { 621 #ifdef DIAGNOSTICS 622 if (diagnostic & TD_OPTIONS) { 623 sprintf(nfrontp, "td: simulating recv\r\n"); 624 nfrontp += strlen(nfrontp); 625 } 626 #endif /* DIAGNOSTICS */ 627 willoption(TELOPT_ECHO); 628 } 629 630 /* 631 * Finally, to clean things up, we turn on our echo. This 632 * will break stupid 4.2 telnets out of local terminal echo. 633 */ 634 635 if (my_state_is_wont(TELOPT_ECHO)) 636 send_will(TELOPT_ECHO, 1); 637 638 /* 639 * Turn on packet mode 640 */ 641 (void) ioctl(p, TIOCPKT, (char *)&on); 642 #ifdef KLUDGELINEMODE 643 /* 644 * Continuing line mode support. If client does not support 645 * real linemode, attempt to negotiate kludge linemode by sending 646 * the do timing mark sequence. 647 */ 648 if (lmodetype < REAL_LINEMODE) 649 send_do(TELOPT_TM, 1); 650 #endif /* KLUDGELINEMODE */ 651 652 /* 653 * Call telrcv() once to pick up anything received during 654 * terminal type negotiation, 4.2/4.3 determination, and 655 * linemode negotiation. 656 */ 657 telrcv(); 658 659 (void) ioctl(f, FIONBIO, (char *)&on); 660 (void) ioctl(p, FIONBIO, (char *)&on); 661 #if defined(CRAY2) && defined(UNICOS5) 662 init_termdriver(f, p, interrupt, sendbrk); 663 #endif 664 665 #if defined(SO_OOBINLINE) 666 (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 667 #endif /* defined(SO_OOBINLINE) */ 668 669 #ifdef SIGTSTP 670 (void) signal(SIGTSTP, SIG_IGN); 671 #endif 672 #ifdef SIGTTOU 673 /* 674 * Ignoring SIGTTOU keeps the kernel from blocking us 675 * in ttioct() in /sys/tty.c. 676 */ 677 (void) signal(SIGTTOU, SIG_IGN); 678 #endif 679 680 (void) signal(SIGCHLD, cleanup); 681 682 #if defined(CRAY2) && defined(UNICOS5) 683 /* 684 * Cray-2 will send a signal when pty modes are changed by slave 685 * side. Set up signal handler now. 686 */ 687 if ((int)signal(SIGUSR1, termstat) < 0) 688 perror("signal"); 689 else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 690 perror("ioctl:TCSIGME"); 691 /* 692 * Make processing loop check terminal characteristics early on. 693 */ 694 termstat(); 695 #endif 696 697 #ifdef TIOCNOTTY 698 { 699 register int t; 700 t = open(_PATH_TTY, O_RDWR); 701 if (t >= 0) { 702 (void) ioctl(t, TIOCNOTTY, (char *)0); 703 (void) close(t); 704 } 705 } 706 #endif 707 708 #if defined(TIOCSCTTY) && defined(CRAY) 709 (void) setsid(); 710 ioctl(p, TIOCSCTTY, 0); 711 #endif 712 713 /* 714 * Show banner that getty never gave. 715 * 716 * We put the banner in the pty input buffer. This way, it 717 * gets carriage return null processing, etc., just like all 718 * other pty --> client data. 719 */ 720 721 #if !defined(CRAY) || !defined(NEWINIT) 722 if (getenv("USER")) 723 hostinfo = 0; 724 #endif 725 (void) gethostname(hostname, sizeof (hostname)); 726 727 if (getent(defent, "default") == 1) { 728 char *getstr(); 729 char *cp=defstrs; 730 731 HE = getstr("he", &cp); 732 HN = getstr("hn", &cp); 733 IM = getstr("im", &cp); 734 if (HN && *HN) 735 (void) strcpy(hostname, HN); 736 if (IM == 0) 737 IM = ""; 738 } else { 739 IM = DEFAULT_IM; 740 HE = 0; 741 } 742 edithost(HE, hostname); 743 if (hostinfo && *IM) 744 putf(IM, ptyibuf2); 745 746 if (pcc) 747 (void) strncat(ptyibuf2, ptyip, pcc+1); 748 ptyip = ptyibuf2; 749 pcc = strlen(ptyip); 750 #ifdef LINEMODE 751 /* 752 * Last check to make sure all our states are correct. 753 */ 754 init_termbuf(); 755 localstat(); 756 #endif /* LINEMODE */ 757 758 #ifdef DIAGNOSTICS 759 if (diagnostic & TD_REPORT) { 760 sprintf(nfrontp, "td: Entering processing loop\r\n"); 761 nfrontp += strlen(nfrontp); 762 } 763 #endif /* DIAGNOSTICS */ 764 765 #ifdef convex 766 startslave(host); 767 #endif 768 769 for (;;) { 770 fd_set ibits, obits, xbits; 771 register int c; 772 773 if (ncc < 0 && pcc < 0) 774 break; 775 776 #if defined(CRAY2) && defined(UNICOS5) 777 if (needtermstat) 778 _termstat(); 779 #endif /* defined(CRAY2) && defined(UNICOS5) */ 780 FD_ZERO(&ibits); 781 FD_ZERO(&obits); 782 FD_ZERO(&xbits); 783 /* 784 * Never look for input if there's still 785 * stuff in the corresponding output buffer 786 */ 787 if (nfrontp - nbackp || pcc > 0) { 788 FD_SET(f, &obits); 789 } else { 790 FD_SET(p, &ibits); 791 } 792 if (pfrontp - pbackp || ncc > 0) { 793 FD_SET(p, &obits); 794 } else { 795 FD_SET(f, &ibits); 796 } 797 if (!SYNCHing) { 798 FD_SET(f, &xbits); 799 } 800 if ((c = select(16, &ibits, &obits, &xbits, 801 (struct timeval *)0)) < 1) { 802 if (c == -1) { 803 if (errno == EINTR) { 804 continue; 805 } 806 } 807 sleep(5); 808 continue; 809 } 810 811 /* 812 * Any urgent data? 813 */ 814 if (FD_ISSET(net, &xbits)) { 815 SYNCHing = 1; 816 } 817 818 /* 819 * Something to read from the network... 820 */ 821 if (FD_ISSET(net, &ibits)) { 822 #if !defined(SO_OOBINLINE) 823 /* 824 * In 4.2 (and 4.3 beta) systems, the 825 * OOB indication and data handling in the kernel 826 * is such that if two separate TCP Urgent requests 827 * come in, one byte of TCP data will be overlaid. 828 * This is fatal for Telnet, but we try to live 829 * with it. 830 * 831 * In addition, in 4.2 (and...), a special protocol 832 * is needed to pick up the TCP Urgent data in 833 * the correct sequence. 834 * 835 * What we do is: if we think we are in urgent 836 * mode, we look to see if we are "at the mark". 837 * If we are, we do an OOB receive. If we run 838 * this twice, we will do the OOB receive twice, 839 * but the second will fail, since the second 840 * time we were "at the mark", but there wasn't 841 * any data there (the kernel doesn't reset 842 * "at the mark" until we do a normal read). 843 * Once we've read the OOB data, we go ahead 844 * and do normal reads. 845 * 846 * There is also another problem, which is that 847 * since the OOB byte we read doesn't put us 848 * out of OOB state, and since that byte is most 849 * likely the TELNET DM (data mark), we would 850 * stay in the TELNET SYNCH (SYNCHing) state. 851 * So, clocks to the rescue. If we've "just" 852 * received a DM, then we test for the 853 * presence of OOB data when the receive OOB 854 * fails (and AFTER we did the normal mode read 855 * to clear "at the mark"). 856 */ 857 if (SYNCHing) { 858 int atmark; 859 860 (void) ioctl(net, SIOCATMARK, (char *)&atmark); 861 if (atmark) { 862 ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 863 if ((ncc == -1) && (errno == EINVAL)) { 864 ncc = read(net, netibuf, sizeof (netibuf)); 865 if (sequenceIs(didnetreceive, gotDM)) { 866 SYNCHing = stilloob(net); 867 } 868 } 869 } else { 870 ncc = read(net, netibuf, sizeof (netibuf)); 871 } 872 } else { 873 ncc = read(net, netibuf, sizeof (netibuf)); 874 } 875 settimer(didnetreceive); 876 #else /* !defined(SO_OOBINLINE)) */ 877 ncc = read(net, netibuf, sizeof (netibuf)); 878 #endif /* !defined(SO_OOBINLINE)) */ 879 if (ncc < 0 && errno == EWOULDBLOCK) 880 ncc = 0; 881 else { 882 if (ncc <= 0) { 883 break; 884 } 885 netip = netibuf; 886 } 887 #ifdef DIAGNOSTICS 888 if (diagnostic & (TD_REPORT | TD_NETDATA)) { 889 sprintf(nfrontp, "td: netread %d chars\r\n", ncc); 890 nfrontp += strlen(nfrontp); 891 } 892 if (diagnostic & TD_NETDATA) { 893 printdata("nd", netip, ncc); 894 } 895 #endif /* DIAGNOSTICS */ 896 } 897 898 /* 899 * Something to read from the pty... 900 */ 901 if (FD_ISSET(p, &ibits)) { 902 pcc = read(p, ptyibuf, BUFSIZ); 903 if (pcc < 0 && errno == EWOULDBLOCK) 904 pcc = 0; 905 else { 906 if (pcc <= 0) 907 break; 908 #if !defined(CRAY2) || !defined(UNICOS5) 909 #ifdef LINEMODE 910 /* 911 * If ioctl from pty, pass it through net 912 */ 913 if (ptyibuf[0] & TIOCPKT_IOCTL) { 914 copy_termbuf(ptyibuf+1, pcc-1); 915 localstat(); 916 pcc = 1; 917 } 918 #endif LINEMODE 919 if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 920 netclear(); /* clear buffer back */ 921 #ifndef NO_URGENT 922 /* 923 * There are client telnets on some 924 * operating systems get screwed up 925 * royally if we send them urgent 926 * mode data. 927 */ 928 *nfrontp++ = IAC; 929 *nfrontp++ = DM; 930 neturg = nfrontp-1; /* off by one XXX */ 931 #endif 932 } 933 if (his_state_is_will(TELOPT_LFLOW) && 934 (ptyibuf[0] & 935 (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 936 (void) sprintf(nfrontp, "%c%c%c%c%c%c", 937 IAC, SB, TELOPT_LFLOW, 938 ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 939 IAC, SE); 940 nfrontp += 6; 941 } 942 pcc--; 943 ptyip = ptyibuf+1; 944 #else /* defined(CRAY2) && defined(UNICOS5) */ 945 if (!uselinemode) { 946 unpcc = pcc; 947 unptyip = ptyibuf; 948 pcc = term_output(&unptyip, ptyibuf2, 949 &unpcc, BUFSIZ); 950 ptyip = ptyibuf2; 951 } else 952 ptyip = ptyibuf; 953 #endif /* defined(CRAY2) && defined(UNICOS5) */ 954 } 955 } 956 957 while (pcc > 0) { 958 if ((&netobuf[BUFSIZ] - nfrontp) < 2) 959 break; 960 c = *ptyip++ & 0377, pcc--; 961 if (c == IAC) 962 *nfrontp++ = c; 963 #if defined(CRAY2) && defined(UNICOS5) 964 else if (c == '\n' && 965 my_state_is_wont(TELOPT_BINARY) && newmap) 966 *nfrontp++ = '\r'; 967 #endif /* defined(CRAY2) && defined(UNICOS5) */ 968 *nfrontp++ = c; 969 if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { 970 if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 971 *nfrontp++ = *ptyip++ & 0377; 972 pcc--; 973 } else 974 *nfrontp++ = '\0'; 975 } 976 } 977 #if defined(CRAY2) && defined(UNICOS5) 978 /* 979 * If chars were left over from the terminal driver, 980 * note their existence. 981 */ 982 if (!uselinemode && unpcc) { 983 pcc = unpcc; 984 unpcc = 0; 985 ptyip = unptyip; 986 } 987 #endif /* defined(CRAY2) && defined(UNICOS5) */ 988 989 if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 990 netflush(); 991 if (ncc > 0) 992 telrcv(); 993 if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 994 ptyflush(); 995 } 996 cleanup(); 997 } /* end of telnet */ 998 999 #ifndef TCSIG 1000 # ifdef TIOCSIG 1001 # define TCSIG TIOCSIG 1002 # endif 1003 #endif 1004 1005 /* 1006 * Send interrupt to process on other side of pty. 1007 * If it is in raw mode, just write NULL; 1008 * otherwise, write intr char. 1009 */ 1010 interrupt() 1011 { 1012 ptyflush(); /* half-hearted */ 1013 1014 #ifdef TCSIG 1015 (void) ioctl(pty, TCSIG, (char *)SIGINT); 1016 #else /* TCSIG */ 1017 init_termbuf(); 1018 *pfrontp++ = slctab[SLC_IP].sptr ? 1019 (unsigned char)*slctab[SLC_IP].sptr : '\177'; 1020 #endif /* TCSIG */ 1021 } 1022 1023 /* 1024 * Send quit to process on other side of pty. 1025 * If it is in raw mode, just write NULL; 1026 * otherwise, write quit char. 1027 */ 1028 sendbrk() 1029 { 1030 ptyflush(); /* half-hearted */ 1031 #ifdef TCSIG 1032 (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 1033 #else /* TCSIG */ 1034 init_termbuf(); 1035 *pfrontp++ = slctab[SLC_ABORT].sptr ? 1036 (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; 1037 #endif /* TCSIG */ 1038 } 1039 1040 sendsusp() 1041 { 1042 #ifdef SIGTSTP 1043 ptyflush(); /* half-hearted */ 1044 # ifdef TCSIG 1045 (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 1046 # else /* TCSIG */ 1047 *pfrontp++ = slctab[SLC_SUSP].sptr ? 1048 (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; 1049 # endif /* TCSIG */ 1050 #endif /* SIGTSTP */ 1051 } 1052 1053 /* 1054 * When we get an AYT, if ^T is enabled, use that. Otherwise, 1055 * just send back "[Yes]". 1056 */ 1057 recv_ayt() 1058 { 1059 #if defined(SIGINFO) && defined(TCSIG) 1060 if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { 1061 (void) ioctl(pty, TCSIG, (char *)SIGINFO); 1062 return; 1063 } 1064 #endif 1065 (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); 1066 nfrontp += 9; 1067 } 1068 1069 doeof() 1070 { 1071 #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) 1072 extern char oldeofc; 1073 #endif 1074 init_termbuf(); 1075 1076 #if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) 1077 if (!tty_isediting()) { 1078 *pfrontp++ = oldeofc; 1079 return; 1080 } 1081 #endif 1082 *pfrontp++ = slctab[SLC_EOF].sptr ? 1083 (unsigned char)*slctab[SLC_EOF].sptr : '\004'; 1084 } 1085