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 |
} |
} |