telnetd.c

来自「<B>Digital的Unix操作系统VAX 4.2源码</B>」· C语言 代码 · 共 1,646 行 · 第 1/3 页

C
1,646
字号
#ifndef lintstatic	char	*sccsid = "@(#)telnetd.c	4.1	(ULTRIX)	7/2/90";#endif lint/************************************************************************ *									* *			Copyright (c) 1984,1988 by			* *		Digital Equipment Corporation, Maynard, MA		* *			All rights reserved.				* *									* *   This software is furnished under a license and may be used and	* *   copied  only  in accordance with the terms of such license and	* *   with the  inclusion  of  the  above  copyright  notice.   This	* *   software  or  any  other copies thereof may not be provided or	* *   otherwise made available to any other person.  No title to and	* *   ownership of the software is hereby transferred.			* *									* *   This software is  derived  from  software  received  from  the	* *   University    of   California,   Berkeley,   and   from   Bell	* *   Laboratories.  Use, duplication, or disclosure is  subject  to	* *   restrictions  under  license  agreements  with  University  of	* *   California and with AT&T.						* *									* *   The information in this software is subject to change  without	* *   notice  and should not be construed as a commitment by Digital	* *   Equipment Corporation.						* *									* *   Digital assumes no responsibility for the use  or  reliability	* *   of its software on equipment which is not supplied by Digital.	* *									* ************************************************************************//* * Copyright (c) 1983,1986 Regents of the University of California. * All rights reserved.  The Berkeley software License Agreement * specifies the terms and conditions for redistribution. *//* * Modification History: * * 30-Aug-88    Michael G. Mc Menemy *      Fixed hang in cleanup by closing master side of pty before *      calling vhangup. *      Flush any remaining output from slave pty to the master pty for *      the telnet client. * * 09-Jun-88	Mark Parenti *	Changed signal handlers to void. * *//*#ifndef lintchar copyright[] =" Copyright (c) 1983 Regents of the University of California.\n\ All rights reserved.\n";#endif not lint#ifndef lintstatic char sccsid[] = "telnetd.c	5.18 (Berkeley) 5/12/86";#endif not lint*//* * Telnet server. */#include <sys/types.h>#include <sys/param.h>#include <sys/socket.h>#include <sys/wait.h>#include <sys/file.h>#include <sys/stat.h>#include <sys/time.h>#include <netinet/in.h>#include <arpa/telnet.h>#include <stdio.h>#include <signal.h>#include <errno.h>#include <sgtty.h>#include <netdb.h>#include <syslog.h>#include <ctype.h>#define	OPT_NO			0		/* won't do this option */#define	OPT_YES			1		/* will do this option */#define	OPT_YES_BUT_ALWAYS_LOOK	2#define	OPT_NO_BUT_ALWAYS_LOOK	3char	hisopts[256];char	myopts[256];char	doopt[] = { IAC, DO, '%', 'c', 0 };char	dont[] = { IAC, DONT, '%', 'c', 0 };char	will[] = { IAC, WILL, '%', 'c', 0 };char	wont[] = { IAC, WONT, '%', 'c', 0 };/* * I/O data buffers, pointers, and counters. */char	ptyibuf[BUFSIZ], *ptyip = ptyibuf;char	ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf;char	netibuf[BUFSIZ], *netip = netibuf;#define	NIACCUM(c)	{   *netip++ = c; \			    ncc++; \			}char	netobuf[BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;char	*neturg = 0;		/* one past last bye of urgent data */	/* the remote system seems to NOT be an old 4.2 */int	not42 = 1;#ifdef 43BSDchar BANNER1[] = "\r\n\r\n 4.3 BSD (",    BANNER2[] = ")\r\n\r\0\r\n\r\0";#elsechar *BANNER1;#endif#define	TABBUFSIZ	512		/* buffer for sub-options */char	subbuffer[100], *subpointer= subbuffer, *subend= subbuffer;#define	SB_CLEAR()	subpointer = subbuffer;#define	SB_TERM()	{ subend = subpointer; SB_CLEAR(); }#define	SB_ACCUM(c)	if (subpointer < (subbuffer+sizeof subbuffer)) { \				*subpointer++ = (c); \			}#define	SB_GET()	((*subpointer++)&0xff)#define	SB_EOF()	(subpointer >= subend)int	pcc, ncc;int	pty, net;int	inter;extern	char **environ;extern	int errno;char	*line;int	SYNCHing = 0;		/* we are in TELNET SYNCH mode *//* * The following are some clocks used to decide how to interpret * the relationship between various variables. */struct {    int	system,			/* what the current time is */	echotoggle,		/* last time user entered echo character */	modenegotiated,		/* last time operating mode negotiated */	didnetreceive,		/* last time we read data from network */	ttypeopt,		/* ttype will/won't received */	ttypesubopt,		/* ttype subopt is received */	getterminal,		/* time started to get terminal information */	gotDM;			/* when did we last see a data mark */} clocks;#define	settimer(x)	(clocks.x = ++clocks.system)#define	sequenceIs(x,y)	(clocks.x < clocks.y)main(argc, argv)	char *argv[];{	struct sockaddr_in from;	int on = 1, fromlen;#if	defined(DEBUG)	{	    int s, ns, foo;	    struct servent *sp;	    static struct sockaddr_in sin = { AF_INET };	    sp = getservbyname("telnet", "tcp");	    if (sp == 0) {		    fprintf(stderr, "telnetd: tcp/telnet: unknown service\n");		    exit(1);	    }	    sin.sin_port = sp->s_port;	    argc--, argv++;	    if (argc > 0) {		    sin.sin_port = atoi(*argv);		    sin.sin_port = htons((u_short)sin.sin_port);	    }	    s = socket(AF_INET, SOCK_STREAM, 0);	    if (s < 0) {		    perror("telnetd: socket");;		    exit(1);	    }	    if (bind(s, &sin, sizeof sin) < 0) {		perror("bind");		exit(1);	    }	    if (listen(s, 1) < 0) {		perror("listen");		exit(1);	    }	    foo = sizeof sin;	    ns = accept(s, &sin, &foo);	    if (ns < 0) {		perror("accept");		exit(1);	    }	    dup2(ns, 0);	    close(s);	}#endif	/* defined(DEBUG) */#ifdef 43BSD	openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON);#else	openlog("telnetd", LOG_PID );#endif	fromlen = sizeof (from);	if (getpeername(0, &from, &fromlen) < 0) {		fprintf(stderr, "%s: ", argv[0]);		perror("getpeername");		_exit(1);	}	if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof (on)) < 0) {		syslog(LOG_WARNING, "setsockopt (SO_KEEPALIVE): %m");	}	doit(0, &from);}char	*terminaltype = 0;char	*envinit[2];void	cleanup();/* * ttloop * *	A small subroutine to flush the network output buffer, get some data * from the network, and pass it through the telnet state machine.  We * also flush the pty input buffer (by dropping its data) if it becomes * too full. */voidttloop(){    if (nfrontp-nbackp) {	netflush();    }    ncc = read(net, netibuf, sizeof netibuf);    if (ncc < 0) {	syslog(LOG_INFO, "ttloop:  read: %m\n");	exit(1);    } else if (ncc == 0) {	syslog(LOG_INFO, "ttloop:  peer died: %m\n");	exit(1);    }    netip = netibuf;    telrcv();			/* state machine */    if (ncc > 0) {	pfrontp = pbackp = ptyobuf;	telrcv();    }}/* * getterminaltype * *	Ask the other end to send along its terminal type. * Output is the variable terminaltype filled in. */voidgetterminaltype(){    static char sbuf[] = { IAC, DO, TELOPT_TTYPE };    settimer(getterminal);    bcopy(sbuf, nfrontp, sizeof sbuf);    nfrontp += sizeof sbuf;    hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK;    while (sequenceIs(ttypeopt, getterminal)) {	ttloop();    }    if (hisopts[TELOPT_TTYPE] == OPT_YES) {	static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE };	bcopy(sbbuf, nfrontp, sizeof sbbuf);	nfrontp += sizeof sbbuf;	while (sequenceIs(ttypesubopt, getterminal)) {	    ttloop();	}    }}/* * Get a pty, scan input lines. */doit(f, who)	int f;	struct sockaddr_in *who;{	char *host, *inet_ntoa();	int i, p, t;	struct sgttyb b;	struct hostent *hp;	int c;	for (c = 'p'; c <= 'z'; c++) {		struct stat stb;		line = "/dev/ptyXX";		line[strlen("/dev/pty")] = c;		line[strlen("/dev/ptyp")] = '0';		if (stat(line, &stb) < 0)			break;		for (i = 0; i < 16; i++) {			line[strlen("/dev/ptyp")] = "0123456789abcdef"[i];			p = open(line, 2);			if (p > 0)				goto gotpty;		}	}	fatal(f, "All network ports in use");	/*NOTREACHED*/gotpty:	dup2(f, 0);	line[strlen("/dev/")] = 't';	t = open("/dev/tty", O_RDWR);	if (t >= 0) {		ioctl(t, TIOCNOTTY, 0);		close(t);	}	t = open(line, O_RDWR);	if (t < 0)		fatalperror(f, line, errno);	ioctl(t, TIOCGETP, &b);	b.sg_flags = CRMOD|XTABS|ANYP;	ioctl(t, TIOCSETP, &b);	ioctl(p, TIOCGETP, &b);	b.sg_flags &= ~ECHO;	ioctl(p, TIOCSETP, &b);	hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr),		who->sin_family);	if (hp)		host = hp->h_name;	else		host = inet_ntoa(who->sin_addr);	net = f;	pty = p;	/*	 * get terminal type.	 */	getterminaltype();	if ((i = fork()) < 0)		fatalperror(f, "fork", errno);	if (i)		telnet(f, p);	close(f);	close(p);	dup2(t, 0);	dup2(t, 1);	dup2(t, 2);	close(t);	envinit[0] = terminaltype;	envinit[1] = 0;	environ = envinit;	/*	 * -h : pass on name of host.	 *		WARNING:  -h is accepted by login if and only if	 *			getuid() == 0.	 * -p : don't clobber the environment (so terminal type stays set).	 */	execl("/bin/login", "login", "-h", host,					terminaltype ? "-p" : 0, 0);	fatalperror(f, "/bin/login", errno);	/*NOTREACHED*/}fatal(f, msg)	int f;	char *msg;{	char buf[BUFSIZ];	(void) sprintf(buf, "telnetd: %s.\r\n", msg);	(void) write(f, buf, strlen(buf));	exit(1);}fatalperror(f, msg, errno)	int f;	char *msg;	int errno;{	char buf[BUFSIZ];	extern char *sys_errlist[];	(void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]);	fatal(f, buf);}/* * Check a descriptor to see if out of band data exists on it. */stilloob(s)int	s;		/* socket number */{    static struct timeval timeout = { 0 };    fd_set	excepts;    int value;    do {	FD_ZERO(&excepts);	FD_SET(s, &excepts);	value = select(s+1, (fd_set *)0, (fd_set *)0, &excepts, &timeout);    } while ((value == -1) && (errno == EINTR));    if (value < 0) {	fatalperror(pty, "select", errno);    }    if (FD_ISSET(s, &excepts)) {	return 1;    } else {	return 0;    }}/* * Main loop.  Select from pty and network, and * hand data to telnet receiver finite state machine. */char hostname[MAXHOSTNAMELEN];telnet(f, p){	int on = 1;	ioctl(f, FIONBIO, &on);	ioctl(p, FIONBIO, &on);#if	defined(SO_OOBINLINE)	setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on);#endif	/* defined(SO_OOBINLINE) */	signal(SIGTSTP, SIG_IGN);        /*	 * Ignoring SIGTTOU keeps the kernel from blocking us	 * in ttioctl() in /sys/tty.c.	 */	signal(SIGTTOU, SIG_IGN);	signal(SIGCHLD, cleanup);	setpgrp(0, 0);	/*	 * Request to do remote echo and to suppress go ahead.	 */	if (!myopts[TELOPT_ECHO]) {	    dooption(TELOPT_ECHO);	}	if (!myopts[TELOPT_SGA]) {	    dooption(TELOPT_SGA);	}	/*	 * Is the client side a 4.2 (NOT 4.3) system?  We need to know this	 * because 4.2 clients are unable to deal with TCP urgent data.	 *	 * To find out, we send out a "DO ECHO".  If the remote system	 * answers "WILL ECHO" it is probably a 4.2 client, and we note	 * that fact ("WILL ECHO" ==> that the client will echo what	 * WE, the server, sends it; it does NOT mean that the client will	 * echo the terminal input).	 */	(void) sprintf(nfrontp, doopt, TELOPT_ECHO);	nfrontp += sizeof doopt-2;	hisopts[TELOPT_ECHO] = OPT_YES_BUT_ALWAYS_LOOK;	/*	 * Show banner that getty never gave.	 *	 * The banner includes some null's (for TELNET CR disambiguation),	 * so we have to be somewhat complicated.	 */	gethostname(hostname, sizeof (hostname));#ifdef 43bsd	bcopy(BANNER1, nfrontp, sizeof BANNER1 -1);	nfrontp += sizeof BANNER1 - 1;	bcopy(hostname, nfrontp, strlen(hostname));	nfrontp += strlen(hostname);	bcopy(BANNER2, nfrontp, sizeof BANNER2 -1);	nfrontp += sizeof BANNER2 - 1;#else	{		char buffer[TABBUFSIZ], barea[TABBUFSIZ];		char *getstr(), *area = barea;		if(getent(buffer, "default") >0) {			BANNER1 = getstr("im", &area);			bcopy(BANNER1, nfrontp, strlen(BANNER1));			nfrontp += strlen(BANNER1);		}	}#endif	/*	 * Call telrcv() once to pick up anything received during	 * terminal type negotiation.	 */	telrcv();	for (;;) {		fd_set ibits, obits, xbits;		register int c;		if (ncc < 0 && pcc < 0)			break;		FD_ZERO(&ibits);		FD_ZERO(&obits);		FD_ZERO(&xbits);		/*		 * Never look for input if there's still		 * stuff in the corresponding output buffer		 */		if (nfrontp - nbackp || pcc > 0) {			FD_SET(f, &obits);		} else {			FD_SET(p, &ibits);		}		if (pfrontp - pbackp || ncc > 0) {			FD_SET(p, &obits);		} else {			FD_SET(f, &ibits);		}		if (!SYNCHing) {			FD_SET(f, &xbits);		}		if ((c = select(16, &ibits, &obits, &xbits,						(struct timeval *)0)) < 1) {			if (c == -1) {				if (errno == EINTR) {					continue;				}			}			sleep(1);			continue;		}		/*		 * Any urgent data?		 */		if (FD_ISSET(net, &xbits)) {

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?