/[inetutils]/inetutils/telnetd/telnetd.c
ViewVC logotype

Diff of /inetutils/telnetd/telnetd.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.25 by gray, Thu Mar 14 14:26:04 2002 UTC revision 1.26 by gray, Fri Apr 5 00:02:43 2002 UTC
# Line 1  Line 1 
1  /*  /* Copyright (C) 1998,2001 Free Software Foundation, Inc.
  * Copyright (c) 1989, 1993  
  *      The Regents of the University of California.  All rights reserved.  
  *  
  * Redistribution and use in source and binary forms, with or without  
  * modification, are permitted provided that the following conditions  
  * are met:  
  * 1. Redistributions of source code must retain the above copyright  
  *    notice, this list of conditions and the following disclaimer.  
  * 2. Redistributions in binary form must reproduce the above copyright  
  *    notice, this list of conditions and the following disclaimer in the  
  *    documentation and/or other materials provided with the distribution.  
  * 4. Neither the name of the University nor the names of its contributors  
  *    may be used to endorse or promote products derived from this software  
  *    without specific prior written permission.  
  *  
  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND  
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE  
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE  
  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE  
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL  
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS  
  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT  
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY  
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF  
  * SUCH DAMAGE.  
  */  
   
 #ifndef lint  
 static char copyright[] =  
 "@(#) Copyright (c) 1989, 1993\n\  
         The Regents of the University of California.  All rights reserved.\n";  
 #endif /* not lint */  
   
 #ifndef lint  
 static char sccsid[] = "@(#)telnetd.c   8.4 (Berkeley) 5/30/95";  
 #endif /* not lint */  
2    
3  #ifdef HAVE_CONFIG_H     This file is part of GNU Inetutils.
 #include <config.h>  
 #endif  
4    
5  #include "telnetd.h"     GNU Inetutils is free software; you can redistribute it and/or modify
6       it under the terms of the GNU General Public License as published by
7  #if     defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)     the Free Software Foundation; either version 2, or (at your option)
8  /*     any later version.
9   * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can  
10   * use it to tell us to turn off all the socket security code,     GNU Inetutils is distributed in the hope that it will be useful,
11   * since that is only used in UNICOS 7.0 and later.     but WITHOUT ANY WARRANTY; without even the implied warranty of
12   */     MERCHANTABILITY or FITNESS FOR PARTICULAR PURPOSE.  See the
13  # undef _SC_CRAY_SECURE_SYS     GNU General Public License for more details.
14  #endif  
15       You should have received a copy of the GNU General Public License
16  #if     defined(_SC_CRAY_SECURE_SYS)     along with GNU Inetutils; see the file COPYING.  If not, write to
17  #include <sys/sysv.h>     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  #include <sys/secdev.h>     Boston, MA 02111-1307, USA. */
 # ifdef SO_SEC_MULTI            /* 8.0 code */  
 #include <sys/secparm.h>  
 #include <sys/usrv.h>  
 # endif /* SO_SEC_MULTI */  
 int     secflag;  
 char    tty_dev[16];  
 struct  secdev dv;  
 struct  sysv sysv;  
 # ifdef SO_SEC_MULTI            /* 8.0 code */  
 struct  socksec ss;  
 # else /* SO_SEC_MULTI */       /* 7.0 code */  
 struct  socket_security ss;  
 # endif /* SO_SEC_MULTI */  
 #endif  /* _SC_CRAY_SECURE_SYS */  
   
 #if     defined(AUTHENTICATION)  
 #include <libtelnet/auth.h>  
 int     auth_level = 0;  
 #endif  
 #if     defined(SecurID)  
 int     require_SecurID = 0;  
 #endif  
19    
20    #include "telnetd.h"
21    #include <getopt.h>
22    #include <version.h>
23  #ifdef HAVE_SYS_UTSNAME_H  #ifdef HAVE_SYS_UTSNAME_H
24  #include <sys/utsname.h>  # include <sys/utsname.h>
25  #endif  #endif
26    
27  #ifdef HAVE_UTMP_UT_HOST  static char short_options[] = "a:D::d:E:HhLl::nS:Uu:VX:";
 extern  int utmp_len;  
 #endif  
 int     registerd_host_only = 0;  
28    
29  #ifdef  HAVE_STREAMSPTY  static struct option long_options[] =
30  # include <stropts.h>  {
31  # include <termio.h>    /* Help options */
32  /* make sure we don't get the bsd version */    {"version", no_argument, NULL, 'V'},
33  # include "/usr/include/sys/tty.h"    {"license", no_argument, NULL, 'L'},
34  # include <sys/ptyvar.h>    {"help",    no_argument, NULL, 'H'},
35      /* Common options */
36  /*    {"authmode", required_argument, NULL, 'a'},
37   * Because of the way ptyibuf is used with streams messages, we need    {"debug", optional_argument, NULL, 'D'},
38   * ptyibuf+1 to be on a full-word boundary.  The following wierdness    {"exec-login", required_argument, NULL, 'E'},
39   * is simply to make that happen.    {"no-hostinfo", no_argument, NULL, 'h'},
40   */    {"linemode", optional_argument, NULL, 'l'},
41  long    ptyibufbuf[BUFSIZ/sizeof(long)+1];    {"no-keepalive", no_argument, NULL, 'n'},
42  char    *ptyibuf = ((char *)&ptyibufbuf[1])-1;    {"reverse-lookup", no_argument, NULL, 'U'},
43  char    *ptyip = ((char *)&ptyibufbuf[1])-1;    {"disable-auth-type", required_argument, NULL, 'X'},
44  char    ptyibuf2[BUFSIZ];    {NULL, 0, NULL, 0}
 unsigned char ctlbuf[BUFSIZ];  
 struct  strbuf strbufc, strbufd;  
   
 int readstream();  
   
 #else   /* ! HAVE_STREAMSPTY */  
   
 /*  
  * I/O data buffers,  
  * pointers, and counters.  
  */  
 char    ptyibuf[BUFSIZ], *ptyip = ptyibuf;  
 char    ptyibuf2[BUFSIZ];  
   
 #endif /* ! HAVE_STREAMSPTY */  
   
 int     hostinfo = 1;                   /* do we print login banner? */  
   
 #ifdef  CRAY  
 extern int      newmap; /* nonzero if \n maps to ^M^J */  
 int     lowpty = 0, highpty;    /* low, high pty numbers */  
 #endif /* CRAY */  
   
 int debug = 0;  
 int keepalive = 1;  
 char *progname;  
   
 extern void usage P((void));  
   
 /*  
  * The string to pass to getopt().  We do it this way so  
  * that only the actual options that we support will be  
  * passed off to getopt().  
  */  
 char valid_opts[] = {  
         'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',  
 #ifdef  AUTHENTICATION  
         'a', ':', 'X', ':',  
 #endif  
 #ifdef BFTPDAEMON  
         'B',  
 #endif  
 #ifdef DIAGNOSTICS  
         'D', ':',  
 #endif  
 #ifdef  ENCRYPTION  
         'e', ':',  
 #endif  
 #if     defined(CRAY) && defined(NEWINIT)  
         'I', ':',  
 #endif  
 #ifdef  LINEMODE  
         'l',  
 #endif  
 #ifdef CRAY  
         'r', ':',  
 #endif  
 #ifdef  SecurID  
         's',  
 #endif  
         '\0'  
45  };  };
46    
47  int  static void telnetd_version ();
48  main(int argc, char **argv)  static void telnetd_license ();
49  {  static void telnetd_help ();
50          struct sockaddr_in from;  static void parse_authmode (char *str);
51          int on = 1, fromlen;  static void parse_linemode (char *str);
52          register int ch;  static void parse_debug_level (char *str);
53          extern char *optarg;  static void telnetd_setup (int fd);
54          extern int optind;  static int telnetd_run ();
55  #if     defined(IPPROTO_IP) && defined(IP_TOS)  
56          int tos = -1;  
57    char *login_invocation; /* Template command line for invoking login program */
58    int keepalive = 1;      /* Should the TCP keepalive bit be set */
59    int reverse_lookup = 0; /* Reject connects from hosts which IP numbers
60                               cannot be reverse mapped to their hostnames */
61    int alwayslinemode;     /* Always set the linemode (1) */
62    int lmodetype;          /* Type of linemode (2) */
63    int hostinfo = 1;       /* Print the host-specific information before
64                               login */
65    
66    int auth_level = 0;     /* Authentication level */
67    
68    int debug_level[debug_max_mode]; /* Debugging levels */
69    int debug_tcp = 0;               /* Should the SO_DEBUG be set? */
70    
71    int net;      /* Network connection socket */
72    int pty;      /* PTY master descriptor */
73    char *remote_hostname;
74    char *local_hostname;
75    
76    char    options[256];
77    char    do_dont_resp[256];
78    char    will_wont_resp[256];
79    int     linemode;       /* linemode on/off */
80    int     uselinemode;    /* what linemode to use (on/off) */
81    int     editmode;       /* edit modes in use */
82    int     useeditmode;    /* edit modes to use */
83    int     alwayslinemode; /* command line option */
84    int     lmodetype;      /* Client support for linemode */
85    int     flowmode;       /* current flow control state */
86    int     restartany;     /* restart output on any character state */
87    int     diagnostic;     /* telnet diagnostic capabilities */
88    #if defined(AUTHENTICATION)
89    int     auth_level;
90  #endif  #endif
91    
92          pfrontp = pbackp = ptyobuf;  slcfun  slctab[NSLC + 1];       /* slc mapping table */
         netip = netibuf;  
         nfrontp = nbackp = netobuf;  
 #ifdef  ENCRYPTION  
         nclearto = 0;  
 #endif  /* ENCRYPTION */  
   
         progname = *argv;  
   
 #ifdef CRAY  
         /*  
          * Get number of pty's before trying to process options,  
          * which may include changing pty range.  
          */  
         highpty = getnpty();  
 #endif /* CRAY */  
93    
94          while ((ch = getopt(argc, argv, valid_opts)) != EOF) {  char    *terminaltype;
                 switch(ch) {  
95    
96  #ifdef  AUTHENTICATION  int     SYNCHing;               /* we are in TELNET SYNCH mode */
97                  case 'a':  struct telnetd_clocks clocks;
                         /*  
                          * Check for required authentication level  
                          */  
                         if (strcmp(optarg, "debug") == 0) {  
                                 extern int auth_debug_mode;  
                                 auth_debug_mode = 1;  
                         } else if (strcasecmp(optarg, "none") == 0) {  
                                 auth_level = 0;  
                         } else if (strcasecmp(optarg, "other") == 0) {  
                                 auth_level = AUTH_OTHER;  
                         } else if (strcasecmp(optarg, "user") == 0) {  
                                 auth_level = AUTH_USER;  
                         } else if (strcasecmp(optarg, "valid") == 0) {  
                                 auth_level = AUTH_VALID;  
                         } else if (strcasecmp(optarg, "off") == 0) {  
                                 /*  
                                  * This hack turns off authentication  
                                  */  
                                 auth_level = -1;  
                         } else {  
                                 fprintf(stderr,  
                             "telnetd: unknown authorization level for -a\n");  
                         }  
                         break;  
 #endif  /* AUTHENTICATION */  
   
 #ifdef BFTPDAEMON  
                 case 'B':  
                         bftpd++;  
                         break;  
 #endif /* BFTPDAEMON */  
   
                 case 'd':  
                         if (strcmp(optarg, "ebug") == 0) {  
                                 debug++;  
                                 break;  
                         }  
                         usage();  
                         /* NOTREACHED */  
                         break;  
   
 #ifdef DIAGNOSTICS  
                 case 'D':  
                         /*  
                          * Check for desired diagnostics capabilities.  
                          */  
                         if (!strcmp(optarg, "report")) {  
                                 diagnostic |= TD_REPORT|TD_OPTIONS;  
                         } else if (!strcmp(optarg, "exercise")) {  
                                 diagnostic |= TD_EXERCISE;  
                         } else if (!strcmp(optarg, "netdata")) {  
                                 diagnostic |= TD_NETDATA;  
                         } else if (!strcmp(optarg, "ptydata")) {  
                                 diagnostic |= TD_PTYDATA;  
                         } else if (!strcmp(optarg, "options")) {  
                                 diagnostic |= TD_OPTIONS;  
                         } else {  
                                 usage();  
                                 /* NOT REACHED */  
                         }  
                         break;  
 #endif /* DIAGNOSTICS */  
   
 #ifdef  ENCRYPTION  
                 case 'e':  
                         if (strcmp(optarg, "debug") == 0) {  
                                 extern int encrypt_debug_mode;  
                                 encrypt_debug_mode = 1;  
                                 break;  
                         }  
                         usage();  
                         /* NOTREACHED */  
                         break;  
 #endif  /* ENCRYPTION */  
   
                 case 'h':  
                         hostinfo = 0;  
                         break;  
   
 #if     defined(CRAY) && defined(NEWINIT)  
                 case 'I':  
                     {  
                         extern char *gen_id;  
                         gen_id = optarg;  
                         break;  
                     }  
 #endif  /* defined(CRAY) && defined(NEWINIT) */  
   
 #ifdef  LINEMODE  
                 case 'l':  
                         alwayslinemode = 1;  
                         break;  
 #endif  /* LINEMODE */  
   
                 case 'k':  
 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)  
                         lmodetype = NO_AUTOKLUDGE;  
 #else  
                         /* ignore -k option if built without kludge linemode */  
 #endif  /* defined(LINEMODE) && defined(KLUDGELINEMODE) */  
                         break;  
   
                 case 'n':  
                         keepalive = 0;  
                         break;  
   
 #ifdef CRAY  
                 case 'r':  
                     {  
                         char *strchr();  
                         char *c;  
   
                         /*  
                          * Allow the specification of alterations  
                          * to the pty search range.  It is legal to  
                          * specify only one, and not change the  
                          * other from its default.  
                          */  
                         c = strchr(optarg, '-');  
                         if (c) {  
                                 *c++ = '\0';  
                                 highpty = atoi(c);  
                         }  
                         if (*optarg != '\0')  
                                 lowpty = atoi(optarg);  
                         if ((lowpty > highpty) || (lowpty < 0) ||  
                                                         (highpty > 32767)) {  
                                 usage();  
                                 /* NOT REACHED */  
                         }  
                         break;  
                     }  
 #endif  /* CRAY */  
   
 #ifdef  SecurID  
                 case 's':  
                         /* SecurID required */  
                         require_SecurID = 1;  
                         break;  
 #endif  /* SecurID */  
                 case 'S':  
 #ifdef  HAS_GETTOS  
                         if ((tos = parsetos(optarg, "tcp")) < 0)  
                                 fprintf(stderr, "%s%s%s\n",  
                                         "telnetd: Bad TOS argument '", optarg,  
                                         "'; will try to use default TOS");  
 #else  
                         fprintf(stderr, "%s%s\n", "TOS option unavailable; ",  
                                                 "-S flag not supported\n");  
 #endif  
                         break;  
   
                 case 'U':  
                         registerd_host_only = 1;  
                         break;  
98    
99    int
100    main (int argc, char **argv)
101    {
102      int c;
103      while ((c = getopt_long (argc, argv, short_options, long_options, NULL))
104             != EOF)
105        {
106          switch (c)
107            {
108            case 'V':
109              telnetd_version ();
110              exit (0);
111              
112            case 'L':
113              telnetd_license ();
114              exit (0);
115              
116            case 'H':
117              telnetd_help ();
118              exit (0);
119              
120            case 'a':
121              parse_authmode (optarg);
122              break;
123              
124            case 'D':
125              parse_debug_level (optarg);
126              break;
127              
128            case 'E':
129              login_invocation = optarg;
130              break;
131              
132            case 'h':
133              hostinfo = 0;
134              break;
135              
136            case 'l':
137              parse_linemode (optarg);
138              break;
139              
140            case 'n':
141              keepalive = 0;
142              break;
143              
144            case 'U':
145              reverse_lookup = 1;
146              break;
147              
148  #ifdef  AUTHENTICATION  #ifdef  AUTHENTICATION
149                  case 'X':          case 'X':
150                          /*            auth_disable_name (optarg);
151                           * Check for invalid authentication types            break;
                          */  
                         auth_disable_name(optarg);  
                         break;  
 #endif  /* AUTHENTICATION */  
   
                 default:  
                         fprintf(stderr, "telnetd: %c: unknown option\n", ch);  
                         /* FALLTHROUGH */  
                 case '?':  
                         usage();  
                         /* NOTREACHED */  
                 }  
         }  
   
         argc -= optind;  
         argv += optind;  
   
         if (debug) {  
             int s, ns, foo;  
             struct servent *sp;  
 #ifdef HAVE_SOCKADDR_IN_SIN_LEN  
             static struct sockaddr_in sin = { sizeof (sin), AF_INET };  
 #else  
             static struct sockaddr_in sin = { AF_INET };  
 #endif  
   
             if (argc > 1) {  
                 usage();  
                 /* NOT REACHED */  
             } else if (argc == 1) {  
                     sp = getservbyname(*argv, "tcp");  
                     if (sp) {  
                         sin.sin_port = sp->s_port;  
                     } else {  
                         sin.sin_port = atoi(*argv);  
                         if ((int)sin.sin_port <= 0) {  
                             fprintf(stderr, "telnetd: %s: bad port #\n", *argv);  
                             usage();  
                             /* NOT REACHED */  
                         }  
                         sin.sin_port = htons((u_short)sin.sin_port);  
                    }  
             } else {  
                 sp = getservbyname("telnet", "tcp");  
                 if (sp == 0) {  
                     fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");  
                     exit(1);  
                 }  
                 sin.sin_port = sp->s_port;  
             }  
   
             s = socket(AF_INET, SOCK_STREAM, 0);  
             if (s < 0) {  
                     perror("telnetd: socket");;  
                     exit(1);  
             }  
             (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,  
                                 (char *)&on, sizeof(on));  
             if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {  
                 perror("bind");  
                 exit(1);  
             }  
             if (listen(s, 1) < 0) {  
                 perror("listen");  
                 exit(1);  
             }  
             foo = sizeof sin;  
             ns = accept(s, (struct sockaddr *)&sin, &foo);  
             if (ns < 0) {  
                 perror("accept");  
                 exit(1);  
             }  
             (void) dup2(ns, 0);  
             (void) close(ns);  
             (void) close(s);  
 #ifdef convex  
         } else if (argc == 1) {  
                 ; /* VOID*/             /* Just ignore the host/port name */  
152  #endif  #endif
153          } else if (argc > 0) {          default:
154                  usage();            fprintf (stderr, "telnetd: unknown command line option: %c\n", c);
155                  /* NOT REACHED */            exit (1);
         }  
   
 #if     defined(_SC_CRAY_SECURE_SYS)  
         secflag = sysconf(_SC_CRAY_SECURE_SYS);  
   
         /*  
          *      Get socket's security label  
          */  
         if (secflag)  {  
                 int szss = sizeof(ss);  
 #ifdef SO_SEC_MULTI                     /* 8.0 code */  
                 int sock_multi;  
                 int szi = sizeof(int);  
 #endif /* SO_SEC_MULTI */  
   
                 memset((char *)&dv, 0, sizeof(dv));  
   
                 if (getsysv(&sysv, sizeof(struct sysv)) != 0) {  
                         perror("getsysv");  
                         exit(1);  
                 }  
   
                 /*  
                  *      Get socket security label and set device values  
                  *         {security label to be set on ttyp device}  
                  */  
 #ifdef SO_SEC_MULTI                     /* 8.0 code */  
                 if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,  
                                (char *)&ss, &szss) < 0) ||  
                     (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,  
                                 (char *)&sock_multi, &szi) < 0)) {  
                         perror("getsockopt");  
                         exit(1);  
                 } else {  
                         dv.dv_actlvl = ss.ss_actlabel.lt_level;  
                         dv.dv_actcmp = ss.ss_actlabel.lt_compart;  
                         if (!sock_multi) {  
                                 dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;  
                                 dv.dv_valcmp = dv.dv_actcmp;  
                         } else {  
                                 dv.dv_minlvl = ss.ss_minlabel.lt_level;  
                                 dv.dv_maxlvl = ss.ss_maxlabel.lt_level;  
                                 dv.dv_valcmp = ss.ss_maxlabel.lt_compart;  
                         }  
                         dv.dv_devflg = 0;  
                 }  
 #else /* SO_SEC_MULTI */                /* 7.0 code */  
                 if (getsockopt(0, SOL_SOCKET, SO_SECURITY,  
                                 (char *)&ss, &szss) >= 0) {  
                         dv.dv_actlvl = ss.ss_slevel;  
                         dv.dv_actcmp = ss.ss_compart;  
                         dv.dv_minlvl = ss.ss_minlvl;  
                         dv.dv_maxlvl = ss.ss_maxlvl;  
                         dv.dv_valcmp = ss.ss_maxcmp;  
                 }  
 #endif /* SO_SEC_MULTI */  
156          }          }
157  #endif  /* _SC_CRAY_SECURE_SYS */      }
158    
159          openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);    if (argc != optind)
160          fromlen = sizeof (from);      {
161          if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {        fprintf (stderr, "telnetd: junk arguments in the command line\n");
162                  fprintf(stderr, "%s: ", progname);        exit (1);
163                  perror("getpeername");      }
164                  _exit(1);    
165          }    openlog ("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
166          if (keepalive &&    telnetd_setup (0);
167              setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,    return telnetd_run ();
168                          (char *)&on, sizeof (on)) < 0) {  }
                 syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");  
         }  
169    
170  #if     defined(IPPROTO_IP) && defined(IP_TOS)  void
171          {  parse_linemode (char *str)
172  # if    defined(HAS_GETTOS)  {
173                  struct tosent *tp;    if (!str)
174                  if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))      alwayslinemode = 1;
175                          tos = tp->t_tos;    else if (strcmp (str, "nokludge") == 0)
176  # endif      lmodetype = NO_AUTOKLUDGE;
177                  if (tos < 0)    else
178                          tos = 020;      /* Low Delay bit */      fprintf (stderr,
179                  if (tos               "telnetd: invalid argument to --linemode\n");
180                     && (setsockopt(0, IPPROTO_IP, IP_TOS,  }
                                   (char *)&tos, sizeof(tos)) < 0)  
                    && (errno != ENOPROTOOPT) )  
                         syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");  
         }  
 #endif  /* defined(IPPROTO_IP) && defined(IP_TOS) */  
         net = 0;  
         doit(&from);  
         /* NOTREACHED */  
 }  /* end of main */  
181    
182  void  void
183  usage()  parse_authmode (char *str)
184  {  {
185          fprintf(stderr, "Usage: telnetd");    if (strcasecmp (str, "none") == 0)
186  #ifdef  AUTHENTICATION      auth_level = 0;
187          fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t");    else if (strcasecmp (str, "other") == 0)
188  #endif      auth_level = AUTH_OTHER;
189  #ifdef BFTPDAEMON    else if (strcasecmp (str, "user") == 0)
190          fprintf(stderr, " [-B]");      auth_level = AUTH_USER;
191  #endif    else if (strcasecmp (str, "valid") == 0)
192          fprintf(stderr, " [-debug]");      auth_level = AUTH_VALID;
193  #ifdef DIAGNOSTICS    else if (strcasecmp (str, "off") == 0)
194          fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");      auth_level = -1;
195  #endif    else
196  #ifdef  AUTHENTICATION      fprintf (stderr,
197          fprintf(stderr, " [-edebug]");               "telnetd: unknown authorization level for -a\n");
 #endif  
         fprintf(stderr, " [-h]");  
 #if     defined(CRAY) && defined(NEWINIT)  
         fprintf(stderr, " [-Iinitid]");  
 #endif  
 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)  
         fprintf(stderr, " [-k]");  
 #endif  
 #ifdef LINEMODE  
         fprintf(stderr, " [-l]");  
 #endif  
         fprintf(stderr, " [-n]");  
 #ifdef  CRAY  
         fprintf(stderr, " [-r[lowpty]-[highpty]]");  
 #endif  
         fprintf(stderr, "\n\t");  
 #ifdef  SecurID  
         fprintf(stderr, " [-s]");  
 #endif  
 #ifdef  HAS_GETTOS  
         fprintf(stderr, " [-S tos]");  
 #endif  
 #ifdef  AUTHENTICATION  
         fprintf(stderr, " [-X auth-type]");  
 #endif  
         fprintf(stderr, " [-u utmp_hostname_length] [-U]");  
         fprintf(stderr, " [port]\n");  
         exit(1);  
198  }  }
199    
200  /*  static struct {
201   * getterminaltype    char *name;
202   *    int modnum;
203   *      Ask the other end to send along its terminal type and speed.  } debug_mode[debug_max_mode] = {
204   * Output is the variable terminaltype filled in.    "options", debug_options,
205   */    "report",  debug_report,
206  static unsigned char ttytype_sbbuf[] = {    "netdata", debug_net_data,
207          IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE    "ptydata", debug_pty_data,
208      "auth", debug_auth,
209  };  };
210    
211  int  void
212  getterminaltype(char *name)  parse_debug_level (char *str)
213  {  {
214      int retval = -1;    int i;
215      void _gettermname();    char *tok;
216    
217      settimer(baseline);    if (!str)
218  #if     defined(AUTHENTICATION)      {
219      /*        for (i = 0; i < debug_max_mode; i++)
220       * Handle the Authentication option before we do anything else.          debug_level[ debug_mode[i].modnum ] = MAX_DEBUG_LEVEL;
221       */        return;
     send_do(TELOPT_AUTHENTICATION, 1);  
     while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))  
         ttloop();  
     if (his_state_is_will(TELOPT_AUTHENTICATION)) {  
         retval = auth_wait(name);  
222      }      }
223  #endif    
224      for (tok = strtok (str, ","); tok; tok = strtok (NULL, ","))
225        {
226          int length, level;
227          char *p;
228    
229  #ifdef  ENCRYPTION        if (strcmp (tok, "tcp") == 0)
230      send_will(TELOPT_ENCRYPT, 1);          {
231  #endif  /* ENCRYPTION */            debug_tcp = 1;
232      send_do(TELOPT_TTYPE, 1);            continue;
233      send_do(TELOPT_TSPEED, 1);          }
     send_do(TELOPT_XDISPLOC, 1);  
     send_do(TELOPT_NEW_ENVIRON, 1);  
     send_do(TELOPT_OLD_ENVIRON, 1);  
     while (  
 #ifdef  ENCRYPTION  
            his_do_dont_is_changing(TELOPT_ENCRYPT) ||  
 #endif  /* ENCRYPTION */  
            his_will_wont_is_changing(TELOPT_TTYPE) ||  
            his_will_wont_is_changing(TELOPT_TSPEED) ||  
            his_will_wont_is_changing(TELOPT_XDISPLOC) ||  
            his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||  
            his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {  
         ttloop();  
     }  
 #ifdef  ENCRYPTION  
     /*  
      * Wait for the negotiation of what type of encryption we can  
      * send with.  If autoencrypt is not set, this will just return.  
      */  
     if (his_state_is_will(TELOPT_ENCRYPT)) {  
         encrypt_wait();  
     }  
 #endif  /* ENCRYPTION */  
     if (his_state_is_will(TELOPT_TSPEED)) {  
         static unsigned char sb[] =  
                         { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };  
   
         memmove(nfrontp, sb, sizeof sb);  
         nfrontp += sizeof sb;  
         DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););  
     }  
     if (his_state_is_will(TELOPT_XDISPLOC)) {  
         static unsigned char sb[] =  
                         { IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };  
   
         memmove(nfrontp, sb, sizeof sb);  
         nfrontp += sizeof sb;  
         DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););  
     }  
     if (his_state_is_will(TELOPT_NEW_ENVIRON)) {  
         static unsigned char sb[] =  
                         { IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };  
   
         memmove(nfrontp, sb, sizeof sb);  
         nfrontp += sizeof sb;  
         DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););  
     }  
     else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {  
         static unsigned char sb[] =  
                         { IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };  
   
         memmove(nfrontp, sb, sizeof sb);  
         nfrontp += sizeof sb;  
         DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););  
     }  
     if (his_state_is_will(TELOPT_TTYPE)) {  
234    
235          memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);        p = strchr (tok, '=');
236          nfrontp += sizeof ttytype_sbbuf;        if (p)
237          DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,          {
238                                          sizeof ttytype_sbbuf - 2););            length = p - tok;
239      }            level  = strtoul (p+1, NULL, 0);
240      if (his_state_is_will(TELOPT_TSPEED)) {          }
241          while (sequenceIs(tspeedsubopt, baseline))        else
242              ttloop();          {
243      }            length = strlen (tok);
244      if (his_state_is_will(TELOPT_XDISPLOC)) {            level  = MAX_DEBUG_LEVEL;
245          while (sequenceIs(xdisplocsubopt, baseline))          }
             ttloop();  
     }  
     if (his_state_is_will(TELOPT_NEW_ENVIRON)) {  
         while (sequenceIs(environsubopt, baseline))  
             ttloop();  
     }  
     if (his_state_is_will(TELOPT_OLD_ENVIRON)) {  
         while (sequenceIs(oenvironsubopt, baseline))  
             ttloop();  
     }  
     if (his_state_is_will(TELOPT_TTYPE)) {  
         char first[256], last[256];  
246    
247          while (sequenceIs(ttypesubopt, baseline))        for (i = 0; i < debug_max_mode; i++)
248              ttloop();          if (strncmp (debug_mode[i].name, tok, length) == 0)
249              {
250                debug_level[ debug_mode[i].modnum ] = level;
251                break;
252              }
253    
254          /*        if (i == debug_max_mode)
255           * If the other side has already disabled the option, then          fprintf (stderr, "telnetd: unknown debug mode: %s", tok);
          * we have to just go with what we (might) have already gotten.  
          */  
         if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {  
             (void) strncpy(first, terminaltype, sizeof(first));  
             for(;;) {  
                 /*  
                  * Save the unknown name, and request the next name.  
                  */  
                 (void) strncpy(last, terminaltype, sizeof(last));  
                 _gettermname();  
                 if (terminaltypeok(terminaltype))  
                     break;  
                 if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||  
                     his_state_is_wont(TELOPT_TTYPE)) {  
                     /*  
                      * We've hit the end.  If this is the same as  
                      * the first name, just go with it.  
                      */  
                     if (strncmp(first, terminaltype, sizeof(first)) == 0)  
                         break;  
                     /*  
                      * Get the terminal name one more time, so that  
                      * RFC1091 compliant telnets will cycle back to  
                      * the start of the list.  
                      */  
                      _gettermname();  
                     if (strncmp(first, terminaltype, sizeof(first)) != 0)  
                         (void) strncpy(terminaltype, first, sizeof(first));  
                     break;  
                 }  
             }  
         }  
256      }      }
     return(retval);  
 }  /* end of getterminaltype */  
   
 void  
 _gettermname()  
 {  
     /*  
      * If the client turned off the option,  
      * we can't send another request, so we  
      * just return.  
      */  
     if (his_state_is_wont(TELOPT_TTYPE))  
         return;  
     settimer(baseline);  
     memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);  
     nfrontp += sizeof ttytype_sbbuf;  
     DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,  
                                         sizeof ttytype_sbbuf - 2););  
     while (sequenceIs(ttypesubopt, baseline))  
         ttloop();  
257  }  }
258    
 int  
 terminaltypeok(char *s)  
 {  
     char buf[1024];  
259    
260      if (terminaltype == NULL)  typedef unsigned int ip_addr_t; /*FIXME*/
         return(1);  
261    
262      /*  void
263       * tgetent() will return 1 if the type is known, and  telnetd_setup (int fd)
      * 0 if it is not known.  If it returns -1, it couldn't  
      * open the database.  But if we can't open the database,  
      * it won't help to say we failed, because we won't be  
      * able to verify anything else.  So, we treat -1 like 1.  
      */  
     if (tgetent(buf, s) == 0)  
         return(0);  
     return(1);  
 }  
   
 #ifndef MAXHOSTNAMELEN  
 #define MAXHOSTNAMELEN 64  
 #endif  /* MAXHOSTNAMELEN */  
   
 char *hostname;  
 char host_name[MAXHOSTNAMELEN];  
 char remote_host_name[MAXHOSTNAMELEN];  
   
 extern void telnet P((int, int, char *));  
   
 /*  
  * Get a pty, scan input lines.  
  */  
 int  
 doit (struct sockaddr_in *who)  
264  {  {
265    char *host, *inet_ntoa();    struct sockaddr_in saddr;
266    int t;    int true = 1;
267      int len;
268    struct hostent *hp;    struct hostent *hp;
269      char user_name[256]; /*FIXME*/
270    int level;    int level;
271    char user_name[256];  
272      len = sizeof (saddr);
273      if (getpeername(fd, (struct sockaddr *)&saddr, &len) < 0)
274        {
275          syslog (LOG_ERR, "getpeername: %m");
276          exit (1);
277        }
278    
279      syslog (LOG_INFO, "Connect from %s", inet_ntoa (saddr.sin_addr.s_addr));
280      
281      hp = gethostbyaddr ((char*)&saddr.sin_addr.s_addr,
282                          sizeof (saddr.sin_addr.s_addr), AF_INET);
283      if (reverse_lookup)
284        {
285          char **ap;
286          
287          if (!hp)
288            {
289              syslog (LOG_AUTH|LOG_NOTICE,
290                      "Can't resolve %s: %s",
291                      inet_ntoa (saddr.sin_addr.s_addr),
292                      hstrerror (h_errno));
293              fatal (fd, "Cannot resolve address.");
294            }
295    
296          remote_hostname = xstrdup (hp->h_name);
297    
298          hp = gethostbyname (remote_hostname);
299          if (!hp)
300            {
301              syslog (LOG_AUTH|LOG_NOTICE,
302                      "Forward resolve of %s failed: %s",
303                      remote_hostname, hstrerror (h_errno));
304              fatal (fd, "Cannot resolve address.");
305            }
306    
307    /* get name of connected client */        for (ap = hp->h_addr_list; *ap; ap++)
308    hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),          if (*(ip_addr_t*)ap == saddr.sin_addr.s_addr)
309                       who->sin_family);            break;
310    
311    if (hp == NULL && registerd_host_only)        if (ap == NULL)
312      fatal(net, "Couldn't resolve your address into a host name.\r\n\          {
313  Please contact your net administrator");            syslog (LOG_AUTH|LOG_NOTICE,
314    else if (hp)                    "None of addresses of %s matched %s",
315      host = hp->h_name;                    remote_hostname,
316                      inet_ntoa (saddr.sin_addr.s_addr));
317              exit (0);
318            }
319        }
320    else    else
321      host = inet_ntoa (who->sin_addr);      {
322          if (hp)
323            remote_hostname = xstrdup (hp->h_name);
324          else
325            remote_hostname = xstrdup (inet_ntoa (saddr.sin_addr.s_addr));
326        }
327      
328      /* Set socket options */
329    
330      if (keepalive
331          && setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE,
332                         (char *)&true, sizeof (true)) < 0)
333        syslog (LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
334    
335    /*    if (debug_tcp
336     * We must make a copy because Kerberos is probably going        && setsockopt (fd, SOL_SOCKET, SO_DEBUG,
337     * to also do a gethost* and overwrite the static data...                       (char *)&true, sizeof (true)) < 0)
338     */      syslog (LOG_WARNING, "setsockopt (SO_DEBUG): %m");
   strncpy (remote_host_name, host, sizeof (remote_host_name)-1);  
   remote_host_name[sizeof (remote_host_name)-1] = 0;  
   host = remote_host_name;  
339    
340    gethostname (host_name, sizeof (host_name));    net = fd;
   hostname = host_name;  
341    
342      local_hostname = localhost ();
343  #if defined(AUTHENTICATION) || defined(ENCRYPTION)  #if defined(AUTHENTICATION) || defined(ENCRYPTION)
344    auth_encrypt_init (hostname, host, "TELNETD", 1);    auth_encrypt_init (remote_hostname, local_hostname, "TELNETD", 1);
345  #endif  #endif
346    
347    init_env ();    io_setup ();
348        
349      /*FIXME: KLUDGE*/
350    #ifdef  ENCRYPTION
351      nclearto = 0;
352    #endif  /* ENCRYPTION */
353      /**/
354                                      
355    /* get terminal type. */    /* get terminal type. */
356    *user_name = 0;    *user_name = 0;
357    level = getterminaltype (user_name);    level = getterminaltype (&user_name);
358    setenv ("TERM", terminaltype ? terminaltype : "network", 1);    setenv ("TERM", terminaltype ? terminaltype : "network", 1);
359    
360    pty = startslave (host, level, user_name);    pty = startslave (remote_hostname, level, user_name);
   telnet(net, pty, host);  
 }  /* end of doit */  
361    
362  #if     defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50)  #ifndef HAVE_STREAMSPTY
363  int    /* Turn on packet mode */
364  Xterm_output(char **ibufp, char **obuf, int *icountp, int *ocount)    ioctl (pty, TIOCPKT, (char *)&true);
365  {  #endif
366          int ret;    ioctl (pty, FIONBIO, (char *)&true);
367          ret = term_output(*ibufp, obuf, *icountp, ocount);    ioctl (net, FIONBIO, (char *)&true);
368          *ibufp += *icountp;    
369          *icountp = 0;  #if defined(SO_OOBINLINE)
370          return(ret);    setsockopt (net, SOL_SOCKET, SO_OOBINLINE, (char *)&true, sizeof true);
371    #endif  
372    
373    #ifdef SIGTSTP
374      signal (SIGTSTP, SIG_IGN);
375    #endif
376    #ifdef  SIGTTOU
377      signal (SIGTTOU, SIG_IGN);
378    #endif
379    
380      signal (SIGCHLD, cleanup);
381  }  }
 #define term_output     Xterm_output  
 #endif  /* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */  
382    
383  /*  int
384   * Main loop.  Select from pty and network, and  telnetd_run ()
  * hand data to telnet receiver finite state machine.  
  */  
 void  
 telnet (int f, int p, char *host)  
385  {  {
   int on = 1;  
 #define TABBUFSIZ       512  
   char  defent[TABBUFSIZ];  
   char  defstrs[TABBUFSIZ];  
 #undef  TABBUFSIZ  
 #undef HE /* Make sure its not defined as a macro. */  
   char *HE;  
   char *HN;  
   char *IM;  
   void netflush();  
386    int nfd;    int nfd;
387      
   /*  
    * Initialize the slc mapping table.  
    */  
388    get_slc_defaults ();    get_slc_defaults ();
389    
390    /*    if (my_state_is_wont(TELOPT_SGA))
391     * Do some tests where it is desireable to wait for a response.      send_will(TELOPT_SGA, 1);
    * Rather than doing them slowly, one at a time, do them all  
    * at once.  
    */  
   if (my_state_is_wont (TELOPT_SGA))  
     send_will (TELOPT_SGA, 1);  
   /*  
    * Is the client side a 4.2 (NOT 4.3) system?  We need to know this  
    * because 4.2 clients are unable to deal with TCP urgent data.  
    *  
    * To find out, we send out a "DO ECHO".  If the remote system  
    * answers "WILL ECHO" it is probably a 4.2 client, and we note  
    * that fact ("WILL ECHO" ==> that the client will echo what  
    * WE, the server, sends it; it does NOT mean that the client will  
    * echo the terminal input).  
    */  
   send_do (TELOPT_ECHO, 1);  
392    
393  #ifdef  LINEMODE    /* Old BSD 4.2 clients are unable to deal with TCP out-of-band data.
394         To find out, we send out a "DO ECHO". If the remote side is
395         a BSD 4.2 it will answer "WILL ECHO". See the response processing
396         below. */
397      send_do(TELOPT_ECHO, 1);
398    if (his_state_is_wont (TELOPT_LINEMODE))    if (his_state_is_wont (TELOPT_LINEMODE))
399      {      {
400        /* Query the peer for linemode support by trying to negotiate        /* Query the peer for linemode support by trying to negotiate
401         * the linemode option.           the linemode option. */
        */  
402        linemode = 0;        linemode = 0;
403        editmode = 0;        editmode = 0;
404        send_do (TELOPT_LINEMODE, 1);  /* send do linemode */        send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
405      }      }
 #endif  /* LINEMODE */  
406    
   /*  
    * Send along a couple of other options that we wish to negotiate.  
    */  
407    send_do (TELOPT_NAWS, 1);    send_do (TELOPT_NAWS, 1);
408    send_will (TELOPT_STATUS, 1);    send_will (TELOPT_STATUS, 1);
409    flowmode = 1;         /* default flow control state */    flowmode = 1;         /* default flow control state */
410    restartany = -1;      /* uninitialized... */    restartany = -1;      /* uninitialized... */
411    send_do (TELOPT_LFLOW, 1);    send_do (TELOPT_LFLOW, 1);
412    
413    /*    /* Wait for a response from the DO ECHO. Reportedly, some broken
414     * Spin, waiting for a response from the DO ECHO.  However,       clients might not respond to it. To work around this, we wait
415     * some REALLY DUMB telnets out there might not respond       for a response to NAWS, which should have been processed after
416     * to the DO ECHO.  So, we spin looking for NAWS, (most dumb       DO ECHO (most dumb telnets respond with WONT for a DO that
417     * telnets so far seem to respond with WONT for a DO that       they don't understand).  
418     * they don't understand...) because by the time we get the         On the other hand, the client might have sent WILL NAWS as
419     * response, it will already have processed the DO ECHO.       part of its startup code, in this case it surely should have
420     * Kludge upon kludge.       answered our DO ECHO, so the second loop is waiting for
421     */       the ECHO to settle down.  */
422    while (his_will_wont_is_changing (TELOPT_NAWS))    ttloop (his_will_wont_is_changing (TELOPT_NAWS));
423      ttloop ();  
   
   /*  
    * But...  
    * The client might have sent a WILL NAWS as part of its  
    * startup code; if so, we'll be here before we get the  
    * response to the DO ECHO.  We'll make the assumption  
    * that any implementation that understands about NAWS  
    * is a modern enough implementation that it will respond  
    * to our DO ECHO request; hence we'll do another spin  
    * waiting for the ECHO option to settle down, which is  
    * what we wanted to do in the first place...  
    */  
424    if (his_want_state_is_will (TELOPT_ECHO)    if (his_want_state_is_will (TELOPT_ECHO)
425        && his_state_is_will(TELOPT_NAWS))        && his_state_is_will (TELOPT_NAWS))
426      {      ttloop (his_will_wont_is_changing (TELOPT_ECHO));
       while (his_will_wont_is_changing (TELOPT_ECHO))  
         ttloop ();  
     }  
427    
428    /*    /* If the remote client is badly broken and did not respond to our
429     * On the off chance that the telnet client is broken and does not       DO ECHO, we simulate the receipt of a will echo. This will also
430     * respond to the DO ECHO we sent, (after all, we did send the       send a WONT ECHO to the client, since we assume that the client
431     * DO NAWS negotiation after the DO ECHO, and we won't get here       failed to respond because it believes that it is already in DO ECHO
432     * until a response to the DO NAWS comes back) simulate the       mode, which we do not want. */
433     * receipt of a will echo.  This will also send a WONT ECHO    
    * to the client, since we assume that the client failed to  
    * respond because it believes that it is already in DO ECHO  
    * mode, which we do not want.  
    */  
434    if (his_want_state_is_will (TELOPT_ECHO))    if (his_want_state_is_will (TELOPT_ECHO))
435      {      {
436        DIAG (TD_OPTIONS,        DEBUG(debug_options, 1, debug_output_data ("td: simulating recv\r\n"));
             {sprintf (nfrontp, "td: simulating recv\r\n");  
             nfrontp += strlen (nfrontp);});  
437        willoption (TELOPT_ECHO);        willoption (TELOPT_ECHO);
438      }      }
439    
440    /*    /* Turn on our echo */
    * Finally, to clean things up, we turn on our echo.  This  
    * will break stupid 4.2 telnets out of local terminal echo.  
    */  
     
441    if (my_state_is_wont (TELOPT_ECHO))    if (my_state_is_wont (TELOPT_ECHO))
442      send_will (TELOPT_ECHO, 1);      send_will (TELOPT_ECHO, 1);
443    
444  #ifndef HAVE_STREAMSPTY    /* Continuing line mode support.  If client does not support
445    /*       real linemode, attempt to negotiate kludge linemode by sending
446     * Turn on packet mode       the do timing mark sequence. */
    */  
   ioctl(p, TIOCPKT, (char *)&on);  
 #endif  
   
 #if     defined(LINEMODE) && defined(KLUDGELINEMODE)  
   /*  
    * Continuing line mode support.  If client does not support  
    * real linemode, attempt to negotiate kludge linemode by sending  
    * the do timing mark sequence.  
    */  
447    if (lmodetype < REAL_LINEMODE)    if (lmodetype < REAL_LINEMODE)
448      send_do (TELOPT_TM, 1);      send_do (TELOPT_TM, 1);
 #endif  /* defined(LINEMODE) && defined(KLUDGELINEMODE) */  
449    
450    /*    /* Pick up anything received during the negotiations */
    * Call telrcv() once to pick up anything received during  
    * terminal type negotiation, 4.2/4.3 determination, and  
    * linemode negotiation.  
    */  
451    telrcv ();    telrcv ();
452    
453    ioctl (f, FIONBIO, (char *)&on);    /*  if (hostinfo && !getenv("USER"))
454    ioctl (p, FIONBIO, (char *)&on);      print_hostinfo ();
455  #if     defined(CRAY2) && defined(UNICOS5)      */
456    init_termdriver (f, p, interrupts, sendbrk);      
 #endif  
   
 #if     defined(SO_OOBINLINE)  
   setsockopt (net, SOL_SOCKET, SO_OOBINLINE, (char *)&on, sizeof on);  
 #endif  /* defined(SO_OOBINLINE) */  
   
 #ifdef  SIGTSTP  
   signal (SIGTSTP, SIG_IGN);  
 #endif  
 #ifdef  SIGTTOU  
   /*  
    * Ignoring SIGTTOU keeps the kernel from blocking us  
    * in ttioct() in /sys/tty.c.  
    */  
   signal (SIGTTOU, SIG_IGN);  
 #endif  
   
   signal (SIGCHLD, cleanup);  
   
 #if     defined(CRAY2) && defined(UNICOS5)  
   /*  
    * Cray-2 will send a signal when pty modes are changed by slave  
    * side.  Set up signal handler now.  
    */  
   if ((int)signal (SIGUSR1, termstat) < 0)  
     perror ("signal");  
   else if (ioctl (p, TCSIGME, (char *)SIGUSR1) < 0)  
     perror ("ioctl:TCSIGME");  
   /*  
    * Make processing loop check terminal characteristics early on.  
    */  
   termstat ();  
 #endif  
   
 #ifdef  TIOCNOTTY  
   {  
     register int t;  
     t = open(PATH_TTY, O_RDWR);  
     if (t >= 0)  
       {  
         ioctl (t, TIOCNOTTY, (char *)0);  
         close (t);  
       }  
   }  
 #endif  
   
 #if     defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY)  
   setsid ();  
   ioctl (p, TIOCSCTTY, 0);  
 #endif  
   
   /*  
    * Show banner that getty never gave.  
    *  
    * We put the banner in the pty input buffer.  This way, it  
    * gets carriage return null processing, etc., just like all  
    * other pty --> client data.  
    */  
   
 #if     !defined(CRAY) || !defined(NEWINIT)  
   if (getenv ("USER"))  
     hostinfo = 0;  
 #endif  
   
   if (getent (defent, "default") == 1)  
     {  
       char *getstr ();  
       char *cp=defstrs;  
                   
       HE = getstr ("he", &cp);  
       HN = getstr ("hn", &cp);  
       IM = getstr ("im", &cp);  
       if (HN && *HN)  
         strcpy (host_name, HN);  
       if (IM == 0)  
         IM = "";  
     }  
   else  
     {  
 #ifdef HAVE_UNAME  
       struct utsname u;  
 #endif  
   
 #ifdef DEFAULT_IM  
       IM = DEFAULT_IM;  
 #else  
       IM = 0;  
 #ifdef HAVE_UNAME  
       if (uname (&u) == 0)  
         {  
           IM = malloc (strlen (UNAME_IM_PREFIX)  
                        + strlen (u.sysname)  
                        + 1 + strlen (u.release)  
                        + strlen (UNAME_IM_SUFFIX) + 1);  
           if (IM)  
             sprintf (IM, "%s%s %s%s",  
                      UNAME_IM_PREFIX,  
                      u.sysname, u.release,  
                      UNAME_IM_SUFFIX);  
         }  
 #endif /* HAVE_UNAME */  
       if (! IM)  
         IM = "\r\n\nUNIX (%h) (%t)\r\n\n";  
 #endif /* DEFAULT_IM */  
   
       HE = 0;  
     }  
   
   edithost (HE, host_name);  
   if (hostinfo && *IM)  
     putf (IM, ptyibuf2);  
   
   if (pcc)  
     strncat (ptyibuf2, ptyip, pcc+1);  
   ptyip = ptyibuf2;  
   pcc = strlen (ptyip);  
 #ifdef  LINEMODE  
   /*  
    * Last check to make sure all our states are correct.  
    */  
457    init_termbuf ();    init_termbuf ();
458    localstat ();    localstat ();
 #endif  /* LINEMODE */  
459    
460    DIAG (TD_REPORT,    DEBUG(debug_report, 1,
461          {sprintf (nfrontp, "td: Entering processing loop\r\n");          debug_output_data ("td: Entering processing loop\r\n"));
462          nfrontp += strlen (nfrontp);});  
463      nfd = ((net > pty) ? net : pty) + 1;
464    
   nfd = ((f > p) ? f : p) + 1;  
465    for (;;)    for (;;)
466      {      {
467        fd_set ibits, obits, xbits;        fd_set ibits, obits, xbits;
468        register int c;        register int c;
469    
470        if (ncc < 0 && pcc < 0)        if (net_input_level () < 0 && pty_input_level () < 0)
471          break;          break;
472    
473  #if     defined(CRAY2) && defined(UNICOS5)        FD_ZERO(&ibits);
474        if (needtermstat)        FD_ZERO(&obits);
475          _termstat ();        FD_ZERO(&xbits);
476  #endif  /* defined(CRAY2) && defined(UNICOS5) */  
477        FD_ZERO (&ibits);        /* Never look for input if there's still stuff in the corresponding
478        FD_ZERO (&obits);           output buffer */
479        FD_ZERO (&xbits);        if (net_output_level () || pty_input_level () > 0)  
480        /*          FD_SET (net, &obits);
        * Never look for input if there's still  
        * stuff in the corresponding output buffer  
        */  
       if (nfrontp - nbackp || pcc > 0)  
         FD_SET (f, &obits);  
481        else        else
482          FD_SET(p, &ibits);          FD_SET(pty, &ibits);
483        
484        if (pfrontp - pbackp || ncc > 0)        if (pty_output_level () || net_input_level () > 0)
485          FD_SET (p, &obits);          FD_SET(pty, &obits);
486        else        else
487          FD_SET (f, &ibits);          FD_SET(net, &ibits);
488                            
489        if (!SYNCHing)        if (!SYNCHing)
490          FD_SET (f, &xbits);          FD_SET(net, &xbits);
491    
492        c = select (nfd, &ibits, &obits, &xbits, NULL);        if ((c = select (nfd, &ibits, &obits, &xbits, NULL)) <= 0)
       if (c == -1)  
493          {          {
494            if (errno != EINTR)            if (c == -1 && errno == EINTR)
495              sleep(5);              continue;
496              sleep(5);
497            continue;            continue;
498          }          }
499    
500        /*        if (FD_ISSET(net, &xbits))
        * Any urgent data?  
        */  
       if (FD_ISSET (net, &xbits))  
501          SYNCHing = 1;          SYNCHing = 1;
502    
503          if (FD_ISSET(net, &ibits))
       /*  
        * Something to read from the network...  
        */  
       if (FD_ISSET (net, &ibits))  
504          {          {
505  #if     !defined(SO_OOBINLINE)            /* Something to read from the network... */
506            /*            /*FIXME: handle  !defined(SO_OOBINLINE) */
507             * In 4.2 (and 4.3 beta) systems, the            net_read ();
            * OOB indication and data handling in the kernel  
            * is such that if two separate TCP Urgent requests  
            * come in, one byte of TCP data will be overlaid.  
            * This is fatal for Telnet, but we try to live  
            * with it.  
            *  
            * In addition, in 4.2 (and...), a special protocol  
            * is needed to pick up the TCP Urgent data in  
            * the correct sequence.  
            *  
            * What we do is:  if we think we are in urgent  
            * mode, we look to see if we are "at the mark".  
            * If we are, we do an OOB receive.  If we run  
            * this twice, we will do the OOB receive twice,  
            * but the second will fail, since the second  
            * time we were "at the mark", but there wasn't  
            * any data there (the kernel doesn't reset  
            * "at the mark" until we do a normal read).  
            * Once we've read the OOB data, we go ahead  
            * and do normal reads.  
            *  
            * There is also another problem, which is that  
            * since the OOB byte we read doesn't put us  
            * out of OOB state, and since that byte is most  
            * likely the TELNET DM (data mark), we would  
            * stay in the TELNET SYNCH (SYNCHing) state.  
            * So, clocks to the rescue.  If we've "just"  
            * received a DM, then we test for the  
            * presence of OOB data when the receive OOB  
            * fails (and AFTER we did the normal mode read  
            * to clear "at the mark").  
            */  
           if (SYNCHing)  
             {  
               int atmark;  
   
               ioctl (net, SIOCATMARK, (char *)&atmark);  
               if (atmark)  
                 {  
                   ncc = recv (net, netibuf, sizeof (netibuf), MSG_OOB);  
                   if (ncc == -1 && errno == EINVAL)  
                     {  
                       ncc = read (net, netibuf, sizeof (netibuf));  
                       if (sequenceIs (didnetreceive, gotDM))  
                         SYNCHing = stilloob (net);  
                     }  
                 }  
               else  
                 {  
                   ncc = read (net, netibuf, sizeof (netibuf));  
                 }  
             }  
           else  
             {  
               ncc = read (net, netibuf, sizeof (netibuf));  
             }  
           settimer (didnetreceive);  
 #else   /* !defined(SO_OOBINLINE)) */  
           ncc = read (net, netibuf, sizeof (netibuf));  
 #endif  /* !defined(SO_OOBINLINE)) */  
           if (ncc < 0 && errno == EWOULDBLOCK)  
             ncc = 0;  
           else  
             {  
               if (ncc <= 0)  
                 break;  
               netip = netibuf;  
             }  
           DIAG ((TD_REPORT | TD_NETDATA),  
                 {sprintf (nfrontp, "td: netread %d chars\r\n", ncc);  
                 nfrontp += strlen (nfrontp);});  
           DIAG (TD_NETDATA, printdata ("nd", netip, ncc));  
508          }          }
509    
510        /*        if (FD_ISSET(pty, &ibits))
        * Something to read from the pty...  
        */  
       if (FD_ISSET (p, &ibits))  
511          {          {
512            pcc = readstream (p, ptyibuf, BUFSIZ);            /* Something to read from the pty... */
513            /*            if (pty_read () < 0)
            * On some systems, if we try to read something  
            * off the master side before the slave side is  
            * opened, we get EIO.  
            */  
           if (pcc < 0 &&  
               (errno == EWOULDBLOCK  
 #ifdef  EAGAIN  
                || errno == EAGAIN  
 #endif  
                || errno == EIO))  
             pcc = 0;  
           else  if (pcc <= 0)  
514              break;              break;
515              c = pty_get_char (1);
516  #if     !defined(CRAY2) || !defined(UNICOS5)  #if defined(TIOCPKT_IOCTL)
517  #if defined (LINEMODE) && defined (TIOCPKT_IOCTL)            if (c & TIOCPKT_IOCTL)
           /*  
            * If ioctl from pty, pass it through net  
            */  
           if (ptyibuf[0] & TIOCPKT_IOCTL)  
518              {              {
519                copy_termbuf(ptyibuf+1, pcc-1);                pty_get_char (0);
520                localstat();                copy_termbuf ();
521                pcc = 1;                localstat ();
522              }              }
 #endif  /* LINEMODE && TIOCPKT_IOCTL */  
           if (ptyibuf[0] & TIOCPKT_FLUSHWRITE)  
             {  
               netclear ();      /* clear buffer back */  
 #ifndef NO_URGENT  
               /*  
                * There are client telnets on some  
                * operating systems get screwed up  
                * royally if we send them urgent  
                * mode data.  
                */  
               *nfrontp++ = IAC;  
               *nfrontp++ = DM;  
               neturg = nfrontp-1; /* off by one XXX */  
               DIAG(TD_OPTIONS,  
                    printoption ("td: send IAC", DM));  
   
523  #endif  #endif
524              if (c & TIOCPKT_FLUSHWRITE)
525                {
526                  static char flushdata[] = { IAC, DM };
527                  pty_get_char (0);
528                  netclear();       /* clear buffer back */
529                  net_output_datalen (flushdata, sizeof (flushdata));
530                  set_neturg ();
531                  DEBUG(debug_options, 1, printoption("td: send IAC", DM));
532              }              }
533    
534            if (his_state_is_will (TELOPT_LFLOW)            if (his_state_is_will(TELOPT_LFLOW)
535                && (ptyibuf[0] &(TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)))                && (c & (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP)))
536              {              {
537                int newflow = ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;                int newflow = c & TIOCPKT_DOSTOP ? 1 : 0;
538                if (newflow != flowmode)                if (newflow != flowmode)
539                  {                  {
540                    flowmode = newflow;                    net_output_data ("%c%c%c%c%c%c",
541                    sprintf(nfrontp,                                     IAC, SB, TELOPT_LFLOW,
542                            "%c%c%c%c%c%c",                                     flowmode ? LFLOW_ON
543                            IAC, SB, TELOPT_LFLOW,                                     : LFLOW_OFF,
544                            flowmode ? LFLOW_ON                                     IAC, SE);
                           : LFLOW_OFF,  
                           IAC, SE);  
                   nfrontp += 6;  
                   DIAG (TD_OPTIONS,  
                         printsub ('>', (unsigned char *)nfrontp-4, 4););  
545                  }                  }
546                  pty_get_char (0);
547              }              }
548            pcc--;  
           ptyip = ptyibuf+1;  
             
 #else   /* defined(CRAY2) && defined(UNICOS5) */  
           if (!uselinemode)  
             {  
               unpcc = pcc;  
               unptyip = ptyibuf;  
               pcc = term_output (&unptyip, ptyibuf2, &unpcc, BUFSIZ);  
               ptyip = ptyibuf2;  
             }  
           else  
             ptyip = ptyibuf;  
 #endif  /* defined(CRAY2) && defined(UNICOS5) */  
549          }          }
550    
551        while (pcc > 0)        while (pty_input_level () > 0)
552          {          {
553            if ((&netobuf[BUFSIZ] - nfrontp) < 2)            if (net_buffer_is_full ())
554              break;              break;
555            c = *ptyip++ & 0377;            c = pty_get_char (0);
           pcc--;  
556            if (c == IAC)            if (c == IAC)
557              *nfrontp++ = c;              net_output_byte (c);
558  #if     defined(CRAY2) && defined(UNICOS5)            net_output_byte (c);
559            else if (c == '\n' && my_state_is_wont(TELOPT_BINARY) && newmap)            if (c == '\r' && my_state_is_wont (TELOPT_BINARY))
             *nfrontp++ = '\r';  
 #endif  /* defined(CRAY2) && defined(UNICOS5) */  
           *nfrontp++ = c;  
           if (c == '\r' && (my_state_is_wont(TELOPT_BINARY)))  
560              {              {
561                if (pcc > 0 && (*ptyip & 0377) == '\n')                if (pty_input_level () > 0 && pty_get_char (1) == '\n')
562                  {                  net_output_byte (pty_get_char (0));
                   *nfrontp++ = *ptyip++ & 0377;  
                   pcc--;  
                 }  
563                else                else
564                  *nfrontp++ = '\0';                  net_output_byte (0);
565              }              }
566          }          }
 #if     defined(CRAY2) && defined(UNICOS5)  
       /*  
        * If chars were left over from the terminal driver,  
        * note their existence.  
        */  
       if (!uselinemode && unpcc)  
         {  
           pcc = unpcc;  
           unpcc = 0;  
           ptyip = unptyip;  
         }  
 #endif  /* defined(CRAY2) && defined(UNICOS5) */  
567    
568        if (FD_ISSET (f, &obits) && (nfrontp - nbackp) > 0)        if (FD_ISSET(net, &obits) && net_output_level () > 0)
569          netflush ();          netflush ();
570        if (ncc > 0)        if (net_input_level () > 0)
571          telrcv ();          telrcv ();
572        if (FD_ISSET (p, &obits) && (pfrontp - pbackp) > 0)        
573          if (FD_ISSET(pty, &obits) && pty_output_level () > 0)
574          ptyflush ();          ptyflush ();
575      }      }
576    cleanup(0);    cleanup (0);
577  }  /* end of telnet */  }
   
 #ifndef TCSIG  
 # ifdef TIOCSIG  
 #  define TCSIG TIOCSIG  
 # endif  
 #endif  
   
 int flowison = -1;  /* current state of flow: -1 is unknown */  
578    
579  int  void
580  readstream(int p, char *ibuf, int bufsize)  print_hostinfo ()
581  {  {
582  #ifndef HAVE_STREAMSPTY    char *im = NULL;
583    return read (p, ibuf, bufsize);  #ifdef HAVE_UNAME
584  #else    struct utsname u;
   int flags = 0;  
   int ret = 0;  
   struct termios *tsp;  
   struct termio *tp;  
   struct iocblk *ip;  
   char vstop, vstart;  
   int ixon;  
   int newflow;  
   
   strbufc.maxlen = BUFSIZ;  
   strbufc.buf = (char *)ctlbuf;  
   strbufd.maxlen = bufsize-1;  
   strbufd.len = 0;  
   strbufd.buf = ibuf+1;  
   ibuf[0] = 0;  
585        
586    ret = getmsg(p, &strbufc, &strbufd, &flags);    if (uname (&u) == 0)
   if (ret < 0)  /* error of some sort -- probably EAGAIN */  
     return -1;  
   
   if (strbufc.len <= 0 || ctlbuf[0] == M_DATA)  
587      {      {
588        /* data message */        im = malloc (strlen (UNAME_IM_PREFIX)
589        if (strbufd.len > 0) /* real data */                     + strlen (u.sysname)
590          return strbufd.len + 1; /* count header char */                     + 1 + strlen (u.release)
591        else                     + strlen (UNAME_IM_SUFFIX) + 1);
592          {        if (im)
593            /* nothing there */          sprintf (im, "%s%s %s%s",
594            errno = EAGAIN;                   UNAME_IM_PREFIX,
595            return -1;                   u.sysname, u.release,
596          }                   UNAME_IM_SUFFIX);
     }  
   
   /*  
    * It's a control message.  Return 1, to look at the flag we set  
    */  
   
   switch (ctlbuf[0])  
     {  
     case M_FLUSH:  
       if (ibuf[1] & FLUSHW)  
         ibuf[0] = TIOCPKT_FLUSHWRITE;  
       return 1;  
   
     case M_IOCTL:  
       ip = (struct iocblk *) (ibuf+1);  
   
       switch (ip->ioc_cmd)  
         {  
         case TCSETS:  
         case TCSETSW:  
         case TCSETSF:  
           tsp = (struct termios *) (ibuf + 1 + sizeof(struct iocblk));  
           vstop = tsp->c_cc[VSTOP];  
           vstart = tsp->c_cc[VSTART];  
           ixon = tsp->c_iflag & IXON;  
           break;  
             
         case TCSETA:  
         case TCSETAW:  
         case TCSETAF:  
           tp = (struct termio *) (ibuf + 1 + sizeof(struct iocblk));  
           vstop = tp->c_cc[VSTOP];  
           vstart = tp->c_cc[VSTART];  
           ixon = tp->c_iflag & IXON;  
           break;  
   
         default:  
           errno = EAGAIN;  
           return -1;  
         }  
   
       newflow =  (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;  
       if (newflow != flowison)  /* it's a change */  
         {  
           flowison = newflow;  
           ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;  
           return 1;  
         }  
597      }      }
598    #endif /* HAVE_UNAME */
599      if (!im)
600        im = "\r\n\nUNIX (%h) (%t)\r\n\n";
601    
602    /* nothing worth doing anything about */    /*FIXME*/
603    errno = EAGAIN;    DEBUG(debug_pty_data, 1, debug_output_data ("sending %s", im));
604    return -1;    pty_output_datalen (im, strlen (im));
 #endif /* HAVE_STREAMSPTY */  
605  }  }
606    
 /*  
  * Send interrupt to process on other side of pty.  
  * If it is in raw mode, just write NULL;  
  * otherwise, write intr char.  
  */  
607  void  void
608  interrupts()  telnetd_version ()
609  {  {
610          ptyflush();     /* half-hearted */    printf ("telnetd - %s %s\n", inetutils_package, inetutils_version);
611      printf ("Copyright (C) 1998,2001,2002 Free Software Foundation, Inc.\n");
612  #if defined(HAVE_STREAMSPTY) && defined(TIOCSIGNAL)    printf ("%s comes with ABSOLUTELY NO WARRANTY.\n", inetutils_package);
613          /* Streams PTY style ioctl to post a signal */    printf ("You may redistribute copies of %s\n", inetutils_package);
614          {    printf ("under the terms of the GNU General Public License.\n");
615                  int sig = SIGINT;    printf ("For more information about these matters, ");
616                  (void) ioctl(pty, TIOCSIGNAL, &sig);    printf ("see the files named COPYING.\n");
                 (void) ioctl(pty, I_FLUSH, FLUSHR);  
         }  
 #else  
 #ifdef  TCSIG  
         (void) ioctl(pty, TCSIG, (char *)SIGINT);  
 #else   /* TCSIG */  
         init_termbuf();  
         *pfrontp++ = slctab[SLC_IP].sptr ?  
                         (unsigned char)*slctab[SLC_IP].sptr : '\177';  
 #endif  /* TCSIG */  
 #endif  
617  }  }
618    
 /*  
  * Send quit to process on other side of pty.  
  * If it is in raw mode, just write NULL;  
  * otherwise, write quit char.  
  */  
619  void  void
620  sendbrk()  telnetd_license ()
621  {  {
622          ptyflush();     /* half-hearted */    static char license_text[] =
623  #ifdef  TCSIG  "   This program is free software; you can redistribute it and/or modify\n"
624          (void) ioctl(pty, TCSIG, (char *)SIGQUIT);  "   it under the terms of the GNU General Public License as published by\n"
625  #else   /* TCSIG */  "   the Free Software Foundation; either version 2, or (at your option)\n"
626          init_termbuf();  "   any later version.\n"
627          *pfrontp++ = slctab[SLC_ABORT].sptr ?  "\n"
628                          (unsigned char)*slctab[SLC_ABORT].sptr : '\034';  "   This program is distributed in the hope that it will be useful,\n"
629  #endif  /* TCSIG */  "   but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
630    "   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
631    "   GNU General Public License for more details.\n"
632    "\n"
633    "   You should have received a copy of the GNU General Public License\n"
634    "   along with this program; if not, write to the Free Software\n"
635    "   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
636      printf ("%s", license_text);
637  }  }
638    
639  void  void
640  sendsusp()  telnetd_help ()
641  {  {
642  #ifdef  SIGTSTP    printf ("\
643          ptyflush();     /* half-hearted */  Usage: telnetd [OPTION]\n\
644  # ifdef TCSIG  \n\
645          (void) ioctl(pty, TCSIG, (char *)SIGTSTP);  Options are:\n\
646  # else  /* TCSIG */      -a, --authmode AUTHMODE  specify what mode to use for authentication\n\
647          *pfrontp++ = slctab[SLC_SUSP].sptr ?      -D, --debug[=LEVEL]      set debugging level\n\
648                          (unsigned char)*slctab[SLC_SUSP].sptr : '\032';  *   -E, --exec-login STRING  set program to be executed instead of /bin/login\n\
649  # endif /* TCSIG */      -h, --no-hostinfo        do not print host information before login has\n\
650  #endif  /* SIGTSTP */                               been completed\n\
651        -l, --linemode[=MODE]    set line mode\n\
652        -n, --no-keepalive       disable TCP keep-alives\n\
653        -U, --reverse-lookup     refuse  connections from addresses that\n\
654                                 cannot be mapped back into a symbolic name\n\
655        -X, --disable-auth-type AUTHTYPE\n\
656                                 disable the use of given authentication option\n\
657    Informational options:\n\
658        -V, --version         display this help and exit\n\
659        -L, --license         display license and exit\n\
660        -H. --help            output version information and exit\n\
661    \n\
662    Options, marked with an * are not yet implemented\n");
663  }  }
664    
665  /*  int
666   * When we get an AYT, if ^T is enabled, use that.  Otherwise,  stop()
  * just send back "[Yes]".  
  */  
 void  
 recv_ayt()  
 {  
 #if     defined(SIGINFO) && defined(TCSIG)  
         if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {  
                 (void) ioctl(pty, TCSIG, (char *)SIGINFO);  
                 return;  
         }  
 #endif  
         (void) strcpy(nfrontp, "\r\n[Yes]\r\n");  
         nfrontp += 9;  
 }  
   
 void  
 doeof()  
667  {  {
668          init_termbuf();    int volatile _s=1;
669    
670  #if     defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)    while (_s)
671          if (!tty_isediting()) {      _s=_s;
672                  extern char oldeofc;    return 0;
                 *pfrontp++ = oldeofc;  
                 return;  
         }  
 #endif  
         *pfrontp++ = slctab[SLC_EOF].sptr ?  
                         (unsigned char)*slctab[SLC_EOF].sptr : '\004';  
673  }  }

Legend:
Removed from v.1.25  
changed lines
  Added in v.1.26

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26