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.
13f53c210emaste * 3. Neither the name of the University nor the names of its contributors
14a9e6ba3markm *    may be used to endorse or promote products derived from this software
15a9e6ba3markm *    without specific prior written permission.
16a9e6ba3markm *
17a9e6ba3markm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18a9e6ba3markm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19a9e6ba3markm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20a9e6ba3markm * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21a9e6ba3markm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22a9e6ba3markm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23a9e6ba3markm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24a9e6ba3markm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25a9e6ba3markm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26a9e6ba3markm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27a9e6ba3markm * SUCH DAMAGE.
28a9e6ba3markm */
29a9e6ba3markm
30c3792f8obrien#if 0
31a9e6ba3markm#ifndef lint
32cd2a6bemarkmstatic const char sccsid[] = "@(#)telnetd.c	8.4 (Berkeley) 5/30/95";
3325d7446charnier#endif
34c3792f8obrien#endif
35c3792f8obrien#include <sys/cdefs.h>
36c3792f8obrien__FBSDID("$FreeBSD$");
37a9e6ba3markm
38a9e6ba3markm#include "telnetd.h"
39a9e6ba3markm#include "pathnames.h"
40a9e6ba3markm
4162fa01amarkm#include <sys/mman.h>
4219fd256markm#include <err.h>
4307625d3brian#include <libutil.h>
4462fa01amarkm#include <paths.h>
4519fd256markm#include <termcap.h>
4607625d3brian
4719fd256markm#include <arpa/inet.h>
4819fd256markm
4919fd256markm#ifdef	AUTHENTICATION
50a9e6ba3markm#include <libtelnet/auth.h>
51a9e6ba3markm#endif
5219fd256markm#ifdef	ENCRYPTION
53cd2a6bemarkm#include <libtelnet/encrypt.h>
54cd2a6bemarkm#endif
55cd2a6bemarkm#include <libtelnet/misc.h>
56a9e6ba3markm
577670f1ebrianchar	remote_hostname[MAXHOSTNAMELEN];
5819fd256markmsize_t	utmp_len = sizeof(remote_hostname) - 1;
59a9e6ba3markmint	registerd_host_only = 0;
60a9e6ba3markm
61a9e6ba3markm
62a9e6ba3markm/*
63a9e6ba3markm * I/O data buffers,
64a9e6ba3markm * pointers, and counters.
65a9e6ba3markm */
66a9e6ba3markmchar	ptyibuf[BUFSIZ], *ptyip = ptyibuf;
67a9e6ba3markmchar	ptyibuf2[BUFSIZ];
68a9e6ba3markm
6919fd256markmint readstream(int, char *, int);
7019fd256markmvoid doit(struct sockaddr *);
7119fd256markmint terminaltypeok(char *);
72a9e6ba3markm
73a9e6ba3markmint	hostinfo = 1;			/* do we print login banner? */
74a9e6ba3markm
75141dcc0markmstatic int debug = 0;
76a9e6ba3markmint keepalive = 1;
7719fd256markmconst char *altlogin;
78a9e6ba3markm
7914227a4markmvoid doit(struct sockaddr *);
8014227a4markmint terminaltypeok(char *);
8114227a4markmvoid startslave(char *, int, char *);
8214227a4markmextern void usage(void);
8314227a4markmstatic void _gettermname(void);
84a9e6ba3markm
85a9e6ba3markm/*
86a9e6ba3markm * The string to pass to getopt().  We do it this way so
87a9e6ba3markm * that only the actual options that we support will be
88a9e6ba3markm * passed off to getopt().
89a9e6ba3markm */
90a9e6ba3markmchar valid_opts[] = {
91cd2a6bemarkm	'd', ':', 'h', 'k', 'n', 'p', ':', 'S', ':', 'u', ':', 'U',
92ce15efbshin	'4', '6',
93a9e6ba3markm#ifdef	AUTHENTICATION
94a9e6ba3markm	'a', ':', 'X', ':',
95a9e6ba3markm#endif
96a9e6ba3markm#ifdef BFTPDAEMON
97a9e6ba3markm	'B',
98a9e6ba3markm#endif
99a9e6ba3markm#ifdef DIAGNOSTICS
100a9e6ba3markm	'D', ':',
101a9e6ba3markm#endif
102a9e6ba3markm#ifdef	ENCRYPTION
103a9e6ba3markm	'e', ':',
104a9e6ba3markm#endif
105a9e6ba3markm#ifdef	LINEMODE
106a9e6ba3markm	'l',
107a9e6ba3markm#endif
108a9e6ba3markm	'\0'
109a9e6ba3markm};
110a9e6ba3markm
111ce15efbshinint family = AF_INET;
112ce15efbshin
11319fd256markm#ifndef	MAXHOSTNAMELEN
11419fd256markm#define	MAXHOSTNAMELEN 256
11519fd256markm#endif	/* MAXHOSTNAMELEN */
11619fd256markm
11719fd256markmchar *hostname;
11819fd256markmchar host_name[MAXHOSTNAMELEN];
11919fd256markm
12014227a4markmextern void telnet(int, int, char *);
12119fd256markm
12219fd256markmint level;
12319fd256markmchar user_name[256];
12419fd256markm
125ce15efbshinint
12619fd256markmmain(int argc, char *argv[])
127a9e6ba3markm{
128ae5da6bmaxim	u_long ultmp;
129ce15efbshin	struct sockaddr_storage from;
130a9e6ba3markm	int on = 1, fromlen;
13119fd256markm	int ch;
132a9e6ba3markm#if	defined(IPPROTO_IP) && defined(IP_TOS)
133a9e6ba3markm	int tos = -1;
134a9e6ba3markm#endif
135ae5da6bmaxim	char *ep;
136a9e6ba3markm
137a9e6ba3markm	pfrontp = pbackp = ptyobuf;
138a9e6ba3markm	netip = netibuf;
139a9e6ba3markm	nfrontp = nbackp = netobuf;
140a9e6ba3markm#ifdef	ENCRYPTION
141a9e6ba3markm	nclearto = 0;
142a9e6ba3markm#endif	/* ENCRYPTION */
143a9e6ba3markm
144e9a0f24uhclem	/*
145e9a0f24uhclem	 * This initialization causes linemode to default to a configuration
146e9a0f24uhclem	 * that works on all telnet clients, including the FreeBSD client.
147e9a0f24uhclem	 * This is not quite the same as the telnet client issuing a "mode
148e9a0f24uhclem	 * character" command, but has most of the same benefits, and is
149e9a0f24uhclem	 * preferable since some clients (like usofts) don't have the
150e9a0f24uhclem	 * mode character command anyway and linemode breaks things.
151e9a0f24uhclem	 * The most notable symptom of fix is that csh "set filec" operations
152e9a0f24uhclem	 * like <ESC> (filename completion) and ^D (choices) keys now work
153e9a0f24uhclem	 * in telnet sessions and can be used more than once on the same line.
154e9a0f24uhclem	 * CR/LF handling is also corrected in some termio modes.  This
155e9a0f24uhclem	 * change resolves problem reports bin/771 and bin/1037.
156e9a0f24uhclem	 */
157e9a0f24uhclem
158e9a0f24uhclem	linemode=1;	/*Default to mode that works on bulk of clients*/
159e9a0f24uhclem
16025d7446charnier	while ((ch = getopt(argc, argv, valid_opts)) != -1) {
161a9e6ba3markm		switch(ch) {
162a9e6ba3markm
163a9e6ba3markm#ifdef	AUTHENTICATION
164a9e6ba3markm		case 'a':
165a9e6ba3markm			/*
166a9e6ba3markm			 * Check for required authentication level
167a9e6ba3markm			 */
168a9e6ba3markm			if (strcmp(optarg, "debug") == 0) {
169a9e6ba3markm				extern int auth_debug_mode;
170a9e6ba3markm				auth_debug_mode = 1;
171a9e6ba3markm			} else if (strcasecmp(optarg, "none") == 0) {
172a9e6ba3markm				auth_level = 0;
173a9e6ba3markm			} else if (strcasecmp(optarg, "other") == 0) {
174a9e6ba3markm				auth_level = AUTH_OTHER;
175a9e6ba3markm			} else if (strcasecmp(optarg, "user") == 0) {
176a9e6ba3markm				auth_level = AUTH_USER;
177a9e6ba3markm			} else if (strcasecmp(optarg, "valid") == 0) {
178a9e6ba3markm				auth_level = AUTH_VALID;
179a9e6ba3markm			} else if (strcasecmp(optarg, "off") == 0) {
180a9e6ba3markm				/*
181a9e6ba3markm				 * This hack turns off authentication
182a9e6ba3markm				 */
183a9e6ba3markm				auth_level = -1;
184a9e6ba3markm			} else {
18525d7446charnier				warnx("unknown authorization level for -a");
186a9e6ba3markm			}
187a9e6ba3markm			break;
188a9e6ba3markm#endif	/* AUTHENTICATION */
189a9e6ba3markm
190a9e6ba3markm#ifdef BFTPDAEMON
191a9e6ba3markm		case 'B':
192a9e6ba3markm			bftpd++;
193a9e6ba3markm			break;
194a9e6ba3markm#endif /* BFTPDAEMON */
195a9e6ba3markm
196a9e6ba3markm		case 'd':
197a9e6ba3markm			if (strcmp(optarg, "ebug") == 0) {
198a9e6ba3markm				debug++;
199a9e6ba3markm				break;
200a9e6ba3markm			}
201a9e6ba3markm			usage();
202a9e6ba3markm			/* NOTREACHED */
203a9e6ba3markm			break;
204a9e6ba3markm
205a9e6ba3markm#ifdef DIAGNOSTICS
206a9e6ba3markm		case 'D':
207a9e6ba3markm			/*
208a9e6ba3markm			 * Check for desired diagnostics capabilities.
209a9e6ba3markm			 */
210a9e6ba3markm			if (!strcmp(optarg, "report")) {
211a9e6ba3markm				diagnostic |= TD_REPORT|TD_OPTIONS;
212a9e6ba3markm			} else if (!strcmp(optarg, "exercise")) {
213a9e6ba3markm				diagnostic |= TD_EXERCISE;
214a9e6ba3markm			} else if (!strcmp(optarg, "netdata")) {
215a9e6ba3markm				diagnostic |= TD_NETDATA;
216a9e6ba3markm			} else if (!strcmp(optarg, "ptydata")) {
217a9e6ba3markm				diagnostic |= TD_PTYDATA;
218a9e6ba3markm			} else if (!strcmp(optarg, "options")) {
219a9e6ba3markm				diagnostic |= TD_OPTIONS;
220a9e6ba3markm			} else {
221a9e6ba3markm				usage();
222a9e6ba3markm				/* NOT REACHED */
223a9e6ba3markm			}
224a9e6ba3markm			break;
225a9e6ba3markm#endif /* DIAGNOSTICS */
226a9e6ba3markm
227a9e6ba3markm#ifdef	ENCRYPTION
228a9e6ba3markm		case 'e':
229a9e6ba3markm			if (strcmp(optarg, "debug") == 0) {
230a9e6ba3markm				extern int encrypt_debug_mode;
231a9e6ba3markm				encrypt_debug_mode = 1;
232a9e6ba3markm				break;
233a9e6ba3markm			}
234a9e6ba3markm			usage();
235a9e6ba3markm			/* NOTREACHED */
236a9e6ba3markm			break;
237a9e6ba3markm#endif	/* ENCRYPTION */
238a9e6ba3markm
239a9e6ba3markm		case 'h':
240a9e6ba3markm			hostinfo = 0;
241a9e6ba3markm			break;
242a9e6ba3markm
243a9e6ba3markm#ifdef	LINEMODE
244a9e6ba3markm		case 'l':
245a9e6ba3markm			alwayslinemode = 1;
246a9e6ba3markm			break;
247a9e6ba3markm#endif	/* LINEMODE */
248a9e6ba3markm
249a9e6ba3markm		case 'k':
250a9e6ba3markm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
251a9e6ba3markm			lmodetype = NO_AUTOKLUDGE;
252a9e6ba3markm#else
253a9e6ba3markm			/* ignore -k option if built without kludge linemode */
254a9e6ba3markm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
255a9e6ba3markm			break;
256a9e6ba3markm
257a9e6ba3markm		case 'n':
258a9e6ba3markm			keepalive = 0;
259a9e6ba3markm			break;
260a9e6ba3markm
261cd2a6bemarkm		case 'p':
262cd2a6bemarkm			altlogin = optarg;
263cd2a6bemarkm			break;
264cd2a6bemarkm
265a9e6ba3markm		case 'S':
266a9e6ba3markm#ifdef	HAS_GETTOS
267a9e6ba3markm			if ((tos = parsetos(optarg, "tcp")) < 0)
26825d7446charnier				warnx("%s%s%s",
26925d7446charnier					"bad TOS argument '", optarg,
270a9e6ba3markm					"'; will try to use default TOS");
271a9e6ba3markm#else
272ae5da6bmaxim#define	MAXTOS	255
273ae5da6bmaxim			ultmp = strtoul(optarg, &ep, 0);
274ae5da6bmaxim			if (*ep || ep == optarg || ultmp > MAXTOS)
275ae5da6bmaxim				warnx("%s%s%s",
276ae5da6bmaxim					"bad TOS argument '", optarg,
277ae5da6bmaxim					"'; will try to use default TOS");
278ae5da6bmaxim			else
279ae5da6bmaxim				tos = ultmp;
280a9e6ba3markm#endif
281a9e6ba3markm			break;
282a9e6ba3markm
283a9e6ba3markm		case 'u':
28419fd256markm			utmp_len = (size_t)atoi(optarg);
28588f6c1abrian			if (utmp_len >= sizeof(remote_hostname))
28688f6c1abrian				utmp_len = sizeof(remote_hostname) - 1;
287a9e6ba3markm			break;
288a9e6ba3markm
289a9e6ba3markm		case 'U':
290a9e6ba3markm			registerd_host_only = 1;
291a9e6ba3markm			break;
292a9e6ba3markm
293a9e6ba3markm#ifdef	AUTHENTICATION
294a9e6ba3markm		case 'X':
295a9e6ba3markm			/*
296a9e6ba3markm			 * Check for invalid authentication types
297a9e6ba3markm			 */
298a9e6ba3markm			auth_disable_name(optarg);
299a9e6ba3markm			break;
300a9e6ba3markm#endif	/* AUTHENTICATION */
301a9e6ba3markm
302ce15efbshin		case '4':
303ce15efbshin			family = AF_INET;
304ce15efbshin			break;
305ce15efbshin
306ce15efbshin#ifdef INET6
307ce15efbshin		case '6':
308ce15efbshin			family = AF_INET6;
309ce15efbshin			break;
310ce15efbshin#endif
311ce15efbshin
312a9e6ba3markm		default:
31325d7446charnier			warnx("%c: unknown option", ch);
314a9e6ba3markm			/* FALLTHROUGH */
315a9e6ba3markm		case '?':
316a9e6ba3markm			usage();
317a9e6ba3markm			/* NOTREACHED */
318a9e6ba3markm		}
319a9e6ba3markm	}
320a9e6ba3markm
321a9e6ba3markm	argc -= optind;
322a9e6ba3markm	argv += optind;
323a9e6ba3markm
324a9e6ba3markm	if (debug) {
325ce15efbshin	    int s, ns, foo, error;
32619fd256markm	    const char *service = "telnet";
327ce15efbshin	    struct addrinfo hints, *res;
328a9e6ba3markm
329a9e6ba3markm	    if (argc > 1) {
330a9e6ba3markm		usage();
331a9e6ba3markm		/* NOT REACHED */
332ce15efbshin	    } else if (argc == 1)
333ce15efbshin		service = *argv;
334ce15efbshin
335ce15efbshin	    memset(&hints, 0, sizeof(hints));
336ce15efbshin	    hints.ai_flags = AI_PASSIVE;
337ce15efbshin	    hints.ai_family = family;
338ce15efbshin	    hints.ai_socktype = SOCK_STREAM;
339ce15efbshin	    hints.ai_protocol = 0;
340ce15efbshin	    error = getaddrinfo(NULL, service, &hints, &res);
341ce15efbshin
342ce15efbshin	    if (error) {
343ce15efbshin		errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
344ce15efbshin		if (error == EAI_SYSTEM)
345ce15efbshin		    errx(1, "tcp/%s: %s\n", service, strerror(errno));
346ce15efbshin		usage();
347a9e6ba3markm	    }
348a9e6ba3markm
349ce15efbshin	    s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
35025d7446charnier	    if (s < 0)
35125d7446charnier		    err(1, "socket");
352a9e6ba3markm	    (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
353a9e6ba3markm				(char *)&on, sizeof(on));
354b61072dharti	    if (debug > 1)
355b61072dharti	        (void) setsockopt(s, SOL_SOCKET, SO_DEBUG,
356b61072dharti				(char *)&on, sizeof(on));
357ce15efbshin	    if (bind(s, res->ai_addr, res->ai_addrlen) < 0)
35825d7446charnier		err(1, "bind");
35925d7446charnier	    if (listen(s, 1) < 0)
36025d7446charnier		err(1, "listen");
361ce15efbshin	    foo = res->ai_addrlen;
362ce15efbshin	    ns = accept(s, res->ai_addr, &foo);
36325d7446charnier	    if (ns < 0)
36425d7446charnier		err(1, "accept");
365b61072dharti	    (void) setsockopt(ns, SOL_SOCKET, SO_DEBUG,
366b61072dharti				(char *)&on, sizeof(on));
367a9e6ba3markm	    (void) dup2(ns, 0);
368a9e6ba3markm	    (void) close(ns);
369a9e6ba3markm	    (void) close(s);
370a9e6ba3markm#ifdef convex
371a9e6ba3markm	} else if (argc == 1) {
372a9e6ba3markm		; /* VOID*/		/* Just ignore the host/port name */
373a9e6ba3markm#endif
374a9e6ba3markm	} else if (argc > 0) {
375a9e6ba3markm		usage();
376a9e6ba3markm		/* NOT REACHED */
377a9e6ba3markm	}
378a9e6ba3markm
379a9e6ba3markm	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);
380a9e6ba3markm	fromlen = sizeof (from);
381a9e6ba3markm	if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) {
38225d7446charnier		warn("getpeername");
383a9e6ba3markm		_exit(1);
384a9e6ba3markm	}
385a9e6ba3markm	if (keepalive &&
386a9e6ba3markm	    setsockopt(0, SOL_SOCKET, SO_KEEPALIVE,
387a9e6ba3markm			(char *)&on, sizeof (on)) < 0) {
388a9e6ba3markm		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");
389a9e6ba3markm	}
390a9e6ba3markm
391a9e6ba3markm#if	defined(IPPROTO_IP) && defined(IP_TOS)
392ce15efbshin	if (from.ss_family == AF_INET) {
393a9e6ba3markm# if	defined(HAS_GETTOS)
394a9e6ba3markm		struct tosent *tp;
395a9e6ba3markm		if (tos < 0 && (tp = gettosbyname("telnet", "tcp")))
396a9e6ba3markm			tos = tp->t_tos;
397a9e6ba3markm# endif
398a9e6ba3markm		if (tos < 0)
399a9e6ba3markm			tos = 020;	/* Low Delay bit */
400a9e6ba3markm		if (tos
401a9e6ba3markm		   && (setsockopt(0, IPPROTO_IP, IP_TOS,
402a9e6ba3markm				  (char *)&tos, sizeof(tos)) < 0)
403a9e6ba3markm		   && (errno != ENOPROTOOPT) )
404a9e6ba3markm			syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
405a9e6ba3markm	}
406a9e6ba3markm#endif	/* defined(IPPROTO_IP) && defined(IP_TOS) */
407a9e6ba3markm	net = 0;
408ce15efbshin	doit((struct sockaddr *)&from);
409a9e6ba3markm	/* NOTREACHED */
41025d7446charnier	return(0);
411a9e6ba3markm}  /* end of main */
412a9e6ba3markm
413a9e6ba3markm	void
414a9e6ba3markmusage()
415a9e6ba3markm{
41625d7446charnier	fprintf(stderr, "usage: telnetd");
417a9e6ba3markm#ifdef	AUTHENTICATION
418a8c7614maxim	fprintf(stderr,
419a8c7614maxim	    " [-4] [-6] [-a (debug|other|user|valid|off|none)]\n\t");
420a9e6ba3markm#endif
421a9e6ba3markm#ifdef BFTPDAEMON
422a9e6ba3markm	fprintf(stderr, " [-B]");
423a9e6ba3markm#endif
424a9e6ba3markm	fprintf(stderr, " [-debug]");
425a9e6ba3markm#ifdef DIAGNOSTICS
426a9e6ba3markm	fprintf(stderr, " [-D (options|report|exercise|netdata|ptydata)]\n\t");
427a9e6ba3markm#endif
428a9e6ba3markm#ifdef	AUTHENTICATION
429a9e6ba3markm	fprintf(stderr, " [-edebug]");
430a9e6ba3markm#endif
431a9e6ba3markm	fprintf(stderr, " [-h]");
432a9e6ba3markm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
433a9e6ba3markm	fprintf(stderr, " [-k]");
434a9e6ba3markm#endif
435a9e6ba3markm#ifdef LINEMODE
436a9e6ba3markm	fprintf(stderr, " [-l]");
437a9e6ba3markm#endif
438a9e6ba3markm	fprintf(stderr, " [-n]");
439a9e6ba3markm	fprintf(stderr, "\n\t");
440a9e6ba3markm#ifdef	HAS_GETTOS
441a9e6ba3markm	fprintf(stderr, " [-S tos]");
442a9e6ba3markm#endif
443a9e6ba3markm#ifdef	AUTHENTICATION
444a9e6ba3markm	fprintf(stderr, " [-X auth-type]");
445a9e6ba3markm#endif
446a9e6ba3markm	fprintf(stderr, " [-u utmp_hostname_length] [-U]");
447a9e6ba3markm	fprintf(stderr, " [port]\n");
448a9e6ba3markm	exit(1);
449a9e6ba3markm}
450a9e6ba3markm
451a9e6ba3markm/*
452a9e6ba3markm * getterminaltype
453a9e6ba3markm *
454a9e6ba3markm *	Ask the other end to send along its terminal type and speed.
455a9e6ba3markm * Output is the variable terminaltype filled in.
456a9e6ba3markm */
457a9e6ba3markmstatic unsigned char ttytype_sbbuf[] = {
458a9e6ba3markm	IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE
459a9e6ba3markm};
460a9e6ba3markm
46119fd256markm
46219fd256markm#ifndef	AUTHENTICATION
46319fd256markm#define undef2 __unused
46419fd256markm#else
46519fd256markm#define undef2
46619fd256markm#endif
46719fd256markm
46819fd256markmstatic int
46919fd256markmgetterminaltype(char *name undef2)
470a9e6ba3markm{
471a9e6ba3markm    int retval = -1;
472a9e6ba3markm
473a9e6ba3markm    settimer(baseline);
47419fd256markm#ifdef	AUTHENTICATION
475a9e6ba3markm    /*
476a9e6ba3markm     * Handle the Authentication option before we do anything else.
477a9e6ba3markm     */
4783bc77fdjhb    if (auth_level >= 0) {
4793bc77fdjhb	send_do(TELOPT_AUTHENTICATION, 1);
4803bc77fdjhb	while (his_will_wont_is_changing(TELOPT_AUTHENTICATION))
4813bc77fdjhb	    ttloop();
4823bc77fdjhb	if (his_state_is_will(TELOPT_AUTHENTICATION)) {
4833bc77fdjhb	    retval = auth_wait(name);
4843bc77fdjhb	}
485a9e6ba3markm    }
486a9e6ba3markm#endif
487a9e6ba3markm
488a9e6ba3markm#ifdef	ENCRYPTION
489a9e6ba3markm    send_will(TELOPT_ENCRYPT, 1);
490a9e6ba3markm#endif	/* ENCRYPTION */
491a9e6ba3markm    send_do(TELOPT_TTYPE, 1);
492a9e6ba3markm    send_do(TELOPT_TSPEED, 1);
493a9e6ba3markm    send_do(TELOPT_XDISPLOC, 1);
494a9e6ba3markm    send_do(TELOPT_NEW_ENVIRON, 1);
495a9e6ba3markm    send_do(TELOPT_OLD_ENVIRON, 1);
496a9e6ba3markm    while (
497a9e6ba3markm#ifdef	ENCRYPTION
498a9e6ba3markm	   his_do_dont_is_changing(TELOPT_ENCRYPT) ||
499a9e6ba3markm#endif	/* ENCRYPTION */
500a9e6ba3markm	   his_will_wont_is_changing(TELOPT_TTYPE) ||
501a9e6ba3markm	   his_will_wont_is_changing(TELOPT_TSPEED) ||
502a9e6ba3markm	   his_will_wont_is_changing(TELOPT_XDISPLOC) ||
503a9e6ba3markm	   his_will_wont_is_changing(TELOPT_NEW_ENVIRON) ||
504a9e6ba3markm	   his_will_wont_is_changing(TELOPT_OLD_ENVIRON)) {
505a9e6ba3markm	ttloop();
506a9e6ba3markm    }
507a9e6ba3markm#ifdef	ENCRYPTION
508a9e6ba3markm    /*
509a9e6ba3markm     * Wait for the negotiation of what type of encryption we can
510a9e6ba3markm     * send with.  If autoencrypt is not set, this will just return.
511a9e6ba3markm     */
512a9e6ba3markm    if (his_state_is_will(TELOPT_ENCRYPT)) {
513a9e6ba3markm	encrypt_wait();
514a9e6ba3markm    }
515a9e6ba3markm#endif	/* ENCRYPTION */
516a9e6ba3markm    if (his_state_is_will(TELOPT_TSPEED)) {
517a9e6ba3markm	static unsigned char sb[] =
518a9e6ba3markm			{ IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE };
519a9e6ba3markm
5209cac33dru	output_datalen(sb, sizeof sb);
521a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
522a9e6ba3markm    }
523a9e6ba3markm    if (his_state_is_will(TELOPT_XDISPLOC)) {
524a9e6ba3markm	static unsigned char sb[] =
525a9e6ba3markm			{ IAC, SB, TELOPT_XDISPLOC, TELQUAL_SEND, IAC, SE };
526a9e6ba3markm
5279cac33dru	output_datalen(sb, sizeof sb);
528a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
529a9e6ba3markm    }
530a9e6ba3markm    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
531a9e6ba3markm	static unsigned char sb[] =
532a9e6ba3markm			{ IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_SEND, IAC, SE };
533a9e6ba3markm
5349cac33dru	output_datalen(sb, sizeof sb);
535a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
536a9e6ba3markm    }
537a9e6ba3markm    else if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
538a9e6ba3markm	static unsigned char sb[] =
539a9e6ba3markm			{ IAC, SB, TELOPT_OLD_ENVIRON, TELQUAL_SEND, IAC, SE };
540a9e6ba3markm
5419cac33dru	output_datalen(sb, sizeof sb);
542a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', sb + 2, sizeof sb - 2););
543a9e6ba3markm    }
544a9e6ba3markm    if (his_state_is_will(TELOPT_TTYPE)) {
545a9e6ba3markm
5469cac33dru	output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf);
547a9e6ba3markm	DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
548a9e6ba3markm					sizeof ttytype_sbbuf - 2););
549a9e6ba3markm    }
550a9e6ba3markm    if (his_state_is_will(TELOPT_TSPEED)) {
551a9e6ba3markm	while (sequenceIs(tspeedsubopt, baseline))
552a9e6ba3markm	    ttloop();
553a9e6ba3markm    }
554a9e6ba3markm    if (his_state_is_will(TELOPT_XDISPLOC)) {
555a9e6ba3markm	while (sequenceIs(xdisplocsubopt, baseline))
556a9e6ba3markm	    ttloop();
557a9e6ba3markm    }
558a9e6ba3markm    if (his_state_is_will(TELOPT_NEW_ENVIRON)) {
559a9e6ba3markm	while (sequenceIs(environsubopt, baseline))
560a9e6ba3markm	    ttloop();
561a9e6ba3markm    }
562a9e6ba3markm    if (his_state_is_will(TELOPT_OLD_ENVIRON)) {
563a9e6ba3markm	while (sequenceIs(oenvironsubopt, baseline))
564a9e6ba3markm	    ttloop();
565a9e6ba3markm    }
566a9e6ba3markm    if (his_state_is_will(TELOPT_TTYPE)) {
567a9e6ba3markm	char first[256], last[256];
568a9e6ba3markm
569a9e6ba3markm	while (sequenceIs(ttypesubopt, baseline))
570a9e6ba3markm	    ttloop();
571a9e6ba3markm
572a9e6ba3markm	/*
573a9e6ba3markm	 * If the other side has already disabled the option, then
574a9e6ba3markm	 * we have to just go with what we (might) have already gotten.
575a9e6ba3markm	 */
576a9e6ba3markm	if (his_state_is_will(TELOPT_TTYPE) && !terminaltypeok(terminaltype)) {
577cd2a6bemarkm	    (void) strncpy(first, terminaltype, sizeof(first)-1);
578cd2a6bemarkm	    first[sizeof(first)-1] = '\0';
579a9e6ba3markm	    for(;;) {
580a9e6ba3markm		/*
581a9e6ba3markm		 * Save the unknown name, and request the next name.
582a9e6ba3markm		 */
583cd2a6bemarkm		(void) strncpy(last, terminaltype, sizeof(last)-1);
584cd2a6bemarkm		last[sizeof(last)-1] = '\0';
585a9e6ba3markm		_gettermname();
586a9e6ba3markm		if (terminaltypeok(terminaltype))
587a9e6ba3markm		    break;
588a9e6ba3markm		if ((strncmp(last, terminaltype, sizeof(last)) == 0) ||
589a9e6ba3markm		    his_state_is_wont(TELOPT_TTYPE)) {
590a9e6ba3markm		    /*
591a9e6ba3markm		     * We've hit the end.  If this is the same as
592a9e6ba3markm		     * the first name, just go with it.
593a9e6ba3markm		     */
594a9e6ba3markm		    if (strncmp(first, terminaltype, sizeof(first)) == 0)
595a9e6ba3markm			break;
596a9e6ba3markm		    /*
597a9e6ba3markm		     * Get the terminal name one more time, so that
598a9e6ba3markm		     * RFC1091 compliant telnets will cycle back to
599a9e6ba3markm		     * the start of the list.
600a9e6ba3markm		     */
601a9e6ba3markm		     _gettermname();
602cd2a6bemarkm		    if (strncmp(first, terminaltype, sizeof(first)) != 0) {
603cd2a6bemarkm			(void) strncpy(terminaltype, first, sizeof(terminaltype)-1);
604cd2a6bemarkm			terminaltype[sizeof(terminaltype)-1] = '\0';
605cd2a6bemarkm		    }
606a9e6ba3markm		    break;
607a9e6ba3markm		}
608a9e6ba3markm	    }
609a9e6ba3markm	}
610a9e6ba3markm    }
611a9e6ba3markm    return(retval);
612a9e6ba3markm}  /* end of getterminaltype */
613a9e6ba3markm
61419fd256markmstatic void
61519fd256markm_gettermname(void)
616a9e6ba3markm{
617a9e6ba3markm    /*
618a9e6ba3markm     * If the client turned off the option,
619a9e6ba3markm     * we can't send another request, so we
620a9e6ba3markm     * just return.
621a9e6ba3markm     */
622a9e6ba3markm    if (his_state_is_wont(TELOPT_TTYPE))
623a9e6ba3markm	return;
624a9e6ba3markm    settimer(baseline);
6259cac33dru    output_datalen(ttytype_sbbuf, sizeof ttytype_sbbuf);
626a9e6ba3markm    DIAG(TD_OPTIONS, printsub('>', ttytype_sbbuf + 2,
627a9e6ba3markm					sizeof ttytype_sbbuf - 2););
628a9e6ba3markm    while (sequenceIs(ttypesubopt, baseline))
629a9e6ba3markm	ttloop();
630a9e6ba3markm}
631a9e6ba3markm
63219fd256markmint
63319fd256markmterminaltypeok(char *s)
634a9e6ba3markm{
635a9e6ba3markm    char buf[1024];
636a9e6ba3markm
637a9e6ba3markm    if (terminaltype == NULL)
638a9e6ba3markm	return(1);
639a9e6ba3markm
640a9e6ba3markm    /*
641a9e6ba3markm     * tgetent() will return 1 if the type is known, and
642a9e6ba3markm     * 0 if it is not known.  If it returns -1, it couldn't
643a9e6ba3markm     * open the database.  But if we can't open the database,
644a9e6ba3markm     * it won't help to say we failed, because we won't be
645a9e6ba3markm     * able to verify anything else.  So, we treat -1 like 1.
646a9e6ba3markm     */
647a9e6ba3markm    if (tgetent(buf, s) == 0)
648a9e6ba3markm	return(0);
649a9e6ba3markm    return(1);
650a9e6ba3markm}
651a9e6ba3markm
652a9e6ba3markm/*
653a9e6ba3markm * Get a pty, scan input lines.
654a9e6ba3markm */
65519fd256markmvoid
65619fd256markmdoit(struct sockaddr *who)
657a9e6ba3markm{
65819fd256markm	int err_; /* XXX */
659a9e6ba3markm	int ptynum;
660a9e6ba3markm
661a9e6ba3markm	/*
662a9e6ba3markm	 * Find an available pty to use.
663a9e6ba3markm	 */
664a9e6ba3markm#ifndef	convex
665a9e6ba3markm	pty = getpty(&ptynum);
666a9e6ba3markm	if (pty < 0)
667a9e6ba3markm		fatal(net, "All network ports in use");
668a9e6ba3markm#else
669a9e6ba3markm	for (;;) {
670a9e6ba3markm		char *lp;
671a9e6ba3markm
672a9e6ba3markm		if ((lp = getpty()) == NULL)
673a9e6ba3markm			fatal(net, "Out of ptys");
674a9e6ba3markm
675a9e6ba3markm		if ((pty = open(lp, 2)) >= 0) {
6765c47bfaasmodai			strlcpy(line,lp,sizeof(line));
677a9e6ba3markm			line[5] = 't';
678a9e6ba3markm			break;
679a9e6ba3markm		}
680a9e6ba3markm	}
681a9e6ba3markm#endif
682a9e6ba3markm
68307625d3brian	/* get name of connected client */
684ce15efbshin	if (realhostname_sa(remote_hostname, sizeof(remote_hostname) - 1,
685ce15efbshin	    who, who->sa_len) == HOSTNAME_INVALIDADDR && registerd_host_only)
686a9e6ba3markm		fatal(net, "Couldn't resolve your address into a host name.\r\n\
68762fa01amarkm	Please contact your net administrator");
68888f6c1abrian	remote_hostname[sizeof(remote_hostname) - 1] = '\0';
68988f6c1abrian
69088f6c1abrian	if (!isdigit(remote_hostname[0]) && strlen(remote_hostname) > utmp_len)
69119fd256markm		err_ = getnameinfo(who, who->sa_len, remote_hostname,
692ce15efbshin				  sizeof(remote_hostname), NULL, 0,
693ed819b8ume				  NI_NUMERICHOST);
69419fd256markm		/* XXX: do 'err_' check */
695a9e6ba3markm
69688f6c1abrian	(void) gethostname(host_name, sizeof(host_name) - 1);
69788f6c1abrian	host_name[sizeof(host_name) - 1] = '\0';
698a9e6ba3markm	hostname = host_name;
699a9e6ba3markm
70019fd256markm#ifdef	AUTHENTICATION
70119fd256markm#ifdef	ENCRYPTION
70219fd256markm/* The above #ifdefs should actually be "or"'ed, not "and"'ed.
70319fd256markm * This is a byproduct of needing "#ifdef" and not "#if defined()"
70419fd256markm * for unifdef. XXX MarkM
70519fd256markm */
70607625d3brian	auth_encrypt_init(hostname, remote_hostname, "TELNETD", 1);
707a9e6ba3markm#endif
70819fd256markm#endif
709a9e6ba3markm
710a9e6ba3markm	init_env();
711a9e6ba3markm	/*
712a9e6ba3markm	 * get terminal type.
713a9e6ba3markm	 */
714a9e6ba3markm	*user_name = 0;
715a9e6ba3markm	level = getterminaltype(user_name);
716a9e6ba3markm	setenv("TERM", terminaltype ? terminaltype : "network", 1);
717a9e6ba3markm
71807625d3brian	telnet(net, pty, remote_hostname);	/* begin server process */
719cd2a6bemarkm
720a9e6ba3markm	/*NOTREACHED*/
721a9e6ba3markm}  /* end of doit */
722a9e6ba3markm
723a9e6ba3markm/*
724a9e6ba3markm * Main loop.  Select from pty and network, and
725a9e6ba3markm * hand data to telnet receiver finite state machine.
726a9e6ba3markm */
72719fd256markmvoid
72819fd256markmtelnet(int f, int p, char *host)
729a9e6ba3markm{
730a9e6ba3markm	int on = 1;
731a9e6ba3markm#define	TABBUFSIZ	512
732a9e6ba3markm	char	defent[TABBUFSIZ];
733a9e6ba3markm	char	defstrs[TABBUFSIZ];
734a9e6ba3markm#undef	TABBUFSIZ
735a9e6ba3markm	char *HE;
736a9e6ba3markm	char *HN;
737a9e6ba3markm	char *IM;
73897ad1f3marcel	char *IF;
73997ad1f3marcel	char *if_buf;
74097ad1f3marcel	int if_fd = -1;
74197ad1f3marcel	struct stat statbuf;
742a9e6ba3markm	int nfd;
743a9e6ba3markm
744a9e6ba3markm	/*
745a9e6ba3markm	 * Initialize the slc mapping table.
746a9e6ba3markm	 */
747a9e6ba3markm	get_slc_defaults();
748a9e6ba3markm
749a9e6ba3markm	/*
750a9e6ba3markm	 * Do some tests where it is desireable to wait for a response.
751a9e6ba3markm	 * Rather than doing them slowly, one at a time, do them all
752a9e6ba3markm	 * at once.
753a9e6ba3markm	 */
754a9e6ba3markm	if (my_state_is_wont(TELOPT_SGA))
755a9e6ba3markm		send_will(TELOPT_SGA, 1);
756a9e6ba3markm	/*
757a9e6ba3markm	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this
758a9e6ba3markm	 * because 4.2 clients are unable to deal with TCP urgent data.
759a9e6ba3markm	 *
760a9e6ba3markm	 * To find out, we send out a "DO ECHO".  If the remote system
761a9e6ba3markm	 * answers "WILL ECHO" it is probably a 4.2 client, and we note
762a9e6ba3markm	 * that fact ("WILL ECHO" ==> that the client will echo what
763a9e6ba3markm	 * WE, the server, sends it; it does NOT mean that the client will
764a9e6ba3markm	 * echo the terminal input).
765a9e6ba3markm	 */
766a9e6ba3markm	send_do(TELOPT_ECHO, 1);
767a9e6ba3markm
768a9e6ba3markm#ifdef	LINEMODE
769a9e6ba3markm	if (his_state_is_wont(TELOPT_LINEMODE)) {
770a9e6ba3markm		/* Query the peer for linemode support by trying to negotiate
771a9e6ba3markm		 * the linemode option.
772a9e6ba3markm		 */
773a9e6ba3markm		linemode = 0;
774a9e6ba3markm		editmode = 0;
775a9e6ba3markm		send_do(TELOPT_LINEMODE, 1);  /* send do linemode */
776a9e6ba3markm	}
777a9e6ba3markm#endif	/* LINEMODE */
778a9e6ba3markm
779a9e6ba3markm	/*
780a9e6ba3markm	 * Send along a couple of other options that we wish to negotiate.
781a9e6ba3markm	 */
782a9e6ba3markm	send_do(TELOPT_NAWS, 1);
783a9e6ba3markm	send_will(TELOPT_STATUS, 1);
784a9e6ba3markm	flowmode = 1;		/* default flow control state */
785a9e6ba3markm	restartany = -1;	/* uninitialized... */
786a9e6ba3markm	send_do(TELOPT_LFLOW, 1);
787a9e6ba3markm
788a9e6ba3markm	/*
789a9e6ba3markm	 * Spin, waiting for a response from the DO ECHO.  However,
790a9e6ba3markm	 * some REALLY DUMB telnets out there might not respond
791a9e6ba3markm	 * to the DO ECHO.  So, we spin looking for NAWS, (most dumb
792a9e6ba3markm	 * telnets so far seem to respond with WONT for a DO that
793a9e6ba3markm	 * they don't understand...) because by the time we get the
794a9e6ba3markm	 * response, it will already have processed the DO ECHO.
795a9e6ba3markm	 * Kludge upon kludge.
796a9e6ba3markm	 */
797a9e6ba3markm	while (his_will_wont_is_changing(TELOPT_NAWS))
798a9e6ba3markm		ttloop();
799a9e6ba3markm
800a9e6ba3markm	/*
801a9e6ba3markm	 * But...
802a9e6ba3markm	 * The client might have sent a WILL NAWS as part of its
803a9e6ba3markm	 * startup code; if so, we'll be here before we get the
804a9e6ba3markm	 * response to the DO ECHO.  We'll make the assumption
805a9e6ba3markm	 * that any implementation that understands about NAWS
806a9e6ba3markm	 * is a modern enough implementation that it will respond
807a9e6ba3markm	 * to our DO ECHO request; hence we'll do another spin
808a9e6ba3markm	 * waiting for the ECHO option to settle down, which is
809a9e6ba3markm	 * what we wanted to do in the first place...
810a9e6ba3markm	 */
811a9e6ba3markm	if (his_want_state_is_will(TELOPT_ECHO) &&
812a9e6ba3markm	    his_state_is_will(TELOPT_NAWS)) {
813a9e6ba3markm		while (his_will_wont_is_changing(TELOPT_ECHO))
814a9e6ba3markm			ttloop();
815a9e6ba3markm	}
816a9e6ba3markm	/*
817a9e6ba3markm	 * On the off chance that the telnet client is broken and does not
818a9e6ba3markm	 * respond to the DO ECHO we sent, (after all, we did send the
819a9e6ba3markm	 * DO NAWS negotiation after the DO ECHO, and we won't get here
820a9e6ba3markm	 * until a response to the DO NAWS comes back) simulate the
821a9e6ba3markm	 * receipt of a will echo.  This will also send a WONT ECHO
822a9e6ba3markm	 * to the client, since we assume that the client failed to
823a9e6ba3markm	 * respond because it believes that it is already in DO ECHO
824a9e6ba3markm	 * mode, which we do not want.
825a9e6ba3markm	 */
826a9e6ba3markm	if (his_want_state_is_will(TELOPT_ECHO)) {
8279cac33dru		DIAG(TD_OPTIONS, output_data("td: simulating recv\r\n"));
828a9e6ba3markm		willoption(TELOPT_ECHO);
829a9e6ba3markm	}
830a9e6ba3markm
831a9e6ba3markm	/*
832a9e6ba3markm	 * Finally, to clean things up, we turn on our echo.  This
833a9e6ba3markm	 * will break stupid 4.2 telnets out of local terminal echo.
834a9e6ba3markm	 */
835a9e6ba3markm
836a9e6ba3markm	if (my_state_is_wont(TELOPT_ECHO))
837a9e6ba3markm		send_will(TELOPT_ECHO, 1);
838a9e6ba3markm
839a9e6ba3markm	/*
840a9e6ba3markm	 * Turn on packet mode
841a9e6ba3markm	 */
842a9e6ba3markm	(void) ioctl(p, TIOCPKT, (char *)&on);
843a9e6ba3markm
844a9e6ba3markm#if	defined(LINEMODE) && defined(KLUDGELINEMODE)
845a9e6ba3markm	/*
846a9e6ba3markm	 * Continuing line mode support.  If client does not support
847a9e6ba3markm	 * real linemode, attempt to negotiate kludge linemode by sending
848a9e6ba3markm	 * the do timing mark sequence.
849a9e6ba3markm	 */
850a9e6ba3markm	if (lmodetype < REAL_LINEMODE)
851a9e6ba3markm		send_do(TELOPT_TM, 1);
852a9e6ba3markm#endif	/* defined(LINEMODE) && defined(KLUDGELINEMODE) */
853a9e6ba3markm
854a9e6ba3markm	/*
855a9e6ba3markm	 * Call telrcv() once to pick up anything received during
856a9e6ba3markm	 * terminal type negotiation, 4.2/4.3 determination, and
857a9e6ba3markm	 * linemode negotiation.
858a9e6ba3markm	 */
859a9e6ba3markm	telrcv();
860a9e6ba3markm
861a9e6ba3markm	(void) ioctl(f, FIONBIO, (char *)&on);
862a9e6ba3markm	(void) ioctl(p, FIONBIO, (char *)&on);
863a9e6ba3markm
864a9e6ba3markm#if	defined(SO_OOBINLINE)
865a9e6ba3markm	(void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE,
866a9e6ba3markm				(char *)&on, sizeof on);
867a9e6ba3markm#endif	/* defined(SO_OOBINLINE) */
868a9e6ba3markm
869a9e6ba3markm#ifdef	SIGTSTP
870a9e6ba3markm	(void) signal(SIGTSTP, SIG_IGN);
871a9e6ba3markm#endif
872a9e6ba3markm#ifdef	SIGTTOU
873a9e6ba3markm	/*
874a9e6ba3markm	 * Ignoring SIGTTOU keeps the kernel from blocking us
875a9e6ba3markm	 * in ttioct() in /sys/tty.c.
876a9e6ba3markm	 */
877a9e6ba3markm	(void) signal(SIGTTOU, SIG_IGN);
878a9e6ba3markm#endif
879a9e6ba3markm
880a9e6ba3markm	(void) signal(SIGCHLD, cleanup);
881a9e6ba3markm
882a9e6ba3markm#ifdef  TIOCNOTTY
883a9e6ba3markm	{
88419fd256markm		int t;
885a9e6ba3markm		t = open(_PATH_TTY, O_RDWR);
886a9e6ba3markm		if (t >= 0) {
887a9e6ba3markm			(void) ioctl(t, TIOCNOTTY, (char *)0);
888a9e6ba3markm			(void) close(t);
889a9e6ba3markm		}
890a9e6ba3markm	}
891a9e6ba3markm#endif
892a9e6ba3markm
893a9e6ba3markm	/*
894a9e6ba3markm	 * Show banner that getty never gave.
895a9e6ba3markm	 *
896a9e6ba3markm	 * We put the banner in the pty input buffer.  This way, it
897a9e6ba3markm	 * gets carriage return null processing, etc., just like all
898a9e6ba3markm	 * other pty --> client data.
899a9e6ba3markm	 */
900a9e6ba3markm
901a9e6ba3markm	if (getent(defent, "default") == 1) {
902a9e6ba3markm		char *cp=defstrs;
903a9e6ba3markm
904e133ecepeter		HE = Getstr("he", &cp);
905e133ecepeter		HN = Getstr("hn", &cp);
906e133ecepeter		IM = Getstr("im", &cp);
90797ad1f3marcel		IF = Getstr("if", &cp);
908a9e6ba3markm		if (HN && *HN)
9095c47bfaasmodai			(void) strlcpy(host_name, HN, sizeof(host_name));
91097ad1f3marcel		if (IF) {
91197ad1f3marcel		    if_fd = open(IF, O_RDONLY, 000);
91297ad1f3marcel		    IM = 0;
91397ad1f3marcel		}
914a9e6ba3markm		if (IM == 0)
91519fd256markm			IM = strdup("");
916a9e6ba3markm	} else {
91719fd256markm		IM = strdup(DEFAULT_IM);
918a9e6ba3markm		HE = 0;
919a9e6ba3markm	}
920a9e6ba3markm	edithost(HE, host_name);
921a9e6ba3markm	if (hostinfo && *IM)
922a9e6ba3markm		putf(IM, ptyibuf2);
92327f5663cem	if (if_fd != -1) {
924a6ba47bmarcel		if (fstat(if_fd, &statbuf) != -1 && statbuf.st_size > 0) {
925a6ba47bmarcel			if_buf = (char *) mmap (0, statbuf.st_size,
926a6ba47bmarcel			    PROT_READ, 0, if_fd, 0);
927a6ba47bmarcel			if (if_buf != MAP_FAILED) {
928a6ba47bmarcel				putf(if_buf, ptyibuf2);
929a6ba47bmarcel				munmap(if_buf, statbuf.st_size);
930a6ba47bmarcel			}
93197ad1f3marcel		}
93297ad1f3marcel		close (if_fd);
93397ad1f3marcel	}
934a9e6ba3markm
935a9e6ba3markm	if (pcc)
936a9e6ba3markm		(void) strncat(ptyibuf2, ptyip, pcc+1);
937a9e6ba3markm	ptyip = ptyibuf2;
938a9e6ba3markm	pcc = strlen(ptyip);
939a9e6ba3markm#ifdef	LINEMODE
940a9e6ba3markm	/*
941a9e6ba3markm	 * Last check to make sure all our states are correct.
942a9e6ba3markm	 */
943a9e6ba3markm	init_termbuf();
944a9e6ba3markm	localstat();
945a9e6ba3markm#endif	/* LINEMODE */
946a9e6ba3markm
9479cac33dru	DIAG(TD_REPORT, output_data("td: Entering processing loop\r\n"));
948a9e6ba3markm
949cd2a6bemarkm	/*
950cd2a6bemarkm	 * Startup the login process on the slave side of the terminal
951cd2a6bemarkm	 * now.  We delay this until here to insure option negotiation
952cd2a6bemarkm	 * is complete.
953cd2a6bemarkm	 */
954cd2a6bemarkm	startslave(host, level, user_name);
955a9e6ba3markm
956a9e6ba3markm	nfd = ((f > p) ? f : p) + 1;
957a9e6ba3markm	for (;;) {
958a9e6ba3markm		fd_set ibits, obits, xbits;
95919fd256markm		int c;
960a9e6ba3markm
961a9e6ba3markm		if (ncc < 0 && pcc < 0)
962a9e6ba3markm			break;
963a9e6ba3markm
964a9e6ba3markm		FD_ZERO(&ibits);
965a9e6ba3markm		FD_ZERO(&obits);
966a9e6ba3markm		FD_ZERO(&xbits);
967a9e6ba3markm		/*
968a9e6ba3markm		 * Never look for input if there's still
969a9e6ba3markm		 * stuff in the corresponding output buffer
970a9e6ba3markm		 */
971a9e6ba3markm		if (nfrontp - nbackp || pcc > 0) {
972a9e6ba3markm			FD_SET(f, &obits);
973a9e6ba3markm		} else {
974a9e6ba3markm			FD_SET(p, &ibits);
975a9e6ba3markm		}
976a9e6ba3markm		if (pfrontp - pbackp || ncc > 0) {
977a9e6ba3markm			FD_SET(p, &obits);
978a9e6ba3markm		} else {
979a9e6ba3markm			FD_SET(f, &ibits);
980a9e6ba3markm		}
981a9e6ba3markm		if (!SYNCHing) {
982a9e6ba3markm			FD_SET(f, &xbits);
983a9e6ba3markm		}
984a9e6ba3markm		if ((c = select(nfd, &ibits, &obits, &xbits,
985a9e6ba3markm						(struct timeval *)0)) < 1) {
986a9e6ba3markm			if (c == -1) {
987a9e6ba3markm				if (errno == EINTR) {
988a9e6ba3markm					continue;
989a9e6ba3markm				}
990a9e6ba3markm			}
991a9e6ba3markm			sleep(5);
992a9e6ba3markm			continue;
993a9e6ba3markm		}
994a9e6ba3markm
995a9e6ba3markm		/*
996a9e6ba3markm		 * Any urgent data?
997a9e6ba3markm		 */
998a9e6ba3markm		if (FD_ISSET(net, &xbits)) {
999a9e6ba3markm		    SYNCHing = 1;
1000a9e6ba3markm		}
1001a9e6ba3markm
1002a9e6ba3markm		/*
1003a9e6ba3markm		 * Something to read from the network...
1004a9e6ba3markm		 */
1005a9e6ba3markm		if (FD_ISSET(net, &ibits)) {
1006a9e6ba3markm#if	!defined(SO_OOBINLINE)
1007a9e6ba3markm			/*
1008a9e6ba3markm			 * In 4.2 (and 4.3 beta) systems, the
1009a9e6ba3markm			 * OOB indication and data handling in the kernel
1010a9e6ba3markm			 * is such that if two separate TCP Urgent requests
1011a9e6ba3markm			 * come in, one byte of TCP data will be overlaid.
1012a9e6ba3markm			 * This is fatal for Telnet, but we try to live
1013a9e6ba3markm			 * with it.
1014a9e6ba3markm			 *
1015a9e6ba3markm			 * In addition, in 4.2 (and...), a special protocol
1016a9e6ba3markm			 * is needed to pick up the TCP Urgent data in
1017a9e6ba3markm			 * the correct sequence.
1018a9e6ba3markm			 *
1019a9e6ba3markm			 * What we do is:  if we think we are in urgent
1020a9e6ba3markm			 * mode, we look to see if we are "at the mark".
1021a9e6ba3markm			 * If we are, we do an OOB receive.  If we run
1022a9e6ba3markm			 * this twice, we will do the OOB receive twice,
1023a9e6ba3markm			 * but the second will fail, since the second
1024a9e6ba3markm			 * time we were "at the mark", but there wasn't
1025a9e6ba3markm			 * any data there (the kernel doesn't reset
1026a9e6ba3markm			 * "at the mark" until we do a normal read).
1027a9e6ba3markm			 * Once we've read the OOB data, we go ahead
1028a9e6ba3markm			 * and do normal reads.
1029a9e6ba3markm			 *
1030a9e6ba3markm			 * There is also another problem, which is that
1031a9e6ba3markm			 * since the OOB byte we read doesn't put us
1032a9e6ba3markm			 * out of OOB state, and since that byte is most
1033a9e6ba3markm			 * likely the TELNET DM (data mark), we would
1034a9e6ba3markm			 * stay in the TELNET SYNCH (SYNCHing) state.
1035a9e6ba3markm			 * So, clocks to the rescue.  If we've "just"
1036a9e6ba3markm			 * received a DM, then we test for the
1037a9e6ba3markm			 * presence of OOB data when the receive OOB
1038a9e6ba3markm			 * fails (and AFTER we did the normal mode read
1039a9e6ba3markm			 * to clear "at the mark").
1040a9e6ba3markm			 */
1041a9e6ba3markm		    if (SYNCHing) {
1042a9e6ba3markm			int atmark;
1043a9e6ba3markm
1044a9e6ba3markm			(void) ioctl(net, SIOCATMARK, (char *)&atmark);
1045a9e6ba3markm			if (atmark) {
1046a9e6ba3markm			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);
1047a9e6ba3markm			    if ((ncc == -1) && (errno == EINVAL)) {
1048a9e6ba3markm				ncc = read(net, netibuf, sizeof (netibuf));
1049a9e6ba3markm				if (sequenceIs(didnetreceive, gotDM)) {
1050a9e6ba3markm				    SYNCHing = stilloob(net);
1051a9e6ba3markm				}
1052a9e6ba3markm			    }
1053a9e6ba3markm			} else {
1054a9e6ba3markm			    ncc = read(net, netibuf, sizeof (netibuf));
1055a9e6ba3markm			}
1056a9e6ba3markm		    } else {
1057a9e6ba3markm			ncc = read(net, netibuf, sizeof (netibuf));
1058a9e6ba3markm		    }
1059a9e6ba3markm		    settimer(didnetreceive);
1060a9e6ba3markm#else	/* !defined(SO_OOBINLINE)) */
1061a9e6ba3markm		    ncc = read(net, netibuf, sizeof (netibuf));
1062a9e6ba3markm#endif	/* !defined(SO_OOBINLINE)) */
1063a9e6ba3markm		    if (ncc < 0 && errno == EWOULDBLOCK)
1064a9e6ba3markm			ncc = 0;
1065a9e6ba3markm		    else {
1066a9e6ba3markm			if (ncc <= 0) {
1067a9e6ba3markm			    break;
1068a9e6ba3markm			}
1069a9e6ba3markm			netip = netibuf;
1070a9e6ba3markm		    }
1071a9e6ba3markm		    DIAG((TD_REPORT | TD_NETDATA),
10729cac33dru			output_data("td: netread %d chars\r\n", ncc));
1073a9e6ba3markm		    DIAG(TD_NETDATA, printdata("nd", netip, ncc));
1074a9e6ba3markm		}
1075a9e6ba3markm
1076a9e6ba3markm		/*
1077a9e6ba3markm		 * Something to read from the pty...
1078a9e6ba3markm		 */
1079a9e6ba3markm		if (FD_ISSET(p, &ibits)) {
1080a9e6ba3markm			pcc = read(p, ptyibuf, BUFSIZ);
1081a9e6ba3markm			/*
1082a9e6ba3markm			 * On some systems, if we try to read something
1083a9e6ba3markm			 * off the master side before the slave side is
1084a9e6ba3markm			 * opened, we get EIO.
1085a9e6ba3markm			 */
1086a9e6ba3markm			if (pcc < 0 && (errno == EWOULDBLOCK ||
1087a9e6ba3markm#ifdef	EAGAIN
1088a9e6ba3markm					errno == EAGAIN ||
1089a9e6ba3markm#endif
1090a9e6ba3markm					errno == EIO)) {
1091a9e6ba3markm				pcc = 0;
1092a9e6ba3markm			} else {
1093a9e6ba3markm				if (pcc <= 0)
1094a9e6ba3markm					break;
1095a9e6ba3markm#ifdef	LINEMODE
1096a9e6ba3markm				/*
1097a9e6ba3markm				 * If ioctl from pty, pass it through net
1098a9e6ba3markm				 */
1099a9e6ba3markm				if (ptyibuf[0] & TIOCPKT_IOCTL) {
1100a9e6ba3markm					copy_termbuf(ptyibuf+1, pcc-1);
1101a9e6ba3markm					localstat();
1102a9e6ba3markm					pcc = 1;
1103a9e6ba3markm				}
1104a9e6ba3markm#endif	/* LINEMODE */
1105a9e6ba3markm				if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) {
1106a9e6ba3markm					netclear();	/* clear buffer back */
1107a9e6ba3markm#ifndef	NO_URGENT
1108a9e6ba3markm					/*
1109a9e6ba3markm					 * There are client telnets on some
1110a9e6ba3markm					 * operating systems get screwed up
1111a9e6ba3markm					 * royally if we send them urgent
1112a9e6ba3markm					 * mode data.
1113a9e6ba3markm					 */
11149cac33dru					output_data("%c%c", IAC, DM);
1115a9e6ba3markm					neturg = nfrontp-1; /* off by one XXX */
1116a9e6ba3markm					DIAG(TD_OPTIONS,
1117a9e6ba3markm					    printoption("td: send IAC", DM));
1118a9e6ba3markm
1119a9e6ba3markm#endif
1120a9e6ba3markm				}
1121a9e6ba3markm				if (his_state_is_will(TELOPT_LFLOW) &&
1122a9e6ba3markm				    (ptyibuf[0] &
1123a9e6ba3markm				     (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) {
1124a9e6ba3markm					int newflow =
1125a9e6ba3markm					    ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0;
1126a9e6ba3markm					if (newflow != flowmode) {
1127a9e6ba3markm						flowmode = newflow;
11289cac33dru						output_data("%c%c%c%c%c%c",
1129a9e6ba3markm							IAC, SB, TELOPT_LFLOW,
1130a9e6ba3markm							flowmode ? LFLOW_ON
1131a9e6ba3markm								 : LFLOW_OFF,
1132a9e6ba3markm							IAC, SE);
1133a9e6ba3markm						DIAG(TD_OPTIONS, printsub('>',
1134a9e6ba3markm						    (unsigned char *)nfrontp-4,
1135a9e6ba3markm						    4););
1136a9e6ba3markm					}
1137a9e6ba3markm				}
1138a9e6ba3markm				pcc--;
1139a9e6ba3markm				ptyip = ptyibuf+1;
1140a9e6ba3markm			}
1141a9e6ba3markm		}
1142a9e6ba3markm
1143a9e6ba3markm		while (pcc > 0) {
1144a9e6ba3markm			if ((&netobuf[BUFSIZ] - nfrontp) < 2)
1145a9e6ba3markm				break;
1146a9e6ba3markm			c = *ptyip++ & 0377, pcc--;
1147a9e6ba3markm			if (c == IAC)
11489cac33dru				output_data("%c", c);
11499cac33dru			output_data("%c", c);
1150a9e6ba3markm			if ((c == '\r') && (my_state_is_wont(TELOPT_BINARY))) {
1151a9e6ba3markm				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {
11529cac33dru					output_data("%c", *ptyip++ & 0377);
1153a9e6ba3markm					pcc--;
1154a9e6ba3markm				} else
11559cac33dru					output_data("%c", '\0');
1156a9e6ba3markm			}
1157a9e6ba3markm		}
1158a9e6ba3markm
1159a9e6ba3markm		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)
1160a9e6ba3markm			netflush();
1161a9e6ba3markm		if (ncc > 0)
1162a9e6ba3markm			telrcv();
1163a9e6ba3markm		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)
1164a9e6ba3markm			ptyflush();
1165a9e6ba3markm	}
1166a9e6ba3markm	cleanup(0);
1167a9e6ba3markm}  /* end of telnet */
1168a9e6ba3markm
1169a9e6ba3markm#ifndef	TCSIG
1170a9e6ba3markm# ifdef	TIOCSIG
1171a9e6ba3markm#  define TCSIG TIOCSIG
1172a9e6ba3markm# endif
1173a9e6ba3markm#endif
1174a9e6ba3markm
1175a9e6ba3markm/*
1176a9e6ba3markm * Send interrupt to process on other side of pty.
1177a9e6ba3markm * If it is in raw mode, just write NULL;
1178a9e6ba3markm * otherwise, write intr char.
1179a9e6ba3markm */
118019fd256markmvoid
118119fd256markminterrupt(void)
1182a9e6ba3markm{
1183a9e6ba3markm	ptyflush();	/* half-hearted */
1184a9e6ba3markm
1185a9e6ba3markm#ifdef	TCSIG
118691aa6d2ru	(void) ioctl(pty, TCSIG, SIGINT);
1187a9e6ba3markm#else	/* TCSIG */
1188a9e6ba3markm	init_termbuf();
1189a9e6ba3markm	*pfrontp++ = slctab[SLC_IP].sptr ?
1190a9e6ba3markm			(unsigned char)*slctab[SLC_IP].sptr : '\177';
1191a9e6ba3markm#endif	/* TCSIG */
1192a9e6ba3markm}
1193a9e6ba3markm
1194a9e6ba3markm/*
1195a9e6ba3markm * Send quit to process on other side of pty.
1196a9e6ba3markm * If it is in raw mode, just write NULL;
1197a9e6ba3markm * otherwise, write quit char.
1198a9e6ba3markm */
119919fd256markmvoid
120019fd256markmsendbrk(void)
1201a9e6ba3markm{
1202a9e6ba3markm	ptyflush();	/* half-hearted */
1203a9e6ba3markm#ifdef	TCSIG
120491aa6d2ru	(void) ioctl(pty, TCSIG, SIGQUIT);
1205a9e6ba3markm#else	/* TCSIG */
1206a9e6ba3markm	init_termbuf();
1207a9e6ba3markm	*pfrontp++ = slctab[SLC_ABORT].sptr ?
1208a9e6ba3markm			(unsigned char)*slctab[SLC_ABORT].sptr : '\034';
1209a9e6ba3markm#endif	/* TCSIG */
1210a9e6ba3markm}
1211a9e6ba3markm
121219fd256markmvoid
121319fd256markmsendsusp(void)
1214a9e6ba3markm{
1215a9e6ba3markm#ifdef	SIGTSTP
1216a9e6ba3markm	ptyflush();	/* half-hearted */
1217a9e6ba3markm# ifdef	TCSIG
121891aa6d2ru	(void) ioctl(pty, TCSIG, SIGTSTP);
1219a9e6ba3markm# else	/* TCSIG */
1220a9e6ba3markm	*pfrontp++ = slctab[SLC_SUSP].sptr ?
1221a9e6ba3markm			(unsigned char)*slctab[SLC_SUSP].sptr : '\032';
1222a9e6ba3markm# endif	/* TCSIG */
1223a9e6ba3markm#endif	/* SIGTSTP */
1224a9e6ba3markm}
1225a9e6ba3markm
1226a9e6ba3markm/*
1227a9e6ba3markm * When we get an AYT, if ^T is enabled, use that.  Otherwise,
1228a9e6ba3markm * just send back "[Yes]".
1229a9e6ba3markm */
123019fd256markmvoid
123119fd256markmrecv_ayt(void)
1232a9e6ba3markm{
1233a9e6ba3markm#if	defined(SIGINFO) && defined(TCSIG)
1234a9e6ba3markm	if (slctab[SLC_AYT].sptr && *slctab[SLC_AYT].sptr != _POSIX_VDISABLE) {
123591aa6d2ru		(void) ioctl(pty, TCSIG, SIGINFO);
1236a9e6ba3markm		return;
1237a9e6ba3markm	}
1238a9e6ba3markm#endif
12399cac33dru	output_data("\r\n[Yes]\r\n");
1240a9e6ba3markm}
1241a9e6ba3markm
124219fd256markmvoid
124319fd256markmdoeof(void)
1244a9e6ba3markm{
1245a9e6ba3markm	init_termbuf();
1246a9e6ba3markm
1247a9e6ba3markm#if	defined(LINEMODE) && defined(USE_TERMIO) && (VEOF == VMIN)
1248a9e6ba3markm	if (!tty_isediting()) {
1249a9e6ba3markm		extern char oldeofc;
1250a9e6ba3markm		*pfrontp++ = oldeofc;
1251a9e6ba3markm		return;
1252a9e6ba3markm	}
1253a9e6ba3markm#endif
1254a9e6ba3markm	*pfrontp++ = slctab[SLC_EOF].sptr ?
1255a9e6ba3markm			(unsigned char)*slctab[SLC_EOF].sptr : '\004';
1256a9e6ba3markm}
1257