143014Swollman/* 22742Swollman * Copyright (c) 1989, 1993 32742Swollman * The Regents of the University of California. All rights reserved. 42742Swollman * 52742Swollman * Redistribution and use in source and binary forms, with or without 62742Swollman * modification, are permitted provided that the following conditions 720094Swollman * are met: 82742Swollman * 1. Redistributions of source code must retain the above copyright 943014Swollman * notice, this list of conditions and the following disclaimer. 1043014Swollman * 2. Redistributions in binary form must reproduce the above copyright 112742Swollman * notice, this list of conditions and the following disclaimer in the 1220094Swollman * documentation and/or other materials provided with the distribution. 1320094Swollman * 3. All advertising materials mentioning features or use of this software 1420094Swollman * must display the following acknowledgement: 1520094Swollman * This product includes software developed by the University of 1620094Swollman * California, Berkeley and its contributors. 1720094Swollman * 4. Neither the name of the University nor the names of its contributors 1820094Swollman * may be used to endorse or promote products derived from this software 1920094Swollman * without specific prior written permission. 2020094Swollman * 212742Swollman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 222742Swollman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 232742Swollman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 242742Swollman * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 252742Swollman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 262742Swollman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2719878Swollman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 282742Swollman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 292742Swollman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 302742Swollman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3119878Swollman * SUCH DAMAGE. 3219878Swollman */ 332742Swollman 3419878Swollman#if 0 3519878Swollman#ifndef lint 3619878Swollmanstatic const char sccsid[] = "@(#)telnetd.c 8.4 (Berkeley) 5/30/95"; 3719878Swollman#endif 3820094Swollman#endif 392742Swollman#include <sys/cdefs.h> 402742Swollman__FBSDID("$FreeBSD$"); 412742Swollman 422742Swollman#include "telnetd.h" 432742Swollman#include "pathnames.h" 4419878Swollman 4514343Swollman#include <sys/mman.h> 469908Swollman#include <err.h> 479908Swollman#include <libutil.h> 489908Swollman#include <paths.h> 499908Swollman#include <termcap.h> 509908Swollman 519908Swollman#include <arpa/inet.h> 529908Swollman 539908Swollman#ifdef AUTHENTICATION 549908Swollman#include <libtelnet/auth.h> 559908Swollmanint auth_level = 0; 5614343Swollman#endif 5714343Swollman#ifdef ENCRYPTION 589908Swollman#include <libtelnet/encrypt.h> 599908Swollman#endif 609908Swollman#include <libtelnet/misc.h> 619908Swollman 629908Swollmanchar remote_hostname[MAXHOSTNAMELEN]; 639908Swollmansize_t utmp_len = sizeof(remote_hostname) - 1; 649908Swollmanint registerd_host_only = 0; 6519878Swollman 662742Swollman 672742Swollman/* 6843014Swollman * I/O data buffers, 6919878Swollman * pointers, and counters. 702742Swollman */ 7119878Swollmanchar ptyibuf[BUFSIZ], *ptyip = ptyibuf; 722742Swollmanchar ptyibuf2[BUFSIZ]; 732742Swollman 742742Swollmanint readstream(int, char *, int); 752742Swollmanvoid doit(struct sockaddr *); 762742Swollmanint terminaltypeok(char *); 772742Swollman 782742Swollmanint hostinfo = 1; /* do we print login banner? */ 7921217Swollman 802742Swollmanstatic int debug = 0; 812742Swollmanint keepalive = 1; 822742Swollmanconst char *altlogin; 832742Swollman 842742Swollmanvoid doit(struct sockaddr *); 852742Swollmanint terminaltypeok(char *); 862742Swollmanvoid startslave(char *, int, char *); 8721217Swollmanextern void usage(void); 882742Swollmanstatic void _gettermname(void); 892742Swollman 902742Swollman/* 912742Swollman * The string to pass to getopt(). We do it this way so 9219878Swollman * that only the actual options that we support will be 932742Swollman * passed off to getopt(). 942742Swollman */ 952742Swollmanchar valid_opts[] = { 962742Swollman 'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U', 972742Swollman '4', '6', 982742Swollman#ifdef AUTHENTICATION 992742Swollman 'a', ':', 'X', ':', 1002742Swollman#endif 1012742Swollman#ifdef BFTPDAEMON 1022742Swollman 'B', 1032742Swollman#endif 1042742Swollman#ifdef DIAGNOSTICS 1052742Swollman 'D', ':', 1062742Swollman#endif 1072742Swollman#ifdef ENCRYPTION 1082742Swollman 'e', ':', 1092742Swollman#endif 1102742Swollman#ifdef LINEMODE 1112742Swollman 'l', 1122742Swollman#endif 1132742Swollman '\0' 1142742Swollman}; 1152742Swollman 1162742Swollmanint family = AF_INET; 1172742Swollman 11817200Swollman#ifndef MAXHOSTNAMELEN 11917200Swollman#define MAXHOSTNAMELEN 256 12017200Swollman#endif /* MAXHOSTNAMELEN */ 12117200Swollman 12217200Swollmanchar *hostname; 12317200Swollmanchar host_name[MAXHOSTNAMELEN]; 12417200Swollman 12517200Swollmanextern void telnet(int, int, char *); 12617200Swollman 12719878Swollmanint level; 12819878Swollmanchar user_name[256]; 12919878Swollman 13019878Swollmanint 13119878Swollmanmain(int argc, char *argv[]) 13219878Swollman{ 13319878Swollman u_long ultmp; 1342742Swollman struct sockaddr_storage from; 1352742Swollman int on = 1, fromlen; 1362742Swollman int ch; 1372742Swollman#if defined(IPPROTO_IP) && defined(IP_TOS) 1382742Swollman int tos = -1; 1392742Swollman#endif 1402742Swollman char *ep; 1412742Swollman 1422742Swollman pfrontp = pbackp = ptyobuf; 1432742Swollman netip = netibuf; 14443014Swollman nfrontp = nbackp = netobuf; 14543014Swollman#ifdef ENCRYPTION 14643014Swollman nclearto = 0; 14743014Swollman#endif /* ENCRYPTION */ 14843014Swollman 14943014Swollman /* 15043014Swollman * This initialization causes linemode to default to a configuration 15143014Swollman * that works on all telnet clients, including the FreeBSD client. 15243014Swollman * This is not quite the same as the telnet client issuing a "mode 15343014Swollman * character" command, but has most of the same benefits, and is 1542742Swollman * preferable since some clients (like usofts) don't have the 15543014Swollman * mode character command anyway and linemode breaks things. 15643014Swollman * The most notable symptom of fix is that csh "set filec" operations 15743014Swollman * like <ESC> (filename completion) and ^D (choices) keys now work 15843014Swollman * in telnet sessions and can be used more than once on the same line. 15943014Swollman * CR/LF handling is also corrected in some termio modes. This 16043014Swollman * change resolves problem reports bin/771 and bin/1037. 16143014Swollman */ 16243014Swollman 1632742Swollman linemode=1; /*Default to mode that works on bulk of clients*/ 16443014Swollman 16543014Swollman while ((ch = getopt(argc, argv, valid_opts)) != -1) { 16643014Swollman switch(ch) { 16743014Swollman 16843014Swollman#ifdef AUTHENTICATION 16943014Swollman case 'a': 1702742Swollman /* 17143014Swollman * Check for required authentication level 1722742Swollman */ 17343014Swollman if (strcmp(optarg, "debug") == 0) { 17443014Swollman extern int auth_debug_mode; 17543014Swollman auth_debug_mode = 1; 17643014Swollman } else if (strcasecmp(optarg, "none") == 0) { 17743014Swollman auth_level = 0; 1782742Swollman } else if (strcasecmp(optarg, "other") == 0) { 17943014Swollman auth_level = AUTH_OTHER; 18043014Swollman } else if (strcasecmp(optarg, "user") == 0) { 18143014Swollman auth_level = AUTH_USER; 1822742Swollman } else if (strcasecmp(optarg, "valid") == 0) { 18343014Swollman auth_level = AUTH_VALID; 18443014Swollman } else if (strcasecmp(optarg, "off") == 0) { 18543014Swollman /* 18643014Swollman * This hack turns off authentication 1872742Swollman */ 18843014Swollman auth_level = -1; 1892742Swollman } else { 19043014Swollman warnx("unknown authorization level for -a"); 1912742Swollman } 19243014Swollman break; 19343014Swollman#endif /* AUTHENTICATION */ 19443014Swollman 1952742Swollman#ifdef BFTPDAEMON 19643014Swollman case 'B': 1972742Swollman bftpd++; 19843014Swollman break; 19943014Swollman#endif /* BFTPDAEMON */ 20043014Swollman 20143014Swollman case 'd': 2022742Swollman if (strcmp(optarg, "ebug") == 0) { 20343014Swollman debug++; 2042742Swollman break; 20543014Swollman } 20643014Swollman usage(); 2072742Swollman /* NOTREACHED */ 20843014Swollman break; 2092742Swollman 21043014Swollman#ifdef DIAGNOSTICS 21143014Swollman case 'D': 21243014Swollman /* 21343014Swollman * Check for desired diagnostics capabilities. 21443014Swollman */ 2152742Swollman if (!strcmp(optarg, "report")) { 21643014Swollman diagnostic |= TD_REPORT|TD_OPTIONS; 2172742Swollman } else if (!strcmp(optarg, "exercise")) { 21843014Swollman diagnostic |= TD_EXERCISE; 21943014Swollman } else if (!strcmp(optarg, "netdata")) { 22043014Swollman diagnostic |= TD_NETDATA; 22143014Swollman } else if (!strcmp(optarg, "ptydata")) { 22243014Swollman diagnostic |= TD_PTYDATA; 22343014Swollman } else if (!strcmp(optarg, "options")) { 22443014Swollman diagnostic |= TD_OPTIONS; 22543014Swollman } else { 22643014Swollman usage(); 22743014Swollman /* NOT REACHED */ 22843014Swollman } 22943014Swollman break; 2302742Swollman#endif /* DIAGNOSTICS */ 23143014Swollman 2322742Swollman#ifdef ENCRYPTION 23343014Swollman case 'e': 23443014Swollman if (strcmp(optarg, "debug") == 0) { 23543014Swollman extern int encrypt_debug_mode; 23643014Swollman encrypt_debug_mode = 1; 23743014Swollman break; 2382742Swollman } 23943014Swollman usage(); 2402742Swollman /* NOTREACHED */ 24143014Swollman break; 24243014Swollman#endif /* ENCRYPTION */ 24343014Swollman 2442742Swollman case 'h': 24543014Swollman hostinfo = 0; 2462742Swollman break; 24743014Swollman 24843014Swollman#ifdef LINEMODE 24943014Swollman case 'l': 2502742Swollman alwayslinemode = 1; 25143014Swollman break; 2522742Swollman#endif /* LINEMODE */ 25343014Swollman 25443014Swollman case 'k': 25543014Swollman#if defined(LINEMODE) && defined(KLUDGELINEMODE) 2562742Swollman lmodetype = NO_AUTOKLUDGE; 25743014Swollman#else 2582742Swollman /* ignore -k option if built without kludge linemode */ 25943014Swollman#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 2602742Swollman break; 26143014Swollman 26243014Swollman case 'n': 2632742Swollman keepalive = 0; 26443014Swollman break; 26543014Swollman 26643014Swollman case 'p': 26743014Swollman altlogin = optarg; 2682742Swollman break; 26943014Swollman 2702742Swollman case 'S': 27143014Swollman#ifdef HAS_GETTOS 27243014Swollman if ((tos = parsetos(optarg, "tcp")) < 0) 27343014Swollman warnx("%s%s%s", 27443014Swollman "bad TOS argument '", optarg, 27543014Swollman "'; will try to use default TOS"); 27643014Swollman#else 27743014Swollman#define MAXTOS 255 27843014Swollman ultmp = strtoul(optarg, &ep, 0); 27943014Swollman if (*ep || ep == optarg || ultmp > MAXTOS) 28043014Swollman warnx("%s%s%s", 28143014Swollman "bad TOS argument '", optarg, 28243014Swollman "'; will try to use default TOS"); 28343014Swollman else 28443014Swollman tos = ultmp; 28543014Swollman#endif 28643014Swollman break; 28743014Swollman 28843014Swollman case 'u': 28943014Swollman utmp_len = (size_t)atoi(optarg); 29043014Swollman if (utmp_len >= sizeof(remote_hostname)) 29143014Swollman utmp_len = sizeof(remote_hostname) - 1; 29243014Swollman break; 29343014Swollman 29443014Swollman case 'U': 29543014Swollman registerd_host_only = 1; 29643014Swollman break; 29743014Swollman 29843014Swollman#ifdef AUTHENTICATION 29943014Swollman case 'X': 30043014Swollman /* 30143014Swollman * Check for invalid authentication types 30243014Swollman */ 30343014Swollman auth_disable_name(optarg); 30443014Swollman break; 30543014Swollman#endif /* AUTHENTICATION */ 30643014Swollman 30743014Swollman case '4': 30843014Swollman family = AF_INET; 30943014Swollman break; 31043014Swollman 31143014Swollman#ifdef INET6 31243014Swollman case '6': 31343014Swollman family = AF_INET6; 31443014Swollman break; 31543014Swollman#endif 31643014Swollman 31743014Swollman default: 31843014Swollman warnx("%c: unknown option", ch); 31943014Swollman /* FALLTHROUGH */ 32043014Swollman case '?': 32143014Swollman usage(); 32243014Swollman /* NOTREACHED */ 32343014Swollman } 32443014Swollman } 32543014Swollman 32643014Swollman argc -= optind; 32743014Swollman argv += optind; 32843014Swollman 32943014Swollman if (debug) { 3302742Swollman int s, ns, foo, error; 3312742Swollman const char *service = "telnet"; 3322742Swollman struct addrinfo hints, *res; 3332742Swollman 33443014Swollman if (argc > 1) { 33543014Swollman usage(); 3362742Swollman /* NOT REACHED */ 33743014Swollman } else if (argc == 1) 3382742Swollman service = *argv; 33943014Swollman 34043014Swollman memset(&hints, 0, sizeof(hints)); 34143014Swollman hints.ai_flags = AI_PASSIVE; 3422742Swollman hints.ai_family = family; 34343014Swollman hints.ai_socktype = SOCK_STREAM; 34443014Swollman hints.ai_protocol = 0; 34543014Swollman error = getaddrinfo(NULL, service, &hints, &res); 34643014Swollman 34743014Swollman if (error) { 34843014Swollman errx(1, "tcp/%s: %s\n", service, gai_strerror(error)); 34943014Swollman if (error == EAI_SYSTEM) 35043014Swollman errx(1, "tcp/%s: %s\n", service, strerror(errno)); 35143014Swollman usage(); 35243014Swollman } 35343014Swollman 35443014Swollman s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 35543014Swollman if (s < 0) 35643014Swollman err(1, "socket"); 35743014Swollman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, 35843014Swollman (char *)&on, sizeof(on)); 35943014Swollman if (debug > 1) 36043014Swollman (void) setsockopt(s, SOL_SOCKET, SO_DEBUG, 36143014Swollman (char *)&on, sizeof(on)); 3622742Swollman if (bind(s, res->ai_addr, res->ai_addrlen) < 0) 36343014Swollman err(1, "bind"); 3642742Swollman if (listen(s, 1) < 0) 36543014Swollman err(1, "listen"); 3662742Swollman foo = res->ai_addrlen; 36743014Swollman ns = accept(s, res->ai_addr, &foo); 36843014Swollman if (ns < 0) 36943014Swollman err(1, "accept"); 37043014Swollman (void) setsockopt(ns, SOL_SOCKET, SO_DEBUG, 37143014Swollman (char *)&on, sizeof(on)); 3722742Swollman (void) dup2(ns, 0); 37343014Swollman (void) close(ns); 3742742Swollman (void) close(s); 37543014Swollman#ifdef convex 3762742Swollman } else if (argc == 1) { 37743014Swollman ; /* VOID*/ /* Just ignore the host/port name */ 37843014Swollman#endif 37943014Swollman } else if (argc > 0) { 38043014Swollman usage(); 38143014Swollman /* NOT REACHED */ 38243014Swollman } 38343014Swollman 3842742Swollman openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 3852742Swollman fromlen = sizeof (from); 38643014Swollman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 3872742Swollman warn("getpeername"); 38843014Swollman _exit(1); 3892742Swollman } 3902742Swollman if (keepalive && 3912742Swollman setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, 39243014Swollman (char *)&on, sizeof (on)) < 0) { 3932742Swollman syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 39443014Swollman } 3952742Swollman 39643014Swollman#if defined(IPPROTO_IP) && defined(IP_TOS) 39743014Swollman if (from.ss_family == AF_INET) { 39843014Swollman# if defined(HAS_GETTOS) 3992742Swollman struct tosent *tp; 40043014Swollman if (tos < 0 && (tp = gettosbyname("telnet", "tcp"))) 4012742Swollman tos = tp->t_tos; 40243014Swollman# endif 40343014Swollman if (tos < 0) 40443014Swollman tos = 020; /* Low Delay bit */ 4052742Swollman if (tos 40643014Swollman && (setsockopt(0, IPPROTO_IP, IP_TOS, 4072742Swollman (char *)&tos, sizeof(tos)) < 0) 40843014Swollman && (errno != ENOPROTOOPT) ) 40943014Swollman syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 41043014Swollman } 41143014Swollman#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */ 4122742Swollman net = 0; 41343014Swollman doit((struct sockaddr *)&from); 41443014Swollman /* NOTREACHED */ 41543014Swollman return(0); 4162742Swollman} /* end of main */ 41743014Swollman 41843014Swollman void 41943014Swollmanusage() 4202742Swollman{ 42143014Swollman fprintf(stderr, "usage: telnetd"); 4222742Swollman#ifdef AUTHENTICATION 42343014Swollman fprintf(stderr, 42443014Swollman " [-4] [-6] [-a (debug|other|user|valid|off|none)]\n\t"); 42543014Swollman#endif 42643014Swollman#ifdef BFTPDAEMON 42743014Swollman fprintf(stderr, " [-B]"); 42843014Swollman#endif 42943014Swollman fprintf(stderr, " [-debug]"); 43043014Swollman#ifdef DIAGNOSTICS 4312742Swollman fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t"); 43243014Swollman#endif 4332742Swollman#ifdef AUTHENTICATION 43443014Swollman fprintf(stderr, " [-edebug]"); 43543014Swollman#endif 43643014Swollman fprintf(stderr, " [-h]"); 43743014Swollman#if defined(LINEMODE) && defined(KLUDGELINEMODE) 43843014Swollman fprintf(stderr, " [-k]"); 4392742Swollman#endif 44043014Swollman#ifdef LINEMODE 4412742Swollman fprintf(stderr, " [-l]"); 44243014Swollman#endif 44343014Swollman fprintf(stderr, " [-n]"); 44443014Swollman fprintf(stderr, "\n\t"); 44543014Swollman#ifdef HAS_GETTOS 44643014Swollman fprintf(stderr, " [-S tos]"); 44743014Swollman#endif 44843014Swollman#ifdef AUTHENTICATION 44943014Swollman fprintf(stderr, " [-X auth-type]"); 4502742Swollman#endif 45143014Swollman fprintf(stderr, " [-u utmp_hostname_length] [-U]"); 4522742Swollman fprintf(stderr, " [port]\n"); 45343014Swollman exit(1); 45443014Swollman} 4552742Swollman 45643014Swollman/* 45743014Swollman * getterminaltype 4582742Swollman * 45943014Swollman * Ask the other end to send along its terminal type and speed. 4602742Swollman * Output is the variable terminaltype filled in. 46143014Swollman */ 46243014Swollmanstatic unsigned char ttytype_sbbuf[] = { 4632742Swollman IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE 46443014Swollman}; 46517200Swollman 46643014Swollman 46743014Swollman#ifndef AUTHENTICATION 46843014Swollman#define undef2 __unused 46943014Swollman#else 47043014Swollman#define undef2 47143014Swollman#endif 47243014Swollman 47343014Swollmanstatic int 47443014Swollmangetterminaltype(char *name undef2) 47543014Swollman{ 47643014Swollman int retval = -1; 47743014Swollman 47843014Swollman settimer(baseline); 47943014Swollman#ifdef AUTHENTICATION 48043014Swollman /* 48143014Swollman * Handle the Authentication option before we do anything else. 48243014Swollman */ 48343014Swollman if (auth_level >= 0) { 48443014Swollman send_do(TELOPT_AUTHENTICATION, 1); 48543014Swollman while (his_will_wont_is_changing(TELOPT_AUTHENTICATION)) 48643014Swollman ttloop(); 48743014Swollman if (his_state_is_will(TELOPT_AUTHENTICATION)) { 48843014Swollman retval = auth_wait(name); 48943014Swollman } 49043014Swollman } 49143014Swollman#endif 49243014Swollman 49343014Swollman#ifdef ENCRYPTION 49443014Swollman send_will(TELOPT_ENCRYPT, 1); 49543014Swollman#endif /* ENCRYPTION */ 49643014Swollman send_do(TELOPT_TTYPE, 1); 49743014Swollman send_do(TELOPT_TSPEED, 1); 49843014Swollman send_do(TELOPT_XDISPLOC, 1); 49943014Swollman send_do(TELOPT_NEW_ENVIRON, 1); 50043014Swollman send_do(TELOPT_OLD_ENVIRON, 1); 50143014Swollman while ( 50243014Swollman#ifdef ENCRYPTION 50343014Swollman his_do_dont_is_changing(TELOPT_ENCRYPT) || 50443014Swollman#endif /* ENCRYPTION */ 50543014Swollman his_will_wont_is_changing(TELOPT_TTYPE) || 50643014Swollman his_will_wont_is_changing(TELOPT_TSPEED) || 50743014Swollman his_will_wont_is_changing(TELOPT_XDISPLOC) || 50843014Swollman his_will_wont_is_changing(TELOPT_NEW_ENVIRON) || 50943014Swollman his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) { 51043014Swollman ttloop(); 51143014Swollman } 51243014Swollman#ifdef ENCRYPTION 51343014Swollman /* 51443014Swollman * Wait for the negotiation of what type of encryption we can 51543014Swollman * send with. If autoencrypt is not set, this will just return. 51643014Swollman */ 51743014Swollman if (his_state_is_will(TELOPT_ENCRYPT)) { 51843014Swollman encrypt_wait(); 51943014Swollman } 52043014Swollman#endif /* ENCRYPTION */ 52143014Swollman if (his_state_is_will(TELOPT_TSPEED)) { 52243014Swollman static unsigned char sb[] = 52343014Swollman { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 52443014Swollman 52543014Swollman output_datalen(sb, sizeof sb); 52617200Swollman DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); 52717200Swollman } 52817200Swollman if (his_state_is_will(TELOPT_XDISPLOC)) { 52917200Swollman static unsigned char sb[] = 53017200Swollman { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; 53117200Swollman 53217200Swollman output_datalen(sb, sizeof sb); 53317200Swollman DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); 53443014Swollman } 5352742Swollman if (his_state_is_will(TELOPT_NEW_ENVIRON)) { 53643014Swollman static unsigned char sb[] = 5372742Swollman { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE }; 53843014Swollman 53943014Swollman output_datalen(sb, sizeof sb); 5402742Swollman DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); 54143014Swollman } 5422742Swollman else if (his_state_is_will(TELOPT_OLD_ENVIRON)) { 54343014Swollman static unsigned char sb[] = 54443014Swollman { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE }; 54543014Swollman 54643014Swollman output_datalen(sb, sizeof sb); 54743014Swollman DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2);); 54843014Swollman } 54943014Swollman if (his_state_is_will(TELOPT_TTYPE)) { 5502742Swollman 55143014Swollman output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf); 5522742Swollman DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, 55343014Swollman sizeof ttytype_sbbuf - 2);); 55443014Swollman } 55543014Swollman if (his_state_is_will(TELOPT_TSPEED)) { 55643014Swollman while (sequenceIs(tspeedsubopt, baseline)) 5572742Swollman ttloop(); 55843014Swollman } 5592742Swollman if (his_state_is_will(TELOPT_XDISPLOC)) { 56043014Swollman while (sequenceIs(xdisplocsubopt, baseline)) 56143014Swollman ttloop(); 56243014Swollman } 56343014Swollman if (his_state_is_will(TELOPT_NEW_ENVIRON)) { 56443014Swollman while (sequenceIs(environsubopt, baseline)) 56543014Swollman ttloop(); 56643014Swollman } 5672742Swollman if (his_state_is_will(TELOPT_OLD_ENVIRON)) { 56843014Swollman while (sequenceIs(oenvironsubopt, baseline)) 5692742Swollman ttloop(); 57043014Swollman } 57143014Swollman if (his_state_is_will(TELOPT_TTYPE)) { 57243014Swollman char first[256], last[256]; 57343014Swollman 5742742Swollman while (sequenceIs(ttypesubopt, baseline)) 57543014Swollman ttloop(); 57643014Swollman 5772742Swollman /* 57843014Swollman * If the other side has already disabled the option, then 57943014Swollman * we have to just go with what we (might) have already gotten. 58043014Swollman */ 58143014Swollman if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { 58243014Swollman (void) strncpy(first, terminaltype, sizeof(first)-1); 58343014Swollman first[sizeof(first)-1] = '\0'; 58443014Swollman for(;;) { 58543014Swollman /* 58643014Swollman * Save the unknown name, and request the next name. 5872742Swollman */ 58843014Swollman (void) strncpy(last, terminaltype, sizeof(last)-1); 5892742Swollman last[sizeof(last)-1] = '\0'; 59043014Swollman _gettermname(); 59143014Swollman if (terminaltypeok(terminaltype)) 59243014Swollman break; 59343014Swollman if ((strncmp(last, terminaltype, sizeof(last)) == 0) || 5942742Swollman his_state_is_wont(TELOPT_TTYPE)) { 59543014Swollman /* 59643014Swollman * We've hit the end. If this is the same as 5972742Swollman * the first name, just go with it. 59843014Swollman */ 59943014Swollman if (strncmp(first, terminaltype, sizeof(first)) == 0) 60043014Swollman break; 60143014Swollman /* 60243014Swollman * Get the terminal name one more time, so that 60343014Swollman * RFC1091 compliant telnets will cycle back to 6042742Swollman * the start of the list. 60543014Swollman */ 6062742Swollman _gettermname(); 60743014Swollman if (strncmp(first, terminaltype, sizeof(first)) != 0) { 60843014Swollman (void) strncpy(terminaltype, first, sizeof(terminaltype)-1); 60943014Swollman terminaltype[sizeof(terminaltype)-1] = '\0'; 61043014Swollman } 6112742Swollman break; 61243014Swollman } 61343014Swollman } 6142742Swollman } 61543014Swollman } 61643014Swollman return(retval); 61743014Swollman} /* end of getterminaltype */ 61843014Swollman 61943014Swollmanstatic void 62043014Swollman_gettermname(void) 6212742Swollman{ 62243014Swollman /* 62343014Swollman * If the client turned off the option, 6249908Swollman * we can't send another request, so we 62543014Swollman * just return. 62643014Swollman */ 62743014Swollman if (his_state_is_wont(TELOPT_TTYPE)) 62843014Swollman return; 62943014Swollman settimer(baseline); 63043014Swollman output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf); 63117200Swollman DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2, 63243014Swollman sizeof ttytype_sbbuf - 2);); 63343014Swollman while (sequenceIs(ttypesubopt, baseline)) 63443014Swollman ttloop(); 63543014Swollman} 63643014Swollman 63743014Swollmanint 63843014Swollmanterminaltypeok(char *s) 63943014Swollman{ 64043014Swollman char buf[1024]; 64143014Swollman 64243014Swollman if (terminaltype == NULL) 64343014Swollman return(1); 64443014Swollman 64543014Swollman /* 64643014Swollman * tgetent() will return 1 if the type is known, and 64743014Swollman * 0 if it is not known. If it returns -1, it couldn't 64843014Swollman * open the database. But if we can't open the database, 64943014Swollman * it won't help to say we failed, because we won't be 65043014Swollman * able to verify anything else. So, we treat -1 like 1. 65143014Swollman */ 65243014Swollman if (tgetent(buf, s) == 0) 65343014Swollman return(0); 65443014Swollman return(1); 65543014Swollman} 65643014Swollman 65743014Swollman/* 65843014Swollman * Get a pty, scan input lines. 65943014Swollman */ 66043014Swollmanvoid 66143014Swollmandoit(struct sockaddr *who) 66243014Swollman{ 66343014Swollman int err_; /* XXX */ 66443014Swollman int ptynum; 66543014Swollman 66643014Swollman /* 66743014Swollman * Find an available pty to use. 66843014Swollman */ 66943014Swollman#ifndef convex 67043014Swollman pty = getpty(&ptynum); 67143014Swollman if (pty < 0) 67243014Swollman fatal(net, "All network ports in use"); 67343014Swollman#else 67443014Swollman for (;;) { 67543014Swollman char *lp; 67643014Swollman 67743014Swollman if ((lp = getpty()) == NULL) 67843014Swollman fatal(net, "Out of ptys"); 67943014Swollman 68043014Swollman if ((pty = open(lp, 2)) >= 0) { 68143014Swollman strlcpy(line,lp,sizeof(line)); 68243014Swollman line[5] = 't'; 68343014Swollman break; 68443014Swollman } 68543014Swollman } 68643014Swollman#endif 68743014Swollman 68843014Swollman /* get name of connected client */ 68943014Swollman if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1, 69043014Swollman who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only) 69143014Swollman fatal(net, "Couldn't resolve your address into a host name.\r\n\ 69243014Swollman Please contact your net administrator"); 69343014Swollman remote_hostname[sizeof(remote_hostname) - 1] = '\0'; 69443014Swollman 69543014Swollman if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len) 69643014Swollman err_ = getnameinfo(who, who->sa_len, remote_hostname, 69743014Swollman sizeof(remote_hostname), NULL, 0, 69843014Swollman NI_NUMERICHOST); 69943014Swollman /* XXX: do 'err_' check */ 7002742Swollman 70143014Swollman (void) gethostname(host_name, sizeof(host_name) - 1); 70243014Swollman host_name[sizeof(host_name) - 1] = '\0'; 70343014Swollman hostname = host_name; 70443014Swollman 70543014Swollman#ifdef AUTHENTICATION 70643014Swollman#ifdef ENCRYPTION 70743014Swollman/* The above #ifdefs should actually be "or"'ed, not "and"'ed. 7082742Swollman * This is a byproduct of needing "#ifdef" and not "#if defined()" 70917200Swollman * for unifdef. XXX MarkM 7102742Swollman */ 71143014Swollman auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1); 7122742Swollman#endif 7132742Swollman#endif 7142742Swollman 7152742Swollman init_env(); 7162742Swollman /* 7172742Swollman * get terminal type. 7182742Swollman */ 7192742Swollman *user_name = 0; 7202742Swollman level = getterminaltype(user_name); 7212742Swollman setenv("TERM", terminaltype ? terminaltype : "network", 1); 72230711Swollman 7232742Swollman telnet(net, pty, remote_hostname); /* begin server process */ 7242742Swollman 7252742Swollman /*NOTREACHED*/ 7262742Swollman} /* end of doit */ 72730711Swollman 7282742Swollman/* 72917200Swollman * Main loop. Select from pty and network, and 73017200Swollman * hand data to telnet receiver finite state machine. 73117200Swollman */ 7322742Swollmanvoid 73317200Swollmantelnet(int f, int p, char *host) 73417200Swollman{ 7352742Swollman int on = 1; 73630711Swollman#define TABBUFSIZ 512 7372742Swollman char defent[TABBUFSIZ]; 73817200Swollman char defstrs[TABBUFSIZ]; 7392742Swollman#undef TABBUFSIZ 74017200Swollman char *HE; 7412742Swollman char *HN; 7422742Swollman char *IM; 74317200Swollman int nfd; 74417200Swollman 74517200Swollman /* 74617200Swollman * Initialize the slc mapping table. 74717200Swollman */ 74817200Swollman get_slc_defaults(); 74917200Swollman 75017200Swollman /* 7512742Swollman * Do some tests where it is desireable to wait for a response. 75243014Swollman * Rather than doing them slowly, one at a time, do them all 7532742Swollman * at once. 7542742Swollman */ 75543014Swollman if (my_state_is_wont(TELOPT_SGA)) 7562742Swollman send_will(TELOPT_SGA, 1); 7572742Swollman /* 75843014Swollman * Is the client side a 4.2 (NOT 4.3) system? We need to know this 7592742Swollman * because 4.2 clients are unable to deal with TCP urgent data. 7602742Swollman * 76143014Swollman * To find out, we send out a "DO ECHO". If the remote system 7622742Swollman * answers "WILL ECHO" it is probably a 4.2 client, and we note 7632742Swollman * that fact ("WILL ECHO" ==> that the client will echo what 76443014Swollman * WE, the server, sends it; it does NOT mean that the client will 7652742Swollman * echo the terminal input). 76643014Swollman */ 7672742Swollman send_do(TELOPT_ECHO, 1); 76843014Swollman 7692742Swollman#ifdef LINEMODE 7702742Swollman if (his_state_is_wont(TELOPT_LINEMODE)) { 77143014Swollman /* Query the peer for linemode support by trying to negotiate 7722742Swollman * the linemode option. 77343014Swollman */ 7742742Swollman linemode = 0; 7752742Swollman editmode = 0; 7762742Swollman send_do(TELOPT_LINEMODE, 1); /* send do linemode */ 77743014Swollman } 7782742Swollman#endif /* LINEMODE */ 77943014Swollman 7802742Swollman /* 78143014Swollman * Send along a couple of other options that we wish to negotiate. 7822742Swollman */ 78343014Swollman send_do(TELOPT_NAWS, 1); 7842742Swollman send_will(TELOPT_STATUS, 1); 78543014Swollman flowmode = 1; /* default flow control state */ 7862742Swollman restartany = -1; /* uninitialized... */ 78743014Swollman send_do(TELOPT_LFLOW, 1); 7882742Swollman 78943014Swollman /* 7902742Swollman * Spin, waiting for a response from the DO ECHO. However, 79143014Swollman * some REALLY DUMB telnets out there might not respond 79243014Swollman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 79343014Swollman * telnets so far seem to respond with WONT for a DO that 79443014Swollman * they don't understand...) because by the time we get the 79543014Swollman * response, it will already have processed the DO ECHO. 79619878Swollman * Kludge upon kludge. 7972742Swollman */ 79843014Swollman while (his_will_wont_is_changing(TELOPT_NAWS)) 79919878Swollman ttloop(); 80043014Swollman 8012742Swollman /* 80243014Swollman * But... 80343014Swollman * The client might have sent a WILL NAWS as part of its 80443014Swollman * startup code; if so, we'll be here before we get the 80543014Swollman * response to the DO ECHO. We'll make the assumption 80643014Swollman * that any implementation that understands about NAWS 80743014Swollman * is a modern enough implementation that it will respond 80843014Swollman * to our DO ECHO request; hence we'll do another spin 8092742Swollman * waiting for the ECHO option to settle down, which is 81019878Swollman * what we wanted to do in the first place... 8112742Swollman */ 8122742Swollman if (his_want_state_is_will(TELOPT_ECHO) && 81343014Swollman his_state_is_will(TELOPT_NAWS)) { 8142742Swollman while (his_will_wont_is_changing(TELOPT_ECHO)) 81543014Swollman ttloop(); 81643014Swollman } 8172742Swollman /* 81843014Swollman * On the off chance that the telnet client is broken and does not 81943014Swollman * respond to the DO ECHO we sent, (after all, we did send the 82043014Swollman * DO NAWS negotiation after the DO ECHO, and we won't get here 82143014Swollman * until a response to the DO NAWS comes back) simulate the 82243014Swollman * receipt of a will echo. This will also send a WONT ECHO 8232742Swollman * to the client, since we assume that the client failed to 82443014Swollman * respond because it believes that it is already in DO ECHO 82543014Swollman * mode, which we do not want. 82643014Swollman */ 82743014Swollman if (his_want_state_is_will(TELOPT_ECHO)) { 8282742Swollman DIAG(TD_OPTIONS, output_data("td: simulating recv\r\n")); 82943014Swollman willoption(TELOPT_ECHO); 8302742Swollman } 83143014Swollman 83243014Swollman /* 83343014Swollman * Finally, to clean things up, we turn on our echo. This 83443014Swollman * will break stupid 4.2 telnets out of local terminal echo. 8352742Swollman */ 83643014Swollman 83743014Swollman if (my_state_is_wont(TELOPT_ECHO)) 83843014Swollman send_will(TELOPT_ECHO, 1); 83943014Swollman 8402742Swollman /* 84143014Swollman * Turn on packet mode 84217200Swollman */ 84343014Swollman (void) ioctl(p, TIOCPKT, (char *)&on); 84443014Swollman 84543014Swollman#if defined(LINEMODE) && defined(KLUDGELINEMODE) 8462742Swollman /* 8472742Swollman * Continuing line mode support. If client does not support 84843014Swollman * real linemode, attempt to negotiate kludge linemode by sending 84943014Swollman * the do timing mark sequence. 85043014Swollman */ 85143014Swollman if (lmodetype < REAL_LINEMODE) 8529908Swollman send_do(TELOPT_TM, 1); 8539908Swollman#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ 85443014Swollman 85543014Swollman /* 85643014Swollman * Call telrcv() once to pick up anything received during 8579908Swollman * terminal type negotiation, 4.2/4.3 determination, and 85843014Swollman * linemode negotiation. 85917200Swollman */ 8602742Swollman telrcv(); 8612742Swollman 8622742Swollman (void) ioctl(f, FIONBIO, (char *)&on); 86317200Swollman (void) ioctl(p, FIONBIO, (char *)&on); 86417200Swollman 8659908Swollman#if defined(SO_OOBINLINE) 86617200Swollman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, 8672742Swollman (char *)&on, sizeof on); 8682742Swollman#endif /* defined(SO_OOBINLINE) */ 86943014Swollman 87017200Swollman#ifdef SIGTSTP 87117200Swollman (void) signal(SIGTSTP, SIG_IGN); 8729908Swollman#endif 87317200Swollman#ifdef SIGTTOU 8742742Swollman /* 8752742Swollman * Ignoring SIGTTOU keeps the kernel from blocking us 87643014Swollman * in ttioct() in /sys/tty.c. 87719878Swollman */ 87817200Swollman (void) signal(SIGTTOU, SIG_IGN); 87917200Swollman#endif 8802742Swollman 88117200Swollman (void) signal(SIGCHLD, cleanup); 8822742Swollman 88317200Swollman#ifdef TIOCNOTTY 88417200Swollman { 88517200Swollman int t; 88617200Swollman t = open(_PATH_TTY, O_RDWR); 8872742Swollman if (t >= 0) { 8882742Swollman (void) ioctl(t, TIOCNOTTY, (char *)0); 8892742Swollman (void) close(t); 8902742Swollman } 8912742Swollman } 89217200Swollman#endif 89317200Swollman 8949908Swollman /* 8959908Swollman * Show banner that getty never gave. 89619878Swollman * 89717200Swollman * We put the banner in the pty input buffer. This way, it 89817200Swollman * gets carriage return null processing, etc., just like all 89917200Swollman * other pty --> client data. 90019878Swollman */ 90117200Swollman 9029908Swollman if (getent(defent, "default") == 1) { 90317200Swollman char *cp=defstrs; 90419878Swollman 9052742Swollman HE = Getstr("he", &cp); 9062742Swollman HN = Getstr("hn", &cp); 9079908Swollman IM = Getstr("im", &cp); 90819878Swollman if (HN && *HN) 9099908Swollman (void) strlcpy(host_name, HN, sizeof(host_name)); 9102742Swollman if (IM == 0) 91119878Swollman IM = strdup(""); 91219878Swollman } else { 91319878Swollman IM = strdup(DEFAULT_IM); 91419878Swollman HE = 0; 91519878Swollman } 91619878Swollman edithost(HE, host_name); 91719878Swollman if (hostinfo && *IM) 91819878Swollman putf(IM, ptyibuf2); 91919878Swollman 92019878Swollman if (pcc) 92119878Swollman (void) strncat(ptyibuf2, ptyip, pcc+1); 9222742Swollman ptyip = ptyibuf2; 92319878Swollman pcc = strlen(ptyip); 92419878Swollman#ifdef LINEMODE 92519878Swollman /* 92619878Swollman * Last check to make sure all our states are correct. 92719878Swollman */ 92819878Swollman init_termbuf(); 92919878Swollman localstat(); 9302742Swollman#endif /* LINEMODE */ 93120094Swollman 93220094Swollman DIAG(TD_REPORT, output_data("td: Entering processing loop\r\n")); 93320094Swollman 93420094Swollman /* 93520094Swollman * Startup the login process on the slave side of the terminal 93620094Swollman * now. We delay this until here to insure option negotiation 93720094Swollman * is complete. 93820094Swollman */ 9392742Swollman startslave(host, level, user_name); 94020094Swollman 94120094Swollman nfd = ((f > p) ? f : p) + 1; 94220094Swollman for (;;) { 94320094Swollman fd_set ibits, obits, xbits; 94420094Swollman int c; 94520094Swollman 94620094Swollman if (ncc < 0 && pcc < 0) 94720094Swollman break; 9482742Swollman 94920094Swollman FD_ZERO(&ibits); 9502742Swollman FD_ZERO(&obits); 95143014Swollman FD_ZERO(&xbits); 95220094Swollman /* 95320094Swollman * Never look for input if there's still 95443014Swollman * stuff in the corresponding output buffer 95520094Swollman */ 95620094Swollman if (nfrontp - nbackp || pcc > 0) { 95743014Swollman FD_SET(f, &obits); 95843014Swollman } else { 95920094Swollman FD_SET(p, &ibits); 96020094Swollman } 96143014Swollman if (pfrontp - pbackp || ncc > 0) { 96220094Swollman FD_SET(p, &obits); 96320094Swollman } else { 96420094Swollman FD_SET(f, &ibits); 96520094Swollman } 96620094Swollman if (!SYNCHing) { 9672742Swollman FD_SET(f, &xbits); 9682742Swollman } 9692742Swollman if ((c = select(nfd, &ibits, &obits, &xbits, 9702742Swollman (struct timeval *)0)) < 1) { 97119878Swollman if (c == -1) { 97219878Swollman if (errno == EINTR) { 97319878Swollman continue; 97419878Swollman } 9752742Swollman } 97619878Swollman sleep(5); 97719878Swollman continue; 9782742Swollman } 97919878Swollman 98019878Swollman /* 98119878Swollman * Any urgent data? 98219878Swollman */ 98319878Swollman if (FD_ISSET(net, &xbits)) { 98419878Swollman SYNCHing = 1; 98519878Swollman } 98619878Swollman 98719878Swollman /* 98819878Swollman * Something to read from the network... 98919878Swollman */ 99019878Swollman if (FD_ISSET(net, &ibits)) { 99119878Swollman#if !defined(SO_OOBINLINE) 99219878Swollman /* 99319878Swollman * In 4.2 (and 4.3 beta) systems, the 99419878Swollman * OOB indication and data handling in the kernel 99519878Swollman * is such that if two separate TCP Urgent requests 99619878Swollman * come in, one byte of TCP data will be overlaid. 99719878Swollman * This is fatal for Telnet, but we try to live 99819878Swollman * with it. 99919878Swollman * 100019878Swollman * In addition, in 4.2 (and...), a special protocol 10012742Swollman * is needed to pick up the TCP Urgent data in 100219878Swollman * the correct sequence. 10032742Swollman * 10042742Swollman * What we do is: if we think we are in urgent 100519878Swollman * mode, we look to see if we are "at the mark". 10062742Swollman * If we are, we do an OOB receive. If we run 100719878Swollman * this twice, we will do the OOB receive twice, 10082742Swollman * but the second will fail, since the second 100919878Swollman * time we were "at the mark", but there wasn't 10102742Swollman * any data there (the kernel doesn't reset 101119878Swollman * "at the mark" until we do a normal read). 10122742Swollman * Once we've read the OOB data, we go ahead 101319878Swollman * and do normal reads. 10142742Swollman * 101519878Swollman * There is also another problem, which is that 10162742Swollman * since the OOB byte we read doesn't put us 101719878Swollman * out of OOB state, and since that byte is most 10182742Swollman * likely the TELNET DM (data mark), we would 101919878Swollman * stay in the TELNET SYNCH (SYNCHing) state. 10202742Swollman * So, clocks to the rescue. If we've "just" 102119878Swollman * received a DM, then we test for the 10222742Swollman * presence of OOB data when the receive OOB 102319878Swollman * fails (and AFTER we did the normal mode read 10242742Swollman * to clear "at the mark"). 102519878Swollman */ 10262742Swollman if (SYNCHing) { 102719878Swollman int atmark; 10282742Swollman 102919878Swollman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 10302742Swollman if (atmark) { 10312742Swollman ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 103219878Swollman if ((ncc == -1) && (errno == EINVAL)) { 103330711Swollman ncc = read(net, netibuf, sizeof (netibuf)); 103430711Swollman if (sequenceIs(didnetreceive, gotDM)) { 103520094Swollman SYNCHing = stilloob(net); 10362742Swollman } 10372742Swollman } 10382742Swollman } else { 10392742Swollman ncc = read(net, netibuf, sizeof (netibuf)); 10402742Swollman } 104119878Swollman } else { 104219878Swollman ncc = read(net, netibuf, sizeof (netibuf)); 10432742Swollman } 10442742Swollman settimer(didnetreceive); 10452742Swollman#else /* !defined(SO_OOBINLINE)) */ 104619878Swollman ncc = read(net, netibuf, sizeof (netibuf)); 10472742Swollman#endif /* !defined(SO_OOBINLINE)) */ 104819878Swollman if (ncc < 0 && errno == EWOULDBLOCK) 10492742Swollman ncc = 0; 105019878Swollman else { 10512742Swollman if (ncc <= 0) { 105219878Swollman break; 105319878Swollman } 10542742Swollman netip = netibuf; 10552742Swollman } 105619878Swollman DIAG((TD_REPORT | TD_NETDATA), 105719878Swollman output_data("td: netread %d chars\r\n", ncc)); 105819878Swollman DIAG(TD_NETDATA, printdata("nd", netip, ncc)); 105919878Swollman } 106019878Swollman 10612742Swollman /* 10622742Swollman * Something to read from the pty... 106343014Swollman */ 10642742Swollman if (FD_ISSET(p, &ibits)) { 10652742Swollman pcc = read(p, ptyibuf, BUFSIZ); 106643014Swollman /* 10672742Swollman * On some systems, if we try to read something 106843014Swollman * off the master side before the slave side is 106943014Swollman * opened, we get EIO. 107020094Swollman */ 107119878Swollman if (pcc < 0 && (errno == EWOULDBLOCK || 107243014Swollman#ifdef EAGAIN 107343014Swollman errno == EAGAIN || 107420094Swollman#endif 10752742Swollman errno == EIO)) { 10762742Swollman pcc = 0; 107730711Swollman } else { 107830711Swollman if (pcc <= 0) 107930711Swollman break; 108030711Swollman#ifdef LINEMODE 108130711Swollman /* 108230711Swollman * If ioctl from pty, pass it through net 108330711Swollman */ 108430711Swollman if (ptyibuf[0] & TIOCPKT_IOCTL) { 108530711Swollman copy_termbuf(ptyibuf+1, pcc-1); 108630711Swollman localstat(); 108730711Swollman pcc = 1; 108830711Swollman } 108930711Swollman#endif /* LINEMODE */ 10902742Swollman if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 109130711Swollman netclear(); /* clear buffer back */ 109230711Swollman#ifndef NO_URGENT 109319878Swollman /* 109430711Swollman * There are client telnets on some 10952742Swollman * operating systems get screwed up 109630711Swollman * royally if we send them urgent 10972742Swollman * mode data. 109819878Swollman */ 109930711Swollman output_data("%c%c", IAC, DM); 110019878Swollman neturg = nfrontp-1; /* off by one XXX */ 110119878Swollman DIAG(TD_OPTIONS, 110219878Swollman printoption("td: send IAC", DM)); 110319878Swollman 110419878Swollman#endif 110519878Swollman } 110630711Swollman if (his_state_is_will(TELOPT_LFLOW) && 110719878Swollman (ptyibuf[0] & 110819878Swollman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 110919878Swollman int newflow = 111030711Swollman ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0; 111119878Swollman if (newflow != flowmode) { 111219878Swollman flowmode = newflow; 111319878Swollman output_data("%c%c%c%c%c%c", 111419878Swollman IAC, SB, TELOPT_LFLOW, 111519878Swollman flowmode ? LFLOW_ON 111619878Swollman : LFLOW_OFF, 111719878Swollman IAC, SE); 11182742Swollman DIAG(TD_OPTIONS, printsub('>', 111930711Swollman (unsigned char *)nfrontp-4, 112030711Swollman 4);); 112119878Swollman } 11222742Swollman } 112319878Swollman pcc--; 11242742Swollman ptyip = ptyibuf+1; 11252742Swollman } 112630711Swollman } 112730711Swollman 112830711Swollman while (pcc > 0) { 112930711Swollman if ((&netobuf[BUFSIZ] - nfrontp) < 2) 113030711Swollman break; 113130711Swollman c = *ptyip++ & 0377, pcc--; 113230711Swollman if (c == IAC) 113319878Swollman output_data("%c", c); 113419878Swollman output_data("%c", c); 11352742Swollman if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { 11362742Swollman if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 113743014Swollman output_data("%c", *ptyip++ & 0377); 11382742Swollman pcc--; 11392742Swollman } else 11402742Swollman output_data("%c", '\0'); 114119878Swollman } 11422742Swollman } 114319878Swollman 11442742Swollman if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 11452742Swollman netflush(); 11462742Swollman if (ncc > 0) 11472742Swollman telrcv(); 114819878Swollman if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 11492742Swollman ptyflush(); 115019878Swollman } 11512742Swollman cleanup(0); 115219878Swollman} /* end of telnet */ 115320094Swollman 115420094Swollman#ifndef TCSIG 11552742Swollman# ifdef TIOCSIG 11562742Swollman# define TCSIG TIOCSIG 115743014Swollman# endif 11582742Swollman#endif 11592742Swollman 116019878Swollman/* 11612742Swollman * Send interrupt to process on other side of pty. 116219878Swollman * If it is in raw mode, just write NULL; 11632742Swollman * otherwise, write intr char. 116419878Swollman */ 11652742Swollmanvoid 116619878Swollmaninterrupt(void) 116719878Swollman{ 116819878Swollman ptyflush(); /* half-hearted */ 11692742Swollman 11702742Swollman#ifdef TCSIG 117119878Swollman (void) ioctl(pty, TCSIG, SIGINT); 117219878Swollman#else /* TCSIG */ 117319878Swollman init_termbuf(); 117419878Swollman *pfrontp++ = slctab[SLC_IP].sptr ? 11752742Swollman (unsigned char)*slctab[SLC_IP].sptr : '\177'; 117643014Swollman#endif /* TCSIG */ 117719878Swollman} 11782742Swollman 117919878Swollman/* 11802742Swollman * Send quit to process on other side of pty. 118119878Swollman * If it is in raw mode, just write NULL; 118219878Swollman * otherwise, write quit char. 11832742Swollman */ 118419878Swollmanvoid 11852742Swollmansendbrk(void) 118619878Swollman{ 11872742Swollman ptyflush(); /* half-hearted */ 118819878Swollman#ifdef TCSIG 11892742Swollman (void) ioctl(pty, TCSIG, SIGQUIT); 11902742Swollman#else /* TCSIG */ 11912742Swollman init_termbuf(); 11922742Swollman *pfrontp++ = slctab[SLC_ABORT].sptr ? 11932742Swollman (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; 11942742Swollman#endif /* TCSIG */ 11952742Swollman} 119619878Swollman 119719878Swollmanvoid 119819878Swollmansendsusp(void) 119919878Swollman{ 120019878Swollman#ifdef SIGTSTP 12012742Swollman ptyflush(); /* half-hearted */ 12029908Swollman# ifdef TCSIG 120319878Swollman (void) ioctl(pty, TCSIG, SIGTSTP); 120420094Swollman# else /* TCSIG */ 120520094Swollman *pfrontp++ = slctab[SLC_SUSP].sptr ? 120620094Swollman (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; 120720094Swollman# endif /* TCSIG */ 120820094Swollman#endif /* SIGTSTP */ 120920094Swollman} 121020094Swollman 121120094Swollman/* 121220094Swollman * When we get an AYT, if ^T is enabled, use that. Otherwise, 121320094Swollman * just send back "[Yes]". 121420094Swollman */ 121520094Swollmanvoid 121620094Swollmanrecv_ayt(void) 121720094Swollman{ 121820094Swollman#if defined(SIGINFO) && defined(TCSIG) 121919878Swollman if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) { 122019878Swollman (void) ioctl(pty, TCSIG, SIGINFO); 122120094Swollman return; 122220094Swollman } 12232742Swollman#endif 122420094Swollman output_data("\r\n[Yes]\r\n"); 122520094Swollman} 122620094Swollman 12272742Swollmanvoid 12282742Swollmandoeof(void) 12299908Swollman{ 12309908Swollman init_termbuf(); 12319908Swollman 12329908Swollman#if defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN) 123320094Swollman if (!tty_isediting()) { 123420094Swollman extern char oldeofc; 123520094Swollman *pfrontp++ = oldeofc; 123620094Swollman return; 123720094Swollman } 123820094Swollman#endif 123920094Swollman *pfrontp++ = slctab[SLC_EOF].sptr ? 124020094Swollman (unsigned char)*slctab[SLC_EOF].sptr : '\004'; 124120094Swollman} 124220094Swollman