telnetd.c

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

C
1,646
字号
		    SYNCHing = 1;		}		/*		 * Something to read from the network...		 */		if (FD_ISSET(net, &ibits)) {#if	!defined(SO_OOBINLINE)			/*			 * In 4.2 (and 4.3 beta) systems, the			 * OOB indication and data handling in the kernel			 * is such that if two separate TCP Urgent requests			 * come in, one byte of TCP data will be overlaid.			 * This is fatal for Telnet, but we try to live			 * with it.			 *			 * In addition, in 4.2 (and...), a special protocol			 * is needed to pick up the TCP Urgent data in			 * the correct sequence.			 *			 * What we do is:  if we think we are in urgent			 * mode, we look to see if we are "at the mark".			 * If we are, we do an OOB receive.  If we run			 * this twice, we will do the OOB receive twice,			 * but the second will fail, since the second			 * time we were "at the mark", but there wasn't			 * any data there (the kernel doesn't reset			 * "at the mark" until we do a normal read).			 * Once we've read the OOB data, we go ahead			 * and do normal reads.			 *			 * There is also another problem, which is that			 * since the OOB byte we read doesn't put us			 * out of OOB state, and since that byte is most			 * likely the TELNET DM (data mark), we would			 * stay in the TELNET SYNCH (SYNCHing) state.			 * So, clocks to the rescue.  If we've "just"			 * received a DM, then we test for the			 * presence of OOB data when the receive OOB			 * fails (and AFTER we did the normal mode read			 * to clear "at the mark").			 */		    if (SYNCHing) {			int atmark;			ioctl(net, SIOCATMARK, (char *)&atmark);			if (atmark) {			    ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB);			    if ((ncc == -1) && (errno == EINVAL)) {				ncc = read(net, netibuf, sizeof (netibuf));				if (sequenceIs(didnetreceive, gotDM)) {				    SYNCHing = stilloob(net);				}			    }			} else {			    ncc = read(net, netibuf, sizeof (netibuf));			}		    } else {			ncc = read(net, netibuf, sizeof (netibuf));		    }		    settimer(didnetreceive);#else	/* !defined(SO_OOBINLINE)) */		    ncc = read(net, netibuf, sizeof (netibuf));#endif	/* !defined(SO_OOBINLINE)) */		    if (ncc < 0 && errno == EWOULDBLOCK)			ncc = 0;		    else {			if (ncc <= 0) {			    break;			}			netip = netibuf;		    }		}		/*		 * Something to read from the pty...		 */		if (FD_ISSET(p, &ibits)) {			pcc = read(p, ptyibuf, BUFSIZ);			if (pcc < 0 && errno == EWOULDBLOCK)				pcc = 0;			else {				if (pcc <= 0)					break;				ptyip = ptyibuf;			}		}		while (pcc > 0) {			if ((&netobuf[BUFSIZ] - nfrontp) < 2)				break;			c = *ptyip++ & 0377, pcc--;			if (c == IAC)				*nfrontp++ = c;			*nfrontp++ = c;			if (c == '\r') {				if (pcc > 0 && ((*ptyip & 0377) == '\n')) {					*nfrontp++ = *ptyip++ & 0377;					pcc--;				} else					*nfrontp++ = '\0';			}		}		if (FD_ISSET(f, &obits) && (nfrontp - nbackp) > 0)			netflush();		if (ncc > 0)			telrcv();		if (FD_ISSET(p, &obits) && (pfrontp - pbackp) > 0)			ptyflush();	}	cleanup();}	/* * State for recv fsm */#define	TS_DATA		0	/* base state */#define	TS_IAC		1	/* look for double IAC's */#define	TS_CR		2	/* CR-LF ->'s CR */#define	TS_SB		3	/* throw away begin's... */#define	TS_SE		4	/* ...end's (suboption negotiation) */#define	TS_WILL		5	/* will option negotiation */#define	TS_WONT		6	/* wont " */#define	TS_DO		7	/* do " */#define	TS_DONT		8	/* dont " */telrcv(){	register int c;	static int state = TS_DATA;	while (ncc > 0) {		if ((&ptyobuf[BUFSIZ] - pfrontp) < 2)			return;		c = *netip++ & 0377, ncc--;		switch (state) {		case TS_CR:			state = TS_DATA;			if ((c == 0) || (c == '\n')) {				break;			}			/* FALL THROUGH */		case TS_DATA:			if (c == IAC) {				state = TS_IAC;				break;			}			if (inter > 0)				break;			/*			 * We now map \r\n ==> \r for pragmatic reasons.			 * Many client implementations send \r\n when			 * the user hits the CarriageReturn key.			 *			 * We USED to map \r\n ==> \n, since \r\n says			 * that we want to be in column 1 of the next			 * printable line, and \n is the standard			 * unix way of saying that (\r is only good			 * if CRMOD is set, which it normally is).			 */			if ((c == '\r') && (hisopts[TELOPT_BINARY] == OPT_NO)) {				state = TS_CR;			}			*pfrontp++ = c;			break;		case TS_IAC:			switch (c) {			/*			 * Send the process on the pty side an			 * interrupt.  Do this with a NULL or			 * interrupt char; depending on the tty mode.			 */			case IP:				interrupt();				break;			case BREAK:				sendbrk();				break;			/*			 * Are You There?			 */			case AYT:				strcpy(nfrontp, "\r\n[Yes]\r\n");				nfrontp += 9;				break;			/*			 * Abort Output			 */			case AO: {					struct ltchars tmpltc;					ptyflush();	/* half-hearted */					ioctl(pty, TIOCGLTC, &tmpltc);					if (tmpltc.t_flushc != '\377') {						*pfrontp++ = tmpltc.t_flushc;					}					netclear();	/* clear buffer back */					*nfrontp++ = IAC;					*nfrontp++ = DM;					neturg = nfrontp-1; /* off by one XXX */					break;				}			/*			 * Erase Character and			 * Erase Line			 */			case EC:			case EL: {					struct sgttyb b;					char ch;					ptyflush();	/* half-hearted */					ioctl(pty, TIOCGETP, &b);					ch = (c == EC) ?						b.sg_erase : b.sg_kill;					if (ch != '\377') {						*pfrontp++ = ch;					}					break;				}			/*			 * Check for urgent data...			 */			case DM:				SYNCHing = stilloob(net);				settimer(gotDM);				break;			/*			 * Begin option subnegotiation...			 */			case SB:				state = TS_SB;				continue;			case WILL:				state = TS_WILL;				continue;			case WONT:				state = TS_WONT;				continue;			case DO:				state = TS_DO;				continue;			case DONT:				state = TS_DONT;				continue;			case IAC:				*pfrontp++ = c;				break;			}			state = TS_DATA;			break;		case TS_SB:			if (c == IAC) {				state = TS_SE;			} else {				SB_ACCUM(c);			}			break;		case TS_SE:			if (c != SE) {				if (c != IAC) {					SB_ACCUM(IAC);				}				SB_ACCUM(c);				state = TS_SB;			} else {				SB_TERM();				suboption();	/* handle sub-option */				state = TS_DATA;			}			break;		case TS_WILL:			if (hisopts[c] != OPT_YES)				willoption(c);			state = TS_DATA;			continue;		case TS_WONT:			if (hisopts[c] != OPT_NO)				wontoption(c);			state = TS_DATA;			continue;		case TS_DO:			if (myopts[c] != OPT_YES)				dooption(c);			state = TS_DATA;			continue;		case TS_DONT:			if (myopts[c] != OPT_NO) {				dontoption(c);			}			state = TS_DATA;			continue;		default:			syslog(LOG_ERR, "telnetd: panic state=%d\n", state);			printf("telnetd: panic state=%d\n", state);			exit(1);		}	}}willoption(option)	int option;{	char *fmt;	switch (option) {	case TELOPT_BINARY:		mode(RAW, 0);		fmt = doopt;		break;	case TELOPT_ECHO:		not42 = 0;		/* looks like a 4.2 system */		/*		 * Now, in a 4.2 system, to break them out of ECHOing		 * (to the terminal) mode, we need to send a "WILL ECHO".		 * Kludge upon kludge!		 */		if (myopts[TELOPT_ECHO] == OPT_YES) {		    dooption(TELOPT_ECHO);		}		fmt = dont;		break;	case TELOPT_TTYPE:		settimer(ttypeopt);		if (hisopts[TELOPT_TTYPE] == OPT_YES_BUT_ALWAYS_LOOK) {		    hisopts[TELOPT_TTYPE] = OPT_YES;		    return;		}		fmt = doopt;		break;	case TELOPT_SGA:		fmt = doopt;		break;	case TELOPT_TM:		fmt = dont;		break;	default:		fmt = dont;		break;	}	if (fmt == doopt) {		hisopts[option] = OPT_YES;	} else {		hisopts[option] = OPT_NO;	}	(void) sprintf(nfrontp, fmt, option);	nfrontp += sizeof (dont) - 2;}wontoption(option)	int option;{	char *fmt;	switch (option) {	case TELOPT_ECHO:		not42 = 1;		/* doesn't seem to be a 4.2 system */		break;	case TELOPT_BINARY:		mode(0, RAW);		break;	case TELOPT_TTYPE:	    settimer(ttypeopt);	    break;	}	fmt = dont;	hisopts[option] = OPT_NO;	sprintf(nfrontp, fmt, option);	nfrontp += sizeof (doopt) - 2;}dooption(option)	int option;{	char *fmt;	switch (option) {	case TELOPT_TM:		fmt = wont;		break;	case TELOPT_ECHO:		mode(ECHO|CRMOD, 0);		fmt = will;		break;	case TELOPT_BINARY:		mode(RAW, 0);		fmt = will;		break;	case TELOPT_SGA:		fmt = will;		break;	default:		fmt = wont;		break;	}	if (fmt == will) {	    myopts[option] = OPT_YES;	} else {	    myopts[option] = OPT_NO;	}	sprintf(nfrontp, fmt, option);	nfrontp += sizeof (doopt) - 2;}dontoption(option)int option;{    char *fmt;    switch (option) {    case TELOPT_ECHO:		/* we should stop echoing */	mode(0, ECHO|CRMOD);	fmt = wont;	break;    default:	fmt = wont;	break;    }    if (fmt = wont) {	myopts[option] = OPT_NO;    } else {	myopts[option] = OPT_YES;    }    (void) sprintf(nfrontp, fmt, option);    nfrontp += sizeof (wont) - 2;}/* * suboption() * *	Look at the sub-option buffer, and try to be helpful to the other * side. * *	Currently we recognize: * *	Terminal type is */suboption(){    switch (SB_GET()) {    case TELOPT_TTYPE: {		/* Yaaaay! */	static char terminalname[5+41] = "TERM=";	settimer(ttypesubopt);	if (SB_GET() != TELQUAL_IS) {	    return;		/* ??? XXX but, this is the most robust */	}	terminaltype = terminalname+strlen(terminalname);	while ((terminaltype < (terminalname + sizeof terminalname-1)) &&								    !SB_EOF()) {	    register int c;	    c = SB_GET();	    if (isupper(c)) {		c = tolower(c);	    }	    *terminaltype++ = c;    /* accumulate name */	}	*terminaltype = 0;	terminaltype = terminalname;	break;    }    default:	;    }}mode(on, off)	int on, off;{	struct sgttyb b;	ptyflush();	ioctl(pty, TIOCGETP, &b);	b.sg_flags |= on;	b.sg_flags &= ~off;	ioctl(pty, TIOCSETP, &b);}/* * Send interrupt to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write intr char. */interrupt(){	struct sgttyb b;	struct tchars tchars;	ptyflush();	/* half-hearted */	ioctl(pty, TIOCGETP, &b);	if (b.sg_flags & RAW) {		*pfrontp++ = '\0';		return;	}	*pfrontp++ = ioctl(pty, TIOCGETC, &tchars) < 0 ?		'\177' : tchars.t_intrc;}/* * Send quit to process on other side of pty. * If it is in raw mode, just write NULL; * otherwise, write quit char.

⌨️ 快捷键说明

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