Path: blob/main/crypto/heimdal/appl/telnet/telnetd/telnetd.c
34889 views
/*1* Copyright (c) 1989, 19932* The Regents of the University of California. All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. All advertising materials mentioning features or use of this software13* must display the following acknowledgement:14* This product includes software developed by the University of15* California, Berkeley and its contributors.16* 4. Neither the name of the University nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#include "telnetd.h"3435RCSID("$Id$");3637#ifdef _SC_CRAY_SECURE_SYS38#include <sys/sysv.h>39#include <sys/secdev.h>40#include <sys/secparm.h>41#include <sys/usrv.h>42int secflag;43char tty_dev[16];44struct secdev dv;45struct sysv sysv;46struct socksec ss;47#endif /* _SC_CRAY_SECURE_SYS */4849#ifdef AUTHENTICATION50int auth_level = 0;51#endif5253#ifdef KRB554#define Authenticator k5_Authenticator55#include <krb5.h>56#undef Authenticator57#endif5859extern int utmp_len;60int registerd_host_only = 0;61#ifdef ENCRYPTION62int require_encryption = 0;63#endif6465#ifdef STREAMSPTY6667#ifdef _AIX68#include <sys/termio.h>69#endif70# ifdef HAVE_SYS_STRTTY_H71# include <sys/strtty.h>72# endif73# ifdef HAVE_SYS_STR_TTY_H74# include <sys/str_tty.h>75# endif76/* make sure we don't get the bsd version */77/* what is this here for? solaris? /joda */78# ifdef HAVE_SYS_TTY_H79# include "/usr/include/sys/tty.h"80# endif81# ifdef HAVE_SYS_PTYVAR_H82# include <sys/ptyvar.h>83# endif8485/*86* Because of the way ptyibuf is used with streams messages, we need87* ptyibuf+1 to be on a full-word boundary. The following wierdness88* is simply to make that happen.89*/90long ptyibufbuf[BUFSIZ/sizeof(long)+1];91char *ptyibuf = ((char *)&ptyibufbuf[1])-1;92char *ptyip = ((char *)&ptyibufbuf[1])-1;93char ptyibuf2[BUFSIZ];94unsigned char ctlbuf[BUFSIZ];95struct strbuf strbufc, strbufd;9697int readstream(int, char*, int);9899#else /* ! STREAMPTY */100101/*102* I/O data buffers,103* pointers, and counters.104*/105char ptyibuf[BUFSIZ], *ptyip = ptyibuf;106char ptyibuf2[BUFSIZ];107108#endif /* ! STREAMPTY */109110int hostinfo = 1; /* do we print login banner? */111112#ifdef _CRAY113extern int newmap; /* nonzero if \n maps to ^M^J */114int lowpty = 0, highpty; /* low, high pty numbers */115#endif /* CRAY */116117int debug = 0;118int keepalive = 1;119char *progname;120121static void usage (int error_code);122123/*124* The string to pass to getopt(). We do it this way so125* that only the actual options that we support will be126* passed off to getopt().127*/128char valid_opts[] = "Bd:hklnS:u:UL:y"129#ifdef AUTHENTICATION130"a:X:z"131#endif132#ifdef ENCRYPTION133"e"134#endif135#ifdef DIAGNOSTICS136"D:"137#endif138#ifdef _CRAY139"r:"140#endif141;142143static void doit(struct sockaddr*, int);144145int146main(int argc, char **argv)147{148struct sockaddr_storage __ss;149struct sockaddr *sa = (struct sockaddr *)&__ss;150int on = 1;151socklen_t sa_size;152int ch;153#if defined(IPPROTO_IP) && defined(IP_TOS)154int tos = -1;155#endif156pfrontp = pbackp = ptyobuf;157netip = netibuf;158nfrontp = nbackp = netobuf;159160setprogname(argv[0]);161162progname = *argv;163#ifdef ENCRYPTION164nclearto = 0;165#endif166167#ifdef _CRAY168/*169* Get number of pty's before trying to process options,170* which may include changing pty range.171*/172highpty = getnpty();173#endif /* CRAY */174175if (argc == 2 && strcmp(argv[1], "--version") == 0) {176print_version(NULL);177exit(0);178}179if (argc == 2 && strcmp(argv[1], "--help") == 0)180usage(0);181182while ((ch = getopt(argc, argv, valid_opts)) != -1) {183switch(ch) {184185#ifdef AUTHENTICATION186case 'a':187/*188* Check for required authentication level189*/190if (strcmp(optarg, "debug") == 0) {191auth_debug_mode = 1;192} else if (strcasecmp(optarg, "none") == 0) {193auth_level = 0;194} else if (strcasecmp(optarg, "otp") == 0) {195auth_level = 0;196require_otp = 1;197} else if (strcasecmp(optarg, "other") == 0) {198auth_level = AUTH_OTHER;199} else if (strcasecmp(optarg, "user") == 0) {200auth_level = AUTH_USER;201} else if (strcasecmp(optarg, "valid") == 0) {202auth_level = AUTH_VALID;203} else if (strcasecmp(optarg, "off") == 0) {204/*205* This hack turns off authentication206*/207auth_level = -1;208} else {209fprintf(stderr,210"telnetd: unknown authorization level for -a\n");211}212break;213#endif /* AUTHENTICATION */214215case 'B': /* BFTP mode is not supported any more */216break;217case 'd':218if (strcmp(optarg, "ebug") == 0) {219debug++;220break;221}222usage(1);223/* NOTREACHED */224break;225226#ifdef DIAGNOSTICS227case 'D':228/*229* Check for desired diagnostics capabilities.230*/231if (!strcmp(optarg, "report")) {232diagnostic |= TD_REPORT|TD_OPTIONS;233} else if (!strcmp(optarg, "exercise")) {234diagnostic |= TD_EXERCISE;235} else if (!strcmp(optarg, "netdata")) {236diagnostic |= TD_NETDATA;237} else if (!strcmp(optarg, "ptydata")) {238diagnostic |= TD_PTYDATA;239} else if (!strcmp(optarg, "options")) {240diagnostic |= TD_OPTIONS;241} else {242usage(1);243/* NOT REACHED */244}245break;246#endif /* DIAGNOSTICS */247248#ifdef ENCRYPTION249case 'e':250require_encryption = 1;251break;252#endif253254case 'h':255hostinfo = 0;256break;257258case 'k': /* Linemode is not supported any more */259case 'l':260break;261262case 'n':263keepalive = 0;264break;265266#ifdef _CRAY267case 'r':268{269char *strchr();270char *c;271272/*273* Allow the specification of alterations274* to the pty search range. It is legal to275* specify only one, and not change the276* other from its default.277*/278c = strchr(optarg, '-');279if (c) {280*c++ = '\0';281highpty = atoi(c);282}283if (*optarg != '\0')284lowpty = atoi(optarg);285if ((lowpty > highpty) || (lowpty < 0) ||286(highpty > 32767)) {287usage(1);288/* NOT REACHED */289}290break;291}292#endif /* CRAY */293294case 'S':295#ifdef HAVE_PARSETOS296if ((tos = parsetos(optarg, "tcp")) < 0)297fprintf(stderr, "%s%s%s\n",298"telnetd: Bad TOS argument '", optarg,299"'; will try to use default TOS");300#else301fprintf(stderr, "%s%s\n", "TOS option unavailable; ",302"-S flag not supported\n");303#endif304break;305306case 'u': {307char *eptr;308309utmp_len = strtol(optarg, &eptr, 0);310if (optarg == eptr)311fprintf(stderr, "telnetd: unknown utmp len (%s)\n", optarg);312break;313}314315case 'U':316registerd_host_only = 1;317break;318319#ifdef AUTHENTICATION320case 'X':321/*322* Check for invalid authentication types323*/324auth_disable_name(optarg);325break;326#endif327case 'y':328no_warn = 1;329break;330#ifdef AUTHENTICATION331case 'z':332log_unauth = 1;333break;334335#endif /* AUTHENTICATION */336337case 'L':338new_login = optarg;339break;340341default:342fprintf(stderr, "telnetd: %c: unknown option\n", ch);343/* FALLTHROUGH */344case '?':345usage(0);346/* NOTREACHED */347}348}349350argc -= optind;351argv += optind;352353if (debug) {354int port = 0;355struct servent *sp;356357if (argc > 1) {358usage (1);359} else if (argc == 1) {360sp = roken_getservbyname (*argv, "tcp");361if (sp)362port = sp->s_port;363else364port = htons(atoi(*argv));365} else {366#ifdef KRB5367port = krb5_getportbyname (NULL, "telnet", "tcp", 23);368#else369port = k_getportbyname("telnet", "tcp", htons(23));370#endif371}372mini_inetd (port, NULL);373} else if (argc > 0) {374usage(1);375/* NOT REACHED */376}377378#ifdef _SC_CRAY_SECURE_SYS379secflag = sysconf(_SC_CRAY_SECURE_SYS);380381/*382* Get socket's security label383*/384if (secflag) {385socklen_t szss = sizeof(ss);386int sock_multi;387socklen_t szi = sizeof(int);388389memset(&dv, 0, sizeof(dv));390391if (getsysv(&sysv, sizeof(struct sysv)) != 0)392fatalperror(net, "getsysv");393394/*395* Get socket security label and set device values396* {security label to be set on ttyp device}397*/398#ifdef SO_SEC_MULTI /* 8.0 code */399if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,400(void *)&ss, &szss) < 0) ||401(getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,402(void *)&sock_multi, &szi) < 0))403fatalperror(net, "getsockopt");404else {405dv.dv_actlvl = ss.ss_actlabel.lt_level;406dv.dv_actcmp = ss.ss_actlabel.lt_compart;407if (!sock_multi) {408dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;409dv.dv_valcmp = dv.dv_actcmp;410} else {411dv.dv_minlvl = ss.ss_minlabel.lt_level;412dv.dv_maxlvl = ss.ss_maxlabel.lt_level;413dv.dv_valcmp = ss.ss_maxlabel.lt_compart;414}415dv.dv_devflg = 0;416}417#else /* SO_SEC_MULTI */ /* 7.0 code */418if (getsockopt(0, SOL_SOCKET, SO_SECURITY,419(void *)&ss, &szss) >= 0) {420dv.dv_actlvl = ss.ss_slevel;421dv.dv_actcmp = ss.ss_compart;422dv.dv_minlvl = ss.ss_minlvl;423dv.dv_maxlvl = ss.ss_maxlvl;424dv.dv_valcmp = ss.ss_maxcmp;425}426#endif /* SO_SEC_MULTI */427}428#endif /* _SC_CRAY_SECURE_SYS */429430roken_openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);431sa_size = sizeof (__ss);432if (getpeername(STDIN_FILENO, sa, &sa_size) < 0) {433fprintf(stderr, "%s: ", progname);434perror("getpeername");435_exit(1);436}437if (keepalive &&438setsockopt(STDIN_FILENO, SOL_SOCKET, SO_KEEPALIVE,439(void *)&on, sizeof (on)) < 0) {440syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");441}442443#if defined(IPPROTO_IP) && defined(IP_TOS) && defined(HAVE_SETSOCKOPT)444{445# ifdef HAVE_GETTOSBYNAME446struct tosent *tp;447if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))448tos = tp->t_tos;449# endif450if (tos < 0)451tos = 020; /* Low Delay bit */452if (tos453&& sa->sa_family == AF_INET454&& (setsockopt(STDIN_FILENO, IPPROTO_IP, IP_TOS,455(void *)&tos, sizeof(tos)) < 0)456&& (errno != ENOPROTOOPT) )457syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");458}459#endif /* defined(IPPROTO_IP) && defined(IP_TOS) */460net = STDIN_FILENO;461doit(sa, sa_size);462/* NOTREACHED */463return 0;464} /* end of main */465466static void467usage(int exit_code)468{469fprintf(stderr, "Usage: telnetd");470fprintf(stderr, " [--help]");471fprintf(stderr, " [--version]");472#ifdef AUTHENTICATION473fprintf(stderr, " [-a (debug|other|otp|user|valid|off|none)]\n\t");474#endif475fprintf(stderr, " [-debug]");476#ifdef DIAGNOSTICS477fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");478#endif479#ifdef AUTHENTICATION480fprintf(stderr, " [-edebug]");481#endif482fprintf(stderr, " [-h]");483fprintf(stderr, " [-L login]");484fprintf(stderr, " [-n]");485#ifdef _CRAY486fprintf(stderr, " [-r[lowpty]-[highpty]]");487#endif488fprintf(stderr, "\n\t");489#ifdef HAVE_GETTOSBYNAME490fprintf(stderr, " [-S tos]");491#endif492#ifdef AUTHENTICATION493fprintf(stderr, " [-X auth-type] [-y] [-z]");494#endif495fprintf(stderr, " [-u utmp_hostname_length] [-U]");496fprintf(stderr, " [port]\n");497exit(exit_code);498}499500/*501* getterminaltype502*503* Ask the other end to send along its terminal type and speed.504* Output is the variable terminaltype filled in.505*/506static unsigned char ttytype_sbbuf[] = {507IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE508};509510int511getterminaltype(char *name, size_t name_sz)512{513int retval = -1;514515settimer(baseline);516#ifdef AUTHENTICATION517/*518* Handle the Authentication option before we do anything else.519*/520send_do(TELOPT_AUTHENTICATION, 1);521while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))522ttloop();523if (his_state_is_will(TELOPT_AUTHENTICATION)) {524retval = auth_wait(name, name_sz);525}526#endif527528#ifdef ENCRYPTION529send_will(TELOPT_ENCRYPT, 1);530send_do(TELOPT_ENCRYPT, 1); /* [email protected] */531#endif532send_do(TELOPT_TTYPE, 1);533send_do(TELOPT_TSPEED, 1);534send_do(TELOPT_XDISPLOC, 1);535send_do(TELOPT_NEW_ENVIRON, 1);536send_do(TELOPT_OLD_ENVIRON, 1);537while (538#ifdef ENCRYPTION539his_do_dont_is_changing(TELOPT_ENCRYPT) ||540#endif541his_will_wont_is_changing(TELOPT_TTYPE) ||542his_will_wont_is_changing(TELOPT_TSPEED) ||543his_will_wont_is_changing(TELOPT_XDISPLOC) ||544his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||545his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {546ttloop();547}548#ifdef ENCRYPTION549/*550* Wait for the negotiation of what type of encryption we can551* send with. If autoencrypt is not set, this will just return.552*/553if (his_state_is_will(TELOPT_ENCRYPT)) {554encrypt_wait();555}556if (require_encryption) {557558while (encrypt_delay())559if (telnet_spin())560fatal(net, "Failed while waiting for encryption");561562if (!encrypt_is_encrypting())563fatal(net, "Encryption required but not turned on by client");564}565#endif566if (his_state_is_will(TELOPT_TSPEED)) {567static unsigned char sb[] =568{ IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };569570telnet_net_write (sb, sizeof sb);571DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););572}573if (his_state_is_will(TELOPT_XDISPLOC)) {574static unsigned char sb[] =575{ IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };576577telnet_net_write (sb, sizeof sb);578DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););579}580if (his_state_is_will(TELOPT_NEW_ENVIRON)) {581static unsigned char sb[] =582{ IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };583584telnet_net_write (sb, sizeof sb);585DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););586}587else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {588static unsigned char sb[] =589{ IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };590591telnet_net_write (sb, sizeof sb);592DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););593}594if (his_state_is_will(TELOPT_TTYPE)) {595596telnet_net_write (ttytype_sbbuf, sizeof ttytype_sbbuf);597DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,598sizeof ttytype_sbbuf - 2););599}600if (his_state_is_will(TELOPT_TSPEED)) {601while (sequenceIs(tspeedsubopt, baseline))602ttloop();603}604if (his_state_is_will(TELOPT_XDISPLOC)) {605while (sequenceIs(xdisplocsubopt, baseline))606ttloop();607}608if (his_state_is_will(TELOPT_NEW_ENVIRON)) {609while (sequenceIs(environsubopt, baseline))610ttloop();611}612if (his_state_is_will(TELOPT_OLD_ENVIRON)) {613while (sequenceIs(oenvironsubopt, baseline))614ttloop();615}616if (his_state_is_will(TELOPT_TTYPE)) {617char first[256], last[256];618619while (sequenceIs(ttypesubopt, baseline))620ttloop();621622/*623* If the other side has already disabled the option, then624* we have to just go with what we (might) have already gotten.625*/626if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {627strlcpy(first, terminaltype, sizeof(first));628for(;;) {629/*630* Save the unknown name, and request the next name.631*/632strlcpy(last, terminaltype, sizeof(last));633_gettermname();634if (terminaltypeok(terminaltype))635break;636if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||637his_state_is_wont(TELOPT_TTYPE)) {638/*639* We've hit the end. If this is the same as640* the first name, just go with it.641*/642if (strncmp(first, terminaltype, sizeof(first)) == 0)643break;644/*645* Get the terminal name one more time, so that646* RFC1091 compliant telnets will cycle back to647* the start of the list.648*/649_gettermname();650if (strncmp(first, terminaltype, sizeof(first)) != 0)651strlcpy(terminaltype, first, sizeof(terminaltype));652break;653}654}655}656}657return(retval);658} /* end of getterminaltype */659660void661_gettermname(void)662{663/*664* If the client turned off the option,665* we can't send another request, so we666* just return.667*/668if (his_state_is_wont(TELOPT_TTYPE))669return;670settimer(baseline);671telnet_net_write (ttytype_sbbuf, sizeof ttytype_sbbuf);672DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,673sizeof ttytype_sbbuf - 2););674while (sequenceIs(ttypesubopt, baseline))675ttloop();676}677678int679terminaltypeok(char *s)680{681return 1;682}683684685char host_name[MaxHostNameLen];686char remote_host_name[MaxHostNameLen];687char remote_utmp_name[MaxHostNameLen];688689/*690* Get a pty, scan input lines.691*/692static void693doit(struct sockaddr *who, int who_len)694{695int level;696int ptynum;697char user_name[256];698int error;699700/*701* Find an available pty to use.702*/703ourpty = getpty(&ptynum);704if (ourpty < 0)705fatal(net, "All network ports in use");706707#ifdef _SC_CRAY_SECURE_SYS708/*709* set ttyp line security label710*/711if (secflag) {712char slave_dev[16];713714snprintf(tty_dev, sizeof(tty_dev), "/dev/pty/%03d", ptynum);715if (setdevs(tty_dev, &dv) < 0)716fatal(net, "cannot set pty security");717snprintf(slave_dev, sizeof(slave_dev), "/dev/ttyp%03d", ptynum);718if (setdevs(slave_dev, &dv) < 0)719fatal(net, "cannot set tty security");720}721#endif /* _SC_CRAY_SECURE_SYS */722723error = getnameinfo_verified (who, who_len,724remote_host_name,725sizeof(remote_host_name),726NULL, 0,727registerd_host_only ? NI_NAMEREQD : 0);728if (error)729fatal(net, "Couldn't resolve your address into a host name.\r\n\730Please contact your net administrator");731732gethostname(host_name, sizeof (host_name));733734strlcpy (remote_utmp_name, remote_host_name, sizeof(remote_utmp_name));735736/* Only trim if too long (and possible) */737if (strlen(remote_utmp_name) > utmp_len) {738char *domain = strchr(host_name, '.');739char *p = strchr(remote_utmp_name, '.');740if (domain != NULL && p != NULL && (strcmp(p, domain) == 0))741*p = '\0'; /* remove domain part */742}743744/*745* If hostname still doesn't fit utmp, use ipaddr.746*/747if (strlen(remote_utmp_name) > utmp_len) {748error = getnameinfo (who, who_len,749remote_utmp_name,750sizeof(remote_utmp_name),751NULL, 0,752NI_NUMERICHOST);753if (error)754fatal(net, "Couldn't get numeric address\r\n");755}756757#ifdef AUTHENTICATION758auth_encrypt_init(host_name, remote_host_name, "TELNETD", 1);759#endif760761init_env();762763/* begin server processing */764765/*766* Initialize the slc mapping table.767*/768769get_slc_defaults();770771/*772* get terminal type.773*/774*user_name = 0;775level = getterminaltype(user_name, sizeof(user_name));776esetenv("TERM", terminaltype[0] ? terminaltype : "network", 1);777778#ifdef _SC_CRAY_SECURE_SYS779if (secflag) {780if (setulvl(dv.dv_actlvl) < 0)781fatal(net,"cannot setulvl()");782if (setucmp(dv.dv_actcmp) < 0)783fatal(net, "cannot setucmp()");784}785#endif /* _SC_CRAY_SECURE_SYS */786787my_telnet(net, ourpty, remote_host_name, remote_utmp_name,788level, user_name);789/*NOTREACHED*/790} /* end of doit */791792/* output contents of /etc/issue.net, or /etc/issue */793static void794show_issue(void)795{796FILE *f;797char buf[128];798f = fopen(SYSCONFDIR "/issue.net", "r");799if(f == NULL)800f = fopen(SYSCONFDIR "/issue", "r");801if(f){802while(fgets(buf, sizeof(buf), f) != NULL) {803size_t len = strcspn(buf, "\r\n");804if(len == strlen(buf)) {805/* there's no newline */806writenet(buf, len);807} else {808/* replace newline with \r\n */809buf[len] = '\0';810writenet(buf, len);811writenet("\r\n", 2);812}813}814fclose(f);815}816}817818/*819* Main loop. Select from pty and network, and820* hand data to telnet receiver finite state machine.821*/822void823my_telnet(int f, int p, const char *host, const char *utmp_host,824int level, char *autoname)825{826int on = 1;827char *he;828char *IM;829int nfd;830int startslave_called = 0;831time_t timeout;832833/*834* Do some tests where it is desireable to wait for a response.835* Rather than doing them slowly, one at a time, do them all836* at once.837*/838if (my_state_is_wont(TELOPT_SGA))839send_will(TELOPT_SGA, 1);840/*841* Is the client side a 4.2 (NOT 4.3) system? We need to know this842* because 4.2 clients are unable to deal with TCP urgent data.843*844* To find out, we send out a "DO ECHO". If the remote system845* answers "WILL ECHO" it is probably a 4.2 client, and we note846* that fact ("WILL ECHO" ==> that the client will echo what847* WE, the server, sends it; it does NOT mean that the client will848* echo the terminal input).849*/850send_do(TELOPT_ECHO, 1);851852/*853* Send along a couple of other options that we wish to negotiate.854*/855send_do(TELOPT_NAWS, 1);856send_will(TELOPT_STATUS, 1);857flowmode = 1; /* default flow control state */858restartany = -1; /* uninitialized... */859send_do(TELOPT_LFLOW, 1);860861/*862* Spin, waiting for a response from the DO ECHO. However,863* some REALLY DUMB telnets out there might not respond864* to the DO ECHO. So, we spin looking for NAWS, (most dumb865* telnets so far seem to respond with WONT for a DO that866* they don't understand...) because by the time we get the867* response, it will already have processed the DO ECHO.868* Kludge upon kludge.869*/870while (his_will_wont_is_changing(TELOPT_NAWS))871ttloop();872873/*874* But...875* The client might have sent a WILL NAWS as part of its876* startup code; if so, we'll be here before we get the877* response to the DO ECHO. We'll make the assumption878* that any implementation that understands about NAWS879* is a modern enough implementation that it will respond880* to our DO ECHO request; hence we'll do another spin881* waiting for the ECHO option to settle down, which is882* what we wanted to do in the first place...883*/884if (his_want_state_is_will(TELOPT_ECHO) &&885his_state_is_will(TELOPT_NAWS)) {886while (his_will_wont_is_changing(TELOPT_ECHO))887ttloop();888}889/*890* On the off chance that the telnet client is broken and does not891* respond to the DO ECHO we sent, (after all, we did send the892* DO NAWS negotiation after the DO ECHO, and we won't get here893* until a response to the DO NAWS comes back) simulate the894* receipt of a will echo. This will also send a WONT ECHO895* to the client, since we assume that the client failed to896* respond because it believes that it is already in DO ECHO897* mode, which we do not want.898*/899if (his_want_state_is_will(TELOPT_ECHO)) {900DIAG(TD_OPTIONS,901{output_data("td: simulating recv\r\n");902});903willoption(TELOPT_ECHO);904}905906/*907* Finally, to clean things up, we turn on our echo. This908* will break stupid 4.2 telnets out of local terminal echo.909*/910911if (my_state_is_wont(TELOPT_ECHO))912send_will(TELOPT_ECHO, 1);913914#ifdef TIOCPKT915#ifdef STREAMSPTY916if (!really_stream)917#endif918/*919* Turn on packet mode920*/921ioctl(p, TIOCPKT, (char *)&on);922#endif923924925/*926* Call telrcv() once to pick up anything received during927* terminal type negotiation, 4.2/4.3 determination, and928* linemode negotiation.929*/930telrcv();931932ioctl(f, FIONBIO, (char *)&on);933ioctl(p, FIONBIO, (char *)&on);934935#if defined(SO_OOBINLINE) && defined(HAVE_SETSOCKOPT)936setsockopt(net, SOL_SOCKET, SO_OOBINLINE,937(void *)&on, sizeof on);938#endif /* defined(SO_OOBINLINE) */939940#ifdef SIGTSTP941signal(SIGTSTP, SIG_IGN);942#endif943#ifdef SIGTTOU944/*945* Ignoring SIGTTOU keeps the kernel from blocking us946* in ttioct() in /sys/tty.c.947*/948signal(SIGTTOU, SIG_IGN);949#endif950951signal(SIGCHLD, cleanup);952953#ifdef TIOCNOTTY954{955int t;956t = open(_PATH_TTY, O_RDWR);957if (t >= 0) {958ioctl(t, TIOCNOTTY, (char *)0);959close(t);960}961}962#endif963964show_issue();965/*966* Show banner that getty never gave.967*968* We put the banner in the pty input buffer. This way, it969* gets carriage return null processing, etc., just like all970* other pty --> client data.971*/972973if (getenv("USER"))974hostinfo = 0;975976IM = DEFAULT_IM;977he = 0;978edithost(he, host_name);979if (hostinfo && *IM)980putf(IM, ptyibuf2);981982if (pcc)983strncat(ptyibuf2, ptyip, pcc+1);984ptyip = ptyibuf2;985pcc = strlen(ptyip);986987DIAG(TD_REPORT, {988output_data("td: Entering processing loop\r\n");989});990991992nfd = ((f > p) ? f : p) + 1;993timeout = time(NULL) + 5;994for (;;) {995fd_set ibits, obits, xbits;996int c;997998/* wait for encryption to be turned on, but don't wait999indefinitely */1000if(!startslave_called && (!encrypt_delay() || timeout > time(NULL))){1001startslave_called = 1;1002startslave(host, utmp_host, level, autoname);1003}10041005if (ncc < 0 && pcc < 0)1006break;10071008FD_ZERO(&ibits);1009FD_ZERO(&obits);1010FD_ZERO(&xbits);10111012if (f >= FD_SETSIZE1013|| p >= FD_SETSIZE)1014fatal(net, "fd too large");10151016/*1017* Never look for input if there's still1018* stuff in the corresponding output buffer1019*/1020if (nfrontp - nbackp || pcc > 0) {1021FD_SET(f, &obits);1022} else {1023FD_SET(p, &ibits);1024}1025if (pfrontp - pbackp || ncc > 0) {1026FD_SET(p, &obits);1027} else {1028FD_SET(f, &ibits);1029}1030if (!SYNCHing) {1031FD_SET(f, &xbits);1032}1033if ((c = select(nfd, &ibits, &obits, &xbits,1034(struct timeval *)0)) < 1) {1035if (c == -1) {1036if (errno == EINTR) {1037continue;1038}1039}1040sleep(5);1041continue;1042}10431044/*1045* Any urgent data?1046*/1047if (FD_ISSET(net, &xbits)) {1048SYNCHing = 1;1049}10501051/*1052* Something to read from the network...1053*/1054if (FD_ISSET(net, &ibits)) {1055#ifndef SO_OOBINLINE1056/*1057* In 4.2 (and 4.3 beta) systems, the1058* OOB indication and data handling in the kernel1059* is such that if two separate TCP Urgent requests1060* come in, one byte of TCP data will be overlaid.1061* This is fatal for Telnet, but we try to live1062* with it.1063*1064* In addition, in 4.2 (and...), a special protocol1065* is needed to pick up the TCP Urgent data in1066* the correct sequence.1067*1068* What we do is: if we think we are in urgent1069* mode, we look to see if we are "at the mark".1070* If we are, we do an OOB receive. If we run1071* this twice, we will do the OOB receive twice,1072* but the second will fail, since the second1073* time we were "at the mark", but there wasn't1074* any data there (the kernel doesn't reset1075* "at the mark" until we do a normal read).1076* Once we've read the OOB data, we go ahead1077* and do normal reads.1078*1079* There is also another problem, which is that1080* since the OOB byte we read doesn't put us1081* out of OOB state, and since that byte is most1082* likely the TELNET DM (data mark), we would1083* stay in the TELNET SYNCH (SYNCHing) state.1084* So, clocks to the rescue. If we've "just"1085* received a DM, then we test for the1086* presence of OOB data when the receive OOB1087* fails (and AFTER we did the normal mode read1088* to clear "at the mark").1089*/1090if (SYNCHing) {1091int atmark;10921093ioctl(net, SIOCATMARK, (char *)&atmark);1094if (atmark) {1095ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);1096if ((ncc == -1) && (errno == EINVAL)) {1097ncc = read(net, netibuf, sizeof (netibuf));1098if (sequenceIs(didnetreceive, gotDM)) {1099SYNCHing = stilloob(net);1100}1101}1102} else {1103ncc = read(net, netibuf, sizeof (netibuf));1104}1105} else {1106ncc = read(net, netibuf, sizeof (netibuf));1107}1108settimer(didnetreceive);1109#else /* !defined(SO_OOBINLINE)) */1110ncc = read(net, netibuf, sizeof (netibuf));1111#endif /* !defined(SO_OOBINLINE)) */1112if (ncc < 0 && errno == EWOULDBLOCK)1113ncc = 0;1114else {1115if (ncc <= 0) {1116break;1117}1118netip = netibuf;1119}1120DIAG((TD_REPORT | TD_NETDATA), {1121output_data("td: netread %d chars\r\n", ncc);1122});1123DIAG(TD_NETDATA, printdata("nd", netip, ncc));1124}11251126/*1127* Something to read from the pty...1128*/1129if (FD_ISSET(p, &ibits)) {1130#ifdef STREAMSPTY1131if (really_stream)1132pcc = readstream(p, ptyibuf, BUFSIZ);1133else1134#endif1135pcc = read(p, ptyibuf, BUFSIZ);11361137/*1138* On some systems, if we try to read something1139* off the master side before the slave side is1140* opened, we get EIO.1141*/1142if (pcc < 0 && (errno == EWOULDBLOCK ||1143#ifdef EAGAIN1144errno == EAGAIN ||1145#endif1146errno == EIO)) {1147pcc = 0;1148} else {1149if (pcc <= 0)1150break;1151if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {1152netclear(); /* clear buffer back */1153#ifndef NO_URGENT1154/*1155* There are client telnets on some1156* operating systems get screwed up1157* royally if we send them urgent1158* mode data.1159*/1160output_data ("%c%c", IAC, DM);11611162neturg = nfrontp-1; /* off by one XXX */1163DIAG(TD_OPTIONS,1164printoption("td: send IAC", DM));11651166#endif1167}1168if (his_state_is_will(TELOPT_LFLOW) &&1169(ptyibuf[0] &1170(TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {1171int newflow =1172ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;1173if (newflow != flowmode) {1174flowmode = newflow;1175output_data("%c%c%c%c%c%c",1176IAC, SB, TELOPT_LFLOW,1177flowmode ? LFLOW_ON1178: LFLOW_OFF,1179IAC, SE);1180DIAG(TD_OPTIONS, printsub('>',1181(unsigned char *)nfrontp-4,11824););1183}1184}1185pcc--;1186ptyip = ptyibuf+1;1187}1188}11891190while (pcc > 0) {1191if ((&netobuf[BUFSIZ] - nfrontp) < 3)1192break;1193c = *ptyip++ & 0377, pcc--;1194if (c == IAC)1195*nfrontp++ = c;1196*nfrontp++ = c;1197if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {1198if (pcc > 0 && ((*ptyip & 0377) == '\n')) {1199*nfrontp++ = *ptyip++ & 0377;1200pcc--;1201} else1202*nfrontp++ = '\0';1203}1204}12051206if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)1207netflush();1208if (ncc > 0)1209telrcv();1210if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)1211ptyflush();1212}1213cleanup(0);1214}12151216#ifndef TCSIG1217# ifdef TIOCSIG1218# define TCSIG TIOCSIG1219# endif1220#endif12211222#ifdef STREAMSPTY12231224int flowison = -1; /* current state of flow: -1 is unknown */12251226int1227readstream(int p, char *ibuf, int bufsize)1228{1229int flags = 0;1230int ret = 0;1231struct termios *tsp;1232#if 01233struct termio *tp;1234#endif1235struct iocblk *ip;1236char vstop, vstart;1237int ixon;1238int newflow;12391240strbufc.maxlen = BUFSIZ;1241strbufc.buf = (char *)ctlbuf;1242strbufd.maxlen = bufsize-1;1243strbufd.len = 0;1244strbufd.buf = ibuf+1;1245ibuf[0] = 0;12461247ret = getmsg(p, &strbufc, &strbufd, &flags);1248if (ret < 0) /* error of some sort -- probably EAGAIN */1249return(-1);12501251if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {1252/* data message */1253if (strbufd.len > 0) { /* real data */1254return(strbufd.len + 1); /* count header char */1255} else {1256/* nothing there */1257errno = EAGAIN;1258return(-1);1259}1260}12611262/*1263* It's a control message. Return 1, to look at the flag we set1264*/12651266switch (ctlbuf[0]) {1267case M_FLUSH:1268if (ibuf[1] & FLUSHW)1269ibuf[0] = TIOCPKT_FLUSHWRITE;1270return(1);12711272case M_IOCTL:1273ip = (struct iocblk *) (ibuf+1);12741275switch (ip->ioc_cmd) {1276#ifdef TCSETS1277case TCSETS:1278case TCSETSW:1279case TCSETSF:1280tsp = (struct termios *)1281(ibuf+1 + sizeof(struct iocblk));1282vstop = tsp->c_cc[VSTOP];1283vstart = tsp->c_cc[VSTART];1284ixon = tsp->c_iflag & IXON;1285break;1286#endif1287#if 01288case TCSETA:1289case TCSETAW:1290case TCSETAF:1291tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk));1292vstop = tp->c_cc[VSTOP];1293vstart = tp->c_cc[VSTART];1294ixon = tp->c_iflag & IXON;1295break;1296#endif1297default:1298errno = EAGAIN;1299return(-1);1300}13011302newflow = (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;1303if (newflow != flowison) { /* it's a change */1304flowison = newflow;1305ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;1306return(1);1307}1308}13091310/* nothing worth doing anything about */1311errno = EAGAIN;1312return(-1);1313}1314#endif /* STREAMSPTY */13151316/*1317* Send interrupt to process on other side of pty.1318* If it is in raw mode, just write NULL;1319* otherwise, write intr char.1320*/1321void1322interrupt()1323{1324ptyflush(); /* half-hearted */13251326#if defined(STREAMSPTY) && defined(TIOCSIGNAL)1327/* Streams PTY style ioctl to post a signal */1328if (really_stream)1329{1330int sig = SIGINT;1331ioctl(ourpty, TIOCSIGNAL, &sig);1332ioctl(ourpty, I_FLUSH, FLUSHR);1333}1334#else1335#ifdef TCSIG1336ioctl(ourpty, TCSIG, (char *)SIGINT);1337#else /* TCSIG */1338init_termbuf();1339*pfrontp++ = slctab[SLC_IP].sptr ?1340(unsigned char)*slctab[SLC_IP].sptr : '\177';1341#endif /* TCSIG */1342#endif1343}13441345/*1346* Send quit to process on other side of pty.1347* If it is in raw mode, just write NULL;1348* otherwise, write quit char.1349*/1350void1351sendbrk()1352{1353ptyflush(); /* half-hearted */1354#ifdef TCSIG1355ioctl(ourpty, TCSIG, (char *)SIGQUIT);1356#else /* TCSIG */1357init_termbuf();1358*pfrontp++ = slctab[SLC_ABORT].sptr ?1359(unsigned char)*slctab[SLC_ABORT].sptr : '\034';1360#endif /* TCSIG */1361}13621363void1364sendsusp()1365{1366#ifdef SIGTSTP1367ptyflush(); /* half-hearted */1368# ifdef TCSIG1369ioctl(ourpty, TCSIG, (char *)SIGTSTP);1370# else /* TCSIG */1371*pfrontp++ = slctab[SLC_SUSP].sptr ?1372(unsigned char)*slctab[SLC_SUSP].sptr : '\032';1373# endif /* TCSIG */1374#endif /* SIGTSTP */1375}13761377/*1378* When we get an AYT, if ^T is enabled, use that. Otherwise,1379* just send back "[Yes]".1380*/1381void1382recv_ayt()1383{1384#if defined(SIGINFO) && defined(TCSIG)1385if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {1386ioctl(ourpty, TCSIG, (char *)SIGINFO);1387return;1388}1389#endif1390output_data("\r\n[Yes]\r\n");1391}13921393void1394doeof()1395{1396init_termbuf();13971398*pfrontp++ = slctab[SLC_EOF].sptr ?1399(unsigned char)*slctab[SLC_EOF].sptr : '\004';1400}140114021403