telnetd.c revision a9e6ba3f81bf9daf246d3fa948bc174190bd15d
1a9e6ba3markm/*
2a9e6ba3markm * Copyright (c) 1989, 1993
3a9e6ba3markm *	The Regents of the University of California.  All rights reserved.
4a9e6ba3markm *
5a9e6ba3markm * Redistribution and use in source and binary forms, with or without
6a9e6ba3markm * modification, are permitted provided that the following conditions
7a9e6ba3markm * are met:
8a9e6ba3markm * 1. Redistributions of source code must retain the above copyright
9a9e6ba3markm *    notice, this list of conditions and the following disclaimer.
10a9e6ba3markm * 2. Redistributions in binary form must reproduce the above copyright
11a9e6ba3markm *    notice, this list of conditions and the following disclaimer in the
12a9e6ba3markm *    documentation and/or other materials provided with the distribution.
13a9e6ba3markm * 3. All advertising materials mentioning features or use of this software
14a9e6ba3markm *    must display the following acknowledgement:
15a9e6ba3markm *	This product includes software developed by the University of
16a9e6ba3markm *	California, Berkeley and its contributors.
17a9e6ba3markm * 4. Neither the name of the University nor the names of its contributors
18a9e6ba3markm *    may be used to endorse or promote products derived from this software
19a9e6ba3markm *    without specific prior written permission.
20a9e6ba3markm *
21a9e6ba3markm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22a9e6ba3markm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23a9e6ba3markm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24a9e6ba3markm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25a9e6ba3markm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26a9e6ba3markm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27a9e6ba3markm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28a9e6ba3markm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29a9e6ba3markm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30a9e6ba3markm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31a9e6ba3markm * SUCH DAMAGE.
32a9e6ba3markm */
33a9e6ba3markm
34a9e6ba3markm#ifndef lint
35a9e6ba3markmstatic char copyright[] =
36a9e6ba3markm"@(#) Copyright (c) 1989, 1993\n\
37a9e6ba3markm	The Regents of the University of California.  All rights reserved.\n";
38a9e6ba3markm#endif /* not lint */
39a9e6ba3markm
40a9e6ba3markm#ifndef lint
41a9e6ba3markmstatic char sccsid[] = "@(#)telnetd.c	8.4 (Berkeley) 5/30/95";
42a9e6ba3markm#endif /* not lint */
43a9e6ba3markm
44a9e6ba3markm#include "telnetd.h"
45a9e6ba3markm#include "pathnames.h"
46a9e6ba3markm
47a9e6ba3markm#if	defined(_SC_CRAY_SECURE_SYS) && !defined(SCM_SECURITY)
48a9e6ba3markm/*
49a9e6ba3markm * UNICOS 6.0/6.1 do not have SCM_SECURITY defined, so we can
50a9e6ba3markm * use it to tell us to turn off all the socket security code,
51a9e6ba3markm * since that is only used in UNICOS 7.0 and later.
52a9e6ba3markm */
53a9e6ba3markm# undef _SC_CRAY_SECURE_SYS
54a9e6ba3markm#endif
55a9e6ba3markm
56a9e6ba3markm#if	defined(_SC_CRAY_SECURE_SYS)
57a9e6ba3markm#include <sys/sysv.h>
58a9e6ba3markm#include <sys/secdev.h>
59a9e6ba3markm# ifdef SO_SEC_MULTI		/* 8.0 code */
60a9e6ba3markm#include <sys/secparm.h>
61a9e6ba3markm#include <sys/usrv.h>
62a9e6ba3markm# endif /* SO_SEC_MULTI */
63a9e6ba3markmint	secflag;
64a9e6ba3markmchar	tty_dev[16];
65a9e6ba3markmstruct	secdev dv;
66a9e6ba3markmstruct	sysv sysv;
67a9e6ba3markm# ifdef SO_SEC_MULTI		/* 8.0 code */
68a9e6ba3markmstruct	socksec ss;
69a9e6ba3markm# else /* SO_SEC_MULTI */	/* 7.0 code */
70a9e6ba3markmstruct	socket_security ss;
71a9e6ba3markm# endif /* SO_SEC_MULTI */
72a9e6ba3markm#endif	/* _SC_CRAY_SECURE_SYS */
73a9e6ba3markm
74a9e6ba3markm#if	defined(AUTHENTICATION)
75a9e6ba3markm#include <libtelnet/auth.h>
76a9e6ba3markmint	auth_level = 0;
77a9e6ba3markm#endif
78a9e6ba3markm#if	defined(SecurID)
79a9e6ba3markmint	require_SecurID = 0;
80a9e6ba3markm#endif
81a9e6ba3markm
82a9e6ba3markmextern	int utmp_len;
83a9e6ba3markmint	registerd_host_only = 0;
84a9e6ba3markm
85a9e6ba3markm#ifdef	STREAMSPTY
86a9e6ba3markm# include <stropts.h>
87a9e6ba3markm# include <termio.h>
88a9e6ba3markm/* make sure we don't get the bsd version */
89a9e6ba3markm# include "/usr/include/sys/tty.h"
90a9e6ba3markm# include <sys/ptyvar.h>
91a9e6ba3markm
92a9e6ba3markm/*
93a9e6ba3markm * Because of the way ptyibuf is used with streams messages, we need
94a9e6ba3markm * ptyibuf+1 to be on a full-word boundary.  The following wierdness
95a9e6ba3markm * is simply to make that happen.
96a9e6ba3markm */
97a9e6ba3markmlong	ptyibufbuf[BUFSIZ/sizeof(long)+1];
98a9e6ba3markmchar	*ptyibuf = ((char *)&ptyibufbuf[1])-1;
99a9e6ba3markmchar	*ptyip = ((char *)&ptyibufbuf[1])-1;
100a9e6ba3markmchar	ptyibuf2[BUFSIZ];
101a9e6ba3markmunsigned char ctlbuf[BUFSIZ];
102a9e6ba3markmstruct	strbuf strbufc, strbufd;
103a9e6ba3markm
104a9e6ba3markmint readstream();
105a9e6ba3markm
106a9e6ba3markm#else	/* ! STREAMPTY */
107a9e6ba3markm
108a9e6ba3markm/*
109a9e6ba3markm * I/O data buffers,
110a9e6ba3markm * pointers, and counters.
111a9e6ba3markm */
112a9e6ba3markmchar	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
113a9e6ba3markmchar	ptyibuf2[BUFSIZ];
114a9e6ba3markm
115a9e6ba3markm#endif /* ! STREAMPTY */
116a9e6ba3markm
117a9e6ba3markmint	hostinfo = 1;			/* do we print login banner? */
118a9e6ba3markm
119a9e6ba3markm#ifdef	CRAY
120a9e6ba3markmextern int      newmap; /* nonzero if \n maps to ^M^J */
121a9e6ba3markmint	lowpty = 0, highpty;	/* low, high pty numbers */
122a9e6ba3markm#endif /* CRAY */
123a9e6ba3markm
124a9e6ba3markmint debug = 0;
125a9e6ba3markmint keepalive = 1;
126a9e6ba3markmchar *progname;
127a9e6ba3markm
128a9e6ba3markmextern void usage P((void));
129a9e6ba3markm
130a9e6ba3markm/*
131a9e6ba3markm * The string to pass to getopt().  We do it this way so
132a9e6ba3markm * that only the actual options that we support will be
133a9e6ba3markm * passed off to getopt().
134a9e6ba3markm */
135a9e6ba3markmchar valid_opts[] = {
136a9e6ba3markm	'd', ':', 'h', 'k', 'n', 'S', ':', 'u', ':', 'U',
137a9e6ba3markm#ifdef	AUTHENTICATION
138a9e6ba3markm	'a', ':', 'X', ':',
139a9e6ba3markm#endif
140a9e6ba3markm#ifdef BFTPDAEMON
141a9e6ba3markm	'B',
142a9e6ba3markm#endif
143a9e6ba3markm#ifdef DIAGNOSTICS
144a9e6ba3markm	'D', ':',
145a9e6ba3markm#endif
146a9e6ba3markm#ifdef	ENCRYPTION
147a9e6ba3markm	'e', ':',
148a9e6ba3markm#endif
149a9e6ba3markm#if	defined(CRAY) && defined(NEWINIT)
150a9e6ba3markm	'I', ':',
151a9e6ba3markm#endif
152a9e6ba3markm#ifdef	LINEMODE
153a9e6ba3markm	'l',
154a9e6ba3markm#endif
155a9e6ba3markm#ifdef CRAY
156a9e6ba3markm	'r', ':',
157a9e6ba3markm#endif
158a9e6ba3markm#ifdef	SecurID
159a9e6ba3markm	's',
160a9e6ba3markm#endif
161a9e6ba3markm	'\0'
162a9e6ba3markm};
163a9e6ba3markm
164a9e6ba3markmmain(argc, argv)
165a9e6ba3markm	char *argv[];
166a9e6ba3markm{
167a9e6ba3markm	struct sockaddr_in from;
168a9e6ba3markm	int on = 1, fromlen;
169a9e6ba3markm	register int ch;
170a9e6ba3markm	extern char *optarg;
171a9e6ba3markm	extern int optind;
172a9e6ba3markm#if	defined(IPPROTO_IP) && defined(IP_TOS)
173a9e6ba3markm	int tos = -1;
174a9e6ba3markm#endif
175a9e6ba3markm
176a9e6ba3markm	pfrontp = pbackp = ptyobuf;
177a9e6ba3markm	netip = netibuf;
178a9e6ba3markm	nfrontp = nbackp = netobuf;
179a9e6ba3markm#ifdef	ENCRYPTION
180a9e6ba3markm	nclearto = 0;
181a9e6ba3markm#endif	/* ENCRYPTION */
182a9e6ba3markm
183a9e6ba3markm	progname = *argv;
184a9e6ba3markm
185a9e6ba3markm#ifdef CRAY
186a9e6ba3markm	/*
187a9e6ba3markm	 * Get number of pty's before trying to process options,
188a9e6ba3markm	 * which may include changing pty range.
189a9e6ba3markm	 */
190a9e6ba3markm	highpty = getnpty();
191a9e6ba3markm#endif /* CRAY */
192a9e6ba3markm
193a9e6ba3markm	while ((ch = getopt(argc, argv, valid_opts)) != EOF) {
194a9e6ba3markm		switch(ch) {
195a9e6ba3markm
196a9e6ba3markm#ifdef	AUTHENTICATION
197a9e6ba3markm		case 'a':
198a9e6ba3markm			/*
199a9e6ba3markm			 * Check for required authentication level
200a9e6ba3markm			 */
201a9e6ba3markm			if (strcmp(optarg, "debug") == 0) {
202a9e6ba3markm				extern int auth_debug_mode;
203a9e6ba3markm				auth_debug_mode = 1;
204a9e6ba3markm			} else if (strcasecmp(optarg, "none") == 0) {
205a9e6ba3markm				auth_level = 0;
206a9e6ba3markm			} else if (strcasecmp(optarg, "other") == 0) {
207a9e6ba3markm				auth_level = AUTH_OTHER;
208a9e6ba3markm			} else if (strcasecmp(optarg, "user") == 0) {
209a9e6ba3markm				auth_level = AUTH_USER;
210a9e6ba3markm			} else if (strcasecmp(optarg, "valid") == 0) {
211a9e6ba3markm				auth_level = AUTH_VALID;
212a9e6ba3markm			} else if (strcasecmp(optarg, "off") == 0) {
213a9e6ba3markm				/*
214a9e6ba3markm				 * This hack turns off authentication
215a9e6ba3markm				 */
216a9e6ba3markm				auth_level = -1;
217a9e6ba3markm			} else {
218a9e6ba3markm				fprintf(stderr,
219a9e6ba3markm			    "telnetd: unknown authorization level for -a\n");
220a9e6ba3markm			}
221a9e6ba3markm			break;
222a9e6ba3markm#endif	/* AUTHENTICATION */
223a9e6ba3markm
224a9e6ba3markm#ifdef BFTPDAEMON
225a9e6ba3markm		case 'B':
226a9e6ba3markm			bftpd++;
227a9e6ba3markm			break;
228a9e6ba3markm#endif /* BFTPDAEMON */
229a9e6ba3markm
230a9e6ba3markm		case 'd':
231a9e6ba3markm			if (strcmp(optarg, "ebug") == 0) {
232a9e6ba3markm				debug++;
233a9e6ba3markm				break;
234a9e6ba3markm			}
235a9e6ba3markm			usage();
236a9e6ba3markm			/* NOTREACHED */
237a9e6ba3markm			break;
238a9e6ba3markm
239a9e6ba3markm#ifdef DIAGNOSTICS
240a9e6ba3markm		case 'D':
241a9e6ba3markm			/*
242a9e6ba3markm			 * Check for desired diagnostics capabilities.
243a9e6ba3markm			 */
244a9e6ba3markm			if (!strcmp(optarg, "report")) {
245a9e6ba3markm				diagnostic |= TD_REPORT|TD_OPTIONS;
246a9e6ba3markm			} else if (!strcmp(optarg, "exercise")) {
247a9e6ba3markm				diagnostic |= TD_EXERCISE;
248a9e6ba3markm			} else if (!strcmp(optarg, "netdata")) {
249a9e6ba3markm				diagnostic |= TD_NETDATA;
250a9e6ba3markm			} else if (!strcmp(optarg, "ptydata")) {
251a9e6ba3markm				diagnostic |= TD_PTYDATA;
252a9e6ba3markm			} else if (!strcmp(optarg, "options")) {
253a9e6ba3markm				diagnostic |= TD_OPTIONS;
254a9e6ba3markm			} else {
255a9e6ba3markm				usage();
256a9e6ba3markm				/* NOT REACHED */
257a9e6ba3markm			}
258a9e6ba3markm			break;
259a9e6ba3markm#endif /* DIAGNOSTICS */
260a9e6ba3markm
261a9e6ba3markm#ifdef	ENCRYPTION
262a9e6ba3markm		case 'e':
263a9e6ba3markm			if (strcmp(optarg, "debug") == 0) {
264a9e6ba3markm				extern int encrypt_debug_mode;
265a9e6ba3markm				encrypt_debug_mode = 1;
266a9e6ba3markm				break;
267a9e6ba3markm			}
268a9e6ba3markm			usage();
269a9e6ba3markm			/* NOTREACHED */
270a9e6ba3markm			break;
271a9e6ba3markm#endif	/* ENCRYPTION */
272a9e6ba3markm
273a9e6ba3markm		case 'h':
274a9e6ba3markm			hostinfo = 0;
275a9e6ba3markm			break;
276a9e6ba3markm
277a9e6ba3markm#if	defined(CRAY) && defined(NEWINIT)
278a9e6ba3markm		case 'I':
279a9e6ba3markm		    {
280a9e6ba3markm			extern char *gen_id;
281a9e6ba3markm			gen_id = optarg;
282a9e6ba3markm			break;
283a9e6ba3markm		    }
284a9e6ba3markm#endif	/* defined(CRAY) && defined(NEWINIT) */
285a9e6ba3markm
286a9e6ba3markm#ifdef	LINEMODE
287a9e6ba3markm		case 'l':
288a9e6ba3markm			alwayslinemode = 1;
289a9e6ba3markm			break;
290a9e6ba3markm#endif	/* LINEMODE */
291a9e6ba3markm
292a9e6ba3markm		case 'k':
293a9e6ba3markm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
294a9e6ba3markm			lmodetype = NO_AUTOKLUDGE;
295a9e6ba3markm#else
296a9e6ba3markm			/* ignore -k option if built without kludge linemode */
297a9e6ba3markm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
298a9e6ba3markm			break;
299a9e6ba3markm
300a9e6ba3markm		case 'n':
301a9e6ba3markm			keepalive = 0;
302a9e6ba3markm			break;
303a9e6ba3markm
304a9e6ba3markm#ifdef CRAY
305a9e6ba3markm		case 'r':
306a9e6ba3markm		    {
307a9e6ba3markm			char *strchr();
308a9e6ba3markm			char *c;
309a9e6ba3markm
310a9e6ba3markm			/*
311a9e6ba3markm			 * Allow the specification of alterations
312a9e6ba3markm			 * to the pty search range.  It is legal to
313a9e6ba3markm			 * specify only one, and not change the
314a9e6ba3markm			 * other from its default.
315a9e6ba3markm			 */
316a9e6ba3markm			c = strchr(optarg, '-');
317a9e6ba3markm			if (c) {
318a9e6ba3markm				*c++ = '\0';
319a9e6ba3markm				highpty = atoi(c);
320a9e6ba3markm			}
321a9e6ba3markm			if (*optarg != '\0')
322a9e6ba3markm				lowpty = atoi(optarg);
323a9e6ba3markm			if ((lowpty > highpty) || (lowpty < 0) ||
324a9e6ba3markm							(highpty > 32767)) {
325a9e6ba3markm				usage();
326a9e6ba3markm				/* NOT REACHED */
327a9e6ba3markm			}
328a9e6ba3markm			break;
329a9e6ba3markm		    }
330a9e6ba3markm#endif	/* CRAY */
331a9e6ba3markm
332a9e6ba3markm#ifdef	SecurID
333a9e6ba3markm		case 's':
334a9e6ba3markm			/* SecurID required */
335a9e6ba3markm			require_SecurID = 1;
336a9e6ba3markm			break;
337a9e6ba3markm#endif	/* SecurID */
338a9e6ba3markm		case 'S':
339a9e6ba3markm#ifdef	HAS_GETTOS
340a9e6ba3markm			if ((tos = parsetos(optarg, "tcp")) < 0)
341a9e6ba3markm				fprintf(stderr, "%s%s%s\n",
342a9e6ba3markm					"telnetd: Bad TOS argument '", optarg,
343a9e6ba3markm					"'; will try to use default TOS");
344a9e6ba3markm#else
345a9e6ba3markm			fprintf(stderr, "%s%s\n", "TOS option unavailable; ",
346a9e6ba3markm						"-S flag not supported\n");
347a9e6ba3markm#endif
348a9e6ba3markm			break;
349a9e6ba3markm
350a9e6ba3markm		case 'u':
351a9e6ba3markm			utmp_len = atoi(optarg);
352a9e6ba3markm			break;
353a9e6ba3markm
354a9e6ba3markm		case 'U':
355a9e6ba3markm			registerd_host_only = 1;
356a9e6ba3markm			break;
357a9e6ba3markm
358a9e6ba3markm#ifdef	AUTHENTICATION
359a9e6ba3markm		case 'X':
360a9e6ba3markm			/*
361a9e6ba3markm			 * Check for invalid authentication types
362a9e6ba3markm			 */
363a9e6ba3markm			auth_disable_name(optarg);
364a9e6ba3markm			break;
365a9e6ba3markm#endif	/* AUTHENTICATION */
366a9e6ba3markm
367a9e6ba3markm		default:
368a9e6ba3markm			fprintf(stderr, "telnetd: %c: unknown option\n", ch);
369a9e6ba3markm			/* FALLTHROUGH */
370a9e6ba3markm		case '?':
371a9e6ba3markm			usage();
372a9e6ba3markm			/* NOTREACHED */
373a9e6ba3markm		}
374a9e6ba3markm	}
375a9e6ba3markm
376a9e6ba3markm	argc -= optind;
377a9e6ba3markm	argv += optind;
378a9e6ba3markm
379a9e6ba3markm	if (debug) {
380a9e6ba3markm	    int s, ns, foo;
381a9e6ba3markm	    struct servent *sp;
382a9e6ba3markm	    static struct sockaddr_in sin = { AF_INET };
383a9e6ba3markm
384a9e6ba3markm	    if (argc > 1) {
385a9e6ba3markm		usage();
386a9e6ba3markm		/* NOT REACHED */
387a9e6ba3markm	    } else if (argc == 1) {
388a9e6ba3markm		    if (sp = getservbyname(*argv, "tcp")) {
389a9e6ba3markm			sin.sin_port = sp->s_port;
390a9e6ba3markm		    } else {
391a9e6ba3markm			sin.sin_port = atoi(*argv);
392a9e6ba3markm			if ((int)sin.sin_port <= 0) {
393a9e6ba3markm			    fprintf(stderr, "telnetd: %s: bad port #\n", *argv);
394a9e6ba3markm			    usage();
395a9e6ba3markm			    /* NOT REACHED */
396a9e6ba3markm			}
397a9e6ba3markm			sin.sin_port = htons((u_short)sin.sin_port);
398a9e6ba3markm		   }
399a9e6ba3markm	    } else {
400a9e6ba3markm		sp = getservbyname("telnet", "tcp");
401a9e6ba3markm		if (sp == 0) {
402a9e6ba3markm		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");
403a9e6ba3markm		    exit(1);
404a9e6ba3markm		}
405a9e6ba3markm		sin.sin_port = sp->s_port;
406a9e6ba3markm	    }
407a9e6ba3markm
408a9e6ba3markm	    s = socket(AF_INET, SOCK_STREAM, 0);
409a9e6ba3markm	    if (s < 0) {
410a9e6ba3markm		    perror("telnetd: socket");;
411a9e6ba3markm		    exit(1);
412a9e6ba3markm	    }
413a9e6ba3markm	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
414a9e6ba3markm				(char *)&on, sizeof(on));
415a9e6ba3markm	    if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) {
416a9e6ba3markm		perror("bind");
417a9e6ba3markm		exit(1);
418a9e6ba3markm	    }
419a9e6ba3markm	    if (listen(s, 1) < 0) {
420a9e6ba3markm		perror("listen");
421a9e6ba3markm		exit(1);
422a9e6ba3markm	    }
423a9e6ba3markm	    foo = sizeof sin;
424a9e6ba3markm	    ns = accept(s, (struct sockaddr *)&sin, &foo);
425a9e6ba3markm	    if (ns < 0) {
426a9e6ba3markm		perror("accept");
427a9e6ba3markm		exit(1);
428a9e6ba3markm	    }
429a9e6ba3markm	    (void) dup2(ns, 0);
430a9e6ba3markm	    (void) close(ns);
431a9e6ba3markm	    (void) close(s);
432a9e6ba3markm#ifdef convex
433a9e6ba3markm	} else if (argc == 1) {
434a9e6ba3markm		; /* VOID*/		/* Just ignore the host/port name */
435a9e6ba3markm#endif
436a9e6ba3markm	} else if (argc > 0) {
437a9e6ba3markm		usage();
438a9e6ba3markm		/* NOT REACHED */
439a9e6ba3markm	}
440a9e6ba3markm
441a9e6ba3markm#if	defined(_SC_CRAY_SECURE_SYS)
442a9e6ba3markm	secflag = sysconf(_SC_CRAY_SECURE_SYS);
443a9e6ba3markm
444a9e6ba3markm	/*
445a9e6ba3markm	 *	Get socket's security label
446a9e6ba3markm	 */
447a9e6ba3markm	if (secflag)  {
448a9e6ba3markm		int szss = sizeof(ss);
449a9e6ba3markm#ifdef SO_SEC_MULTI			/* 8.0 code */
450a9e6ba3markm		int sock_multi;
451a9e6ba3markm		int szi = sizeof(int);
452a9e6ba3markm#endif /* SO_SEC_MULTI */
453a9e6ba3markm
454a9e6ba3markm		memset((char *)&dv, 0, sizeof(dv));
455a9e6ba3markm
456a9e6ba3markm		if (getsysv(&sysv, sizeof(struct sysv)) != 0) {
457a9e6ba3markm			perror("getsysv");
458a9e6ba3markm			exit(1);
459a9e6ba3markm		}
460a9e6ba3markm
461a9e6ba3markm		/*
462a9e6ba3markm		 *	Get socket security label and set device values
463a9e6ba3markm		 *	   {security label to be set on ttyp device}
464a9e6ba3markm		 */
465a9e6ba3markm#ifdef SO_SEC_MULTI			/* 8.0 code */
466a9e6ba3markm		if ((getsockopt(0, SOL_SOCKET, SO_SECURITY,
467a9e6ba3markm			       (char *)&ss, &szss) < 0) ||
468a9e6ba3markm		    (getsockopt(0, SOL_SOCKET, SO_SEC_MULTI,
469a9e6ba3markm				(char *)&sock_multi, &szi) < 0)) {
470a9e6ba3markm			perror("getsockopt");
471a9e6ba3markm			exit(1);
472a9e6ba3markm		} else {
473a9e6ba3markm			dv.dv_actlvl = ss.ss_actlabel.lt_level;
474a9e6ba3markm			dv.dv_actcmp = ss.ss_actlabel.lt_compart;
475a9e6ba3markm			if (!sock_multi) {
476a9e6ba3markm				dv.dv_minlvl = dv.dv_maxlvl = dv.dv_actlvl;
477a9e6ba3markm				dv.dv_valcmp = dv.dv_actcmp;
478a9e6ba3markm			} else {
479a9e6ba3markm				dv.dv_minlvl = ss.ss_minlabel.lt_level;
480a9e6ba3markm				dv.dv_maxlvl = ss.ss_maxlabel.lt_level;
481a9e6ba3markm				dv.dv_valcmp = ss.ss_maxlabel.lt_compart;
482a9e6ba3markm			}
483a9e6ba3markm			dv.dv_devflg = 0;
484a9e6ba3markm		}
485a9e6ba3markm#else /* SO_SEC_MULTI */		/* 7.0 code */
486a9e6ba3markm		if (getsockopt(0, SOL_SOCKET, SO_SECURITY,
487a9e6ba3markm				(char *)&ss, &szss) >= 0) {
488a9e6ba3markm			dv.dv_actlvl = ss.ss_slevel;
489a9e6ba3markm			dv.dv_actcmp = ss.ss_compart;
490a9e6ba3markm			dv.dv_minlvl = ss.ss_minlvl;
491a9e6ba3markm			dv.dv_maxlvl = ss.ss_maxlvl;
492a9e6ba3markm			dv.dv_valcmp = ss.ss_maxcmp;
493a9e6ba3markm		}
494a9e6ba3markm#endif /* SO_SEC_MULTI */
495a9e6ba3markm	}
496a9e6ba3markm#endif	/* _SC_CRAY_SECURE_SYS */
497a9e6ba3markm
498a9e6ba3markm	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
499a9e6ba3markm	fromlen = sizeof (from);
500a9e6ba3markm	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
501a9e6ba3markm		fprintf(stderr, "%s: ", progname);
502a9e6ba3markm		perror("getpeername");
503a9e6ba3markm		_exit(1);
504a9e6ba3markm	}
505a9e6ba3markm	if (keepalive &&
506a9e6ba3markm	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
507a9e6ba3markm			(char *)&on, sizeof (on)) < 0) {
508a9e6ba3markm		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
509a9e6ba3markm	}
510a9e6ba3markm
511a9e6ba3markm#if	defined(IPPROTO_IP) && defined(IP_TOS)
512a9e6ba3markm	{
513a9e6ba3markm# if	defined(HAS_GETTOS)
514a9e6ba3markm		struct tosent *tp;
515a9e6ba3markm		if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
516a9e6ba3markm			tos = tp->t_tos;
517a9e6ba3markm# endif
518a9e6ba3markm		if (tos < 0)
519a9e6ba3markm			tos = 020;	/* Low Delay bit */
520a9e6ba3markm		if (tos
521a9e6ba3markm		   && (setsockopt(0, IPPROTO_IP, IP_TOS,
522a9e6ba3markm				  (char *)&tos, sizeof(tos)) < 0)
523a9e6ba3markm		   && (errno != ENOPROTOOPT) )
524a9e6ba3markm			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
525a9e6ba3markm	}
526a9e6ba3markm#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
527a9e6ba3markm	net = 0;
528a9e6ba3markm	doit(&from);
529a9e6ba3markm	/* NOTREACHED */
530a9e6ba3markm}  /* end of main */
531a9e6ba3markm
532a9e6ba3markm	void
533a9e6ba3markmusage()
534a9e6ba3markm{
535a9e6ba3markm	fprintf(stderr, "Usage: telnetd");
536a9e6ba3markm#ifdef	AUTHENTICATION
537a9e6ba3markm	fprintf(stderr, " [-a (debug|other|user|valid|off|none)]\n\t");
538a9e6ba3markm#endif
539a9e6ba3markm#ifdef BFTPDAEMON
540a9e6ba3markm	fprintf(stderr, " [-B]");
541a9e6ba3markm#endif
542a9e6ba3markm	fprintf(stderr, " [-debug]");
543a9e6ba3markm#ifdef DIAGNOSTICS
544a9e6ba3markm	fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
545a9e6ba3markm#endif
546a9e6ba3markm#ifdef	AUTHENTICATION
547a9e6ba3markm	fprintf(stderr, " [-edebug]");
548a9e6ba3markm#endif
549a9e6ba3markm	fprintf(stderr, " [-h]");
550a9e6ba3markm#if	defined(CRAY) && defined(NEWINIT)
551a9e6ba3markm	fprintf(stderr, " [-Iinitid]");
552a9e6ba3markm#endif
553a9e6ba3markm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
554a9e6ba3markm	fprintf(stderr, " [-k]");
555a9e6ba3markm#endif
556a9e6ba3markm#ifdef LINEMODE
557a9e6ba3markm	fprintf(stderr, " [-l]");
558a9e6ba3markm#endif
559a9e6ba3markm	fprintf(stderr, " [-n]");
560a9e6ba3markm#ifdef	CRAY
561a9e6ba3markm	fprintf(stderr, " [-r[lowpty]-[highpty]]");
562a9e6ba3markm#endif
563a9e6ba3markm	fprintf(stderr, "\n\t");
564a9e6ba3markm#ifdef	SecurID
565a9e6ba3markm	fprintf(stderr, " [-s]");
566a9e6ba3markm#endif
567a9e6ba3markm#ifdef	HAS_GETTOS
568a9e6ba3markm	fprintf(stderr, " [-S tos]");
569a9e6ba3markm#endif
570a9e6ba3markm#ifdef	AUTHENTICATION
571a9e6ba3markm	fprintf(stderr, " [-X auth-type]");
572a9e6ba3markm#endif
573a9e6ba3markm	fprintf(stderr, " [-u utmp_hostname_length] [-U]");
574a9e6ba3markm	fprintf(stderr, " [port]\n");
575a9e6ba3markm	exit(1);
576a9e6ba3markm}
577a9e6ba3markm
578a9e6ba3markm/*
579a9e6ba3markm * getterminaltype
580a9e6ba3markm *
581a9e6ba3markm *	Ask the other end to send along its terminal type and speed.
582a9e6ba3markm * Output is the variable terminaltype filled in.
583a9e6ba3markm */
584a9e6ba3markmstatic unsigned char ttytype_sbbuf[] = {
585a9e6ba3markm	IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
586a9e6ba3markm};
587a9e6ba3markm
588a9e6ba3markm    int
589a9e6ba3markmgetterminaltype(name)
590a9e6ba3markm    char *name;
591a9e6ba3markm{
592a9e6ba3markm    int retval = -1;
593a9e6ba3markm    void _gettermname();
594a9e6ba3markm
595a9e6ba3markm    settimer(baseline);
596a9e6ba3markm#if	defined(AUTHENTICATION)
597a9e6ba3markm    /*
598a9e6ba3markm     * Handle the Authentication option before we do anything else.
599a9e6ba3markm     */
600a9e6ba3markm    send_do(TELOPT_AUTHENTICATION, 1);
601a9e6ba3markm    while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
602a9e6ba3markm	ttloop();
603a9e6ba3markm    if (his_state_is_will(TELOPT_AUTHENTICATION)) {
604a9e6ba3markm	retval = auth_wait(name);
605a9e6ba3markm    }
606a9e6ba3markm#endif
607a9e6ba3markm
608a9e6ba3markm#ifdef	ENCRYPTION
609a9e6ba3markm    send_will(TELOPT_ENCRYPT, 1);
610a9e6ba3markm#endif	/* ENCRYPTION */
611a9e6ba3markm    send_do(TELOPT_TTYPE, 1);
612a9e6ba3markm    send_do(TELOPT_TSPEED, 1);
613a9e6ba3markm    send_do(TELOPT_XDISPLOC, 1);
614a9e6ba3markm    send_do(TELOPT_NEW_ENVIRON, 1);
615a9e6ba3markm    send_do(TELOPT_OLD_ENVIRON, 1);
616a9e6ba3markm    while (
617a9e6ba3markm#ifdef	ENCRYPTION
618a9e6ba3markm	   his_do_dont_is_changing(TELOPT_ENCRYPT) ||
619a9e6ba3markm#endif	/* ENCRYPTION */
620a9e6ba3markm	   his_will_wont_is_changing(TELOPT_TTYPE) ||
621a9e6ba3markm	   his_will_wont_is_changing(TELOPT_TSPEED) ||
622a9e6ba3markm	   his_will_wont_is_changing(TELOPT_XDISPLOC) ||
623a9e6ba3markm	   his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
624a9e6ba3markm	   his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
625a9e6ba3markm	ttloop();
626a9e6ba3markm    }
627a9e6ba3markm#ifdef	ENCRYPTION
628a9e6ba3markm    /*
629a9e6ba3markm     * Wait for the negotiation of what type of encryption we can
630a9e6ba3markm     * send with.  If autoencrypt is not set, this will just return.
631a9e6ba3markm     */
632a9e6ba3markm    if (his_state_is_will(TELOPT_ENCRYPT)) {
633a9e6ba3markm	encrypt_wait();
634a9e6ba3markm    }
635a9e6ba3markm#endif	/* ENCRYPTION */
636a9e6ba3markm    if (his_state_is_will(TELOPT_TSPEED)) {
637a9e6ba3markm	static unsigned char sb[] =
638a9e6ba3markm			{ IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
639a9e6ba3markm
640a9e6ba3markm	memmove(nfrontp, sb, sizeof sb);
641a9e6ba3markm	nfrontp += sizeof sb;
642a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
643a9e6ba3markm    }
644a9e6ba3markm    if (his_state_is_will(TELOPT_XDISPLOC)) {
645a9e6ba3markm	static unsigned char sb[] =
646a9e6ba3markm			{ IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
647a9e6ba3markm
648a9e6ba3markm	memmove(nfrontp, sb, sizeof sb);
649a9e6ba3markm	nfrontp += sizeof sb;
650a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
651a9e6ba3markm    }
652a9e6ba3markm    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
653a9e6ba3markm	static unsigned char sb[] =
654a9e6ba3markm			{ IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
655a9e6ba3markm
656a9e6ba3markm	memmove(nfrontp, sb, sizeof sb);
657a9e6ba3markm	nfrontp += sizeof sb;
658a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
659a9e6ba3markm    }
660a9e6ba3markm    else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
661a9e6ba3markm	static unsigned char sb[] =
662a9e6ba3markm			{ IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
663a9e6ba3markm
664a9e6ba3markm	memmove(nfrontp, sb, sizeof sb);
665a9e6ba3markm	nfrontp += sizeof sb;
666a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
667a9e6ba3markm    }
668a9e6ba3markm    if (his_state_is_will(TELOPT_TTYPE)) {
669a9e6ba3markm
670a9e6ba3markm	memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);
671a9e6ba3markm	nfrontp += sizeof ttytype_sbbuf;
672a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
673a9e6ba3markm					sizeof ttytype_sbbuf - 2););
674a9e6ba3markm    }
675a9e6ba3markm    if (his_state_is_will(TELOPT_TSPEED)) {
676a9e6ba3markm	while (sequenceIs(tspeedsubopt, baseline))
677a9e6ba3markm	    ttloop();
678a9e6ba3markm    }
679a9e6ba3markm    if (his_state_is_will(TELOPT_XDISPLOC)) {
680a9e6ba3markm	while (sequenceIs(xdisplocsubopt, baseline))
681a9e6ba3markm	    ttloop();
682a9e6ba3markm    }
683a9e6ba3markm    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
684a9e6ba3markm	while (sequenceIs(environsubopt, baseline))
685a9e6ba3markm	    ttloop();
686a9e6ba3markm    }
687a9e6ba3markm    if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
688a9e6ba3markm	while (sequenceIs(oenvironsubopt, baseline))
689a9e6ba3markm	    ttloop();
690a9e6ba3markm    }
691a9e6ba3markm    if (his_state_is_will(TELOPT_TTYPE)) {
692a9e6ba3markm	char first[256], last[256];
693a9e6ba3markm
694a9e6ba3markm	while (sequenceIs(ttypesubopt, baseline))
695a9e6ba3markm	    ttloop();
696a9e6ba3markm
697a9e6ba3markm	/*
698a9e6ba3markm	 * If the other side has already disabled the option, then
699a9e6ba3markm	 * we have to just go with what we (might) have already gotten.
700a9e6ba3markm	 */
701a9e6ba3markm	if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
702a9e6ba3markm	    (void) strncpy(first, terminaltype, sizeof(first));
703a9e6ba3markm	    for(;;) {
704a9e6ba3markm		/*
705a9e6ba3markm		 * Save the unknown name, and request the next name.
706a9e6ba3markm		 */
707a9e6ba3markm		(void) strncpy(last, terminaltype, sizeof(last));
708a9e6ba3markm		_gettermname();
709a9e6ba3markm		if (terminaltypeok(terminaltype))
710a9e6ba3markm		    break;
711a9e6ba3markm		if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
712a9e6ba3markm		    his_state_is_wont(TELOPT_TTYPE)) {
713a9e6ba3markm		    /*
714a9e6ba3markm		     * We've hit the end.  If this is the same as
715a9e6ba3markm		     * the first name, just go with it.
716a9e6ba3markm		     */
717a9e6ba3markm		    if (strncmp(first, terminaltype, sizeof(first)) == 0)
718a9e6ba3markm			break;
719a9e6ba3markm		    /*
720a9e6ba3markm		     * Get the terminal name one more time, so that
721a9e6ba3markm		     * RFC1091 compliant telnets will cycle back to
722a9e6ba3markm		     * the start of the list.
723a9e6ba3markm		     */
724a9e6ba3markm		     _gettermname();
725a9e6ba3markm		    if (strncmp(first, terminaltype, sizeof(first)) != 0)
726a9e6ba3markm			(void) strncpy(terminaltype, first, sizeof(first));
727a9e6ba3markm		    break;
728a9e6ba3markm		}
729a9e6ba3markm	    }
730a9e6ba3markm	}
731a9e6ba3markm    }
732a9e6ba3markm    return(retval);
733a9e6ba3markm}  /* end of getterminaltype */
734a9e6ba3markm
735a9e6ba3markm    void
736a9e6ba3markm_gettermname()
737a9e6ba3markm{
738a9e6ba3markm    /*
739a9e6ba3markm     * If the client turned off the option,
740a9e6ba3markm     * we can't send another request, so we
741a9e6ba3markm     * just return.
742a9e6ba3markm     */
743a9e6ba3markm    if (his_state_is_wont(TELOPT_TTYPE))
744a9e6ba3markm	return;
745a9e6ba3markm    settimer(baseline);
746a9e6ba3markm    memmove(nfrontp, ttytype_sbbuf, sizeof ttytype_sbbuf);
747a9e6ba3markm    nfrontp += sizeof ttytype_sbbuf;
748a9e6ba3markm    DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
749a9e6ba3markm					sizeof ttytype_sbbuf - 2););
750a9e6ba3markm    while (sequenceIs(ttypesubopt, baseline))
751a9e6ba3markm	ttloop();
752a9e6ba3markm}
753a9e6ba3markm
754a9e6ba3markm    int
755a9e6ba3markmterminaltypeok(s)
756a9e6ba3markm    char *s;
757a9e6ba3markm{
758a9e6ba3markm    char buf[1024];
759a9e6ba3markm
760a9e6ba3markm    if (terminaltype == NULL)
761a9e6ba3markm	return(1);
762a9e6ba3markm
763a9e6ba3markm    /*
764a9e6ba3markm     * tgetent() will return 1 if the type is known, and
765a9e6ba3markm     * 0 if it is not known.  If it returns -1, it couldn't
766a9e6ba3markm     * open the database.  But if we can't open the database,
767a9e6ba3markm     * it won't help to say we failed, because we won't be
768a9e6ba3markm     * able to verify anything else.  So, we treat -1 like 1.
769a9e6ba3markm     */
770a9e6ba3markm    if (tgetent(buf, s) == 0)
771a9e6ba3markm	return(0);
772a9e6ba3markm    return(1);
773a9e6ba3markm}
774a9e6ba3markm
775a9e6ba3markm#ifndef	MAXHOSTNAMELEN
776a9e6ba3markm#define	MAXHOSTNAMELEN 64
777a9e6ba3markm#endif	/* MAXHOSTNAMELEN */
778a9e6ba3markm
779a9e6ba3markmchar *hostname;
780a9e6ba3markmchar host_name[MAXHOSTNAMELEN];
781a9e6ba3markmchar remote_host_name[MAXHOSTNAMELEN];
782a9e6ba3markm
783a9e6ba3markm#ifndef	convex
784a9e6ba3markmextern void telnet P((int, int));
785a9e6ba3markm#else
786a9e6ba3markmextern void telnet P((int, int, char *));
787a9e6ba3markm#endif
788a9e6ba3markm
789a9e6ba3markm/*
790a9e6ba3markm * Get a pty, scan input lines.
791a9e6ba3markm */
792a9e6ba3markmdoit(who)
793a9e6ba3markm	struct sockaddr_in *who;
794a9e6ba3markm{
795a9e6ba3markm	char *host, *inet_ntoa();
796a9e6ba3markm	int t;
797a9e6ba3markm	struct hostent *hp;
798a9e6ba3markm	int level;
799a9e6ba3markm	int ptynum;
800a9e6ba3markm	char user_name[256];
801a9e6ba3markm
802a9e6ba3markm	/*
803a9e6ba3markm	 * Find an available pty to use.
804a9e6ba3markm	 */
805a9e6ba3markm#ifndef	convex
806a9e6ba3markm	pty = getpty(&ptynum);
807a9e6ba3markm	if (pty < 0)
808a9e6ba3markm		fatal(net, "All network ports in use");
809a9e6ba3markm#else
810a9e6ba3markm	for (;;) {
811a9e6ba3markm		char *lp;
812a9e6ba3markm		extern char *line, *getpty();
813a9e6ba3markm
814a9e6ba3markm		if ((lp = getpty()) == NULL)
815a9e6ba3markm			fatal(net, "Out of ptys");
816a9e6ba3markm
817a9e6ba3markm		if ((pty = open(lp, 2)) >= 0) {
818a9e6ba3markm			strcpy(line,lp);
819a9e6ba3markm			line[5] = 't';
820a9e6ba3markm			break;
821a9e6ba3markm		}
822a9e6ba3markm	}
823a9e6ba3markm#endif
824a9e6ba3markm
825a9e6ba3markm#if	defined(_SC_CRAY_SECURE_SYS)
826a9e6ba3markm	/*
827a9e6ba3markm	 *	set ttyp line security label
828a9e6ba3markm	 */
829a9e6ba3markm	if (secflag) {
830a9e6ba3markm		char slave_dev[16];
831a9e6ba3markm
832a9e6ba3markm		sprintf(tty_dev, "/dev/pty/%03d", ptynum);
833a9e6ba3markm		if (setdevs(tty_dev, &dv) < 0)
834a9e6ba3markm		 	fatal(net, "cannot set pty security");
835a9e6ba3markm		sprintf(slave_dev, "/dev/ttyp%03d", ptynum);
836a9e6ba3markm		if (setdevs(slave_dev, &dv) < 0)
837a9e6ba3markm		 	fatal(net, "cannot set tty security");
838a9e6ba3markm	}
839a9e6ba3markm#endif	/* _SC_CRAY_SECURE_SYS */
840a9e6ba3markm
841a9e6ba3markm	/* get name of connected client */
842a9e6ba3markm	hp = gethostbyaddr((char *)&who->sin_addr, sizeof (struct in_addr),
843a9e6ba3markm		who->sin_family);
844a9e6ba3markm
845a9e6ba3markm	if (hp == NULL && registerd_host_only) {
846a9e6ba3markm		fatal(net, "Couldn't resolve your address into a host name.\r\n\
847a9e6ba3markm	 Please contact your net administrator");
848a9e6ba3markm	} else if (hp &&
849a9e6ba3markm	    (strlen(hp->h_name) <= (unsigned int)((utmp_len < 0) ? -utmp_len
850a9e6ba3markm								 : utmp_len))) {
851a9e6ba3markm		host = hp->h_name;
852a9e6ba3markm	} else {
853a9e6ba3markm		host = inet_ntoa(who->sin_addr);
854a9e6ba3markm	}
855a9e6ba3markm	/*
856a9e6ba3markm	 * We must make a copy because Kerberos is probably going
857a9e6ba3markm	 * to also do a gethost* and overwrite the static data...
858a9e6ba3markm	 */
859a9e6ba3markm	strncpy(remote_host_name, host, sizeof(remote_host_name)-1);
860a9e6ba3markm	remote_host_name[sizeof(remote_host_name)-1] = 0;
861a9e6ba3markm	host = remote_host_name;
862a9e6ba3markm
863a9e6ba3markm	(void) gethostname(host_name, sizeof (host_name));
864a9e6ba3markm	hostname = host_name;
865a9e6ba3markm
866a9e6ba3markm#if	defined(AUTHENTICATION) || defined(ENCRYPTION)
867a9e6ba3markm	auth_encrypt_init(hostname, host, "TELNETD", 1);
868a9e6ba3markm#endif
869a9e6ba3markm
870a9e6ba3markm	init_env();
871a9e6ba3markm	/*
872a9e6ba3markm	 * get terminal type.
873a9e6ba3markm	 */
874a9e6ba3markm	*user_name = 0;
875a9e6ba3markm	level = getterminaltype(user_name);
876a9e6ba3markm	setenv("TERM", terminaltype ? terminaltype : "network", 1);
877a9e6ba3markm
878a9e6ba3markm	/*
879a9e6ba3markm	 * Start up the login process on the slave side of the terminal
880a9e6ba3markm	 */
881a9e6ba3markm#ifndef	convex
882a9e6ba3markm	startslave(host, level, user_name);
883a9e6ba3markm
884a9e6ba3markm#if	defined(_SC_CRAY_SECURE_SYS)
885a9e6ba3markm	if (secflag) {
886a9e6ba3markm		if (setulvl(dv.dv_actlvl) < 0)
887a9e6ba3markm			fatal(net,"cannot setulvl()");
888a9e6ba3markm		if (setucmp(dv.dv_actcmp) < 0)
889a9e6ba3markm			fatal(net, "cannot setucmp()");
890a9e6ba3markm	}
891a9e6ba3markm#endif	/* _SC_CRAY_SECURE_SYS */
892a9e6ba3markm
893a9e6ba3markm	telnet(net, pty);  /* begin server processing */
894a9e6ba3markm#else
895a9e6ba3markm	telnet(net, pty, host);
896a9e6ba3markm#endif
897a9e6ba3markm	/*NOTREACHED*/
898a9e6ba3markm}  /* end of doit */
899a9e6ba3markm
900a9e6ba3markm#if	defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50)
901a9e6ba3markm	int
902a9e6ba3markmXterm_output(ibufp, obuf, icountp, ocount)
903a9e6ba3markm	char **ibufp, *obuf;
904a9e6ba3markm	int *icountp, ocount;
905a9e6ba3markm{
906a9e6ba3markm	int ret;
907a9e6ba3markm	ret = term_output(*ibufp, obuf, *icountp, ocount);
908a9e6ba3markm	*ibufp += *icountp;
909a9e6ba3markm	*icountp = 0;
910a9e6ba3markm	return(ret);
911a9e6ba3markm}
912a9e6ba3markm#define	term_output	Xterm_output
913a9e6ba3markm#endif	/* defined(CRAY2) && defined(UNICOS5) && defined(UNICOS50) */
914a9e6ba3markm
915a9e6ba3markm/*
916a9e6ba3markm * Main loop.  Select from pty and network, and
917a9e6ba3markm * hand data to telnet receiver finite state machine.
918a9e6ba3markm */
919a9e6ba3markm	void
920a9e6ba3markm#ifndef	convex
921a9e6ba3markmtelnet(f, p)
922a9e6ba3markm#else
923a9e6ba3markmtelnet(f, p, host)
924a9e6ba3markm#endif
925a9e6ba3markm	int f, p;
926a9e6ba3markm#ifdef convex
927a9e6ba3markm	char *host;
928a9e6ba3markm#endif
929a9e6ba3markm{
930a9e6ba3markm	int on = 1;
931a9e6ba3markm#define	TABBUFSIZ	512
932a9e6ba3markm	char	defent[TABBUFSIZ];
933a9e6ba3markm	char	defstrs[TABBUFSIZ];
934a9e6ba3markm#undef	TABBUFSIZ
935a9e6ba3markm	char *HE;
936a9e6ba3markm	char *HN;
937a9e6ba3markm	char *IM;
938a9e6ba3markm	void netflush();
939a9e6ba3markm	int nfd;
940a9e6ba3markm
941a9e6ba3markm	/*
942a9e6ba3markm	 * Initialize the slc mapping table.
943a9e6ba3markm	 */
944a9e6ba3markm	get_slc_defaults();
945a9e6ba3markm
946a9e6ba3markm	/*
947a9e6ba3markm	 * Do some tests where it is desireable to wait for a response.
948a9e6ba3markm	 * Rather than doing them slowly, one at a time, do them all
949a9e6ba3markm	 * at once.
950a9e6ba3markm	 */
951a9e6ba3markm	if (my_state_is_wont(TELOPT_SGA))
952a9e6ba3markm		send_will(TELOPT_SGA, 1);
953a9e6ba3markm	/*
954a9e6ba3markm	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
955a9e6ba3markm	 * because 4.2 clients are unable to deal with TCP urgent data.
956a9e6ba3markm	 *
957a9e6ba3markm	 * To find out, we send out a "DO ECHO".  If the remote system
958a9e6ba3markm	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
959a9e6ba3markm	 * that fact ("WILL ECHO" ==> that the client will echo what
960a9e6ba3markm	 * WE, the server, sends it; it does NOT mean that the client will
961a9e6ba3markm	 * echo the terminal input).
962a9e6ba3markm	 */
963a9e6ba3markm	send_do(TELOPT_ECHO, 1);
964a9e6ba3markm
965a9e6ba3markm#ifdef	LINEMODE
966a9e6ba3markm	if (his_state_is_wont(TELOPT_LINEMODE)) {
967a9e6ba3markm		/* Query the peer for linemode support by trying to negotiate
968a9e6ba3markm		 * the linemode option.
969a9e6ba3markm		 */
970a9e6ba3markm		linemode = 0;
971a9e6ba3markm		editmode = 0;
972a9e6ba3markm		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
973a9e6ba3markm	}
974a9e6ba3markm#endif	/* LINEMODE */
975a9e6ba3markm
976a9e6ba3markm	/*
977a9e6ba3markm	 * Send along a couple of other options that we wish to negotiate.
978a9e6ba3markm	 */
979a9e6ba3markm	send_do(TELOPT_NAWS, 1);
980a9e6ba3markm	send_will(TELOPT_STATUS, 1);
981a9e6ba3markm	flowmode = 1;		/* default flow control state */
982a9e6ba3markm	restartany = -1;	/* uninitialized... */
983a9e6ba3markm	send_do(TELOPT_LFLOW, 1);
984a9e6ba3markm
985a9e6ba3markm	/*
986a9e6ba3markm	 * Spin, waiting for a response from the DO ECHO.  However,
987a9e6ba3markm	 * some REALLY DUMB telnets out there might not respond
988a9e6ba3markm	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
989a9e6ba3markm	 * telnets so far seem to respond with WONT for a DO that
990a9e6ba3markm	 * they don't understand...) because by the time we get the
991a9e6ba3markm	 * response, it will already have processed the DO ECHO.
992a9e6ba3markm	 * Kludge upon kludge.
993a9e6ba3markm	 */
994a9e6ba3markm	while (his_will_wont_is_changing(TELOPT_NAWS))
995a9e6ba3markm		ttloop();
996a9e6ba3markm
997a9e6ba3markm	/*
998a9e6ba3markm	 * But...
999a9e6ba3markm	 * The client might have sent a WILL NAWS as part of its
1000a9e6ba3markm	 * startup code; if so, we'll be here before we get the
1001a9e6ba3markm	 * response to the DO ECHO.  We'll make the assumption
1002a9e6ba3markm	 * that any implementation that understands about NAWS
1003a9e6ba3markm	 * is a modern enough implementation that it will respond
1004a9e6ba3markm	 * to our DO ECHO request; hence we'll do another spin
1005a9e6ba3markm	 * waiting for the ECHO option to settle down, which is
1006a9e6ba3markm	 * what we wanted to do in the first place...
1007a9e6ba3markm	 */
1008a9e6ba3markm	if (his_want_state_is_will(TELOPT_ECHO) &&
1009a9e6ba3markm	    his_state_is_will(TELOPT_NAWS)) {
1010a9e6ba3markm		while (his_will_wont_is_changing(TELOPT_ECHO))
1011a9e6ba3markm			ttloop();
1012a9e6ba3markm	}
1013a9e6ba3markm	/*
1014a9e6ba3markm	 * On the off chance that the telnet client is broken and does not
1015a9e6ba3markm	 * respond to the DO ECHO we sent, (after all, we did send the
1016a9e6ba3markm	 * DO NAWS negotiation after the DO ECHO, and we won't get here
1017a9e6ba3markm	 * until a response to the DO NAWS comes back) simulate the
1018a9e6ba3markm	 * receipt of a will echo.  This will also send a WONT ECHO
1019a9e6ba3markm	 * to the client, since we assume that the client failed to
1020a9e6ba3markm	 * respond because it believes that it is already in DO ECHO
1021a9e6ba3markm	 * mode, which we do not want.
1022a9e6ba3markm	 */
1023a9e6ba3markm	if (his_want_state_is_will(TELOPT_ECHO)) {
1024a9e6ba3markm		DIAG(TD_OPTIONS,
1025a9e6ba3markm			{sprintf(nfrontp, "td: simulating recv\r\n");
1026a9e6ba3markm			 nfrontp += strlen(nfrontp);});
1027a9e6ba3markm		willoption(TELOPT_ECHO);
1028a9e6ba3markm	}
1029a9e6ba3markm
1030a9e6ba3markm	/*
1031a9e6ba3markm	 * Finally, to clean things up, we turn on our echo.  This
1032a9e6ba3markm	 * will break stupid 4.2 telnets out of local terminal echo.
1033a9e6ba3markm	 */
1034a9e6ba3markm
1035a9e6ba3markm	if (my_state_is_wont(TELOPT_ECHO))
1036a9e6ba3markm		send_will(TELOPT_ECHO, 1);
1037a9e6ba3markm
1038a9e6ba3markm#ifndef	STREAMSPTY
1039a9e6ba3markm	/*
1040a9e6ba3markm	 * Turn on packet mode
1041a9e6ba3markm	 */
1042a9e6ba3markm	(void) ioctl(p, TIOCPKT, (char *)&on);
1043a9e6ba3markm#endif
1044a9e6ba3markm
1045a9e6ba3markm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
1046a9e6ba3markm	/*
1047a9e6ba3markm	 * Continuing line mode support.  If client does not support
1048a9e6ba3markm	 * real linemode, attempt to negotiate kludge linemode by sending
1049a9e6ba3markm	 * the do timing mark sequence.
1050a9e6ba3markm	 */
1051a9e6ba3markm	if (lmodetype < REAL_LINEMODE)
1052a9e6ba3markm		send_do(TELOPT_TM, 1);
1053a9e6ba3markm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
1054a9e6ba3markm
1055a9e6ba3markm	/*
1056a9e6ba3markm	 * Call telrcv() once to pick up anything received during
1057a9e6ba3markm	 * terminal type negotiation, 4.2/4.3 determination, and
1058a9e6ba3markm	 * linemode negotiation.
1059a9e6ba3markm	 */
1060a9e6ba3markm	telrcv();
1061a9e6ba3markm
1062a9e6ba3markm	(void) ioctl(f, FIONBIO, (char *)&on);
1063a9e6ba3markm	(void) ioctl(p, FIONBIO, (char *)&on);
1064a9e6ba3markm#if	defined(CRAY2) && defined(UNICOS5)
1065a9e6ba3markm	init_termdriver(f, p, interrupt, sendbrk);
1066a9e6ba3markm#endif
1067a9e6ba3markm
1068a9e6ba3markm#if	defined(SO_OOBINLINE)
1069a9e6ba3markm	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE,
1070a9e6ba3markm				(char *)&on, sizeof on);
1071a9e6ba3markm#endif	/* defined(SO_OOBINLINE) */
1072a9e6ba3markm
1073a9e6ba3markm#ifdef	SIGTSTP
1074a9e6ba3markm	(void) signal(SIGTSTP, SIG_IGN);
1075a9e6ba3markm#endif
1076a9e6ba3markm#ifdef	SIGTTOU
1077a9e6ba3markm	/*
1078a9e6ba3markm	 * Ignoring SIGTTOU keeps the kernel from blocking us
1079a9e6ba3markm	 * in ttioct() in /sys/tty.c.
1080a9e6ba3markm	 */
1081a9e6ba3markm	(void) signal(SIGTTOU, SIG_IGN);
1082a9e6ba3markm#endif
1083a9e6ba3markm
1084a9e6ba3markm	(void) signal(SIGCHLD, cleanup);
1085a9e6ba3markm
1086a9e6ba3markm#if	defined(CRAY2) && defined(UNICOS5)
1087a9e6ba3markm	/*
1088a9e6ba3markm	 * Cray-2 will send a signal when pty modes are changed by slave
1089a9e6ba3markm	 * side.  Set up signal handler now.
1090a9e6ba3markm	 */
1091a9e6ba3markm	if ((int)signal(SIGUSR1, termstat) < 0)
1092a9e6ba3markm		perror("signal");
1093a9e6ba3markm	else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0)
1094a9e6ba3markm		perror("ioctl:TCSIGME");
1095a9e6ba3markm	/*
1096a9e6ba3markm	 * Make processing loop check terminal characteristics early on.
1097a9e6ba3markm	 */
1098a9e6ba3markm	termstat();
1099a9e6ba3markm#endif
1100a9e6ba3markm
1101a9e6ba3markm#ifdef  TIOCNOTTY
1102a9e6ba3markm	{
1103a9e6ba3markm		register int t;
1104a9e6ba3markm		t = open(_PATH_TTY, O_RDWR);
1105a9e6ba3markm		if (t >= 0) {
1106a9e6ba3markm			(void) ioctl(t, TIOCNOTTY, (char *)0);
1107a9e6ba3markm			(void) close(t);
1108a9e6ba3markm		}
1109a9e6ba3markm	}
1110a9e6ba3markm#endif
1111a9e6ba3markm
1112a9e6ba3markm#if	defined(CRAY) && defined(NEWINIT) && defined(TIOCSCTTY)
1113a9e6ba3markm	(void) setsid();
1114a9e6ba3markm	ioctl(p, TIOCSCTTY, 0);
1115a9e6ba3markm#endif
1116a9e6ba3markm
1117a9e6ba3markm	/*
1118a9e6ba3markm	 * Show banner that getty never gave.
1119a9e6ba3markm	 *
1120a9e6ba3markm	 * We put the banner in the pty input buffer.  This way, it
1121a9e6ba3markm	 * gets carriage return null processing, etc., just like all
1122a9e6ba3markm	 * other pty --> client data.
1123a9e6ba3markm	 */
1124a9e6ba3markm
1125a9e6ba3markm#if	!defined(CRAY) || !defined(NEWINIT)
1126a9e6ba3markm	if (getenv("USER"))
1127a9e6ba3markm		hostinfo = 0;
1128a9e6ba3markm#endif
1129a9e6ba3markm
1130a9e6ba3markm	if (getent(defent, "default") == 1) {
1131a9e6ba3markm		char *getstr();
1132a9e6ba3markm		char *cp=defstrs;
1133a9e6ba3markm
1134a9e6ba3markm		HE = getstr("he", &cp);
1135a9e6ba3markm		HN = getstr("hn", &cp);
1136a9e6ba3markm		IM = getstr("im", &cp);
1137a9e6ba3markm		if (HN && *HN)
1138a9e6ba3markm			(void) strcpy(host_name, HN);
1139a9e6ba3markm		if (IM == 0)
1140a9e6ba3markm			IM = "";
1141a9e6ba3markm	} else {
1142a9e6ba3markm		IM = DEFAULT_IM;
1143a9e6ba3markm		HE = 0;
1144a9e6ba3markm	}
1145a9e6ba3markm	edithost(HE, host_name);
1146a9e6ba3markm	if (hostinfo && *IM)
1147a9e6ba3markm		putf(IM, ptyibuf2);
1148a9e6ba3markm
1149a9e6ba3markm	if (pcc)
1150a9e6ba3markm		(void) strncat(ptyibuf2, ptyip, pcc+1);
1151a9e6ba3markm	ptyip = ptyibuf2;
1152a9e6ba3markm	pcc = strlen(ptyip);
1153a9e6ba3markm#ifdef	LINEMODE
1154a9e6ba3markm	/*
1155a9e6ba3markm	 * Last check to make sure all our states are correct.
1156a9e6ba3markm	 */
1157a9e6ba3markm	init_termbuf();
1158a9e6ba3markm	localstat();
1159a9e6ba3markm#endif	/* LINEMODE */
1160a9e6ba3markm
1161a9e6ba3markm	DIAG(TD_REPORT,
1162a9e6ba3markm		{sprintf(nfrontp, "td: Entering processing loop\r\n");
1163a9e6ba3markm		 nfrontp += strlen(nfrontp);});
1164a9e6ba3markm
1165a9e6ba3markm#ifdef	convex
1166a9e6ba3markm	startslave(host);
1167a9e6ba3markm#endif
1168a9e6ba3markm
1169a9e6ba3markm	nfd = ((f > p) ? f : p) + 1;
1170a9e6ba3markm	for (;;) {
1171a9e6ba3markm		fd_set ibits, obits, xbits;
1172a9e6ba3markm		register int c;
1173a9e6ba3markm
1174a9e6ba3markm		if (ncc < 0 && pcc < 0)
1175a9e6ba3markm			break;
1176a9e6ba3markm
1177a9e6ba3markm#if	defined(CRAY2) && defined(UNICOS5)
1178a9e6ba3markm		if (needtermstat)
1179a9e6ba3markm			_termstat();
1180a9e6ba3markm#endif	/* defined(CRAY2) && defined(UNICOS5) */
1181a9e6ba3markm		FD_ZERO(&ibits);
1182a9e6ba3markm		FD_ZERO(&obits);
1183a9e6ba3markm		FD_ZERO(&xbits);
1184a9e6ba3markm		/*
1185a9e6ba3markm		 * Never look for input if there's still
1186a9e6ba3markm		 * stuff in the corresponding output buffer
1187a9e6ba3markm		 */
1188a9e6ba3markm		if (nfrontp - nbackp || pcc > 0) {
1189a9e6ba3markm			FD_SET(f, &obits);
1190a9e6ba3markm		} else {
1191a9e6ba3markm			FD_SET(p, &ibits);
1192a9e6ba3markm		}
1193a9e6ba3markm		if (pfrontp - pbackp || ncc > 0) {
1194a9e6ba3markm			FD_SET(p, &obits);
1195a9e6ba3markm		} else {
1196a9e6ba3markm			FD_SET(f, &ibits);
1197a9e6ba3markm		}
1198a9e6ba3markm		if (!SYNCHing) {
1199a9e6ba3markm			FD_SET(f, &xbits);
1200a9e6ba3markm		}
1201a9e6ba3markm		if ((c = select(nfd, &ibits, &obits, &xbits,
1202a9e6ba3markm						(struct timeval *)0)) < 1) {
1203a9e6ba3markm			if (c == -1) {
1204a9e6ba3markm				if (errno == EINTR) {
1205a9e6ba3markm					continue;
1206a9e6ba3markm				}
1207a9e6ba3markm			}
1208a9e6ba3markm			sleep(5);
1209a9e6ba3markm			continue;
1210a9e6ba3markm		}
1211a9e6ba3markm
1212a9e6ba3markm		/*
1213a9e6ba3markm		 * Any urgent data?
1214a9e6ba3markm		 */
1215a9e6ba3markm		if (FD_ISSET(net, &xbits)) {
1216a9e6ba3markm		    SYNCHing = 1;
1217a9e6ba3markm		}
1218a9e6ba3markm
1219a9e6ba3markm		/*
1220a9e6ba3markm		 * Something to read from the network...
1221a9e6ba3markm		 */
1222a9e6ba3markm		if (FD_ISSET(net, &ibits)) {
1223a9e6ba3markm#if	!defined(SO_OOBINLINE)
1224a9e6ba3markm			/*
1225a9e6ba3markm			 * In 4.2 (and 4.3 beta) systems, the
1226a9e6ba3markm			 * OOB indication and data handling in the kernel
1227a9e6ba3markm			 * is such that if two separate TCP Urgent requests
1228a9e6ba3markm			 * come in, one byte of TCP data will be overlaid.
1229a9e6ba3markm			 * This is fatal for Telnet, but we try to live
1230a9e6ba3markm			 * with it.
1231a9e6ba3markm			 *
1232a9e6ba3markm			 * In addition, in 4.2 (and...), a special protocol
1233a9e6ba3markm			 * is needed to pick up the TCP Urgent data in
1234a9e6ba3markm			 * the correct sequence.
1235a9e6ba3markm			 *
1236a9e6ba3markm			 * What we do is:  if we think we are in urgent
1237a9e6ba3markm			 * mode, we look to see if we are "at the mark".
1238a9e6ba3markm			 * If we are, we do an OOB receive.  If we run
1239a9e6ba3markm			 * this twice, we will do the OOB receive twice,
1240a9e6ba3markm			 * but the second will fail, since the second
1241a9e6ba3markm			 * time we were "at the mark", but there wasn't
1242a9e6ba3markm			 * any data there (the kernel doesn't reset
1243a9e6ba3markm			 * "at the mark" until we do a normal read).
1244a9e6ba3markm			 * Once we've read the OOB data, we go ahead
1245a9e6ba3markm			 * and do normal reads.
1246a9e6ba3markm			 *
1247a9e6ba3markm			 * There is also another problem, which is that
1248a9e6ba3markm			 * since the OOB byte we read doesn't put us
1249a9e6ba3markm			 * out of OOB state, and since that byte is most
1250a9e6ba3markm			 * likely the TELNET DM (data mark), we would
1251a9e6ba3markm			 * stay in the TELNET SYNCH (SYNCHing) state.
1252a9e6ba3markm			 * So, clocks to the rescue.  If we've "just"
1253a9e6ba3markm			 * received a DM, then we test for the
1254a9e6ba3markm			 * presence of OOB data when the receive OOB
1255a9e6ba3markm			 * fails (and AFTER we did the normal mode read
1256a9e6ba3markm			 * to clear "at the mark").
1257a9e6ba3markm			 */
1258a9e6ba3markm		    if (SYNCHing) {
1259a9e6ba3markm			int atmark;
1260a9e6ba3markm
1261a9e6ba3markm			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
1262a9e6ba3markm			if (atmark) {
1263a9e6ba3markm			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
1264a9e6ba3markm			    if ((ncc == -1) && (errno == EINVAL)) {
1265a9e6ba3markm				ncc = read(net, netibuf, sizeof (netibuf));
1266a9e6ba3markm				if (sequenceIs(didnetreceive, gotDM)) {
1267a9e6ba3markm				    SYNCHing = stilloob(net);
1268a9e6ba3markm				}
1269a9e6ba3markm			    }
1270a9e6ba3markm			} else {
1271a9e6ba3markm			    ncc = read(net, netibuf, sizeof (netibuf));
1272a9e6ba3markm			}
1273a9e6ba3markm		    } else {
1274a9e6ba3markm			ncc = read(net, netibuf, sizeof (netibuf));
1275a9e6ba3markm		    }
1276a9e6ba3markm		    settimer(didnetreceive);
1277a9e6ba3markm#else	/* !defined(SO_OOBINLINE)) */
1278a9e6ba3markm		    ncc = read(net, netibuf, sizeof (netibuf));
1279a9e6ba3markm#endif	/* !defined(SO_OOBINLINE)) */
1280a9e6ba3markm		    if (ncc < 0 && errno == EWOULDBLOCK)
1281a9e6ba3markm			ncc = 0;
1282a9e6ba3markm		    else {
1283a9e6ba3markm			if (ncc <= 0) {
1284a9e6ba3markm			    break;
1285a9e6ba3markm			}
1286a9e6ba3markm			netip = netibuf;
1287a9e6ba3markm		    }
1288a9e6ba3markm		    DIAG((TD_REPORT | TD_NETDATA),
1289a9e6ba3markm			    {sprintf(nfrontp, "td: netread %d chars\r\n", ncc);
1290a9e6ba3markm			     nfrontp += strlen(nfrontp);});
1291a9e6ba3markm		    DIAG(TD_NETDATA, printdata("nd", netip, ncc));
1292a9e6ba3markm		}
1293a9e6ba3markm
1294a9e6ba3markm		/*
1295a9e6ba3markm		 * Something to read from the pty...
1296a9e6ba3markm		 */
1297a9e6ba3markm		if (FD_ISSET(p, &ibits)) {
1298a9e6ba3markm#ifndef	STREAMSPTY
1299a9e6ba3markm			pcc = read(p, ptyibuf, BUFSIZ);
1300a9e6ba3markm#else
1301a9e6ba3markm			pcc = readstream(p, ptyibuf, BUFSIZ);
1302a9e6ba3markm#endif
1303a9e6ba3markm			/*
1304a9e6ba3markm			 * On some systems, if we try to read something
1305a9e6ba3markm			 * off the master side before the slave side is
1306a9e6ba3markm			 * opened, we get EIO.
1307a9e6ba3markm			 */
1308a9e6ba3markm			if (pcc < 0 && (errno == EWOULDBLOCK ||
1309a9e6ba3markm#ifdef	EAGAIN
1310a9e6ba3markm					errno == EAGAIN ||
1311a9e6ba3markm#endif
1312a9e6ba3markm					errno == EIO)) {
1313a9e6ba3markm				pcc = 0;
1314a9e6ba3markm			} else {
1315a9e6ba3markm				if (pcc <= 0)
1316a9e6ba3markm					break;
1317a9e6ba3markm#if	!defined(CRAY2) || !defined(UNICOS5)
1318a9e6ba3markm#ifdef	LINEMODE
1319a9e6ba3markm				/*
1320a9e6ba3markm				 * If ioctl from pty, pass it through net
1321a9e6ba3markm				 */
1322a9e6ba3markm				if (ptyibuf[0] & TIOCPKT_IOCTL) {
1323a9e6ba3markm					copy_termbuf(ptyibuf+1, pcc-1);
1324a9e6ba3markm					localstat();
1325a9e6ba3markm					pcc = 1;
1326a9e6ba3markm				}
1327a9e6ba3markm#endif	/* LINEMODE */
1328a9e6ba3markm				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
1329a9e6ba3markm					netclear();	/* clear buffer back */
1330a9e6ba3markm#ifndef	NO_URGENT
1331a9e6ba3markm					/*
1332a9e6ba3markm					 * There are client telnets on some
1333a9e6ba3markm					 * operating systems get screwed up
1334a9e6ba3markm					 * royally if we send them urgent
1335a9e6ba3markm					 * mode data.
1336a9e6ba3markm					 */
1337a9e6ba3markm					*nfrontp++ = IAC;
1338a9e6ba3markm					*nfrontp++ = DM;
1339a9e6ba3markm					neturg = nfrontp-1; /* off by one XXX */
1340a9e6ba3markm					DIAG(TD_OPTIONS,
1341a9e6ba3markm					    printoption("td: send IAC", DM));
1342a9e6ba3markm
1343a9e6ba3markm#endif
1344a9e6ba3markm				}
1345a9e6ba3markm				if (his_state_is_will(TELOPT_LFLOW) &&
1346a9e6ba3markm				    (ptyibuf[0] &
1347a9e6ba3markm				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
1348a9e6ba3markm					int newflow =
1349a9e6ba3markm					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
1350a9e6ba3markm					if (newflow != flowmode) {
1351a9e6ba3markm						flowmode = newflow;
1352a9e6ba3markm						(void) sprintf(nfrontp,
1353a9e6ba3markm							"%c%c%c%c%c%c",
1354a9e6ba3markm							IAC, SB, TELOPT_LFLOW,
1355a9e6ba3markm							flowmode ? LFLOW_ON
1356a9e6ba3markm								 : LFLOW_OFF,
1357a9e6ba3markm							IAC, SE);
1358a9e6ba3markm						nfrontp += 6;
1359a9e6ba3markm						DIAG(TD_OPTIONS, printsub('>',
1360a9e6ba3markm						    (unsigned char *)nfrontp-4,
1361a9e6ba3markm						    4););
1362a9e6ba3markm					}
1363a9e6ba3markm				}
1364a9e6ba3markm				pcc--;
1365a9e6ba3markm				ptyip = ptyibuf+1;
1366a9e6ba3markm#else	/* defined(CRAY2) && defined(UNICOS5) */
1367a9e6ba3markm				if (!uselinemode) {
1368a9e6ba3markm					unpcc = pcc;
1369a9e6ba3markm					unptyip = ptyibuf;
1370a9e6ba3markm					pcc = term_output(&unptyip, ptyibuf2,
1371a9e6ba3markm								&unpcc, BUFSIZ);
1372a9e6ba3markm					ptyip = ptyibuf2;
1373a9e6ba3markm				} else
1374a9e6ba3markm					ptyip = ptyibuf;
1375a9e6ba3markm#endif	/* defined(CRAY2) && defined(UNICOS5) */
1376a9e6ba3markm			}
1377a9e6ba3markm		}
1378a9e6ba3markm
1379a9e6ba3markm		while (pcc > 0) {
1380a9e6ba3markm			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
1381a9e6ba3markm				break;
1382a9e6ba3markm			c = *ptyip++ & 0377, pcc--;
1383a9e6ba3markm			if (c == IAC)
1384a9e6ba3markm				*nfrontp++ = c;
1385a9e6ba3markm#if	defined(CRAY2) && defined(UNICOS5)
1386a9e6ba3markm			else if (c == '\n' &&
1387a9e6ba3markm				     my_state_is_wont(TELOPT_BINARY) && newmap)
1388a9e6ba3markm				*nfrontp++ = '\r';
1389a9e6ba3markm#endif	/* defined(CRAY2) && defined(UNICOS5) */
1390a9e6ba3markm			*nfrontp++ = c;
1391a9e6ba3markm			if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
1392a9e6ba3markm				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
1393a9e6ba3markm					*nfrontp++ = *ptyip++ & 0377;
1394a9e6ba3markm					pcc--;
1395a9e6ba3markm				} else
1396a9e6ba3markm					*nfrontp++ = '\0';
1397a9e6ba3markm			}
1398a9e6ba3markm		}
1399a9e6ba3markm#if	defined(CRAY2) && defined(UNICOS5)
1400a9e6ba3markm		/*
1401a9e6ba3markm		 * If chars were left over from the terminal driver,
1402a9e6ba3markm		 * note their existence.
1403a9e6ba3markm		 */
1404a9e6ba3markm		if (!uselinemode && unpcc) {
1405a9e6ba3markm			pcc = unpcc;
1406a9e6ba3markm			unpcc = 0;
1407a9e6ba3markm			ptyip = unptyip;
1408a9e6ba3markm		}
1409a9e6ba3markm#endif	/* defined(CRAY2) && defined(UNICOS5) */
1410a9e6ba3markm
1411a9e6ba3markm		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
1412a9e6ba3markm			netflush();
1413a9e6ba3markm		if (ncc > 0)
1414a9e6ba3markm			telrcv();
1415a9e6ba3markm		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
1416a9e6ba3markm			ptyflush();
1417a9e6ba3markm	}
1418a9e6ba3markm	cleanup(0);
1419a9e6ba3markm}  /* end of telnet */
1420a9e6ba3markm
1421a9e6ba3markm#ifndef	TCSIG
1422a9e6ba3markm# ifdef	TIOCSIG
1423a9e6ba3markm#  define TCSIG TIOCSIG
1424a9e6ba3markm# endif
1425a9e6ba3markm#endif
1426a9e6ba3markm
1427a9e6ba3markm#ifdef	STREAMSPTY
1428a9e6ba3markm
1429a9e6ba3markmint flowison = -1;  /* current state of flow: -1 is unknown */
1430a9e6ba3markm
1431a9e6ba3markmint readstream(p, ibuf, bufsize)
1432a9e6ba3markm	int p;
1433a9e6ba3markm	char *ibuf;
1434a9e6ba3markm	int bufsize;
1435a9e6ba3markm{
1436a9e6ba3markm	int flags = 0;
1437a9e6ba3markm	int ret = 0;
1438a9e6ba3markm	struct termios *tsp;
1439a9e6ba3markm	struct termio *tp;
1440a9e6ba3markm	struct iocblk *ip;
1441a9e6ba3markm	char vstop, vstart;
1442a9e6ba3markm	int ixon;
1443a9e6ba3markm	int newflow;
1444a9e6ba3markm
1445a9e6ba3markm	strbufc.maxlen = BUFSIZ;
1446a9e6ba3markm	strbufc.buf = (char *)ctlbuf;
1447a9e6ba3markm	strbufd.maxlen = bufsize-1;
1448a9e6ba3markm	strbufd.len = 0;
1449a9e6ba3markm	strbufd.buf = ibuf+1;
1450a9e6ba3markm	ibuf[0] = 0;
1451a9e6ba3markm
1452a9e6ba3markm	ret = getmsg(p, &strbufc, &strbufd, &flags);
1453a9e6ba3markm	if (ret < 0)  /* error of some sort -- probably EAGAIN */
1454a9e6ba3markm		return(-1);
1455a9e6ba3markm
1456a9e6ba3markm	if (strbufc.len <= 0 || ctlbuf[0] == M_DATA) {
1457a9e6ba3markm		/* data message */
1458a9e6ba3markm		if (strbufd.len > 0) {			/* real data */
1459a9e6ba3markm			return(strbufd.len + 1);	/* count header char */
1460a9e6ba3markm		} else {
1461a9e6ba3markm			/* nothing there */
1462a9e6ba3markm			errno = EAGAIN;
1463a9e6ba3markm			return(-1);
1464a9e6ba3markm		}
1465a9e6ba3markm	}
1466a9e6ba3markm
1467a9e6ba3markm	/*
1468a9e6ba3markm	 * It's a control message.  Return 1, to look at the flag we set
1469a9e6ba3markm	 */
1470a9e6ba3markm
1471a9e6ba3markm	switch (ctlbuf[0]) {
1472a9e6ba3markm	case M_FLUSH:
1473a9e6ba3markm		if (ibuf[1] & FLUSHW)
1474a9e6ba3markm			ibuf[0] = TIOCPKT_FLUSHWRITE;
1475a9e6ba3markm		return(1);
1476a9e6ba3markm
1477a9e6ba3markm	case M_IOCTL:
1478a9e6ba3markm		ip = (struct iocblk *) (ibuf+1);
1479a9e6ba3markm
1480a9e6ba3markm		switch (ip->ioc_cmd) {
1481a9e6ba3markm		case TCSETS:
1482a9e6ba3markm		case TCSETSW:
1483a9e6ba3markm		case TCSETSF:
1484a9e6ba3markm			tsp = (struct termios *)
1485a9e6ba3markm					(ibuf+1 + sizeof(struct iocblk));
1486a9e6ba3markm			vstop = tsp->c_cc[VSTOP];
1487a9e6ba3markm			vstart = tsp->c_cc[VSTART];
1488a9e6ba3markm			ixon = tsp->c_iflag & IXON;
1489a9e6ba3markm			break;
1490a9e6ba3markm		case TCSETA:
1491a9e6ba3markm		case TCSETAW:
1492a9e6ba3markm		case TCSETAF:
1493a9e6ba3markm			tp = (struct termio *) (ibuf+1 + sizeof(struct iocblk));
1494a9e6ba3markm			vstop = tp->c_cc[VSTOP];
1495a9e6ba3markm			vstart = tp->c_cc[VSTART];
1496a9e6ba3markm			ixon = tp->c_iflag & IXON;
1497a9e6ba3markm			break;
1498a9e6ba3markm		default:
1499a9e6ba3markm			errno = EAGAIN;
1500a9e6ba3markm			return(-1);
1501a9e6ba3markm		}
1502a9e6ba3markm
1503a9e6ba3markm		newflow =  (ixon && (vstart == 021) && (vstop == 023)) ? 1 : 0;
1504a9e6ba3markm		if (newflow != flowison) {  /* it's a change */
1505a9e6ba3markm			flowison = newflow;
1506a9e6ba3markm			ibuf[0] = newflow ? TIOCPKT_DOSTOP : TIOCPKT_NOSTOP;
1507a9e6ba3markm			return(1);
1508a9e6ba3markm		}
1509a9e6ba3markm	}
1510a9e6ba3markm
1511a9e6ba3markm	/* nothing worth doing anything about */
1512a9e6ba3markm	errno = EAGAIN;
1513a9e6ba3markm	return(-1);
1514a9e6ba3markm}
1515a9e6ba3markm#endif /* STREAMSPTY */
1516a9e6ba3markm
1517a9e6ba3markm/*
1518a9e6ba3markm * Send interrupt to process on other side of pty.
1519a9e6ba3markm * If it is in raw mode, just write NULL;
1520a9e6ba3markm * otherwise, write intr char.
1521a9e6ba3markm */
1522a9e6ba3markm	void
1523a9e6ba3markminterrupt()
1524a9e6ba3markm{
1525a9e6ba3markm	ptyflush();	/* half-hearted */
1526a9e6ba3markm
1527a9e6ba3markm#if defined(STREAMSPTY) && defined(TIOCSIGNAL)
1528a9e6ba3markm	/* Streams PTY style ioctl to post a signal */
1529a9e6ba3markm	{
1530a9e6ba3markm		int sig = SIGINT;
1531a9e6ba3markm		(void) ioctl(pty, TIOCSIGNAL, &sig);
1532a9e6ba3markm		(void) ioctl(pty, I_FLUSH, FLUSHR);
1533a9e6ba3markm	}
1534a9e6ba3markm#else
1535a9e6ba3markm#ifdef	TCSIG
1536a9e6ba3markm	(void) ioctl(pty, TCSIG, (char *)SIGINT);
1537a9e6ba3markm#else	/* TCSIG */
1538a9e6ba3markm	init_termbuf();
1539a9e6ba3markm	*pfrontp++ = slctab[SLC_IP].sptr ?
1540a9e6ba3markm			(unsigned char)*slctab[SLC_IP].sptr : '\177';
1541a9e6ba3markm#endif	/* TCSIG */
1542a9e6ba3markm#endif
1543a9e6ba3markm}
1544a9e6ba3markm
1545a9e6ba3markm/*
1546a9e6ba3markm * Send quit to process on other side of pty.
1547a9e6ba3markm * If it is in raw mode, just write NULL;
1548a9e6ba3markm * otherwise, write quit char.
1549a9e6ba3markm */
1550a9e6ba3markm	void
1551a9e6ba3markmsendbrk()
1552a9e6ba3markm{
1553a9e6ba3markm	ptyflush();	/* half-hearted */
1554a9e6ba3markm#ifdef	TCSIG
1555a9e6ba3markm	(void) ioctl(pty, TCSIG, (char *)SIGQUIT);
1556a9e6ba3markm#else	/* TCSIG */
1557a9e6ba3markm	init_termbuf();
1558a9e6ba3markm	*pfrontp++ = slctab[SLC_ABORT].sptr ?
1559a9e6ba3markm			(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
1560a9e6ba3markm#endif	/* TCSIG */
1561a9e6ba3markm}
1562a9e6ba3markm
1563a9e6ba3markm	void
1564a9e6ba3markmsendsusp()
1565a9e6ba3markm{
1566a9e6ba3markm#ifdef	SIGTSTP
1567a9e6ba3markm	ptyflush();	/* half-hearted */
1568a9e6ba3markm# ifdef	TCSIG
1569a9e6ba3markm	(void) ioctl(pty, TCSIG, (char *)SIGTSTP);
1570a9e6ba3markm# else	/* TCSIG */
1571a9e6ba3markm	*pfrontp++ = slctab[SLC_SUSP].sptr ?
1572a9e6ba3markm			(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
1573a9e6ba3markm# endif	/* TCSIG */
1574a9e6ba3markm#endif	/* SIGTSTP */
1575a9e6ba3markm}
1576a9e6ba3markm
1577a9e6ba3markm/*
1578a9e6ba3markm * When we get an AYT, if ^T is enabled, use that.  Otherwise,
1579a9e6ba3markm * just send back "[Yes]".
1580a9e6ba3markm */
1581a9e6ba3markm	void
1582a9e6ba3markmrecv_ayt()
1583a9e6ba3markm{
1584a9e6ba3markm#if	defined(SIGINFO) && defined(TCSIG)
1585a9e6ba3markm	if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
1586a9e6ba3markm		(void) ioctl(pty, TCSIG, (char *)SIGINFO);
1587a9e6ba3markm		return;
1588a9e6ba3markm	}
1589a9e6ba3markm#endif
1590a9e6ba3markm	(void) strcpy(nfrontp, "\r\n[Yes]\r\n");
1591a9e6ba3markm	nfrontp += 9;
1592a9e6ba3markm}
1593a9e6ba3markm
1594a9e6ba3markm	void
1595a9e6ba3markmdoeof()
1596a9e6ba3markm{
1597a9e6ba3markm	init_termbuf();
1598a9e6ba3markm
1599a9e6ba3markm#if	defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
1600a9e6ba3markm	if (!tty_isediting()) {
1601a9e6ba3markm		extern char oldeofc;
1602a9e6ba3markm		*pfrontp++ = oldeofc;
1603a9e6ba3markm		return;
1604a9e6ba3markm	}
1605a9e6ba3markm#endif
1606a9e6ba3markm	*pfrontp++ = slctab[SLC_EOF].sptr ?
1607a9e6ba3markm			(unsigned char)*slctab[SLC_EOF].sptr : '\004';
1608a9e6ba3markm}
1609