121182Sdist /* 238904Sborman * Copyright (c) 1989 Regents of the University of California. 333687Sbostic * All rights reserved. 433687Sbostic * 542673Sbostic * %sccs.include.redist.c% 621182Sdist */ 721182Sdist 86295Sroot #ifndef lint 921182Sdist char copyright[] = 1038904Sborman "@(#) Copyright (c) 1989 Regents of the University of California.\n\ 1121182Sdist All rights reserved.\n"; 1233687Sbostic #endif /* not lint */ 136295Sroot 1421182Sdist #ifndef lint 15*44545Smarc static char sccsid[] = "@(#)telnetd.c 5.46 (Berkeley) 06/28/90"; 1633687Sbostic #endif /* not lint */ 1721182Sdist 1838904Sborman #include "telnetd.h" 1938904Sborman 206002Sroot /* 2138904Sborman * I/O data buffers, 2238904Sborman * pointers, and counters. 236002Sroot */ 2438904Sborman char ptyibuf[BUFSIZ], *ptyip = ptyibuf; 2538904Sborman char ptyibuf2[BUFSIZ]; 269218Ssam 2738904Sborman #ifdef CRAY 2838904Sborman int hostinfo = 1; /* do we print login banner? */ 2938904Sborman #endif 309218Ssam 3138904Sborman #ifdef CRAY 3238904Sborman extern int newmap; /* nonzero if \n maps to ^M^J */ 3340242Sborman int lowpty = 0, highpty; /* low, high pty numbers */ 3438904Sborman #endif /* CRAY */ 3512216Ssam 3638904Sborman int debug = 0; 3738904Sborman char *progname; 389218Ssam 3944363Sborman #if defined(NEED_GETTOS) 4044363Sborman struct tosent { 4144363Sborman char *t_name; /* name */ 4244363Sborman char **t_aliases; /* alias list */ 4344363Sborman char *t_proto; /* protocol */ 4444363Sborman int t_tos; /* Type Of Service bits */ 4544363Sborman }; 4644363Sborman 4744363Sborman struct tosent * 4844363Sborman gettosbyname(name, proto) 4944363Sborman char *name, *proto; 5044363Sborman { 5144363Sborman static struct tosent te; 5244363Sborman static char *aliasp = 0; 5344363Sborman 5444363Sborman te.t_name = name; 5544363Sborman te.t_aliases = &aliasp; 5644363Sborman te.t_proto = proto; 5744363Sborman te.t_tos = 020; /* Low Delay bit */ 5844363Sborman return(&te); 5944363Sborman } 6044363Sborman #endif 6144363Sborman 6238904Sborman main(argc, argv) 6338904Sborman char *argv[]; 6438904Sborman { 6538904Sborman struct sockaddr_in from; 6638904Sborman int on = 1, fromlen; 6744363Sborman #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 6840242Sborman struct tosent *tp; 6944363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 706002Sroot 7138904Sborman pfrontp = pbackp = ptyobuf; 7238904Sborman netip = netibuf; 7338904Sborman nfrontp = nbackp = netobuf; 746002Sroot 7538904Sborman progname = *argv; 7640242Sborman 7740242Sborman #ifdef CRAY 7840242Sborman /* 7940242Sborman * Get number of pty's before trying to process options, 8040242Sborman * which may include changing pty range. 8140242Sborman */ 8240242Sborman highpty = getnpty(); 8340242Sborman #endif /* CRAY */ 8440242Sborman 8538904Sborman top: 8638904Sborman argc--, argv++; 8727649Sminshall 8838904Sborman if (argc > 0 && strcmp(*argv, "-debug") == 0) { 8938904Sborman debug++; 9038904Sborman goto top; 9138904Sborman } 9227649Sminshall 9338904Sborman #ifdef LINEMODE 9438904Sborman if (argc > 0 && !strcmp(*argv, "-l")) { 9538904Sborman alwayslinemode = 1; 9638904Sborman goto top; 9738904Sborman } 9838904Sborman #endif /* LINEMODE */ 9927649Sminshall 10038904Sborman #ifdef CRAY 10138904Sborman if (argc > 0 && !strcmp(*argv, "-h")) { 10238904Sborman hostinfo = 0; 10338904Sborman goto top; 10438904Sborman } 10527649Sminshall 10638904Sborman if (argc > 0 && !strncmp(*argv, "-r", 2)) { 10738904Sborman char *strchr(); 10838904Sborman char *c; 10927649Sminshall 11040242Sborman /* 11140242Sborman * Allow the specification of alterations to the pty search 11240242Sborman * range. It is legal to specify only one, and not change the 11340242Sborman * other from its default. 11440242Sborman */ 11538904Sborman *argv += 2; 11638904Sborman if (**argv == '\0' && argc) 11738904Sborman argv++, argc--; 11838904Sborman c = strchr(*argv, '-'); 11938904Sborman if (c) { 12038904Sborman *c++ = '\0'; 12138904Sborman highpty = atoi(c); 12240242Sborman } 12340242Sborman if (**argv != '\0') 12440242Sborman lowpty = atoi(*argv); 12540242Sborman if ((lowpty > highpty) || (lowpty < 0) || (highpty > 32767)) { 12644363Sborman usage(); 12744363Sborman /* NOT REACHED */ 12838904Sborman } 12938904Sborman goto top; 13038904Sborman } 13138904Sborman # ifdef NEWINIT 13238904Sborman if (argc > 0 && !strncmp(*argv, "-I", 2)) { 13338904Sborman extern char *gen_id; 13427649Sminshall 13538904Sborman *argv += 2; 13638904Sborman if (**argv == '\0') { 13744363Sborman if (argc < 2) { 13844363Sborman usage(); 13944363Sborman /* NOT REACHED */ 14044363Sborman } 14138904Sborman argv++, argc--; 14244363Sborman if (**argv == '\0') { 14344363Sborman usage(); 14444363Sborman /* NOT REACHED */ 14544363Sborman } 14638904Sborman } 14738904Sborman gen_id = *argv; 14838904Sborman goto top; 14938904Sborman } 15038904Sborman # endif /* NEWINIT */ 15138904Sborman #endif /* CRAY */ 1526002Sroot 15344363Sborman #ifdef DIAGNOSTICS 15444363Sborman /* 15544363Sborman * Check for desired diagnostics capabilities. 15644363Sborman */ 15744363Sborman if (argc > 0 && !strncmp(*argv, "-D", 2)) { 15844363Sborman *argv += 2; 15944363Sborman if (**argv == '\0') { 16044363Sborman if (argc < 2) { 16144363Sborman usage(); 16244363Sborman /* NOT REACHED */ 16344363Sborman } 16444363Sborman argv++, argc--; 16544363Sborman if (**argv == '\0') { 16644363Sborman usage(); 16744363Sborman /* NOT REACHED */ 16844363Sborman } 16944363Sborman } 17044363Sborman if (!strcmp(*argv, "report")) { 17144363Sborman diagnostic |= TD_REPORT|TD_OPTIONS; 17244363Sborman } else if (!strcmp(*argv, "exercise")) { 17344363Sborman diagnostic |= TD_EXERCISE; 17444363Sborman } else if (!strcmp(*argv, "netdata")) { 17544363Sborman diagnostic |= TD_NETDATA; 17644363Sborman } else if (!strcmp(*argv, "ptydata")) { 17744363Sborman diagnostic |= TD_PTYDATA; 17844363Sborman } else if (!strcmp(*argv, "options")) { 17944363Sborman diagnostic |= TD_OPTIONS; 18044363Sborman } else { 18144363Sborman usage(); 18244363Sborman /* NOT REACHED */ 18344363Sborman } 18444363Sborman goto top; 18544363Sborman } 18644363Sborman #endif /* DIAGNOSTICS */ 18744363Sborman 18844363Sborman #ifdef BFTPDAEMON 18944363Sborman /* 19044363Sborman * Check for bftp daemon 19144363Sborman */ 19244363Sborman if (argc > 0 && !strncmp(*argv, "-B", 2)) { 19344363Sborman bftpd++; 19444363Sborman goto top; 19544363Sborman } 19644363Sborman #endif /* BFTPDAEMON */ 19744363Sborman 19844363Sborman if (argc > 0 && **argv == '-') { 19944363Sborman fprintf(stderr, "telnetd: %s: unknown option\n", *argv+1); 20044363Sborman usage(); 20144363Sborman /* NOT REACHED */ 20244363Sborman } 20344363Sborman 20438904Sborman if (debug) { 20527185Sminshall int s, ns, foo; 20627185Sminshall struct servent *sp; 20727185Sminshall static struct sockaddr_in sin = { AF_INET }; 20827185Sminshall 20944363Sborman if (argc > 1) { 21044363Sborman usage(); 21144363Sborman /* NOT REACHED */ 21244363Sborman } else if (argc == 1) { 21338904Sborman if (sp = getservbyname(*argv, "tcp")) { 21438904Sborman sin.sin_port = sp->s_port; 21538904Sborman } else { 21638904Sborman sin.sin_port = atoi(*argv); 21738904Sborman if ((int)sin.sin_port <= 0) { 21838904Sborman fprintf(stderr, "telnetd: %s: bad port #\n", *argv); 21944363Sborman usage(); 22044363Sborman /* NOT REACHED */ 22138904Sborman } 22238904Sborman sin.sin_port = htons((u_short)sin.sin_port); 22338904Sborman } 22437210Sminshall } else { 22537210Sminshall sp = getservbyname("telnet", "tcp"); 22637210Sminshall if (sp == 0) { 22744363Sborman fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); 22838904Sborman exit(1); 22937210Sminshall } 23037210Sminshall sin.sin_port = sp->s_port; 23127185Sminshall } 23227185Sminshall 23327185Sminshall s = socket(AF_INET, SOCK_STREAM, 0); 23427185Sminshall if (s < 0) { 23527185Sminshall perror("telnetd: socket");; 23627185Sminshall exit(1); 23727185Sminshall } 23838904Sborman (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 23938904Sborman if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { 24027185Sminshall perror("bind"); 24127185Sminshall exit(1); 24227185Sminshall } 24327185Sminshall if (listen(s, 1) < 0) { 24427185Sminshall perror("listen"); 24527185Sminshall exit(1); 24627185Sminshall } 24727185Sminshall foo = sizeof sin; 24838904Sborman ns = accept(s, (struct sockaddr *)&sin, &foo); 24927185Sminshall if (ns < 0) { 25027185Sminshall perror("accept"); 25127185Sminshall exit(1); 25227185Sminshall } 25338904Sborman (void) dup2(ns, 0); 25438904Sborman (void) close(ns); 25538904Sborman (void) close(s); 25644363Sborman } else if (argc > 0) { 25744363Sborman usage(); 25844363Sborman /* NOT REACHED */ 25927185Sminshall } 26038904Sborman 26124855Seric openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); 26216371Skarels fromlen = sizeof (from); 26338904Sborman if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { 26438904Sborman fprintf(stderr, "%s: ", progname); 26516371Skarels perror("getpeername"); 26616371Skarels _exit(1); 2678346Ssam } 26817156Ssam if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) { 26917187Sralph syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m"); 27010418Ssam } 27140242Sborman 27244363Sborman #if defined(HAS_IP_TOS) || defined(NEED_GETTOS) 27344363Sborman if ((tp = gettosbyname("telnet", "tcp")) && 27444363Sborman (setsockopt(0, IPPROTO_IP, IP_TOS, &tp->t_tos, sizeof(int)) < 0)) 27540242Sborman syslog(LOG_WARNING, "setsockopt (IP_TOS): %m"); 27644363Sborman #endif /* defined(HAS_IP_TOS) || defined(NEED_GETTOS) */ 27738904Sborman net = 0; 27838904Sborman doit(&from); 27938904Sborman /* NOTREACHED */ 28038904Sborman } /* end of main */ 2816002Sroot 28244363Sborman usage() 28344363Sborman { 28444363Sborman fprintf(stderr, "Usage: telnetd [-debug] [-h]"); 28544363Sborman #ifdef NEWINIT 28644363Sborman fprintf(stderr, " [-Iinitid]"); 28744363Sborman #endif /* NEWINIT */ 28844363Sborman #ifdef DIAGNOSTICS 28944363Sborman fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]"); 29044363Sborman #endif /* DIAGNOSTICS */ 29144363Sborman #ifdef LINEMODE 29244363Sborman fprintf(stderr, " [-l]"); 29344363Sborman #endif 29444363Sborman #ifdef CRAY 29544363Sborman fprintf(stderr, " [-r[lowpty]-[highpty]]"); 29644363Sborman #endif 29744363Sborman #ifdef BFTPDAEMON 29844363Sborman fprintf(stderr, " [-B]"); 29944363Sborman #endif /* BFTPDAEMON */ 30044363Sborman fprintf(stderr, " [port]\n"); 30144363Sborman exit(1); 30244363Sborman } 30344363Sborman 30440242Sborman void cleanup(); 30527649Sminshall 30627649Sminshall /* 30727983Sminshall * getterminaltype 30827649Sminshall * 30938904Sborman * Ask the other end to send along its terminal type and speed. 31027983Sminshall * Output is the variable terminaltype filled in. 31127649Sminshall */ 31238904Sborman static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; 31327983Sminshall void 31427983Sminshall getterminaltype() 31527649Sminshall { 31638904Sborman void ttloop(); 31727649Sminshall 31838904Sborman settimer(baseline); 31939503Sborman send_do(TELOPT_TTYPE, 1); 32039503Sborman send_do(TELOPT_TSPEED, 1); 32144363Sborman send_do(TELOPT_XDISPLOC, 1); 32244363Sborman send_do(TELOPT_ENVIRON, 1); 32344363Sborman while (his_will_wont_is_changing(TELOPT_TTYPE) || 32444363Sborman his_will_wont_is_changing(TELOPT_TSPEED) || 32544363Sborman his_will_wont_is_changing(TELOPT_XDISPLOC) || 32644363Sborman his_will_wont_is_changing(TELOPT_ENVIRON)) { 32727983Sminshall ttloop(); 32827649Sminshall } 32944363Sborman if (his_state_is_will(TELOPT_TSPEED)) { 33038904Sborman static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; 33127983Sminshall 33227983Sminshall bcopy(sbbuf, nfrontp, sizeof sbbuf); 33327983Sminshall nfrontp += sizeof sbbuf; 33438904Sborman } 33544363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) { 33644363Sborman static char sbbuf[] = { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE }; 33738904Sborman 33844363Sborman bcopy(sbbuf, nfrontp, sizeof sbbuf); 33944363Sborman nfrontp += sizeof sbbuf; 34044363Sborman } 34144363Sborman if (his_state_is_will(TELOPT_ENVIRON)) { 34244363Sborman static char sbbuf[] = { IAC, SB, TELOPT_ENVIRON, TELQUAL_SEND, IAC, SE }; 34344363Sborman 34444363Sborman bcopy(sbbuf, nfrontp, sizeof sbbuf); 34544363Sborman nfrontp += sizeof sbbuf; 34644363Sborman } 34744363Sborman if (his_state_is_will(TELOPT_TTYPE)) { 34844363Sborman 34938904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 35038904Sborman nfrontp += sizeof ttytype_sbbuf; 35138904Sborman } 35244363Sborman if (his_state_is_will(TELOPT_TSPEED)) { 35338904Sborman while (sequenceIs(tspeedsubopt, baseline)) 35427983Sminshall ttloop(); 35538904Sborman } 35644363Sborman if (his_state_is_will(TELOPT_XDISPLOC)) { 35744363Sborman while (sequenceIs(xdisplocsubopt, baseline)) 35844363Sborman ttloop(); 35944363Sborman } 36044363Sborman if (his_state_is_will(TELOPT_ENVIRON)) { 36144363Sborman while (sequenceIs(environsubopt, baseline)) 36244363Sborman ttloop(); 36344363Sborman } 36444363Sborman if (his_state_is_will(TELOPT_TTYPE)) { 36538904Sborman char first[256], last[256]; 36638904Sborman 36738904Sborman while (sequenceIs(ttypesubopt, baseline)) 36838904Sborman ttloop(); 36938904Sborman 37044363Sborman /* 37144363Sborman * If the other side has already disabled the option, then 37244363Sborman * we have to just go with what we (might) have already gotten. 37344363Sborman */ 37444363Sborman if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) { 37538904Sborman (void) strncpy(first, terminaltype, sizeof(first)); 37638904Sborman for(;;) { 37738904Sborman /* 37838904Sborman * Save the unknown name, and request the next name. 37938904Sborman */ 38038904Sborman (void) strncpy(last, terminaltype, sizeof(last)); 38138904Sborman _gettermname(); 38244363Sborman if (terminaltypeok(terminaltype)) 38338904Sborman break; 38444363Sborman if ((strncmp(last, terminaltype, sizeof(last)) == 0) || 38544363Sborman his_state_is_wont(TELOPT_TTYPE)) { 38638904Sborman /* 38738904Sborman * We've hit the end. If this is the same as 38838904Sborman * the first name, just go with it. 38938904Sborman */ 39038904Sborman if (strncmp(first, terminaltype, sizeof(first) == 0)) 39138904Sborman break; 39238904Sborman /* 39344363Sborman * Get the terminal name one more time, so that 39438904Sborman * RFC1091 compliant telnets will cycle back to 39538904Sborman * the start of the list. 39638904Sborman */ 39744363Sborman _gettermname(); 39838904Sborman if (strncmp(first, terminaltype, sizeof(first) != 0)) 39938904Sborman (void) strncpy(terminaltype, first, sizeof(first)); 40038904Sborman break; 40138904Sborman } 40238904Sborman } 40327983Sminshall } 40427983Sminshall } 40538904Sborman } /* end of getterminaltype */ 40638904Sborman 40738904Sborman _gettermname() 40838904Sborman { 40944363Sborman /* 41044363Sborman * If the client turned off the option, 41144363Sborman * we can't send another request, so we 41244363Sborman * just return. 41344363Sborman */ 41444363Sborman if (his_state_is_wont(TELOPT_TTYPE)) 41544363Sborman return; 41638904Sborman settimer(baseline); 41738904Sborman bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); 41838904Sborman nfrontp += sizeof ttytype_sbbuf; 41938904Sborman while (sequenceIs(ttypesubopt, baseline)) 42038904Sborman ttloop(); 42127649Sminshall } 42227649Sminshall 42338904Sborman terminaltypeok(s) 42438904Sborman char *s; 42538904Sborman { 42638904Sborman char buf[1024]; 42738904Sborman 42838904Sborman if (terminaltype == NULL) 42938904Sborman return(1); 43038904Sborman 43138904Sborman /* 43238904Sborman * tgetent() will return 1 if the type is known, and 43338904Sborman * 0 if it is not known. If it returns -1, it couldn't 43438904Sborman * open the database. But if we can't open the database, 43538904Sborman * it won't help to say we failed, because we won't be 43638904Sborman * able to verify anything else. So, we treat -1 like 1. 43738904Sborman */ 43838904Sborman if (tgetent(buf, s) == 0) 43938904Sborman return(0); 44038904Sborman return(1); 44138904Sborman } 44238904Sborman 4436002Sroot /* 4446002Sroot * Get a pty, scan input lines. 4456002Sroot */ 44638904Sborman doit(who) 44712683Ssam struct sockaddr_in *who; 4486002Sroot { 44920188Skarels char *host, *inet_ntoa(); 45038904Sborman int t; 45112683Ssam struct hostent *hp; 452*44545Smarc #if BSD > 43 453*44545Smarc extern char *line; 4546002Sroot 455*44545Smarc if (openpty(&pty, &t, line, NULL, NULL) == -1) 456*44545Smarc fatal(net, "All network ports in use"); 457*44545Smarc init_termbuf(); 458*44545Smarc #else 459*44545Smarc 46038904Sborman /* 46138904Sborman * Find an available pty to use. 46238904Sborman */ 46338904Sborman pty = getpty(); 46438904Sborman if (pty < 0) 46538904Sborman fatal(net, "All network ports in use"); 46620188Skarels 46738904Sborman t = getptyslave(); 468*44545Smarc #endif 46938904Sborman 47038904Sborman /* get name of connected client */ 47138904Sborman hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr), 47212683Ssam who->sin_family); 47312683Ssam if (hp) 47412683Ssam host = hp->h_name; 47512683Ssam else 47617444Sralph host = inet_ntoa(who->sin_addr); 47727983Sminshall 47844363Sborman init_env(); 47927983Sminshall /* 48038904Sborman * get terminal type. 48127983Sminshall */ 48227983Sminshall getterminaltype(); 48344363Sborman setenv("TERM", terminaltype ? terminaltype : "network", 1); 48427983Sminshall 48527649Sminshall /* 48638904Sborman * Start up the login process on the slave side of the terminal 48727649Sminshall */ 48838904Sborman startslave(t, host); 48938904Sborman 49038904Sborman telnet(net, pty); /* begin server processing */ 4919244Ssam /*NOTREACHED*/ 49238904Sborman } /* end of doit */ 4939244Ssam 49438904Sborman #ifndef MAXHOSTNAMELEN 49538904Sborman #define MAXHOSTNAMELEN 64 49638904Sborman #endif MAXHOSTNAMELEN 4976002Sroot /* 4986002Sroot * Main loop. Select from pty and network, and 4996002Sroot * hand data to telnet receiver finite state machine. 5006002Sroot */ 5016002Sroot telnet(f, p) 50238904Sborman int f, p; 5036002Sroot { 5046002Sroot int on = 1; 50527898Skarels char hostname[MAXHOSTNAMELEN]; 50640242Sborman #if defined(CRAY2) && defined(UNICOS5) 50738904Sborman int termstat(); 50838904Sborman int interrupt(), sendbrk(); 50938904Sborman #endif 51033271Sminshall #define TABBUFSIZ 512 51133271Sminshall char defent[TABBUFSIZ]; 51233271Sminshall char defstrs[TABBUFSIZ]; 51333271Sminshall #undef TABBUFSIZ 51433271Sminshall char *HE; 51533271Sminshall char *HN; 51633271Sminshall char *IM; 51738904Sborman void netflush(); 51838904Sborman 51932400Sminshall /* 52038904Sborman * Initialize the slc mapping table. 52132400Sminshall */ 52238904Sborman get_slc_defaults(); 5236002Sroot 5248379Ssam /* 52538904Sborman * Do some tests where it is desireable to wait for a response. 52638904Sborman * Rather than doing them slowly, one at a time, do them all 52738904Sborman * at once. 5288379Ssam */ 52944363Sborman if (my_state_is_wont(TELOPT_SGA)) 53039503Sborman send_will(TELOPT_SGA, 1); 53112713Ssam /* 53227649Sminshall * Is the client side a 4.2 (NOT 4.3) system? We need to know this 53327649Sminshall * because 4.2 clients are unable to deal with TCP urgent data. 53427649Sminshall * 53527649Sminshall * To find out, we send out a "DO ECHO". If the remote system 53627649Sminshall * answers "WILL ECHO" it is probably a 4.2 client, and we note 53727649Sminshall * that fact ("WILL ECHO" ==> that the client will echo what 53827649Sminshall * WE, the server, sends it; it does NOT mean that the client will 53927649Sminshall * echo the terminal input). 54027649Sminshall */ 54139503Sborman send_do(TELOPT_ECHO, 1); 54227649Sminshall 54338904Sborman #ifdef LINEMODE 54444363Sborman if (his_state_is_wont(TELOPT_LINEMODE)) { 54538904Sborman /* Query the peer for linemode support by trying to negotiate 54638904Sborman * the linemode option. 54738904Sborman */ 54844363Sborman linemode = 0; 54938904Sborman editmode = 0; 55039503Sborman send_do(TELOPT_LINEMODE, 1); /* send do linemode */ 55138904Sborman } 55238904Sborman #endif /* LINEMODE */ 55338904Sborman 55427649Sminshall /* 55538904Sborman * Send along a couple of other options that we wish to negotiate. 55638904Sborman */ 55739503Sborman send_do(TELOPT_NAWS, 1); 55839503Sborman send_will(TELOPT_STATUS, 1); 55938904Sborman flowmode = 1; /* default flow control state */ 56039503Sborman send_do(TELOPT_LFLOW, 1); 56138904Sborman 56238904Sborman /* 56338904Sborman * Spin, waiting for a response from the DO ECHO. However, 56438904Sborman * some REALLY DUMB telnets out there might not respond 56538904Sborman * to the DO ECHO. So, we spin looking for NAWS, (most dumb 56638904Sborman * telnets so far seem to respond with WONT for a DO that 56738904Sborman * they don't understand...) because by the time we get the 56838904Sborman * response, it will already have processed the DO ECHO. 56938904Sborman * Kludge upon kludge. 57038904Sborman */ 57144363Sborman while (his_will_wont_is_changing(TELOPT_NAWS)) 57238904Sborman ttloop(); 57338904Sborman 57438904Sborman /* 57544363Sborman * But... 57644363Sborman * The client might have sent a WILL NAWS as part of its 57744363Sborman * startup code; if so, we'll be here before we get the 57844363Sborman * response to the DO ECHO. We'll make the assumption 57944363Sborman * that any implementation that understands about NAWS 58044363Sborman * is a modern enough implementation that it will respond 58144363Sborman * to our DO ECHO request; hence we'll do another spin 58244363Sborman * waiting for the ECHO option to settle down, which is 58344363Sborman * what we wanted to do in the first place... 58444363Sborman */ 58544363Sborman if (his_want_state_is_will(TELOPT_ECHO) && 58644363Sborman his_state_is_will(TELOPT_NAWS)) { 58744363Sborman while (his_will_wont_is_changing(TELOPT_ECHO)) 58844363Sborman ttloop(); 58944363Sborman } 59044363Sborman /* 59138995Sborman * On the off chance that the telnet client is broken and does not 59238995Sborman * respond to the DO ECHO we sent, (after all, we did send the 59338995Sborman * DO NAWS negotiation after the DO ECHO, and we won't get here 59438995Sborman * until a response to the DO NAWS comes back) simulate the 59538995Sborman * receipt of a will echo. This will also send a WONT ECHO 59638995Sborman * to the client, since we assume that the client failed to 59738995Sborman * respond because it believes that it is already in DO ECHO 59838995Sborman * mode, which we do not want. 59938995Sborman */ 60044363Sborman if (his_want_state_is_will(TELOPT_ECHO)) { 60144363Sborman #ifdef DIAGNOSTICS 60244363Sborman if (diagnostic & TD_OPTIONS) { 60344363Sborman sprintf(nfrontp, "td: simulating recv\r\n"); 60444363Sborman nfrontp += strlen(nfrontp); 60544363Sborman } 60644363Sborman #endif /* DIAGNOSTICS */ 60739503Sborman willoption(TELOPT_ECHO); 60840242Sborman } 60938995Sborman 61038995Sborman /* 61138995Sborman * Finally, to clean things up, we turn on our echo. This 61238995Sborman * will break stupid 4.2 telnets out of local terminal echo. 61338995Sborman */ 61438995Sborman 61544363Sborman if (my_state_is_wont(TELOPT_ECHO)) 61639503Sborman send_will(TELOPT_ECHO, 1); 61738995Sborman 61838995Sborman /* 61938904Sborman * Turn on packet mode, and default to line at at time mode. 62038904Sborman */ 62138904Sborman (void) ioctl(p, TIOCPKT, (char *)&on); 62238904Sborman #ifdef LINEMODE 62338904Sborman tty_setlinemode(1); 62438904Sborman 62538904Sborman # ifdef KLUDGELINEMODE 62638904Sborman /* 62738904Sborman * Continuing line mode support. If client does not support 62838904Sborman * real linemode, attempt to negotiate kludge linemode by sending 62938904Sborman * the do timing mark sequence. 63038904Sborman */ 63138904Sborman if (lmodetype < REAL_LINEMODE) 63239503Sborman send_do(TELOPT_TM, 1); 63338904Sborman # endif /* KLUDGELINEMODE */ 63438904Sborman #endif /* LINEMODE */ 63538904Sborman 63638904Sborman /* 63738904Sborman * Call telrcv() once to pick up anything received during 63838904Sborman * terminal type negotiation, 4.2/4.3 determination, and 63938904Sborman * linemode negotiation. 64038904Sborman */ 64138904Sborman telrcv(); 64238904Sborman 64338904Sborman (void) ioctl(f, FIONBIO, (char *)&on); 64438904Sborman (void) ioctl(p, FIONBIO, (char *)&on); 64540242Sborman #if defined(CRAY2) && defined(UNICOS5) 64638904Sborman init_termdriver(f, p, interrupt, sendbrk); 64738904Sborman #endif 64838904Sborman 64938904Sborman #if defined(SO_OOBINLINE) 65038904Sborman (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); 65138904Sborman #endif /* defined(SO_OOBINLINE) */ 65238904Sborman 65338904Sborman #ifdef SIGTSTP 65438904Sborman (void) signal(SIGTSTP, SIG_IGN); 65538904Sborman #endif 65638904Sborman #ifdef SIGTTOU 65738904Sborman /* 65838904Sborman * Ignoring SIGTTOU keeps the kernel from blocking us 65938904Sborman * in ttioct() in /sys/tty.c. 66038904Sborman */ 66138904Sborman (void) signal(SIGTTOU, SIG_IGN); 66238904Sborman #endif 66338904Sborman 66438904Sborman (void) signal(SIGCHLD, cleanup); 66538904Sborman 66640242Sborman #if defined(CRAY2) && defined(UNICOS5) 66738904Sborman /* 66838904Sborman * Cray-2 will send a signal when pty modes are changed by slave 66938904Sborman * side. Set up signal handler now. 67038904Sborman */ 67138904Sborman if ((int)signal(SIGUSR1, termstat) < 0) 67238904Sborman perror("signal"); 67338904Sborman else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) 67438904Sborman perror("ioctl:TCSIGME"); 67538904Sborman /* 67638904Sborman * Make processing loop check terminal characteristics early on. 67738904Sborman */ 67838904Sborman termstat(); 67938904Sborman #endif 68038904Sborman 68144363Sborman #ifdef NO_SETSID 68238904Sborman (void) setpgrp(0, 0); 68344363Sborman #else 68444363Sborman (void) setsid(); 68540242Sborman #endif 68644363Sborman #if defined(TIOCSCTTY) && defined(CRAY) 68744363Sborman ioctl(p, TIOCSCTTY, 0); 68844363Sborman #endif 68938904Sborman 69038904Sborman /* 69112713Ssam * Show banner that getty never gave. 69227797Sminshall * 69333271Sminshall * We put the banner in the pty input buffer. This way, it 69433271Sminshall * gets carriage return null processing, etc., just like all 69533271Sminshall * other pty --> client data. 69612713Ssam */ 69727797Sminshall 69838904Sborman (void) gethostname(hostname, sizeof (hostname)); 69938904Sborman 70033271Sminshall if (getent(defent, "default") == 1) { 70133271Sminshall char *getstr(); 70238904Sborman char *cp=defstrs; 70327649Sminshall 70438904Sborman HE = getstr("he", &cp); 70538904Sborman HN = getstr("hn", &cp); 70638904Sborman IM = getstr("im", &cp); 70733271Sminshall if (HN && *HN) 70838904Sborman (void) strcpy(hostname, HN); 70938904Sborman if (IM == 0) 71038904Sborman IM = ""; 71133271Sminshall } else { 71238904Sborman #ifdef CRAY 71338904Sborman if (hostinfo == 0) 71438904Sborman IM = 0; 71538904Sborman else 71638904Sborman #endif 71738904Sborman IM = DEFAULT_IM; 71838904Sborman HE = 0; 71933271Sminshall } 72038904Sborman edithost(HE, hostname); 72138904Sborman if (IM && *IM) 72238904Sborman putf(IM, ptyibuf2); 72327797Sminshall 72438904Sborman if (pcc) 72538904Sborman (void) strncat(ptyibuf2, ptyip, pcc+1); 72638904Sborman ptyip = ptyibuf2; 72738904Sborman pcc = strlen(ptyip); 72840242Sborman #ifdef LINEMODE 72940242Sborman /* 73040242Sborman * Last check to make sure all our states are correct. 73140242Sborman */ 73240242Sborman init_termbuf(); 73340242Sborman localstat(); 73440242Sborman #endif /* LINEMODE */ 73533271Sminshall 73644363Sborman #ifdef DIAGNOSTICS 73744363Sborman if (diagnostic & TD_REPORT) { 73844363Sborman sprintf(nfrontp, "td: Entering processing loop\r\n"); 73944363Sborman nfrontp += strlen(nfrontp); 74044363Sborman } 74144363Sborman #endif /* DIAGNOSTICS */ 74244363Sborman 7436002Sroot for (;;) { 74427185Sminshall fd_set ibits, obits, xbits; 7456002Sroot register int c; 7466002Sroot 74727185Sminshall if (ncc < 0 && pcc < 0) 74827185Sminshall break; 74927185Sminshall 75040242Sborman #if defined(CRAY2) && defined(UNICOS5) 75138904Sborman if (needtermstat) 75238904Sborman _termstat(); 75340242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 75427185Sminshall FD_ZERO(&ibits); 75527185Sminshall FD_ZERO(&obits); 75627185Sminshall FD_ZERO(&xbits); 7576002Sroot /* 7586002Sroot * Never look for input if there's still 7596002Sroot * stuff in the corresponding output buffer 7606002Sroot */ 76127185Sminshall if (nfrontp - nbackp || pcc > 0) { 76227185Sminshall FD_SET(f, &obits); 76327185Sminshall } else { 76427185Sminshall FD_SET(p, &ibits); 76527185Sminshall } 76627185Sminshall if (pfrontp - pbackp || ncc > 0) { 76727185Sminshall FD_SET(p, &obits); 76827185Sminshall } else { 76927185Sminshall FD_SET(f, &ibits); 77027185Sminshall } 77127185Sminshall if (!SYNCHing) { 77227185Sminshall FD_SET(f, &xbits); 77327185Sminshall } 77427185Sminshall if ((c = select(16, &ibits, &obits, &xbits, 77527185Sminshall (struct timeval *)0)) < 1) { 77627185Sminshall if (c == -1) { 77727185Sminshall if (errno == EINTR) { 77827185Sminshall continue; 77927185Sminshall } 78027185Sminshall } 7816002Sroot sleep(5); 7826002Sroot continue; 7836002Sroot } 7846002Sroot 7856002Sroot /* 78627185Sminshall * Any urgent data? 78727185Sminshall */ 78827185Sminshall if (FD_ISSET(net, &xbits)) { 78927185Sminshall SYNCHing = 1; 79027185Sminshall } 79127185Sminshall 79227185Sminshall /* 7936002Sroot * Something to read from the network... 7946002Sroot */ 79527185Sminshall if (FD_ISSET(net, &ibits)) { 79627649Sminshall #if !defined(SO_OOBINLINE) 79727185Sminshall /* 79827898Skarels * In 4.2 (and 4.3 beta) systems, the 79927185Sminshall * OOB indication and data handling in the kernel 80027185Sminshall * is such that if two separate TCP Urgent requests 80127185Sminshall * come in, one byte of TCP data will be overlaid. 80227185Sminshall * This is fatal for Telnet, but we try to live 80327185Sminshall * with it. 80427185Sminshall * 80527185Sminshall * In addition, in 4.2 (and...), a special protocol 80627185Sminshall * is needed to pick up the TCP Urgent data in 80727185Sminshall * the correct sequence. 80827185Sminshall * 80927185Sminshall * What we do is: if we think we are in urgent 81027185Sminshall * mode, we look to see if we are "at the mark". 81127185Sminshall * If we are, we do an OOB receive. If we run 81227185Sminshall * this twice, we will do the OOB receive twice, 81327185Sminshall * but the second will fail, since the second 81427185Sminshall * time we were "at the mark", but there wasn't 81527185Sminshall * any data there (the kernel doesn't reset 81627185Sminshall * "at the mark" until we do a normal read). 81727185Sminshall * Once we've read the OOB data, we go ahead 81827185Sminshall * and do normal reads. 81927185Sminshall * 82027185Sminshall * There is also another problem, which is that 82127185Sminshall * since the OOB byte we read doesn't put us 82227185Sminshall * out of OOB state, and since that byte is most 82327185Sminshall * likely the TELNET DM (data mark), we would 82427185Sminshall * stay in the TELNET SYNCH (SYNCHing) state. 82527185Sminshall * So, clocks to the rescue. If we've "just" 82627185Sminshall * received a DM, then we test for the 82727185Sminshall * presence of OOB data when the receive OOB 82827185Sminshall * fails (and AFTER we did the normal mode read 82927185Sminshall * to clear "at the mark"). 83027185Sminshall */ 83127185Sminshall if (SYNCHing) { 83227185Sminshall int atmark; 83327185Sminshall 83438904Sborman (void) ioctl(net, SIOCATMARK, (char *)&atmark); 83527185Sminshall if (atmark) { 83627185Sminshall ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); 83727185Sminshall if ((ncc == -1) && (errno == EINVAL)) { 83827185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 83927983Sminshall if (sequenceIs(didnetreceive, gotDM)) { 84027185Sminshall SYNCHing = stilloob(net); 84127185Sminshall } 84227185Sminshall } 84327185Sminshall } else { 84427185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 8456002Sroot } 84627185Sminshall } else { 84727185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 84827185Sminshall } 84927185Sminshall settimer(didnetreceive); 85027649Sminshall #else /* !defined(SO_OOBINLINE)) */ 85127185Sminshall ncc = read(net, netibuf, sizeof (netibuf)); 85227649Sminshall #endif /* !defined(SO_OOBINLINE)) */ 85327185Sminshall if (ncc < 0 && errno == EWOULDBLOCK) 85427185Sminshall ncc = 0; 85527185Sminshall else { 85627185Sminshall if (ncc <= 0) { 85727185Sminshall break; 85827185Sminshall } 85927185Sminshall netip = netibuf; 86027185Sminshall } 86144363Sborman #ifdef DIAGNOSTICS 86244363Sborman if (diagnostic & (TD_REPORT | TD_NETDATA)) { 86344363Sborman sprintf(nfrontp, "td: netread %d chars\r\n", ncc); 86444363Sborman nfrontp += strlen(nfrontp); 86544363Sborman } 86644363Sborman if (diagnostic & TD_NETDATA) { 86744363Sborman printdata("nd", netip, ncc); 86844363Sborman } 86944363Sborman #endif /* DIAGNOSTICS */ 8706002Sroot } 8716002Sroot 8726002Sroot /* 8736002Sroot * Something to read from the pty... 8746002Sroot */ 87538904Sborman if (FD_ISSET(p, &ibits)) { 8766002Sroot pcc = read(p, ptyibuf, BUFSIZ); 8776002Sroot if (pcc < 0 && errno == EWOULDBLOCK) 8786002Sroot pcc = 0; 8796002Sroot else { 8806002Sroot if (pcc <= 0) 8816002Sroot break; 88240242Sborman #if !defined(CRAY2) || !defined(UNICOS5) 88338904Sborman #ifdef LINEMODE 88438904Sborman /* 88538904Sborman * If ioctl from pty, pass it through net 88638904Sborman */ 88738904Sborman if (ptyibuf[0] & TIOCPKT_IOCTL) { 88838904Sborman copy_termbuf(ptyibuf+1, pcc-1); 88938904Sborman localstat(); 89038904Sborman pcc = 1; 89138904Sborman } 89238904Sborman #endif LINEMODE 89337210Sminshall if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { 89438904Sborman netclear(); /* clear buffer back */ 89540242Sborman #ifdef notdef 89640242Sborman /* 89740242Sborman * We really should have this in, but 89840242Sborman * there are client telnets on some 89940242Sborman * operating systems get screwed up 90040242Sborman * royally if we send them urgent 90140242Sborman * mode data. So, for now, we'll not 90240242Sborman * do this... 90340242Sborman */ 90437210Sminshall *nfrontp++ = IAC; 90537210Sminshall *nfrontp++ = DM; 90637210Sminshall neturg = nfrontp-1; /* off by one XXX */ 90740242Sborman #endif 90837210Sminshall } 90944363Sborman if (his_state_is_will(TELOPT_LFLOW) && 91037210Sminshall (ptyibuf[0] & 91138904Sborman (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { 91238904Sborman (void) sprintf(nfrontp, "%c%c%c%c%c%c", 91337210Sminshall IAC, SB, TELOPT_LFLOW, 91437210Sminshall ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, 91537210Sminshall IAC, SE); 91637210Sminshall nfrontp += 6; 91737210Sminshall } 91833267Sminshall pcc--; 91933267Sminshall ptyip = ptyibuf+1; 92040242Sborman #else /* defined(CRAY2) && defined(UNICOS5) */ 92138904Sborman if (!uselinemode) { 92239531Sborman unpcc = pcc; 92339531Sborman unptyip = ptyibuf; 92439531Sborman pcc = term_output(&unptyip, ptyibuf2, 92539531Sborman &unpcc, BUFSIZ); 92638904Sborman ptyip = ptyibuf2; 92738904Sborman } else 92838904Sborman ptyip = ptyibuf; 92940242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 93038904Sborman } 9316002Sroot } 9326002Sroot 9336002Sroot while (pcc > 0) { 9346002Sroot if ((&netobuf[BUFSIZ] - nfrontp) < 2) 9356002Sroot break; 9366002Sroot c = *ptyip++ & 0377, pcc--; 9376002Sroot if (c == IAC) 9386002Sroot *nfrontp++ = c; 93940242Sborman #if defined(CRAY2) && defined(UNICOS5) 94038904Sborman else if (c == '\n' && 94144363Sborman my_state_is_wont(TELOPT_BINARY) && newmap) 94238904Sborman *nfrontp++ = '\r'; 94340242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 9446002Sroot *nfrontp++ = c; 94544363Sborman if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) { 94627020Sminshall if (pcc > 0 && ((*ptyip & 0377) == '\n')) { 94727020Sminshall *nfrontp++ = *ptyip++ & 0377; 94827020Sminshall pcc--; 94927020Sminshall } else 95027020Sminshall *nfrontp++ = '\0'; 95127020Sminshall } 9526002Sroot } 95340242Sborman #if defined(CRAY2) && defined(UNICOS5) 95439531Sborman /* 95539531Sborman * If chars were left over from the terminal driver, 95639531Sborman * note their existence. 95739531Sborman */ 95839531Sborman if (!uselinemode && unpcc) { 95939531Sborman pcc = unpcc; 96039531Sborman unpcc = 0; 96139531Sborman ptyip = unptyip; 96239531Sborman } 96340242Sborman #endif /* defined(CRAY2) && defined(UNICOS5) */ 96439531Sborman 96527185Sminshall if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0) 9666002Sroot netflush(); 9676002Sroot if (ncc > 0) 9686002Sroot telrcv(); 96927185Sminshall if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0) 9706002Sroot ptyflush(); 9716002Sroot } 9726002Sroot cleanup(); 97338904Sborman } /* end of telnet */ 9746002Sroot 97538904Sborman #ifndef TCSIG 97638904Sborman # ifdef TIOCSIG 97738904Sborman # define TCSIG TIOCSIG 97838904Sborman # endif 97938904Sborman #endif 9806002Sroot 98137212Sminshall /* 9826002Sroot * Send interrupt to process on other side of pty. 9836002Sroot * If it is in raw mode, just write NULL; 9846002Sroot * otherwise, write intr char. 9856002Sroot */ 9866002Sroot interrupt() 9876002Sroot { 98838904Sborman ptyflush(); /* half-hearted */ 9896002Sroot 99038904Sborman #ifdef TCSIG 99138904Sborman (void) ioctl(pty, TCSIG, (char *)SIGINT); 99238904Sborman #else /* TCSIG */ 99338904Sborman init_termbuf(); 99440242Sborman *pfrontp++ = slctab[SLC_IP].sptr ? 99540242Sborman (unsigned char)*slctab[SLC_IP].sptr : '\177'; 99638904Sborman #endif /* TCSIG */ 9976002Sroot } 9986002Sroot 99927229Sminshall /* 100027229Sminshall * Send quit to process on other side of pty. 100127229Sminshall * If it is in raw mode, just write NULL; 100227229Sminshall * otherwise, write quit char. 100327229Sminshall */ 100427229Sminshall sendbrk() 100527229Sminshall { 100627229Sminshall ptyflush(); /* half-hearted */ 100738904Sborman #ifdef TCSIG 100838904Sborman (void) ioctl(pty, TCSIG, (char *)SIGQUIT); 100938904Sborman #else /* TCSIG */ 101038904Sborman init_termbuf(); 101140242Sborman *pfrontp++ = slctab[SLC_ABORT].sptr ? 101240242Sborman (unsigned char)*slctab[SLC_ABORT].sptr : '\034'; 101338904Sborman #endif /* TCSIG */ 101427229Sminshall } 101527229Sminshall 101638904Sborman sendsusp() 10176002Sroot { 101838904Sborman #ifdef SIGTSTP 101938904Sborman ptyflush(); /* half-hearted */ 102038904Sborman # ifdef TCSIG 102138904Sborman (void) ioctl(pty, TCSIG, (char *)SIGTSTP); 102238904Sborman # else /* TCSIG */ 102340242Sborman *pfrontp++ = slctab[SLC_SUSP].sptr ? 102440242Sborman (unsigned char)*slctab[SLC_SUSP].sptr : '\032'; 102538904Sborman # endif /* TCSIG */ 102638904Sborman #endif /* SIGTSTP */ 10276002Sroot } 10286002Sroot 102938904Sborman doeof() 10306002Sroot { 103140242Sborman #if defined(USE_TERMIO) && defined(SYSV_TERMIO) 103240242Sborman extern char oldeofc; 103340242Sborman #endif 103438904Sborman init_termbuf(); 10356002Sroot 103640242Sborman #if defined(USE_TERMIO) && defined(SYSV_TERMIO) 103740242Sborman if (!tty_isediting()) { 103840242Sborman *pfrontp++ = oldeofc; 103940242Sborman return; 104040242Sborman } 104140242Sborman #endif 104240242Sborman *pfrontp++ = slctab[SLC_EOF].sptr ? 104340242Sborman (unsigned char)*slctab[SLC_EOF].sptr : '\004'; 10446002Sroot } 1045