From ea13930276965c17b866c28b229e4fb9829d6272 Mon Sep 17 00:00:00 2001 From: Paul Borman Date: Fri, 1 Sep 1989 22:17:55 -0800 Subject: [PATCH] New telnetd, with LINEMODE support SCCS-vsn: libexec/telnetd/Makefile 5.5 SCCS-vsn: libexec/telnetd/pathnames.h 5.3 SCCS-vsn: libexec/telnetd/telnetd.c 5.38 SCCS-vsn: libexec/telnetd/defs.h 5.1 SCCS-vsn: libexec/telnetd/ext.h 5.1 SCCS-vsn: libexec/telnetd/global.c 5.1 SCCS-vsn: libexec/telnetd/slc.c 5.1 SCCS-vsn: libexec/telnetd/state.c 5.1 SCCS-vsn: libexec/telnetd/sys_term.c 5.1 SCCS-vsn: libexec/telnetd/telnetd.h 5.1 SCCS-vsn: libexec/telnetd/termstat.c 5.1 SCCS-vsn: libexec/telnetd/utility.c 5.1 --- usr/src/libexec/telnetd/Makefile | 40 +- usr/src/libexec/telnetd/defs.h | 144 +++ usr/src/libexec/telnetd/ext.h | 86 ++ usr/src/libexec/telnetd/global.c | 32 + usr/src/libexec/telnetd/pathnames.h | 15 +- usr/src/libexec/telnetd/slc.c | 440 ++++++++ usr/src/libexec/telnetd/state.c | 1034 ++++++++++++++++++ usr/src/libexec/telnetd/sys_term.c | 1015 +++++++++++++++++ usr/src/libexec/telnetd/telnetd.c | 1579 +++++++-------------------- usr/src/libexec/telnetd/telnetd.h | 27 + usr/src/libexec/telnetd/termstat.c | 557 ++++++++++ usr/src/libexec/telnetd/utility.c | 421 +++++++ 12 files changed, 4221 insertions(+), 1169 deletions(-) create mode 100644 usr/src/libexec/telnetd/defs.h create mode 100644 usr/src/libexec/telnetd/ext.h create mode 100644 usr/src/libexec/telnetd/global.c create mode 100644 usr/src/libexec/telnetd/slc.c create mode 100644 usr/src/libexec/telnetd/state.c create mode 100644 usr/src/libexec/telnetd/sys_term.c create mode 100644 usr/src/libexec/telnetd/telnetd.h create mode 100644 usr/src/libexec/telnetd/termstat.c create mode 100644 usr/src/libexec/telnetd/utility.c diff --git a/usr/src/libexec/telnetd/Makefile b/usr/src/libexec/telnetd/Makefile index 1f43888f9d..4e24b65f18 100644 --- a/usr/src/libexec/telnetd/Makefile +++ b/usr/src/libexec/telnetd/Makefile @@ -14,19 +14,47 @@ # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. # -# @(#)Makefile 5.4 (Berkeley) %G% +# @(#)Makefile 5.5 (Berkeley) %G% # -CFLAGS= -O + +DEFINES= LIBC= /lib/libc.a -SRCS= telnetd.c get_date.c gettytab.c -OBJS= telnetd.o get_date.o gettytab.o +# 4.4BSD +# LIBS= -lutil -ltermcap +# GETTYOBJ= get_date.o gettytab.o +# GETTYSRC= get_date.c gettytab.c +# LIBEXEC=${DESTDIR}/usr/libexec +# +# 4.3BSD LIBS= -ltermcap +# GETTYOBJ= get_date.o gettytab.o +# GETTYSRC= get_date.c gettytab.c +# LIBEXEC=${DESTDIR}/etc +# +# Cray UNICOS +# LIBS= -lnet -lcurses +# GETTYOBJ= +# GETTYSRC= +# LIBEXEC=${DESTDIR}/etc + +LIBS= -lutil -ltermcap +GETTYOBJ= get_date.o gettytab.o +GETTYSRC= get_date.c gettytab.c +LIBEXEC= ${DESTDIR}/usr/libexec + +SRC1= telnetd.c state.c termstat.c slc.c sys_term.c utility.c global.c +OBJ1= telnetd.o state.o termstat.o slc.o sys_term.o utility.o global.o + +OBJS= ${OBJ1} ${GETTYOBJ} +SRCS= ${SRC1} ${GETTYSRC} + VPATH= ../getty MAN= telnetd.0 +CFLAGS= -O ${DEFINES} all: telnetd telnetd: ${OBJS} ${LIBC} - ${CC} -o $@ ${CFLAGS} ${OBJS} -lutil + ${CC} -o $@ ${CFLAGS} ${OBJS} ${LIBS} clean: rm -f ${OBJS} core telnetd @@ -38,7 +66,7 @@ depend: ${SRCS} mkdep ${CFLAGS} ${SRCS} install: ${MAN} - install -s -o bin -g bin -m 755 telnetd ${DESTDIR}/usr/libexec + install -s -o bin -g bin -m 755 telnetd ${LIBEXEC} install -c -o bin -g bin -m 444 ${MAN} ${DESTDIR}/usr/man/cat8 lint: ${SRCS} diff --git a/usr/src/libexec/telnetd/defs.h b/usr/src/libexec/telnetd/defs.h new file mode 100644 index 0000000000..fc58002ebd --- /dev/null +++ b/usr/src/libexec/telnetd/defs.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)defs.h 5.1 (Berkeley) %G% + */ + +/* + * Telnet server defines + */ +#include +#include + +#ifndef BSD +# define BSD 43 +#endif + +#if BSD > 43 +#define USE_TERMIO +#endif + +#ifdef CRAY +# define NEWINIT +# define SYSV_TERMIO +# define NO_GETTYTAB +# define signal sigset +#endif /* CRAY */ + +#ifdef SYSV_TERMIO +# define USE_TERMIO +#endif + +#include +#ifndef CRAY +#include +#endif /* CRAY */ +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifndef USE_TERMIO +#include +#else +# ifdef SYSV_TERMIO +# include +# else +# include +# endif +#endif + +#ifdef CRAY +#include +#include +# ifdef CRAY1 +# include +# ifndef FD_ZERO +# include +# endif /* FD_ZERO */ +# endif /* CRAY1 */ + +#include +#endif /* CRAY */ + +#if defined(TCSIG) || defined(TIOCPKT_IOCTL) +# define LINEMODE +# define KLUDGELINEMODE +#endif + +#ifndef FD_SET +typedef struct fd_set { int fds_bits[1]; } fd_set; + +#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) +#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) +#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) +#define FD_ZERO(p) ((p)->fds_bits[0] = 0) +#endif /* FD_SET */ + +#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 3 + +/* + * I/O data buffers defines + */ +#define NETSLOP 64 +#ifdef CRAY +#undef BUFSIZ +#define BUFSIZ 2048 +#endif + +#define NIACCUM(c) { *netip++ = c; \ + ncc++; \ + } + +/* clock manipulations */ +#define settimer(x) (clocks.x = ++clocks.system) +#define sequenceIs(x,y) (clocks.x < clocks.y) + +/* + * Linemode support states, in decreasing order of importance + */ +#define REAL_LINEMODE 0x02 +#define KLUDGE_LINEMODE 0x01 +#define NO_LINEMODE 0x00 + +/* + * Structures of information for each special character function. + */ +typedef struct { + unsigned char flag; /* the flags for this function */ + unsigned char val; /* the value of the special character */ +} slcent, *Slcent; + +typedef struct { + slcent defset; /* the default settings */ + slcent current; /* the current settings */ + unsigned char *sptr; /* a pointer to the char in */ + /* system data structures */ +} slcfun, *Slcfun; diff --git a/usr/src/libexec/telnetd/ext.h b/usr/src/libexec/telnetd/ext.h new file mode 100644 index 0000000000..2d3dbbb7b8 --- /dev/null +++ b/usr/src/libexec/telnetd/ext.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)ext.h 5.1 (Berkeley) %G% + */ + +/* + * Telnet server variable declarations + */ +extern char hisopts[256]; +extern char myopts[256]; +extern char hiswants[256]; +extern char mywants[256]; +extern char resp[256]; +extern int linemode; /* linemode on/off */ +#ifdef LINEMODE +extern int uselinemode; /* what linemode to use (on/off) */ +extern int editmode; /* edit modes in use */ +extern int useeditmode; /* edit modes to use */ +extern int alwayslinemode; /* command line option */ +# ifdef KLUDGELINEMODE +extern int lmodetype; /* Client support for linemode */ +# endif /* KLUDGELINEMODE */ +#endif /* LINEMODE */ +extern int flowmode; /* current flow control state */ + +extern slcfun slctab[NSLC + 1]; /* slc mapping table */ + +char *terminaltype; + +/* + * I/O data buffers, pointers, and counters. + */ +extern char ptyobuf[BUFSIZ+NETSLOP], *pfrontp, *pbackp; + +extern char netibuf[BUFSIZ], *netip; + +extern char netobuf[BUFSIZ+NETSLOP], *nfrontp, *nbackp; +extern char *neturg; /* one past last bye of urgent data */ + +extern int pcc, ncc; + +extern int pty, net; +extern char *line; +extern int SYNCHing; /* we are in TELNET SYNCH mode */ + +/* + * The following are some clocks used to decide how to interpret + * the relationship between various variables. + */ + +extern 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 */ + ttypesubopt, /* ttype subopt is received */ + tspeedsubopt, /* tspeed subopt is received */ + baseline, /* time started to do timed action */ + gotDM; /* when did we last see a data mark */ +} clocks; + + +#ifdef CRAY2 +extern int needtermstat; +#endif + +#ifndef CRAY +#define DEFAULT_IM "\r\n\r\n4.3 BSD UNIX (%h) (%t)\r\n\r\r\n\r" +#else +#define DEFAULT_IM "\r\n\r\nCray UNICOS (%h) (%t)\r\n\r\r\n\r" +#endif diff --git a/usr/src/libexec/telnetd/global.c b/usr/src/libexec/telnetd/global.c new file mode 100644 index 0000000000..6f5e9fc0d1 --- /dev/null +++ b/usr/src/libexec/telnetd/global.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char sccsid[] = "@(#)global.c 5.1 (Berkeley) %G%"; +#endif /* not lint */ + +/* + * Allocate global variables. We do this + * by including the header file that defines + * them all as externs, but first we define + * the keyword "extern" to be nothing, so that + * we will actually allocate the space. + */ + +#include "defs.h" +#define extern +#include "ext.h" diff --git a/usr/src/libexec/telnetd/pathnames.h b/usr/src/libexec/telnetd/pathnames.h index ebad6273e9..b8a1f08842 100644 --- a/usr/src/libexec/telnetd/pathnames.h +++ b/usr/src/libexec/telnetd/pathnames.h @@ -14,9 +14,18 @@ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * - * @(#)pathnames.h 5.2 (Berkeley) %G% + * @(#)pathnames.h 5.3 (Berkeley) %G% */ -#include +#if BSD > 43 -#define _PATH_LOGIN "/usr/bin/login" +# include + +# define _PATH_LOGIN "/usr/bin/login" + +#else + +# define _PATH_TTY "/dev/tty" +# define _PATH_LOGIN "/bin/login" + +#endif diff --git a/usr/src/libexec/telnetd/slc.c b/usr/src/libexec/telnetd/slc.c new file mode 100644 index 0000000000..ed8272762d --- /dev/null +++ b/usr/src/libexec/telnetd/slc.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char sccsid[] = "@(#)slc.c 5.1 (Berkeley) %G%"; +#endif /* not lint */ + +#include "telnetd.h" + +#ifdef LINEMODE +/* + * local varibles + */ +static char *def_slcbuf = (char *)0;; +static int def_slclen = 0; +static int slcchange; /* change to slc is requested */ +static char *slcptr; /* pointer into slc buffer */ +static char slcbuf[NSLC*6]; /* buffer for slc negotiation */ + +/* + * send_slc + * + * Write out the current special characters to the client. + */ +send_slc() +{ + register int i; + + /* + * Send out list of triplets of special characters + * to client. We only send info on the characters + * that are currently supported. + */ + for (i = 1; i <= NSLC; i++) { + if ((slctab[i].current.flag & SLC_LEVELBITS) != SLC_NOSUPPORT) { + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + } + +} /* end of send_slc */ + +/* + * default_slc + * + * Set pty special characters to all the defaults. + */ +default_slc() +{ + register int i; + + for (i = 1; i <= NSLC; i++) { + slctab[i].current.flag = slctab[i].defset.flag; + slctab[i].current.val = slctab[i].defset.val; + if (slctab[i].sptr) { + *(slctab[i].sptr) = slctab[i].defset.val; + } + } + slcchange = 1; + +} /* end of default_slc */ +#endif LINEMODE + +/* + * get_slc_defaults + * + * Initialize the slc mapping table. + */ +get_slc_defaults() +{ + register int i; + + init_termbuf(); + + for (i = 1; i <= NSLC; i++) { + slctab[i].defset.flag = + spcset(i, &slctab[i].defset.val, &slctab[i].sptr); + slctab[i].current.flag = SLC_NOSUPPORT; + slctab[i].current.val = 0; + } + +} /* end of get_slc_defaults */ + +#ifdef LINEMODE +/* + * add_slc + * + * Add an slc triplet to the slc buffer. + */ +add_slc(func, flag, val) + register unsigned char func, flag, val; +{ + + if (func == 0xff) + *slcptr++ = 0xff; + *slcptr++ = func; + + if (flag == 0xff) + *slcptr++ = 0xff; + *slcptr++ = flag; + + if (val == 0xff) + *slcptr++ = 0xff; + *slcptr++ = val; + +} /* end of add_slc */ + +/* + * start_slc + * + * Get ready to process incoming slc's and respond to them. + * + * The parameter getit is non-zero if it is necessary to grab a copy + * of the terminal control structures. + */ +start_slc(getit) + register int getit; +{ + + slcchange = 0; + if (getit) + init_termbuf(); + (void) sprintf(slcbuf, "%c%c%c%c", IAC, SB, TELOPT_LINEMODE, LM_SLC); + slcptr = slcbuf + 4; + +} /* end of start_slc */ + +/* + * end_slc + * + * Finish up the slc negotiation. If something to send, then send it. + */ +end_slc(bufp) +register char **bufp; +{ + register int len; + void netflush(); + + /* + * If a change has occured, store the new terminal control + * structures back to the terminal driver. + */ + if (slcchange) { + set_termbuf(); + } + + /* + * If the pty state has not yet been fully processed and there is a + * deferred slc request from the client, then do not send any + * sort of slc negotiation now. We will respond to the client's + * request very soon. + */ + if (def_slcbuf && (terminit() == 0)) { + return; + } + + if (slcptr > (slcbuf + 4)) { + if (bufp) { + *bufp = &slcbuf[4]; + return(slcptr - slcbuf - 4); + } else { + (void) sprintf(slcptr, "%c%c", IAC, SE); + slcptr += 2; + len = slcptr - slcbuf; + writenet(slcbuf, len); + netflush(); /* force it out immediately */ + } + } + +} /* end of end_slc */ + +/* + * process_slc + * + * Figure out what to do about the client's slc + */ +process_slc(func, flag, val) + register unsigned char func, flag, val; +{ + register int hislevel, mylevel, ack; + + /* + * Ensure that we know something about this function + */ + if (func > NSLC) { + add_slc(func, SLC_NOSUPPORT, 0); + return; + } + + /* + * Process the special case requests of 0 SLC_DEFAULT 0 + * and 0 SLC_VARIABLE 0. Be a little forgiving here, don't + * worry about whether the value is actually 0 or not. + */ + if (func == 0) { + if ((flag = flag & SLC_LEVELBITS) == SLC_DEFAULT) { + default_slc(); + } + if (flag == SLC_DEFAULT || flag == SLC_VARIABLE) { + send_slc(); + } + return; + } + + /* + * Appears to be a function that we know something about. So + * get on with it and see what we know. + */ + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].current.flag & SLC_LEVELBITS; + ack = flag & SLC_ACK; + /* + * ignore the command if: + * the function value and level are the same as what we already have; + * or the level is the same and the ack bit is set + */ + if (hislevel == mylevel && (val == slctab[func].current.val || ack)) { + return; + } else { + change_slc(func, flag, val); + } + +} /* end of process_slc */ + +/* + * change_slc + * + * Process a request to change one of our special characters. + * Compare client's request with what we are capable of supporting. + */ +change_slc(func, flag, val) + register unsigned char func, flag, val; +{ + register int hislevel, mylevel; + + hislevel = flag & SLC_LEVELBITS; + mylevel = slctab[func].defset.flag & SLC_LEVELBITS; + /* + * If client is setting a function to NOSUPPORT + * or DEFAULT, then we can easily and directly + * accomodate the request. + */ + if (hislevel == SLC_NOSUPPORT) { + slctab[func].current.flag = flag; + slctab[func].current.val = val; + flag |= SLC_ACK; + add_slc(func, flag, val); + return; + } + if (hislevel == SLC_DEFAULT) { + /* + * Special case here. If client tells us to use + * the default on a function we don't support, then + * return NOSUPPORT instead of what we may have as a + * default level of DEFAULT. + */ + if (mylevel == SLC_DEFAULT) { + slctab[func].current.flag = SLC_NOSUPPORT; + } else { + slctab[func].current.flag = slctab[func].defset.flag; + } + slctab[func].current.val = slctab[func].defset.val; + add_slc(func, slctab[func].current.flag, + slctab[func].current.val); + return; + } + + /* + * Client wants us to change to a new value or he + * is telling us that he can't change to our value. + * Some of the slc's we support and can change, + * some we do support but can't change, + * and others we don't support at all. + * If we can change it then we have a pointer to + * the place to put the new value, so change it, + * otherwise, continue the negotiation. + */ + if (slctab[func].sptr) { + /* + * We can change this one. + */ + slctab[func].current.val = val; + *(slctab[func].sptr) = val; + slctab[func].current.flag = flag; + flag |= SLC_ACK; + slcchange = 1; + add_slc(func, flag, val); + } else { + /* + * It is not possible for us to support this + * request as he asks. + * + * If our level is DEFAULT, then just ack whatever was + * sent. + * + * If he can't change and we can't change, + * then degenerate to NOSUPPORT. + * + * Otherwise we send our level back to him, (CANTCHANGE + * or NOSUPPORT) and if CANTCHANGE, send + * our value as well. + */ + if (mylevel == SLC_DEFAULT) { + slctab[func].current.flag = flag; + slctab[func].current.val = val; + flag |= SLC_ACK; + } else if (hislevel == SLC_CANTCHANGE && + mylevel == SLC_CANTCHANGE) { + flag &= ~SLC_LEVELBITS; + flag |= SLC_NOSUPPORT; + slctab[func].current.flag = flag; + } else { + flag &= ~SLC_LEVELBITS; + flag |= mylevel; + slctab[func].current.flag = flag; + if (mylevel == SLC_CANTCHANGE) { + slctab[func].current.val = + slctab[func].defset.val; + val = slctab[func].current.val; + } + + } + add_slc(func, flag, val); + } + +} /* end of change_slc */ + +/* + * check_slc + * + * Check the special characters in use and notify the client if any have + * changed. Only those characters that are capable of being changed are + * likely to have changed. If a local change occurs, kick the support level + * and flags up to the defaults. + */ +check_slc() +{ + register int i; + + for (i = 1; i <= NSLC; i++) { +#if defined(USE_TERMIO) && defined(SYSV_TERMIO) + /* + * In a perfect world this would be a neat little + * function. But in this world, we should not notify + * client of changes to the VEOF char when + * ICANON is off, because it is not representing + * a special character. + */ + if (!tty_isediting() && i == SLC_EOF) + continue; +#endif /* defined(USE_TERMIO) && defined(SYSV_TERMIO) */ + if (slctab[i].sptr && + (*(slctab[i].sptr) != slctab[i].current.val)) { + slctab[i].current.val = *(slctab[i].sptr); + slctab[i].current.flag = slctab[i].defset.flag; + add_slc((unsigned char)i, slctab[i].current.flag, + slctab[i].current.val); + } + } + +} /* check_slc */ + +/* + * do_opt_slc + * + * Process an slc option buffer. Defer processing of incoming slc's + * until after the terminal state has been processed. Save the first slc + * request that comes along, but discard all others. + * + * ptr points to the beginning of the buffer, len is the length. + */ +do_opt_slc(ptr, len) +register char *ptr; +register int len; +{ + register unsigned char func, flag, val; + register char *end = (char *)(ptr + len); + char *malloc(); + + if (terminit()) { /* go ahead */ + while (ptr < end) { + func = *ptr++; + if (ptr >= end) break; + flag = *ptr++; + if (ptr >= end) break; + val = *ptr++; + + process_slc(func, flag, val); + + } + } else { + /* + * save this slc buffer if it is the first, otherwise dump + * it. + */ + if (def_slcbuf == (char *)0) { + def_slclen = len; + def_slcbuf = malloc((unsigned)len); + if (def_slcbuf == (char *)0) + return; /* too bad */ + bcopy(ptr, def_slcbuf, len); + } + } + +} /* end of do_opt_slc */ + +/* + * deferslc + * + * Do slc stuff that was deferred. + */ +deferslc() +{ + if (def_slcbuf) { + start_slc(1); + do_opt_slc(def_slcbuf, def_slclen); + end_slc(0); + free(def_slcbuf); + def_slcbuf = (char *)0; + def_slclen = 0; + } + +} /* end of deferslc */ + +#endif /* LINEMODE */ diff --git a/usr/src/libexec/telnetd/state.c b/usr/src/libexec/telnetd/state.c new file mode 100644 index 0000000000..986e383d40 --- /dev/null +++ b/usr/src/libexec/telnetd/state.c @@ -0,0 +1,1034 @@ +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char sccsid[] = "@(#)state.c 5.1 (Berkeley) %G%"; +#endif /* not lint */ + +#include "telnetd.h" + +char doopt[] = { IAC, DO, '%', 'c', 0 }; +char dont[] = { IAC, DONT, '%', 'c', 0 }; +char will[] = { IAC, WILL, '%', 'c', 0 }; +char wont[] = { IAC, WONT, '%', 'c', 0 }; +int not42 = 1; + +/* + * Buffer for sub-options, and macros + * for suboptions buffer manipulations + */ +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) + + + +/* + * 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; +#ifdef CRAY2 + char *opfrontp = pfrontp; +#endif + + while (ncc > 0) { + if ((&ptyobuf[BUFSIZ] - pfrontp) < 2) + break; + c = *netip++ & 0377, ncc--; + switch (state) { + + case TS_CR: + state = TS_DATA; + /* Strip off \n or \0 after a \r */ + if ((c == 0) || (c == '\n')) { + break; + } + /* FALL THROUGH */ + + case TS_DATA: + if (c == IAC) { + state = TS_IAC; + 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)) { + /* + * If we are operating in linemode, + * convert to local end-of-line. + */ + if ((linemode) && (ncc > 0)&&('\n' == *netip)) { + netip++; ncc--; + c = '\n'; + } else { + state = TS_CR; + } + } + *pfrontp++ = c; + break; + + case TS_IAC: +gotiac: 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: + (void) strcpy(nfrontp, "\r\n[Yes]\r\n"); + nfrontp += 9; + break; + + /* + * Abort Output + */ + case AO: + { + ptyflush(); /* half-hearted */ + init_termbuf(); + + if (slctab[SLC_AO].sptr && + *slctab[SLC_AO].sptr != '\377') { + *pfrontp++ = *slctab[SLC_AO].sptr; + } + + 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: + { + unsigned char ch; + + ptyflush(); /* half-hearted */ + init_termbuf(); + ch = (c == EC) ? *slctab[SLC_EC].sptr : + *slctab[SLC_EL].sptr; + 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; + SB_CLEAR(); + 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 EOR: + if (hisopts[TELOPT_EOR]) + doeof(); + break; + + /* + * Handle RFC 10xx Telnet linemode option additions + * to command stream (EOF, SUSP, ABORT). + */ + case xEOF: + doeof(); + break; + + case SUSP: + sendsusp(); + break; + + case ABORT: + sendbrk(); + break; + + 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) { + /* + * bad form of suboption negotiation. + * handle it in such a way as to avoid + * damage to local state. Parse + * suboption buffer found so far, + * then treat remaining stream as + * another command sequence. + */ + SB_TERM(); + suboption(); + state = TS_IAC; + goto gotiac; + } + SB_ACCUM(c); + state = TS_SB; + } else { + SB_TERM(); + suboption(); /* handle sub-option */ + state = TS_DATA; + } + break; + + case TS_WILL: + willoption(c, 0); + state = TS_DATA; + continue; + + case TS_WONT: + wontoption(c, 0); + state = TS_DATA; + continue; + + case TS_DO: + dooption(c, 0); + state = TS_DATA; + continue; + + case TS_DONT: + dontoption(c, 0); + state = TS_DATA; + continue; + + default: + syslog(LOG_ERR, "telnetd: panic state=%d\n", state); + printf("telnetd: panic state=%d\n", state); + exit(1); + } + } +#if CRAY2 + if (!linemode) { + char xptyobuf[BUFSIZ+NETSLOP]; + char xbuf2[BUFSIZ]; + register char *cp; + int n = pfrontp - opfrontp, oc; + bcopy(opfrontp, xptyobuf, n); + pfrontp = opfrontp; + pfrontp += term_input(xptyobuf, pfrontp, n, BUFSIZ+NETSLOP, + xbuf2, &oc, BUFSIZ); + for (cp = xbuf2; oc > 0; --oc) + if ((*nfrontp++ = *cp++) == IAC) + *nfrontp++ = IAC; + } +#endif /* CRAY2 */ +} /* end of telrcv */ + +/* + * The will/wont/do/dont state machines are based on Dave Borman's + * Telnet option processing state machine. We keep track of the full + * state of the option negotiation with the following state variables + * myopts, hisopts - The last fully negotiated state for each + * side of the connection. + * mywants, hiswants - The state we wish to be in after a completed + * negotiation. (hiswants is slightly misleading, + * this is more precisely the state I want him to + * be in. + * resp - We count the number of requests we have sent out. + * + * These correspond to the following states: + * my_state = the last negotiated state + * want_state = what I want the state to go to + * want_resp = how many requests I have sent + * All state defaults are negative, and resp defaults to 0. + * + * When initiating a request to change state to new_state: + * + * if ((want_resp == 0 && new_state == my_state) || want_state == new_state) { + * do nothing; + * } else { + * want_state = new_state; + * send new_state; + * want_resp++; + * } + * + * When receiving new_state: + * + * if (want_resp) { + * want_resp--; + * if (want_resp && (new_state == my_state)) + * want_resp--; + * } + * if ((want_resp == 0) && (new_state != want_state)) { + * if (ok_to_switch_to new_state) + * want_state = new_state; + * else + * want_resp++; + * send want_state; + * } + * my_state = new_state; + * + * Note that new_state is implied in these functions by the function itself. + * will and do imply positive new_state, wont and dont imply negative. + * + * Finally, there is one catch. If we send a negative response to a + * positive request, my_state will be the positive while want_state will + * remain negative. my_state will revert to negative when the negative + * acknowlegment arrives from the peer. Thus, my_state generally tells + * us not only the last negotiated state, but also tells us what the peer + * wants to be doing as well. It is important to understand this difference + * as we may wish to be processing data streams based on our desired state + * (want_state) or based on what the peer thinks the state is (my_state). + * + * This all works fine because if the peer sends a positive request, the data + * that we receive prior to negative acknowlegment will probably be affected + * by the positive state, and we can process it as such (if we can; if we + * can't then it really doesn't matter). If it is that important, then the + * peer probably should be buffering until this option state negotiation + * is complete. + * + * In processing options, request signifies whether this is a request + * to send or a response. request is true if this is a request to + * send generated locally. + */ +willoption(option, request) + int option, request; +{ + int changeok = 0; + char *fmt = (char *)0; + + /* + * process input from peer. + */ + if (request == 0) { + switch (option) { + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(1); + set_termbuf(); + changeok++; + 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, 1); + } + /* + * Fool the state machine into sending a don't. + * This also allows the initial echo sending code to + * break out of the loop that it is + * in. (Look in telnet()) + */ + hiswants[TELOPT_ECHO] = OPT_NO; + break; + + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * This telnetd implementation does not really support + * timing marks, it just uses them to support the kludge + * linemode stuff. If we receive a will or wont TM in + * response to our do TM request that may have been sent + * to determine kludge linemode support, process it, + * otherwise TM should get a negative response back. + */ + /* + * Handle the linemode kludge stuff. + * If we are not currently supporting any + * linemode at all, then we assume that this + * is the client telling us to use kludge + * linemode in response to our query. Set the + * linemode type that is to be supported, note + * that the client wishes to use linemode, and + * eat the will TM as though it never arrived. + */ + if (lmodetype < KLUDGE_LINEMODE) { + lmodetype = KLUDGE_LINEMODE; + clientstat(TELOPT_LINEMODE, WILL, 0); + dontoption(TELOPT_SGA, 0); + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + /* + * cheat the state machine so that it + * looks like we never sent the TM at + * all. The bad part of this is that + * if the client sends a will TM on his + * own to turn on linemode, then he + * won't get a response. + */ + hiswants[TELOPT_TM] = OPT_NO; + resp[TELOPT_TM]--; + return; + + case TELOPT_LFLOW: + /* + * If we are going to support flow control option, + * then don't worry peer that we can't change the + * flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_DEFAULT; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_DEFAULT; + case TELOPT_TTYPE: + case TELOPT_SGA: + case TELOPT_NAWS: + case TELOPT_TSPEED: +#ifdef LINEMODE + case TELOPT_LINEMODE: +#endif LINEMODE + changeok++; + break; + + default: + break; + } + + } + + if (request) { + if (!((resp[option] == 0 && hisopts[option] == OPT_YES) || + hiswants[option] == OPT_YES)) { + hiswants[option] = OPT_YES; + fmt = doopt; + resp[option]++; + } + } else { + if (resp[option]) { + resp[option]--; + if (resp[option] && hisopts[option] == OPT_YES) + resp[option]--; + } + if ((resp[option] == 0) && (hiswants[option] != OPT_YES)) { + if (changeok) + hiswants[option] = OPT_YES; + else + resp[option]++; + fmt = (hiswants[option] ? doopt : dont); + } + hisopts[option] = OPT_YES; + } + + if (fmt) { + (void) sprintf(nfrontp, fmt, option); + nfrontp += sizeof (dont) - 2; + } + + /* + * Handle other processing that should occur after we have + * responded to client input. + */ + if (!request) { + switch (option) { +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * Note client's desire to use linemode. + */ + lmodetype = REAL_LINEMODE; +# endif /* KLUDGELINEMODE */ + clientstat(TELOPT_LINEMODE, WILL, 0); + break; +#endif LINEMODE + + default: + break; + } + } + +} /* end of willoption */ + +wontoption(option, request) + int option, request; +{ + char *fmt = (char *)0; + + /* + * Process client input. + */ + if (!request) { + switch (option) { + case TELOPT_ECHO: + not42 = 1; /* doesn't seem to be a 4.2 system */ + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryin(0); + set_termbuf(); + break; + +#ifdef LINEMODE + case TELOPT_LINEMODE: +# ifdef KLUDGELINEMODE + /* + * If real linemode is supported, then client is + * asking to turn linemode off. + */ + if (lmodetype == REAL_LINEMODE) +# endif /* KLUDGELINEMODE */ + clientstat(TELOPT_LINEMODE, WONT, 0); + break; +#endif LINEMODE + + case TELOPT_TM: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + if (lmodetype < REAL_LINEMODE) { + lmodetype = NO_LINEMODE; + clientstat(TELOPT_LINEMODE, WONT, 0); + dooption(TELOPT_SGA, 0); + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + /* + * If we get a WONT TM, and had sent a DO TM, don't + * respond with a DONT TM, just leave it as is. + * Short circut the state machine to achive this. + * The bad part of this is that if the client sends + * a WONT TM on his own to turn off linemode, then he + * won't get a response. + */ + hiswants[TELOPT_TM] = OPT_NO; + resp[TELOPT_TM]--; + return; + + case TELOPT_LFLOW: + /* + * If we are not going to support flow control option, + * then let peer know that we can't change the + * flow control characters. + */ + slctab[SLC_XON].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XON].defset.flag |= SLC_CANTCHANGE; + slctab[SLC_XOFF].defset.flag &= ~SLC_LEVELBITS; + slctab[SLC_XOFF].defset.flag |= SLC_CANTCHANGE; + break; + + default: + break; + } + } + + + if (request) { + if (!((resp[option] == 0 && hisopts[option] == OPT_NO) || + hiswants[option] == OPT_NO)) { + hiswants[option] = OPT_NO; + fmt = dont; + resp[option]++; + } + } else { + if (resp[option]) { + resp[option]--; + if (resp[option] && hisopts[option] == OPT_NO) + resp[option]--; + } + if ((resp[option] == 0) && (hiswants[option] != OPT_NO)) { + /* it is always ok to change to negative state */ + hiswants[option] = OPT_NO; + fmt = dont; + } + hisopts[option] = OPT_NO; + } + + if (fmt) { + (void) sprintf(nfrontp, fmt, option); + nfrontp += sizeof (doopt) - 2; + } + +} /* end of wontoption */ + +dooption(option, request) + int option, request; +{ + int changeok = 0; + char *fmt = (char *)0; + + /* + * Process client input. + */ + if (!request) { + switch (option) { + case TELOPT_ECHO: +#ifdef LINEMODE + if (hisopts[TELOPT_LINEMODE] == OPT_NO) { +#endif + init_termbuf(); + tty_setecho(1); + set_termbuf(); +#ifdef LINEMODE + } +#endif + changeok++; + break; + + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(1); + set_termbuf(); + changeok++; + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we must process + * an incoming do SGA for linemode purposes. + */ + if (lmodetype == KLUDGE_LINEMODE) { + /* + * Receipt of "do SGA" in kludge linemode + * is the peer asking us to turn off linemode. + * Make note of the request. + */ + clientstat(TELOPT_LINEMODE, WONT, 0); + /* + * If linemode did not get turned off then + * don't tell peer that we did. Breaking + * here forces a wont SGA to be returned. + */ + if (linemode) + break; + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + changeok++; + break; + + case TELOPT_STATUS: + changeok++; + break; + + case TELOPT_TM: + case TELOPT_LINEMODE: + case TELOPT_TTYPE: + case TELOPT_NAWS: + case TELOPT_TSPEED: + case TELOPT_LFLOW: + default: + break; + } + } + + if (request) { + if (!((resp[option] == 0 && myopts[option] == OPT_YES) || + mywants[option] == OPT_YES)) { + mywants[option] = OPT_YES; + fmt = will; + resp[option]++; + } + } else { + if (resp[option]) { + resp[option]--; + if (resp[option] && myopts[option] == OPT_YES) + resp[option]--; + } + if ((resp[option] == 0) && (mywants[option] != OPT_YES)) { + if (changeok) + mywants[option] = OPT_YES; + else + resp[option]++; + fmt = (mywants[option] ? will : wont); + } + myopts[option] = OPT_YES; + } + + if (fmt) { + (void) sprintf(nfrontp, fmt, option); + nfrontp += sizeof (doopt) - 2; + } + +} /* end of dooption */ + + +dontoption(option, request) + int option, request; +{ + char *fmt = (char *)0; + + /* + * Process client input. + */ + if (!request) { + switch (option) { + case TELOPT_BINARY: + init_termbuf(); + tty_binaryout(0); + set_termbuf(); + break; + + case TELOPT_ECHO: /* we should stop echoing */ +#ifdef LINEMODE + if (hisopts[TELOPT_LINEMODE] == OPT_NO) { +#endif + init_termbuf(); + tty_setecho(0); + set_termbuf(); +#ifdef LINEMODE + } +#endif + break; + + case TELOPT_SGA: +#if defined(LINEMODE) && defined(KLUDGELINEMODE) + /* + * If kludge linemode is in use, then we must process an + * incoming do SGA for linemode purposes. + */ + if (lmodetype == KLUDGE_LINEMODE) { + /* + * The client is asking us to turn linemode + * on. + */ + clientstat(TELOPT_LINEMODE, WILL, 0); + /* + * If we did not turn line mode on, then what do + * we say? Will SGA? This violates design of + * telnet. Gross. Very Gross. + */ + } +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + + default: + break; + } + } + + if (request) { + if (!((resp[option] == 0 && myopts[option] == OPT_NO) || + mywants[option] == OPT_NO)) { + mywants[option] = OPT_NO; + fmt = wont; + resp[option]++; + } + } else { + if (resp[option]) { + resp[option]--; + if (resp[option] && myopts[option] == OPT_NO) + resp[option]--; + } + if ((resp[option] == 0) && (mywants[option] != OPT_NO)) { + mywants[option] = OPT_NO; + fmt = wont; + } + myopts[option] = OPT_NO; + } + + if (fmt) { + (void) sprintf(nfrontp, fmt, option); + nfrontp += sizeof (wont) - 2; + } + +} /* end of dontoption */ + +/* + * suboption() + * + * Look at the sub-option buffer, and try to be helpful to the other + * side. + * + * Currently we recognize: + * + * Terminal type is + * Linemode + * Window size + * Terminal speed + */ +suboption() +{ + register int subchar; + + subchar = SB_GET(); + switch (subchar) { + case TELOPT_TSPEED: { + register int xspeed, rspeed; + + if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ + break; + + settimer(tspeedsubopt); + + if (SB_EOF() || SB_GET() != TELQUAL_IS) + return; + + xspeed = atoi(subpointer); + + while (SB_GET() != ',' && !SB_EOF()); + if (SB_EOF()) + return; + + rspeed = atoi(subpointer); + clientstat(TELOPT_TSPEED, xspeed, rspeed); + + break; + + } /* end of case TELOPT_TSPEED */ + + case TELOPT_TTYPE: { /* Yaaaay! */ + static char terminalname[5+41] = "TERM="; + + if (hisopts[TELOPT_TSPEED] == OPT_NO) /* Ignore if option disabled */ + break; + settimer(ttypesubopt); + + if (SB_GET() != TELQUAL_IS) { + return; /* ??? XXX but, this is the most robust */ + } + + terminaltype = terminalname+sizeof("TERM=")-1; + + 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; + } /* end of case TELOPT_TTYPE */ + + case TELOPT_NAWS: { + register int xwinsize, ywinsize; + + if (hisopts[TELOPT_NAWS] == OPT_NO) /* Ignore if option disabled */ + break; + + if (SB_EOF()) + return; + xwinsize = SB_GET() << 8; + if (SB_EOF()) + return; + xwinsize |= SB_GET(); + if (SB_EOF()) + return; + ywinsize = SB_GET() << 8; + if (SB_EOF()) + return; + ywinsize |= SB_GET(); + clientstat(TELOPT_NAWS, xwinsize, ywinsize); + + break; + + } /* end of case TELOPT_NAWS */ + +#ifdef LINEMODE + case TELOPT_LINEMODE: { + register int request; + + if (hisopts[TELOPT_LINEMODE] == OPT_NO) /* Ignore if option disabled */ + break; + /* + * Process linemode suboptions. + */ + if (SB_EOF()) break; /* garbage was sent */ + request = SB_GET(); /* get will/wont */ + if (SB_EOF()) break; /* another garbage check */ + + if (request == LM_SLC) { /* SLC is not preceeded by WILL or WONT */ + /* + * Process suboption buffer of slc's + */ + start_slc(1); + do_opt_slc(subpointer, subend - subpointer); + end_slc(0); + + } else if (request == LM_MODE) { + useeditmode = SB_GET(); /* get mode flag */ + clientstat(LM_MODE, 0, 0); + } + + switch (SB_GET()) { /* what suboption? */ + case LM_FORWARDMASK: + /* + * According to spec, only server can send request for + * forwardmask, and client can only return a positive response. + * So don't worry about it. + */ + + default: + break; + } + + } /* end of case TELOPT_LINEMODE */ +#endif + case TELOPT_STATUS: { + int mode; + + mode = SB_GET(); + switch (mode) { + case TELQUAL_SEND: + if (myopts[TELOPT_STATUS] == OPT_YES) + send_status(); + break; + + case TELQUAL_IS: + break; + + default: + break; + } + } + + default: + break; + } /* end of switch */ + +} /* end of suboption */ + +#define ADD(c) *ncp++ = c; +#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; } +send_status() +{ + char statusbuf[256]; + register char *ncp; + register int i; + + ncp = statusbuf; + + netflush(); /* get rid of anything waiting to go out */ + + ADD(IAC); + ADD(SB); + ADD(TELOPT_STATUS); + ADD(TELQUAL_IS); + + for (i = 0; i < NTELOPTS; i++) { + if (myopts[i] == OPT_YES) { + ADD(WILL); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + if (hisopts[i] == OPT_YES) { + ADD(DO); + ADD_DATA(i); + if (i == IAC) + ADD(IAC); + } + } + +#ifdef LINEMODE + if (hisopts[TELOPT_LINEMODE] == OPT_YES) { + char *cp, *cpe; + int len; + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_MODE); + ADD_DATA(editmode); + if (editmode == IAC) + ADD(IAC); + ADD(SE); + + ADD(SB); + ADD(TELOPT_LINEMODE); + ADD(LM_SLC); + start_slc(0); + send_slc(); + len = end_slc(&cp); + for (cpe = cp + len; cp < cpe; cp++) + ADD_DATA(*cp); + ADD(SE); + } +#endif /* LINEMODE */ + + ADD(IAC); + ADD(SE); + + writenet(statusbuf, ncp - statusbuf); + netflush(); /* Send it on its way */ +} diff --git a/usr/src/libexec/telnetd/sys_term.c b/usr/src/libexec/telnetd/sys_term.c new file mode 100644 index 0000000000..8152239403 --- /dev/null +++ b/usr/src/libexec/telnetd/sys_term.c @@ -0,0 +1,1015 @@ +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char sccsid[] = "@(#)sys_term.c 5.1 (Berkeley) %G%"; +#endif /* not lint */ + +#include "telnetd.h" +#include "pathnames.h" + +#ifdef NEWINIT +#include +#else /* NEWINIT*/ +#include +struct utmp wtmp; + +# ifndef CRAY +char wtmpf[] = "/usr/adm/wtmp"; +char utmpf[] = "/etc/utmp"; +# else /* CRAY */ +char wtmpf[] = "/etc/wtmp"; +# endif /* CRAY */ +#endif /* NEWINIT */ + +#define SCPYN(a, b) (void) strncpy(a, b, sizeof(a)) +#define SCMPN(a, b) strncmp(a, b, sizeof(a)) + +#include +#ifdef t_erase +#undef t_erase +#undef t_kill +#undef t_intrc +#undef t_quitc +#undef t_startc +#undef t_stopc +#undef t_eofc +#undef t_brkc +#undef t_suspc +#undef t_dsuspc +#undef t_rprntc +#undef t_flushc +#undef t_werasc +#undef t_lnextc +#endif + +#ifndef USE_TERMIO +struct termbuf { + struct sgttyb sg; + struct tchars tc; + struct ltchars ltc; + int state; + int lflags; +} termbuf, termbuf2; +#else /* USE_TERMIO */ +# ifndef EXTPROC +# define EXTPROC 0400 +# endif +# ifdef SYSV_TERMIO +# define termios termio +# endif +struct termios termbuf, termbuf2; /* pty control structure */ +#endif /* USE_TERMIO */ + +/* + * init_termbuf() + * copy_termbuf(cp) + * set_termbuf() + * + * These three routines are used to get and set the "termbuf" structure + * to and from the kernel. init_termbuf() gets the current settings. + * copy_termbuf() hands in a new "termbuf" to write to the kernel, and + * set_termbuf() writes the structure into the kernel. + */ + +init_termbuf() +{ +#ifndef USE_TERMIO + (void) ioctl(pty, TIOCGETP, (char *)&termbuf.sg); + (void) ioctl(pty, TIOCGETC, (char *)&termbuf.tc); + (void) ioctl(pty, TIOCGLTC, (char *)&termbuf.ltc); +# ifdef TIOCGSTATE + (void) ioctl(pty, TIOCGSTATE, (char *)&termbuf.state); +# endif +#else + (void) ioctl(pty, TCGETA, (char *)&termbuf); +#endif + termbuf2 = termbuf; +} + +#if defined(LINEMODE) && defined(TIOCPKT_IOCTL) +copy_termbuf(cp, len) +char *cp; +int len; +{ + if (len > sizeof(termbuf)) + len = sizeof(termbuf); + bcopy(cp, (char *)&termbuf, len); + termbuf2 = termbuf; +} +#endif /* defined(LINEMODE) && defined(TIOCPKT_IOCTL) */ + +set_termbuf() +{ + /* + * Only make the necessary changes. + */ +#ifndef USE_TERMIO + if (bcmp((char *)&termbuf.sg, (char *)&termbuf2.sg, sizeof(termbuf.sg))) + (void) ioctl(pty, TIOCSETP, (char *)&termbuf.sg); + if (bcmp((char *)&termbuf.tc, (char *)&termbuf2.tc, sizeof(termbuf.tc))) + (void) ioctl(pty, TIOCSETC, (char *)&termbuf.tc); + if (bcmp((char *)&termbuf.ltc, (char *)&termbuf2.ltc, + sizeof(termbuf.ltc))) + (void) ioctl(pty, TIOCSLTC, (char *)&termbuf.ltc); + if (termbuf.lflags != termbuf2.lflags) + (void) ioctl(pty, TIOCLSET, (char *)&termbuf.lflags); +#else /* USE_TERMIO */ + if (bcmp((char *)&termbuf, (char *)&termbuf2, sizeof(termbuf))) + (void) ioctl(pty, TCSETA, (char *)&termbuf); +# ifdef CRAY2 + needtermstat = 1; +# endif +#endif /* USE_TERMIO */ +} + + +/* + * spcset(func, valp, valpp) + * + * This function takes various special characters (func), and + * sets *valp to the current value of that character, and + * *valpp to point to where in the "termbuf" structure that + * value is kept. + * + * It returns the SLC_ level of support for this function. + */ + +#ifndef USE_TERMIO +spcset(func, valp, valpp) +int func; +unsigned char *valp; +unsigned char **valpp; +{ + switch(func) { + case SLC_EOF: + *valp = termbuf.tc.t_eofc; + *valpp = (unsigned char *)&termbuf.tc.t_eofc; + return(SLC_VARIABLE); + case SLC_EC: + *valp = termbuf.sg.sg_erase; + *valpp = (unsigned char *)&termbuf.sg.sg_erase; + return(SLC_VARIABLE); + case SLC_EL: + *valp = termbuf.sg.sg_kill; + *valpp = (unsigned char *)&termbuf.sg.sg_kill; + return(SLC_VARIABLE); + case SLC_IP: + *valp = termbuf.tc.t_intrc; + *valpp = (unsigned char *)&termbuf.tc.t_intrc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + *valp = termbuf.tc.t_quitc; + *valpp = (unsigned char *)&termbuf.tc.t_quitc; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: + *valp = termbuf.tc.t_startc; + *valpp = (unsigned char *)&termbuf.tc.t_startc; + return(SLC_VARIABLE); + case SLC_XOFF: + *valp = termbuf.tc.t_stopc; + *valpp = (unsigned char *)&termbuf.tc.t_stopc; + return(SLC_VARIABLE); + case SLC_AO: + *valp = termbuf.ltc.t_flushc; + *valpp = (unsigned char *)&termbuf.ltc.t_flushc; + return(SLC_VARIABLE); + case SLC_SUSP: + *valp = termbuf.ltc.t_suspc; + *valpp = (unsigned char *)&termbuf.ltc.t_suspc; + return(SLC_VARIABLE); + case SLC_EW: + *valp = termbuf.ltc.t_werasc; + *valpp = (unsigned char *)&termbuf.ltc.t_werasc; + return(SLC_VARIABLE); + case SLC_RP: + *valp = termbuf.ltc.t_rprntc; + *valpp = (unsigned char *)&termbuf.ltc.t_rprntc; + return(SLC_VARIABLE); + case SLC_LNEXT: + *valp = termbuf.ltc.t_lnextc; + *valpp = (unsigned char *)&termbuf.ltc.t_lnextc; + return(SLC_VARIABLE); + case SLC_BRK: + case SLC_SYNCH: + case SLC_AYT: + case SLC_EOR: + *valp = 0; + *valpp = 0; + return(SLC_DEFAULT); + default: + *valp = 0; + *valpp = 0; + return(SLC_NOSUPPORT); + } +} + +#else /* USE_TERMIO */ + +spcset(func, valp, valpp) +int func; +unsigned char *valp; +unsigned char **valpp; +{ + switch(func) { + case SLC_EOF: + *valp = termbuf.c_cc[VEOF]; + *valpp = &termbuf.c_cc[VEOF]; + return(SLC_VARIABLE); + case SLC_EC: + *valp = termbuf.c_cc[VERASE]; + *valpp = &termbuf.c_cc[VERASE]; + return(SLC_VARIABLE); + case SLC_EL: + *valp = termbuf.c_cc[VKILL]; + *valpp = &termbuf.c_cc[VKILL]; + return(SLC_VARIABLE); + case SLC_IP: + *valp = termbuf.c_cc[VINTR]; + *valpp = &termbuf.c_cc[VINTR]; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_ABORT: + *valp = termbuf.c_cc[VQUIT]; + *valpp = &termbuf.c_cc[VQUIT]; + return(SLC_VARIABLE|SLC_FLUSHIN|SLC_FLUSHOUT); + case SLC_XON: + *valp = 0x13; + *valpp = 0; + return(SLC_DEFAULT); + case SLC_XOFF: + *valp = 0x11; + *valpp = 0; + return(SLC_DEFAULT); + case SLC_EW: + case SLC_RP: + case SLC_LNEXT: + case SLC_BRK: + case SLC_SYNCH: + case SLC_AYT: + case SLC_EOR: + *valp = 0; + *valpp = 0; + return(SLC_DEFAULT); + case SLC_AO: + case SLC_SUSP: + default: + *valp = 0; + *valpp = 0; + return(SLC_NOSUPPORT); + } +} +#endif /* USE_TERMIO */ + +/* + * getpty() + * + * Allocate a pty. As a side effect, the external character + * array "line" contains the name of the slave side. + * + * Returns the file descriptor of the opened pty. + */ +char *line = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +getpty() +{ + register int p; +#ifndef CRAY + register char c, *p1, *p2; + register int i; + + (void) sprintf(line, "/dev/ptyXX"); + p1 = &line[8]; + p2 = &line[9]; + + for (c = 'p'; c <= 's'; c++) { + struct stat stb; + + *p1 = c; + *p2 = '0'; + if (stat(line, &stb) < 0) + break; + for (i = 0; i < 16; i++) { + *p2 = "0123456789abcdef"[i]; + p = open(line, 2); + if (p > 0) { + line[5] = 't'; + return(p); + } + } + } +#else /* CRAY */ + register int npty; + extern lowpty, highpty; + + for (npty = lowpty; npty <= highpty; npty++) { + (void) sprintf(line, "/dev/pty/%03d", npty); + p = open(line, 2); + if (p < 0) + continue; + (void) sprintf(line, "/dev/ttyp%03d", npty); + if (access(line, 6) == 0) + return(p); + else { + /* no tty side to pty so skip it */ + (void) close(p); + } + } +#endif /* CRAY */ + return(-1); +} + +#ifdef LINEMODE +/* + * tty_flowmode() Find out if flow control is enabled or disabled. + * tty_linemode() Find out if linemode (external processing) is enabled. + * tty_setlinemod(on) Turn on/off linemode. + * tty_isecho() Find out if echoing is turned on. + * tty_setecho(on) Enable/disable character echoing. + * tty_israw() Find out if terminal is in RAW mode. + * tty_binaryin(on) Turn on/off BINARY on input. + * tty_binaryout(on) Turn on/off BINARY on output. + * tty_isediting() Find out if line editing is enabled. + * tty_istrapsig() Find out if signal trapping is enabled. + * tty_setedit(on) Turn on/off line editing. + * tty_setsig(on) Turn on/off signal trapping. + * tty_tspeed(val) Set transmit speed to val. + * tty_rspeed(val) Set receive speed to val. + */ + +tty_flowmode() +{ +#ifndef USE_TERMIO + return((termbuf.tc.t_startc) > 0 && (termbuf.tc.t_stopc) > 0); +#else + return(termbuf.c_iflag & IXON ? 1 : 0); +#endif +} + +tty_linemode() +{ +#ifndef USE_TERMIO + return(termbuf.state & TS_EXTPROC); +#else + return(termbuf.c_lflag & EXTPROC); +#endif +} + +tty_setlinemode(on) +int on; +{ +#ifdef TIOCEXT + (void) ioctl(pty, TIOCEXT, (char *)&on); +#else /* !TIOCEXT */ +#ifdef EXTPROC + if (on) + termbuf.c_lflag |= EXTPROC; + else + termbuf.c_lflag &= ~EXTPROC; +#endif + set_termbuf(); +#endif /* TIOCEXT */ +} + +tty_isecho() +{ +#ifndef USE_TERMIO + return (termbuf.sg.sg_flags & ECHO); +#else + return (termbuf.c_lflag & ECHO); +#endif +} +#endif /* LINEMODE */ + +tty_setecho(on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags |= ECHO|CRMOD; + else + termbuf.sg.sg_flags &= ~(ECHO|CRMOD); +#else + if (on) + termbuf.c_lflag |= ECHO; + else + termbuf.c_lflag &= ~ECHO; +#endif +} + +#if defined(LINEMODE) && defined(KLUDGELINEMODE) +tty_israw() +{ +#ifndef USE_TERMIO + return(termbuf.sg.sg_flags & RAW); +#else + return(!(termbuf.c_lflag & ICANON)); +#endif +} +#endif /* defined(LINEMODE) && defined(KLUDGELINEMODE) */ + +tty_binaryin(on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LPASS8; + else + termbuf.lflags &= ~LPASS8; +#else + if (on) { + termbuf.c_lflag &= ~ISTRIP; + } else { + termbuf.c_lflag |= ISTRIP; + } +#endif +} + +tty_binaryout(on) +{ +#ifndef USE_TERMIO + if (on) + termbuf.lflags |= LLITOUT; + else + termbuf.lflags &= ~LLITOUT; +#else + if (on) { + termbuf.c_cflag &= ~(CSIZE|PARENB); + termbuf.c_cflag |= CS8; + termbuf.c_oflag &= ~OPOST; + } else { + termbuf.c_cflag &= ~CSIZE; + termbuf.c_cflag |= CS7|PARENB; + termbuf.c_oflag |= OPOST; + } +#endif +} + +tty_isbinaryin() +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LPASS8); +#else + return(!(termbuf.c_lflag & ISTRIP)); +#endif +} + +tty_isbinaryout() +{ +#ifndef USE_TERMIO + return(termbuf.lflags & LLITOUT); +#else + return(mywants[TELOPT_BINARY] == OPT_YES); +#endif +} + +#ifdef LINEMODE +tty_isediting() +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags & (CBREAK|RAW))); +#else + return(termbuf.c_lflag & ICANON); +#endif +} + +tty_istrapsig() +{ +#ifndef USE_TERMIO + return(!(termbuf.sg.sg_flags&RAW)); +#else + return(termbuf.c_lflag & ISIG); +#endif +} + +tty_setedit(on) +int on; +{ +#ifndef USE_TERMIO + if (on) + termbuf.sg.sg_flags &= ~CBREAK; + else + termbuf.sg.sg_flags |= CBREAK; +#else + if (on) + termbuf.c_lflag |= ICANON; + else + termbuf.c_lflag &= ~ICANON; +#endif +} + +tty_setsig(on) +int on; +{ +#ifndef USE_TERMIO + if (on) + ; +#else + if (on) + termbuf.c_lflag |= ISIG; + else + termbuf.c_lflag &= ~ISIG; +#endif +} +#endif /* LINEMODE */ + +/* + * A table of available terminal speeds + */ +struct termspeeds { + int speed; + int value; +} termspeeds[] = { + { 0, B0 }, { 50, B50 }, { 75, B75 }, + { 110, B110 }, { 134, B134 }, { 150, B150 }, + { 200, B200 }, { 300, B300 }, { 600, B600 }, + { 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, + { 4800, B4800 }, { 9600, B9600 }, { 19200, B9600 }, + { 38400, B9600 }, { -1, B9600 } +}; + +tty_tspeed(val) +{ + register struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; +#ifndef USE_TERMIO + termbuf.sg.sg_ospeed = tp->value; +#else +# ifdef SYSV_TERMIO + termbuf.c_cflag &= ~CBAUD; + termbuf.c_cflag |= tp->value; +# else + termbuf.c_ospeed = tp->value; +# endif +#endif +} + +tty_rspeed(val) +{ + register struct termspeeds *tp; + + for (tp = termspeeds; (tp->speed != -1) && (val > tp->speed); tp++) + ; +#ifndef USE_TERMIO + termbuf.sg.sg_ispeed = tp->value; +#else +# ifdef SYSV_TERMIO + termbuf.c_cflag &= ~CBAUD; + termbuf.c_cflag |= tp->value; +# else + termbuf.c_ispeed = tp->value; +# endif +#endif +} + +#ifdef CRAY2 +tty_isnewmap() +{ + return((termbuf.c_oflag & OPOST) && (termbuf.c_oflag & ONLCR) && + !(termbuf.c_oflag & ONLRET)); +} +#endif + +#ifdef CRAY +# ifndef NEWINIT +extern struct utmp wtmp; +extern char wtmpf[]; +# else /* NEWINIT */ +int gotalarm; +nologinproc() +{ + gotalarm++; +} +# endif /* NEWINIT */ +#endif /* CRAY */ + +/* + * getptyslave() + * + * Open the slave side of the pty, and do any initialization + * that is necessary. The return value is a file descriptor + * for the slave side. + */ +getptyslave() +{ + register int t = -1; + +#ifndef CRAY + /* + * Disassociate self from control terminal and open ttyp side. + * Set important flags on ttyp and ptyp. + */ + t = open(_PATH_TTY, O_RDWR); + if (t >= 0) { + (void) ioctl(t, TIOCNOTTY, (char *)0); + (void) close(t); + } + + t = open(line, O_RDWR); + if (t < 0) + fatalperror(net, line); + if (fchmod(t, 0)) + fatalperror(net, line); + (void) signal(SIGHUP, SIG_IGN); + vhangup(); + (void) signal(SIGHUP, SIG_DFL); + t = open(line, O_RDWR); + if (t < 0) + fatalperror(net, line); + + init_termbuf(); +#ifndef USE_TERMIO + termbuf.sg.sg_flags |= CRMOD|ANYP|ECHO; + termbuf.sg.sg_ospeed = termbuf.sg.sg_ispeed = B9600; +#else + termbuf.c_lflag |= ECHO; + termbuf.c_oflag |= ONLCR|OXTABS; + termbuf.c_iflag |= ICRNL; + termbuf.c_iflag &= ~IXOFF; +# ifdef SYSV_TERMIO + termbuf.sg.sg_ospeed = termbuf.sg.sg_ispeed = B9600; +# else SYSV_TERMIO + termbuf.c_ospeed = termbuf.c_ispeed = B9600; +# endif +#endif + set_termbuf(); +#else /* CRAY */ + (void) chown(line, 0, 0); + (void) chmod(line, 0600); +#endif /* CRAY */ + return(t); +} + +#ifdef NEWINIT +char *gen_id = "fe"; +#endif + +/* + * startslave(t, host) + * + * Given a file descriptor (t) for a tty, and a hostname, do whatever + * is necessary to startup the login process on the slave side of the pty. + */ + +/* ARGSUSED */ +startslave(t, host) +int t; +char *host; +{ + register int i; + long time(); + +#ifndef NEWINIT +# ifdef CRAY + utmp_sig_init(); +# endif /* CRAY */ + + if ((i = fork()) < 0) + fatalperror(net, "fork"); + if (i) { +# ifdef CRAY + /* + * Cray parent will create utmp entry for child and send + * signal to child to tell when done. Child waits for signal + * before doing anything important. + */ + register int pid = i; + + setpgrp(); + (void) signal(SIGUSR1, func); /* reset handler to default */ + /* + * Create utmp entry for child + */ + (void) time(&wtmp.ut_time); + wtmp.ut_type = LOGIN_PROCESS; + wtmp.ut_pid = pid; + SCPYN(wtmp.ut_user, "LOGIN"); + SCPYN(wtmp.ut_host, host); + SCPYN(wtmp.ut_line, line + sizeof("/dev/") - 1); + SCPYN(wtmp.ut_id, wtmp.ut_line+3); + pututline(&wtmp); + endutent(); + if ((i = open(wtmpf, O_WRONLY|O_APPEND)) >= 0) { + (void) write(i, (char *)&wtmp, sizeof(struct utmp)); + (void) close(i); + } + utmp_sig_notify(pid); +# endif /* CRAY */ + (void) close(t); + } else { + start_login(t, host); + /*NOTREACHED*/ + } +#else /* NEWINIT */ + + extern char *ptyip; + struct init_request request; + int nologinproc(); + register int n; + + /* + * Init will start up login process if we ask nicely. We only wait + * for it to start up and begin normal telnet operation. + */ + if ((i = open(INIT_FIFO, O_WRONLY)) < 0) { + char tbuf[128]; + (void) sprintf(tbuf, "Can't open %s\n", INIT_FIFO); + fatalperror(net, tbuf); + } + memset((char *)&request, 0, sizeof(request)); + request.magic = INIT_MAGIC; + SCPYN(request.gen_id, gen_id); + SCPYN(request.tty_id, &line[8]); + SCPYN(request.host, host); + SCPYN(request.term_type, &terminaltype[5]); + if (write(i, (char *)&request, sizeof(request)) < 0) { + char tbuf[128]; + (void) sprintf(tbuf, "Can't write to %s\n", INIT_FIFO); + fatalperror(net, tbuf); + } + (void) close(i); + (void) signal(SIGALRM, nologinproc); + for (i = 0; ; i++) { + alarm(15); + n = read(pty, ptyip, BUFSIZ); + if (i == 3 || n >= 0 || !gotalarm) + break; + gotalarm = 0; + (void) write(net, "telnetd: waiting for /etc/init to start login process.\r\n", 56); + } + if (n < 0 && gotalarm) + fatal(net, "/etc/init didn't start login process"); + pcc += n; + alarm(0); + (void) signal(SIGALRM, SIG_DFL); + + /* + * Set tab expansion the way we like, in case init did something + * different. + */ + init_termbuf(); + termbuf.c_oflag &= ~TABDLY; + termbuf.c_oflag |= TAB0; + set_termbuf(); + return; +#endif /* NEWINIT */ +} + +#ifndef NEWINIT +char *envinit[3]; + +/* + * start_login(t, host) + * + * Assuming that we are now running as a child processes, this + * function will turn us into the login process. + */ + +start_login(t, host) +int t; +char *host; +{ + extern char *getenv(); + char **envp; + +#ifdef CRAY + utmp_sig_wait(); +# ifndef TCVHUP + setpgrp(); +# endif + t = open(line, 2); /* open ttyp */ + if (t < 0) + fatalperror(net, line); +# ifdef TCVHUP + /* + * Hangup anybody else using this ttyp, then reopen it for + * ourselves. + */ + (void) chown(line, 0, 0); + (void) chmod(line, 0600); + (void) signal(SIGHUP, SIG_IGN); + (void) ioctl(t, TCVHUP, (char *)0); + (void) signal(SIGHUP, SIG_DFL); + setpgrp(); + i = open(line, 2); + if (i < 0) + fatalperror(net, line); + (void) close(t); + t = i; +# endif /* TCVHUP */ + /* + * set ttyp modes as we like them to be + */ + init_termbuf(); + termbuf.c_oflag = OPOST|ONLCR; + termbuf.c_iflag = IGNPAR|ISTRIP|ICRNL|IXON; + termbuf.c_lflag = ISIG|ICANON|ECHO|ECHOE|ECHOK; + termbuf.c_cflag = EXTB|HUPCL|CS8; + set_termbuf(); +#endif /* CRAY */ + + /* + * set up standard paths before forking to login + */ +#ifdef BSD >43 + if (setsid() < 0) + fatalperror(net, "setsid"); + if (ioctl(t, TIOCSCTTY, (char *)0) < 0) + fatalperror(net, "ioctl(sctty)"); +#endif + (void) close(net); + (void) close(pty); + (void) dup2(t, 0); + (void) dup2(t, 1); + (void) dup2(t, 2); + (void) close(t); + envp = envinit; + *envp++ = terminaltype; + if (*envp = getenv("TZ")) + *envp++ -= 3; +#ifdef CRAY + else + *envp++ = "TZ=GMT0"; +#endif + *envp = 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(_PATH_LOGIN, "login", "-h", host, +#ifndef CRAY + terminaltype ? "-p" : 0, +#endif + 0); + syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN); + fatalperror(net, _PATH_LOGIN); + /*NOTREACHED*/ +} +#endif NEWINIT + +/* + * cleanup() + * + * This is the routine to call when we are all through, to + * clean up anything that needs to be cleaned up. + */ +cleanup() +{ + +#ifndef CRAY +# if BSD > 43 + char *p; + + p = line + sizeof("/dev/") - 1; + if (logout(p)) + logwtmp(p, "", ""); + (void)chmod(line, 0666); + (void)chown(line, 0, 0); + *p = 'p'; + (void)chmod(line, 0666); + (void)chown(line, 0, 0); +# else + rmut(); + vhangup(); /* XXX */ +# endif + (void) shutdown(net, 2); +#else /* CRAY */ +# ifndef NEWINIT + rmut(line); + (void) shutdown(net, 2); + kill(0, SIGHUP); +# else /* NEWINIT */ + (void) shutdown(net, 2); + sleep(5); +# endif /* NEWINT */ +#endif /* CRAY */ + exit(1); +} + +#if defined(CRAY) && !defined(NEWINIT) +/* + * _utmp_sig_rcv + * utmp_sig_init + * utmp_sig_wait + * These three functions are used to coordinate the handling of + * the utmp file between the server and the soon-to-be-login shell. + * The server actually creates the utmp structure, the child calls + * utmp_sig_wait(), until the server calls utmp_sig_notify() and + * signals the future-login shell to proceed. + */ +static int caught=0; /* NZ when signal intercepted */ +static void (*func)(); /* address of previous handler */ + +void +_utmp_sig_rcv(sig) +int sig; +{ + caught = 1; + (void) signal(SIGUSR1, func); +} + +utmp_sig_init() +{ + /* + * register signal handler for UTMP creation + */ + if ((int)(func = signal(SIGUSR1, _utmp_sig_rcv)) == -1) + fatalperror(net, "telnetd/signal"); +} + +utmp_sig_wait() +{ + /* + * Wait for parent to write our utmp entry. + */ + sigoff(); + while (caught == 0) { + pause(); /* wait until we get a signal (sigon) */ + sigoff(); /* turn off signals while we check caught */ + } + sigon(); /* turn on signals again */ +} + +utmp_sig_notify(pid) +{ + kill(pid, SIGUSR1); +} +#endif /* defined(CRAY) && !defined(NEWINIT) */ + +/* + * rmut() + * + * This is the function called by cleanup() to + * remove the utmp entry for this person. + */ + +#if !defined(CRAY) && BSD <= 43 +rmut() +{ + register f; + int found = 0; + struct utmp *u, *utmp; + int nutmp; + struct stat statbf; + char *malloc(); + long time(); + off_t lseek(); + + f = open(utmpf, O_RDWR); + if (f >= 0) { + (void) fstat(f, &statbf); + utmp = (struct utmp *)malloc((unsigned)statbf.st_size); + if (!utmp) + syslog(LOG_ERR, "utmp malloc failed"); + if (statbf.st_size && utmp) { + nutmp = read(f, (char *)utmp, (int)statbf.st_size); + nutmp /= sizeof(struct utmp); + + for (u = utmp ; u < &utmp[nutmp] ; u++) { + if (SCMPN(u->ut_line, line+5) || + u->ut_name[0]==0) + continue; + (void) lseek(f, ((long)u)-((long)utmp), L_SET); + SCPYN(u->ut_name, ""); + SCPYN(u->ut_host, ""); + (void) time(&u->ut_time); + (void) write(f, (char *)u, sizeof(wtmp)); + found++; + } + } + (void) close(f); + } + if (found) { + f = open(wtmpf, O_WRONLY|O_APPEND); + if (f >= 0) { + SCPYN(wtmp.ut_line, line+5); + SCPYN(wtmp.ut_name, ""); + SCPYN(wtmp.ut_host, ""); + (void) time(&wtmp.ut_time); + (void) write(f, (char *)&wtmp, sizeof(wtmp)); + (void) close(f); + } + } + (void) chmod(line, 0666); + (void) chown(line, 0, 0); + line[strlen("/dev/")] = 'p'; + (void) chmod(line, 0666); + (void) chown(line, 0, 0); +} /* end of rmut */ +#endif /* CRAY */ diff --git a/usr/src/libexec/telnetd/telnetd.c b/usr/src/libexec/telnetd/telnetd.c index b08a95dd0c..2f07a875d5 100644 --- a/usr/src/libexec/telnetd/telnetd.c +++ b/usr/src/libexec/telnetd/telnetd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1983, 1986 Regents of the University of California. + * Copyright (c) 1989 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted @@ -17,127 +17,132 @@ #ifndef lint char copyright[] = -"@(#) Copyright (c) 1983, 1986 Regents of the University of California.\n\ +"@(#) Copyright (c) 1989 Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint -static char sccsid[] = "@(#)telnetd.c 5.37 (Berkeley) %G%"; +static char sccsid[] = "@(#)telnetd.c 5.38 (Berkeley) %G%"; #endif /* not lint */ -/* - * Telnet server. - */ -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include "pathnames.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 3 -char 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 }; +#include "telnetd.h" /* - * I/O data buffers, pointers, and counters. + * I/O data buffers, + * pointers, and counters. */ char ptyibuf[BUFSIZ], *ptyip = ptyibuf; +char ptyibuf2[BUFSIZ]; -char ptyobuf[BUFSIZ], *pfrontp = ptyobuf, *pbackp = ptyobuf; - -char netibuf[BUFSIZ], *netip = netibuf; -#define NIACCUM(c) { *netip++ = c; \ - ncc++; \ - } +#ifdef CRAY +int hostinfo = 1; /* do we print login banner? */ +#endif -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; - -#define BANNER "\r\n\r\n4.3 BSD UNIX (%s)\r\n\r\r\n\r" - - /* 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) +#ifdef CRAY +extern int newmap; /* nonzero if \n maps to ^M^J */ +int lowpty = 0, highpty = 128; /* low, high pty numbers */ +#endif /* CRAY */ -int pcc, ncc; +int debug = 0; +char *progname; -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 ((argc > 1) && (strcmp(argv[1], "-debug") == 0)) { + pfrontp = pbackp = ptyobuf; + netip = netibuf; + nfrontp = nbackp = netobuf; + + progname = *argv; +top: + argc--, argv++; + + if (argc > 0 && strcmp(*argv, "-debug") == 0) { + debug++; + goto top; + } + +#ifdef LINEMODE + if (argc > 0 && !strcmp(*argv, "-l")) { + alwayslinemode = 1; + goto top; + } +#endif /* LINEMODE */ + +#ifdef CRAY + if (argc > 0 && !strcmp(*argv, "-h")) { + hostinfo = 0; + goto top; + } + + if (argc > 0 && !strncmp(*argv, "-r", 2)) { + char *strchr(); + char *c; + + *argv += 2; + if (**argv == '\0' && argc) + argv++, argc--; + c = strchr(*argv, '-'); + if (c) { + *c++ = '\0'; + highpty = atoi(c); + } else + highpty = -1; + lowpty = atoi(*argv); + if ((lowpty > highpty) || (lowpty < 0) || (highpty > 999)) { + usage: + fprintf(stderr, "Usage: telnetd [-debug] [-h] "); +# ifdef NEWINIT + fprintf(stderr, "[-Iinitid] "); +# endif /* NEWINIT */ + fprintf(stderr, "[-l] [-rlowpty-highpty] [port]\n"); + exit(1); + } + goto top; + } +# ifdef NEWINIT + if (argc > 0 && !strncmp(*argv, "-I", 2)) { + extern char *gen_id; + + *argv += 2; + if (**argv == '\0') { + if (argc < 2) + goto usage; + argv++, argc--; + if (**argv == '\0') + goto usage; + } + gen_id = *argv; + goto top; + } +# endif /* NEWINIT */ +#endif /* CRAY */ + + if (debug) { int s, ns, foo; struct servent *sp; static struct sockaddr_in sin = { AF_INET }; - argc--, argv++; - if (argc > 1) { - sin.sin_port = atoi(argv[1]); - sin.sin_port = htons((u_short)sin.sin_port); + if (argc > 0) { + if (sp = getservbyname(*argv, "tcp")) { + sin.sin_port = sp->s_port; + } else { + sin.sin_port = atoi(*argv); + if ((int)sin.sin_port <= 0) { + fprintf(stderr, "telnetd: %s: bad port #\n", *argv); + exit(1); + } + sin.sin_port = htons((u_short)sin.sin_port); + } } else { sp = getservbyname("telnet", "tcp"); if (sp == 0) { fprintf(stderr, "telnetd: tcp/telnet: unknown service\n"); - exit(1); + exit(1); } sin.sin_port = sp->s_port; } @@ -147,7 +152,8 @@ main(argc, argv) perror("telnetd: socket");; exit(1); } - if (bind(s, &sin, sizeof sin) < 0) { + (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); + if (bind(s, (struct sockaddr *)&sin, sizeof sin) < 0) { perror("bind"); exit(1); } @@ -156,270 +162,193 @@ main(argc, argv) exit(1); } foo = sizeof sin; - ns = accept(s, &sin, &foo); + ns = accept(s, (struct sockaddr *)&sin, &foo); if (ns < 0) { perror("accept"); exit(1); } - dup2(ns, 0); - close(s); + (void) dup2(ns, 0); + (void) close(ns); + (void) close(s); } + openlog("telnetd", LOG_PID | LOG_ODELAY, LOG_DAEMON); fromlen = sizeof (from); - if (getpeername(0, &from, &fromlen) < 0) { - fprintf(stderr, "%s: ", argv[0]); + if (getpeername(0, (struct sockaddr *)&from, &fromlen) < 0) { + fprintf(stderr, "%s: ", progname); 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); -} + net = 0; + doit(&from); + /* NOTREACHED */ +} /* end of main */ -char *terminaltype = 0; -char *envinit[2]; int 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. - */ - -void -ttloop() -{ - 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(); - } -} - -/* - * getterminalspeed - * - * Ask the other end to send along its terminal speed. - * subopt does the rest. - */ - -void -getterminalspeed() -{ - static char sbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; - - bcopy(sbuf, nfrontp, sizeof sbuf); - nfrontp += sizeof sbuf; -} - /* * getterminaltype * - * Ask the other end to send along its terminal type. + * Ask the other end to send along its terminal type and speed. * Output is the variable terminaltype filled in. */ - +static char ttytype_sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; void getterminaltype() { - static char sbuf[] = { IAC, DO, TELOPT_TTYPE }; + void ttloop(); - settimer(getterminal); - bcopy(sbuf, nfrontp, sizeof sbuf); - nfrontp += sizeof sbuf; - hisopts[TELOPT_TTYPE] = OPT_YES_BUT_ALWAYS_LOOK; - while (sequenceIs(ttypeopt, getterminal)) { + settimer(baseline); + willoption(TELOPT_TTYPE, 1); + willoption(TELOPT_TSPEED, 1); + while ((hiswants[TELOPT_TTYPE] != hisopts[TELOPT_TTYPE]) || + (hiswants[TELOPT_TSPEED] != hisopts[TELOPT_TSPEED])) { ttloop(); } - if (hisopts[TELOPT_TTYPE] == OPT_YES) { - static char sbbuf[] = { IAC, SB, TELOPT_TTYPE, TELQUAL_SEND, IAC, SE }; + if (hisopts[TELOPT_TSPEED] == OPT_YES) { + static char sbbuf[] = { IAC, SB, TELOPT_TSPEED, TELQUAL_SEND, IAC, SE }; bcopy(sbbuf, nfrontp, sizeof sbbuf); nfrontp += sizeof sbbuf; - while (sequenceIs(ttypesubopt, getterminal)) { + } + if (hisopts[TELOPT_TTYPE] == OPT_YES) { + + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + } + if (hisopts[TELOPT_TSPEED] == OPT_YES) { + while (sequenceIs(tspeedsubopt, baseline)) + ttloop(); + } + if (hisopts[TELOPT_TTYPE] == OPT_YES) { + char first[256], last[256]; + + while (sequenceIs(ttypesubopt, baseline)) ttloop(); + + if (!terminaltypeok(&terminaltype[5])) { + (void) strncpy(first, terminaltype, sizeof(first)); + for(;;) { + /* + * Save the unknown name, and request the next name. + */ + (void) strncpy(last, terminaltype, sizeof(last)); + _gettermname(); + if (terminaltypeok(&terminaltype[5])) + break; + if (strncmp(last, terminaltype, sizeof(last)) == 0) { + /* + * We've hit the end. If this is the same as + * the first name, just go with it. + */ + if (strncmp(first, terminaltype, sizeof(first) == 0)) + break; + /* + * Get the terminal name one more type, so that + * RFC1091 compliant telnets will cycle back to + * the start of the list. + */ + _gettermname(); + if (strncmp(first, terminaltype, sizeof(first) != 0)) + (void) strncpy(terminaltype, first, sizeof(first)); + break; + } + } } } +} /* end of getterminaltype */ + +_gettermname() +{ + settimer(baseline); + bcopy(ttytype_sbbuf, nfrontp, sizeof ttytype_sbbuf); + nfrontp += sizeof ttytype_sbbuf; + while (sequenceIs(ttypesubopt, baseline)) + ttloop(); +} + +terminaltypeok(s) +char *s; +{ + char buf[1024]; + + if (terminaltype == NULL) + return(1); + + /* + * tgetent() will return 1 if the type is known, and + * 0 if it is not known. If it returns -1, it couldn't + * open the database. But if we can't open the database, + * it won't help to say we failed, because we won't be + * able to verify anything else. So, we treat -1 like 1. + */ + if (tgetent(buf, s) == 0) + return(0); + return(1); } /* * Get a pty, scan input lines. */ -doit(f, who) - int f; +doit(who) struct sockaddr_in *who; { char *host, *inet_ntoa(); - int i, p, t; - struct sgttyb b; + int t; struct hostent *hp; - int c; - for (c = 'p'; c <= 's'; c++) { - struct stat stb; + /* + * Find an available pty to use. + */ + pty = getpty(); + if (pty < 0) + fatal(net, "All network ports in use"); - 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[sizeof("/dev/ptyp") - 1] = "0123456789abcdef"[i]; - p = open(line, O_RDWR); - if (p > 0) - goto gotpty; - } - } - fatal(f, "All network ports in use"); - /*NOTREACHED*/ -gotpty: - dup2(f, 0); - line[strlen("/dev/")] = 't'; - t = open(_PATH_TTY, O_RDWR); - if (t >= 0) { - ioctl(t, TIOCNOTTY, 0); - close(t); - } - t = open(line, O_RDWR); - if (t < 0) - fatalperror(f, line); - if (fchmod(t, 0)) - fatalperror(f, line); - (void)signal(SIGHUP, SIG_IGN); - vhangup(); - (void)signal(SIGHUP, SIG_DFL); - t = open(line, O_RDWR); - if (t < 0) - fatalperror(f, line); - ioctl(t, TIOCGETP, &b); - b.sg_flags = CRMOD|XTABS|ANYP; - ioctl(t, TIOCSETP, &b); - ioctl(p, TIOCGETP, &b); - b.sg_flags &= ~ECHO; - b.sg_ospeed = b.sg_ispeed = B9600; - ioctl(p, TIOCSETP, &b); - hp = gethostbyaddr(&who->sin_addr, sizeof (struct in_addr), + t = getptyslave(); + + /* get name of connected client */ + hp = gethostbyaddr((char *)&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 and size. + * get terminal type. */ getterminaltype(); + if (terminaltype == NULL) + terminaltype = "TERM=network"; - if ((i = fork()) < 0) - fatalperror(f, "fork"); - if (i) { - close(t); - telnet(f, p); - } - if (setsid() < 0) - fatalperror(f, "setsid"); - if (ioctl(t, TIOCSCTTY, 0) < 0) - fatalperror(f, "ioctl(sctty)"); - 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). + * Start up the login process on the slave side of the terminal */ - execl(_PATH_LOGIN, "login", "-h", host, - terminaltype ? "-p" : 0, 0); - syslog(LOG_ERR, "%s: %m\n", _PATH_LOGIN); - fatalperror(2, _PATH_LOGIN); - /*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) - int f; - char *msg; -{ - 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. - */ + startslave(t, host); + telnet(net, pty); /* begin server processing */ + /*NOTREACHED*/ +} /* end of doit */ -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"); - } - if (FD_ISSET(s, &excepts)) { - return 1; - } else { - return 0; - } -} - +#ifndef MAXHOSTNAMELEN +#define MAXHOSTNAMELEN 64 +#endif MAXHOSTNAMELEN /* * Main loop. Select from pty and network, and * hand data to telnet receiver finite state machine. */ telnet(f, p) +int f, p; { int on = 1; char hostname[MAXHOSTNAMELEN]; +#ifdef CRAY2 + int termstat(); + int interrupt(), sendbrk(); +#endif #define TABBUFSIZ 512 char defent[TABBUFSIZ]; char defstrs[TABBUFSIZ]; @@ -427,44 +356,22 @@ telnet(f, p) char *HE; char *HN; char *IM; - - ioctl(f, FIONBIO, &on); - ioctl(p, FIONBIO, &on); - ioctl(p, TIOCPKT, &on); -#if defined(SO_OOBINLINE) - setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); -#endif /* defined(SO_OOBINLINE) */ - signal(SIGTSTP, SIG_IGN); + void netflush(); + /* - * Ignoring SIGTTOU keeps the kernel from blocking us - * in ttioctl() in /sys/tty.c. + * Initialize the slc mapping table. */ - signal(SIGTTOU, SIG_IGN); - signal(SIGCHLD, cleanup); - setpgrp(0, 0); + get_slc_defaults(); /* - * Request to do remote echo and to suppress go ahead. + * Do some tests where it is desireable to wait for a response. + * Rather than doing them slowly, one at a time, do them all + * at once. */ - if (!myopts[TELOPT_ECHO]) { - dooption(TELOPT_ECHO); - } - if (!myopts[TELOPT_SGA]) { - dooption(TELOPT_SGA); - } - if (!hisopts[TELOPT_NAWS]) { - willoption(TELOPT_NAWS); - hisopts[TELOPT_NAWS] = OPT_NO; - } - if (!hisopts[TELOPT_TSPEED]) { - willoption(TELOPT_TSPEED); - hisopts[TELOPT_TSPEED] = OPT_NO; - } - if (!hisopts[TELOPT_LFLOW]) { - willoption(TELOPT_LFLOW); - hisopts[TELOPT_LFLOW] = OPT_NO; - } - + 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. @@ -475,9 +382,103 @@ telnet(f, p) * 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; + willoption(TELOPT_ECHO, 1); + +#ifdef LINEMODE + if (hisopts[TELOPT_LINEMODE] == OPT_NO) { + /* Query the peer for linemode support by trying to negotiate + * the linemode option. + */ + linemode = 1; + editmode = 0; + willoption(TELOPT_LINEMODE, 1); /* send do linemode */ + } +#endif /* LINEMODE */ + + /* + * Send along a couple of other options that we wish to negotiate. + */ + willoption(TELOPT_NAWS, 1); + dooption(TELOPT_STATUS, 1); + flowmode = 1; /* default flow control state */ + willoption(TELOPT_LFLOW, 1); + + /* + * Spin, waiting for a response from the DO ECHO. However, + * some REALLY DUMB telnets out there might not respond + * to the DO ECHO. So, we spin looking for NAWS, (most dumb + * telnets so far seem to respond with WONT for a DO that + * they don't understand...) because by the time we get the + * response, it will already have processed the DO ECHO. + * Kludge upon kludge. + */ + while (hiswants[TELOPT_NAWS] != hisopts[TELOPT_NAWS]) + ttloop(); + + /* + * Turn on packet mode, and default to line at at time mode. + */ + (void) ioctl(p, TIOCPKT, (char *)&on); +#ifdef LINEMODE + tty_setlinemode(1); + +# ifdef KLUDGELINEMODE + /* + * Continuing line mode support. If client does not support + * real linemode, attempt to negotiate kludge linemode by sending + * the do timing mark sequence. + */ + if (lmodetype < REAL_LINEMODE) + willoption(TELOPT_TM, 1); +# endif /* KLUDGELINEMODE */ +#endif /* LINEMODE */ + + /* + * Call telrcv() once to pick up anything received during + * terminal type negotiation, 4.2/4.3 determination, and + * linemode negotiation. + */ + telrcv(); + + (void) ioctl(f, FIONBIO, (char *)&on); + (void) ioctl(p, FIONBIO, (char *)&on); +#ifdef CRAY2 + init_termdriver(f, p, interrupt, sendbrk); +#endif + +#if defined(SO_OOBINLINE) + (void) setsockopt(net, SOL_SOCKET, SO_OOBINLINE, &on, sizeof on); +#endif /* defined(SO_OOBINLINE) */ + +#ifdef SIGTSTP + (void) signal(SIGTSTP, SIG_IGN); +#endif +#ifdef SIGTTOU + /* + * Ignoring SIGTTOU keeps the kernel from blocking us + * in ttioct() in /sys/tty.c. + */ + (void) signal(SIGTTOU, SIG_IGN); +#endif + + (void) signal(SIGCHLD, cleanup); + +#if defined(CRAY2) + /* + * Cray-2 will send a signal when pty modes are changed by slave + * side. Set up signal handler now. + */ + if ((int)signal(SIGUSR1, termstat) < 0) + perror("signal"); + else if (ioctl(p, TCSIGME, (char *)SIGUSR1) < 0) + perror("ioctl:TCSIGME"); + /* + * Make processing loop check terminal characteristics early on. + */ + termstat(); +#endif + + (void) setpgrp(0, 0); /* * Show banner that getty never gave. @@ -487,34 +488,36 @@ telnet(f, p) * other pty --> client data. */ - gethostname(hostname, sizeof (hostname)); + (void) gethostname(hostname, sizeof (hostname)); + if (getent(defent, "default") == 1) { char *getstr(); - char *p=defstrs; + char *cp=defstrs; - HE = getstr("he", &p); - HN = getstr("hn", &p); - IM = getstr("im", &p); + HE = getstr("he", &cp); + HN = getstr("hn", &cp); + IM = getstr("im", &cp); if (HN && *HN) - strcpy(hostname, HN); - edithost(HE, hostname); - if (IM && *IM) - putf(IM, ptyibuf+1); + (void) strcpy(hostname, HN); + if (IM == 0) + IM = ""; } else { - sprintf(ptyibuf+1, BANNER, hostname); - } - - ptyip = ptyibuf+1; /* Prime the pump */ - pcc = strlen(ptyip); /* ditto */ - - /* Clear ptybuf[0] - where the packet information is received */ - ptyibuf[0] = 0; - - /* - * Call telrcv() once to pick up anything received during - * terminal type negotiation. - */ - telrcv(); +#ifdef CRAY + if (hostinfo == 0) + IM = 0; + else +#endif + IM = DEFAULT_IM; + HE = 0; + } + edithost(HE, hostname); + if (IM && *IM) + putf(IM, ptyibuf2); + + if (pcc) + (void) strncat(ptyibuf2, ptyip, pcc+1); + ptyip = ptyibuf2; + pcc = strlen(ptyip); for (;;) { fd_set ibits, obits, xbits; @@ -523,6 +526,10 @@ telnet(f, p) if (ncc < 0 && pcc < 0) break; +#ifdef CRAY2 + if (needtermstat) + _termstat(); +#endif /* CRAY2 */ FD_ZERO(&ibits); FD_ZERO(&obits); FD_ZERO(&xbits); @@ -543,7 +550,6 @@ telnet(f, p) if (!SYNCHing) { FD_SET(f, &xbits); } - FD_SET(p, &xbits); if ((c = select(16, &ibits, &obits, &xbits, (struct timeval *)0)) < 1) { if (c == -1) { @@ -604,7 +610,7 @@ telnet(f, p) if (SYNCHing) { int atmark; - ioctl(net, SIOCATMARK, (char *)&atmark); + (void) ioctl(net, SIOCATMARK, (char *)&atmark); if (atmark) { ncc = recv(net, netibuf, sizeof (netibuf), MSG_OOB); if ((ncc == -1) && (errno == EINVAL)) { @@ -636,23 +642,34 @@ telnet(f, p) /* * Something to read from the pty... */ - if (FD_ISSET(p, &ibits) || FD_ISSET(p, &xbits)) { + if (FD_ISSET(p, &ibits)) { pcc = read(p, ptyibuf, BUFSIZ); if (pcc < 0 && errno == EWOULDBLOCK) pcc = 0; else { if (pcc <= 0) break; +#ifndef CRAY2 +#ifdef LINEMODE + /* + * If ioctl from pty, pass it through net + */ + if (ptyibuf[0] & TIOCPKT_IOCTL) { + copy_termbuf(ptyibuf+1, pcc-1); + localstat(); + pcc = 1; + } +#endif LINEMODE if (ptyibuf[0] & TIOCPKT_FLUSHWRITE) { - netclear(); /* clear buffer back */ + netclear(); /* clear buffer back */ *nfrontp++ = IAC; *nfrontp++ = DM; neturg = nfrontp-1; /* off by one XXX */ } if (hisopts[TELOPT_LFLOW] && (ptyibuf[0] & - (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { - sprintf(nfrontp,"%c%c%c%c%c%c", + (TIOCPKT_NOSTOP|TIOCPKT_DOSTOP))) { + (void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB, TELOPT_LFLOW, ptyibuf[0] & TIOCPKT_DOSTOP ? 1 : 0, IAC, SE); @@ -660,7 +677,15 @@ telnet(f, p) } pcc--; ptyip = ptyibuf+1; - } +#else /* CRAY2 */ + if (!uselinemode) { + pcc = term_output(ptyibuf, ptyibuf2, + pcc, BUFSIZ); + ptyip = ptyibuf2; + } else + ptyip = ptyibuf; +#endif /* CRAY2 */ + } } while (pcc > 0) { @@ -669,8 +694,12 @@ telnet(f, p) c = *ptyip++ & 0377, pcc--; if (c == IAC) *nfrontp++ = c; +#ifdef CRAY2 + else if (c == '\n' && + myopts[TELOPT_BINARY] == OPT_NO && newmap) + *nfrontp++ = '\r'; +#endif /* CRAY2 */ *nfrontp++ = c; - /* Don't do CR-NUL if we are in binary mode */ if ((c == '\r') && (myopts[TELOPT_BINARY] == OPT_NO)) { if (pcc > 0 && ((*ptyip & 0377) == '\n')) { *nfrontp++ = *ptyip++ & 0377; @@ -687,519 +716,13 @@ telnet(f, p) ptyflush(); } cleanup(); -} +} /* end of telnet */ -/* - * 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; - /* Strip off \n or \0 after a \r */ - 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; - SB_CLEAR(); - 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; - if (c == TELOPT_TSPEED) - getterminalspeed(); - 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_NAWS: - case TELOPT_TSPEED: - case TELOPT_LFLOW: - 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; - (void) 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; - } - (void) 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); - 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; -} - -/* - * Given a string, assign the "best" speed which we support. - * - * "Best" is defined as rounding up, unless what is presented is - * higher than the highest. - */ - -string2speed(s) - char *s; -{ - /* - * The order here is important. The index of each speed needs to - * correspond with the sgtty structure value for that speed. - * - * Additionally, the search algorithm assumes the table is in - * ascending sequence. - */ - static int ttyspeeds[] = { - 0, 50, 75, 110, 134, 150, 200, 300, - 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400 }; -#define NUMSPEEDS sizeof ttyspeeds/sizeof ttyspeeds[0] - int i; - int theirspeed = atoi(s); - - for (i = 0; i < NUMSPEEDS; i++) { - if (ttyspeeds[i] == theirspeed) { /* Exact match */ - return(i); - } else if (ttyspeeds[i] > theirspeed) { - if (i > 0) { - return i-1; - } - } - } - /* Their number is greater than any of our numbers */ - return(NUMSPEEDS-1); -} - -/* - * suboption() - * - * Look at the sub-option buffer, and try to be helpful to the other - * side. - * - * Currently we recognize: - * - * Terminal type is - * Terminal size - * Terminal speed 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; - } - case TELOPT_NAWS: { - struct winsize win; - - ioctl(pty, TIOCGWINSZ, &win); - settimer(ttypesubopt); - - syslog(LOG_INFO, "%x %x %x %x", - subpointer[0],subpointer[1],subpointer[2],subpointer[3]); - win.ws_col = SB_GET() << 8; - win.ws_col |= SB_GET(); - win.ws_row = SB_GET() << 8; - win.ws_row |= SB_GET(); - syslog(LOG_INFO, "col %d row %d", win.ws_col, win.ws_row); - ioctl(pty, TIOCSWINSZ, &win); - break; - } - case TELOPT_TSPEED: { - char speeds[41],*cp=speeds; - struct sgttyb b; - int ispeed,ospeed; - char *ispeeds,*ospeeds; - - if (SB_GET() != TELQUAL_IS) { - return; /* ??? XXX but, this is the most robust */ - } - - ispeeds = NULL; - ospeeds = speeds; - ispeed = 0; - ospeed = 0; - while ((cp < (speeds + sizeof speeds-1)) && !SB_EOF()) { - register int c; - - c = SB_GET(); - if (c == ',') { - c = 0; - ispeeds = cp+1; - } - *cp++ = c; /* accumulate name */ - } - *cp = 0; - - if (ispeeds) - ispeed = string2speed(ispeeds); - if (ospeeds) - ospeed = string2speed(ospeeds); - - if (ispeed && ospeed) { - ioctl(pty, TIOCGETP, &b); - b.sg_ospeed = ospeed; - b.sg_ispeed = ispeed; - ioctl(pty, TIOCSETP, &b); - } - - 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); -} +#ifndef TCSIG +# ifdef TIOCSIG +# define TCSIG TIOCSIG +# endif +#endif /* * Send interrupt to process on other side of pty. @@ -1208,17 +731,14 @@ mode(on, off) */ 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; + +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGINT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_IP].sptr ? *slctab[SLC_IP].sptr : '\177'; +#endif /* TCSIG */ } /* @@ -1228,291 +748,30 @@ interrupt() */ sendbrk() { - 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 ? - '\034' : tchars.t_quitc; -} - -ptyflush() -{ - int n; - - if ((n = pfrontp - pbackp) > 0) - n = write(pty, pbackp, n); - if (n < 0) - return; - pbackp += n; - if (pbackp == pfrontp) - pbackp = pfrontp = ptyobuf; -} - -/* - * nextitem() - * - * Return the address of the next "item" in the TELNET data - * stream. This will be the address of the next character if - * the current address is a user data character, or it will - * be the address of the character following the TELNET command - * if the current address is a TELNET IAC ("I Am a Command") - * character. - */ - -char * -nextitem(current) -char *current; -{ - if ((*current&0xff) != IAC) { - return current+1; - } - switch (*(current+1)&0xff) { - case DO: - case DONT: - case WILL: - case WONT: - return current+3; - case SB: /* loop forever looking for the SE */ - { - register char *look = current+2; - - for (;;) { - if ((*look++&0xff) == IAC) { - if ((*look++&0xff) == SE) { - return look; - } - } - } - } - default: - return current+2; - } -} - - -/* - * netclear() - * - * We are about to do a TELNET SYNCH operation. Clear - * the path to the network. - * - * Things are a bit tricky since we may have sent the first - * byte or so of a previous TELNET command into the network. - * So, we have to scan the network buffer from the beginning - * until we are up to where we want to be. - * - * A side effect of what we do, just to keep things - * simple, is to clear the urgent data pointer. The principal - * caller should be setting the urgent data pointer AFTER calling - * us in any case. - */ - -netclear() -{ - register char *thisitem, *next; - char *good; -#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ - ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) - - thisitem = netobuf; - - while ((next = nextitem(thisitem)) <= nbackp) { - thisitem = next; - } - - /* Now, thisitem is first before/at boundary. */ - - good = netobuf; /* where the good bytes go */ - - while (nfrontp > thisitem) { - if (wewant(thisitem)) { - int length; - - next = thisitem; - do { - next = nextitem(next); - } while (wewant(next) && (nfrontp > next)); - length = next-thisitem; - bcopy(thisitem, good, length); - good += length; - thisitem = next; - } else { - thisitem = nextitem(thisitem); - } - } - - nbackp = netobuf; - nfrontp = good; /* next byte to be sent */ - neturg = 0; +#ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGQUIT); +#else /* TCSIG */ + init_termbuf(); + *pfrontp++ = slctab[SLC_ABORT].sptr ? *slctab[SLC_ABORT].sptr : '\034'; +#endif /* TCSIG */ } - -/* - * netflush - * Send as much data as possible to the network, - * handling requests for urgent data. - */ - -netflush() +sendsusp() { - int n; - - if ((n = nfrontp - nbackp) > 0) { - /* - * if no urgent data, or if the other side appears to be an - * old 4.2 client (and thus unable to survive TCP urgent data), - * write the entire buffer in non-OOB mode. - */ - if ((neturg == 0) || (not42 == 0)) { - n = write(net, nbackp, n); /* normal write */ - } else { - n = neturg - nbackp; - /* - * In 4.2 (and 4.3) systems, there is some question about - * what byte in a sendOOB operation is the "OOB" data. - * To make ourselves compatible, we only send ONE byte - * out of band, the one WE THINK should be OOB (though - * we really have more the TCP philosophy of urgent data - * rather than the Unix philosophy of OOB data). - */ - if (n > 1) { - n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ - } else { - n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ - } - } - } - if (n < 0) { - if (errno == EWOULDBLOCK) - return; - /* should blow this guy away... */ - return; - } - nbackp += n; - if (nbackp >= neturg) { - neturg = 0; - } - if (nbackp == nfrontp) { - nbackp = nfrontp = netobuf; - } -} - -cleanup() -{ - char *p; - - p = line + sizeof("/dev/") - 1; - if (logout(p)) - logwtmp(p, "", ""); - (void)chmod(line, 0666); - (void)chown(line, 0, 0); - *p = 'p'; - (void)chmod(line, 0666); - (void)chown(line, 0, 0); - shutdown(net, 2); - exit(1); -} - -char editedhost[32]; - -edithost(pat, host) - register char *pat; - register char *host; -{ - register char *res = editedhost; - - if (!pat) - pat = ""; - while (*pat) { - switch (*pat) { - - case '#': - if (*host) - host++; - break; - - case '@': - if (*host) - *res++ = *host++; - break; - - default: - *res++ = *pat; - break; - - } - if (res == &editedhost[sizeof editedhost - 1]) { - *res = '\0'; - return; - } - pat++; - } - if (*host) - strncpy(res, host, sizeof editedhost - (res - editedhost) - 1); - else - *res = '\0'; - editedhost[sizeof editedhost - 1] = '\0'; -} - -static char *putlocation; - -puts(s) -register char *s; -{ - - while (*s) - putchr(*s++); -} - -putchr(cc) -{ - *putlocation++ = cc; +#ifdef SIGTSTP + ptyflush(); /* half-hearted */ +# ifdef TCSIG + (void) ioctl(pty, TCSIG, (char *)SIGTSTP); +# else /* TCSIG */ + *pfrontp++ = slctab[SLC_SUSP].sptr ? *slctab[SLC_SUSP].sptr : '\032'; +# endif /* TCSIG */ +#endif /* SIGTSTP */ } -putf(cp, where) -register char *cp; -char *where; +doeof() { - char *slash; - char datebuffer[60]; - extern char *rindex(); + init_termbuf(); - putlocation = where; - - while (*cp) { - if (*cp != '%') { - putchr(*cp++); - continue; - } - switch (*++cp) { - - case 't': - slash = rindex(line, '/'); - if (slash == (char *) 0) - puts(line); - else - puts(&slash[1]); - break; - - case 'h': - puts(editedhost); - break; - - case 'd': - get_date(datebuffer); - puts(datebuffer); - break; - - case '%': - putchr('%'); - break; - } - cp++; - } + *pfrontp++ = slctab[SLC_EOF].sptr ? *slctab[SLC_EOF].sptr : '\004'; } diff --git a/usr/src/libexec/telnetd/telnetd.h b/usr/src/libexec/telnetd/telnetd.h new file mode 100644 index 0000000000..b495540c6d --- /dev/null +++ b/usr/src/libexec/telnetd/telnetd.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)telnetd.h 5.1 (Berkeley) %G% + */ + + +#include "defs.h" +#include "ext.h" + +/* other external variables */ +extern char **environ; +extern int errno; + diff --git a/usr/src/libexec/telnetd/termstat.c b/usr/src/libexec/telnetd/termstat.c new file mode 100644 index 0000000000..bd9484c802 --- /dev/null +++ b/usr/src/libexec/telnetd/termstat.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char sccsid[] = "@(#)termstat.c 5.1 (Berkeley) %G%"; +#endif /* not lint */ + +#include "telnetd.h" + +/* + * local variables + */ +#ifdef LINEMODE +static int _terminit = 0; +static int def_tspeed = -1, def_rspeed = -1; +#ifdef TIOCSWINSZ +static int def_row = 0, def_col = 0; +#endif +#endif LINEMODE + +#ifdef CRAY2 +int newmap = 1; /* nonzero if \n maps to ^M^J */ +#endif + +#ifdef LINEMODE +/* + * localstat + * + * This function handles all management of linemode. + * + * Linemode allows the client to do the local editing of data + * and send only complete lines to the server. Linemode state is + * based on the state of the pty driver. If the pty is set for + * external processing, then we can use linemode. Further, if we + * can use real linemode, then we can look at the edit control bits + * in the pty to determine what editing the client should do. + * + * Linemode support uses the following state flags to keep track of + * current and desired linemode state. + * alwayslinemode : true if -l was specified on the telnetd + * command line. It means to have linemode on as much as + * possible. + * + * lmodetype: signifies whether the client can + * handle real linemode, or if use of kludgeomatic linemode + * is preferred. It will be set to one of the following: + * REAL_LINEMODE : use linemode option + * KLUDGE_LINEMODE : use kludge linemode + * NO_LINEMODE : client is ignorant of linemode + * + * linemode, uselinemode : linemode is true if linemode + * is currently on, uselinemode is the state that we wish + * to be in. If another function wishes to turn linemode + * on or off, it sets or clears uselinemode. + * + * editmode, useeditmode : like linemode/uselinemode, but + * these contain the edit mode states (edit and trapsig). + * + * The state variables correspond to some of the state information + * in the pty. + * linemode: + * In real linemode, this corresponds to whether the pty + * expects external processing of incoming data. + * In kludge linemode, this more closely corresponds to the + * whether normal processing is on or not. (ICANON in + * system V, or COOKED mode in BSD.) + * If the -l option was specified (alwayslinemode), then + * an attempt is made to force external processing on at + * all times. + * + * The following heuristics are applied to determine linemode + * handling within the server. + * 1) Early on in starting up the server, an attempt is made + * to negotiate the linemode option. If this succeeds + * then lmodetype is set to REAL_LINEMODE and all linemode + * processing occurs in the context of the linemode option. + * 2) If the attempt to negotiate the linemode option failed, + * then we try to use kludge linemode. We test for this + * capability by sending "do Timing Mark". If a positive + * response comes back, then we assume that the client + * understands kludge linemode (ech!) and the + * lmodetype flag is set to KLUDGE_LINEMODE. + * 3) Otherwise, linemode is not supported at all and + * lmodetype remains set to NO_LINEMODE (which happens + * to be 0 for convenience). + * 4) At any time a command arrives that implies a higher + * state of linemode support in the client, we move to that + * linemode support. + * + * A short explanation of kludge linemode is in order here. + * 1) The heuristic to determine support for kludge linemode + * is to send a do timing mark. We assume that a client + * that supports timing marks also supports kludge linemode. + * A risky proposition at best. + * 2) Further negotiation of linemode is done by changing the + * the server's state regarding SGA. If server will SGA, + * then linemode is off, if server won't SGA, then linemode + * is on. + */ +localstat() +{ + void netflush(); + +#ifdef CRAY2 + /* + * Keep track of that ol' CR/NL mapping while we're in the + * neighborhood. + */ + newmap = tty_isnewmap(); +#endif /* CRAY2 */ + + /* + * Check for state of BINARY options. + */ + if (tty_isbinaryin()) { + if (hiswants[TELOPT_BINARY] == OPT_NO) + willoption(TELOPT_BINARY, 1); + } else { + if (hiswants[TELOPT_BINARY] == OPT_YES) + wontoption(TELOPT_BINARY, 1); + } + + if (tty_isbinaryout()) { + if (mywants[TELOPT_BINARY] == OPT_NO) + dooption(TELOPT_BINARY, 1); + } else { + if (mywants[TELOPT_BINARY] == OPT_YES) + dontoption(TELOPT_BINARY, 1); + } + + /* + * Check for changes to flow control if client supports it. + */ + if (hisopts[TELOPT_LFLOW] == OPT_YES) { + if (tty_flowmode() != flowmode) { + flowmode = tty_flowmode(); + (void) sprintf(nfrontp, "%c%c%c%c%c%c", IAC, SB, + TELOPT_LFLOW, flowmode, IAC, SE); + nfrontp += 6; + } + } + + /* + * Check linemode on/off state + */ + uselinemode = tty_linemode(); + + /* + * If alwayslinemode is on, and pty is changing to turn it off, then + * force linemode back on. + */ + if (alwayslinemode && linemode && !uselinemode) { + uselinemode = 1; + tty_setlinemode(uselinemode); + } + +# ifdef KLUDGELINEMODE + /* + * If using kludge linemode and linemode is desired, it can't + * be done if normal line editing is not available on the + * pty. This becomes the test for linemode on/off when + * using kludge linemode. + */ + if (lmodetype == KLUDGE_LINEMODE && uselinemode && tty_israw()) + uselinemode = 0; +# endif /* KLUDGELINEMODE */ + + /* + * Do echo mode handling as soon as we know what the + * linemode is going to be. + * If the pty has echo turned off, then tell the client that + * the server will echo. If echo is on, then the server + * will echo if in character mode, but in linemode the + * client should do local echoing. The state machine will + * not send anything if it is unnecessary, so don't worry + * about that here. + */ + if (tty_isecho() && uselinemode) + dontoption(TELOPT_ECHO, 1); + else + dooption(TELOPT_ECHO, 1); + + /* + * If linemode is being turned off, send appropriate + * command and then we're all done. + */ + if (!uselinemode && linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == REAL_LINEMODE) +# endif /* KLUDGELINEMODE */ + wontoption(TELOPT_LINEMODE, 1); +# ifdef KLUDGELINEMODE + else if (lmodetype == KLUDGE_LINEMODE) + dooption(TELOPT_SGA, 1); +# endif /* KLUDGELINEMODE */ + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * If using real linemode check edit modes for possible later use. + */ + if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig()) + useeditmode |= MODE_TRAPSIG; +# ifdef KLUDGELINEMODE + } +# endif /* KLUDGELINEMODE */ + + /* + * Negotiate linemode on if pty state has changed to turn it on. + * Send appropriate command and send along edit mode, then all done. + */ + if (uselinemode && !linemode) { +# ifdef KLUDGELINEMODE + if (lmodetype == KLUDGE_LINEMODE) { + dontoption(TELOPT_SGA, 1); + } else if (lmodetype == REAL_LINEMODE) { +# endif /* KLUDGELINEMODE */ + willoption(TELOPT_LINEMODE, 1); + /* send along edit modes */ + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + nfrontp += 7; + editmode = useeditmode; +# ifdef KLUDGELINEMODE + } +# endif /* KLUDGELINEMODE */ + linemode = uselinemode; + goto done; + } + +# ifdef KLUDGELINEMODE + /* + * None of what follows is of any value if not using + * real linemode. + */ + if (lmodetype < REAL_LINEMODE) + goto done; +# endif /* KLUDGELINEMODE */ + + if (linemode) { + /* + * If edit mode changed, send edit mode. + */ + if (useeditmode != editmode) { + /* + * Send along appropriate edit mode mask. + */ + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, SB, + TELOPT_LINEMODE, LM_MODE, useeditmode, + IAC, SE); + nfrontp += 7; + editmode = useeditmode; + } + + + /* + * Check for changes to special characters in use. + */ + start_slc(0); + check_slc(); + end_slc(0); + } + +done: + /* + * Some things should be deferred until after the pty state has + * been set by the local process. Do those things that have been + * deferred now. This only happens once. + */ + if (_terminit == 0) { + _terminit = 1; + defer_terminit(); + } + + netflush(); + set_termbuf(); + return; + +} /* end of localstat */ +#endif /* LINEMODE */ + + +/* + * clientstat + * + * Process linemode related requests from the client. + * Client can request a change to only one of linemode, editmode or slc's + * at a time, and if using kludge linemode, then only linemode may be + * affected. + */ +clientstat(code, parm1, parm2) +register int code, parm1, parm2; +{ + void netflush(); + + /* + * Get a copy of terminal characteristics. + */ + init_termbuf(); + + /* + * Process request from client. code tells what it is. + */ + switch (code) { +#ifdef LINEMODE + case TELOPT_LINEMODE: + /* + * Don't do anything unless client is asking us to change + * modes. + */ + uselinemode = (parm1 == WILL); + if (uselinemode != linemode) { +# ifdef KLUDGELINEMODE + /* + * If using kludge linemode, make sure that + * we can do what the client asks. + * We can not turn off linemode if alwayslinemode + * or if the ICANON bit is set. + */ + if (lmodetype == KLUDGE_LINEMODE) { + if (alwayslinemode || tty_isediting()) { + uselinemode = 1; + } + } + + /* + * Quit now if we can't do it. + */ + if (uselinemode == linemode) + return; + + /* + * If using real linemode and linemode is being + * turned on, send along the edit mode mask. + */ + if (lmodetype == REAL_LINEMODE && uselinemode) +# else /* KLUDGELINEMODE */ + if (uselinemode) +# endif /* KLUDGELINEMODE */ + { + useeditmode = 0; + if (tty_isediting()) + useeditmode |= MODE_EDIT; + if (tty_istrapsig) + useeditmode |= MODE_TRAPSIG; + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode, IAC, SE); + nfrontp += 7; + editmode = useeditmode; + } + + + tty_setlinemode(uselinemode); + + linemode = uselinemode; + + } + break; + + case LM_MODE: + { + register int mode, sig, ack; + + /* + * Client has sent along a mode mask. If it agrees with + * what we are currently doing, ignore it; if not, it could + * be viewed as a request to change. Note that the server + * will change to the modes in an ack if it is different from + * what we currently have, but we will not ack the ack. + */ + useeditmode &= MODE_MASK; + ack = (useeditmode & MODE_ACK); + useeditmode &= ~MODE_ACK; + + if (useeditmode != editmode) { + mode = (useeditmode & MODE_EDIT); + sig = (useeditmode & MODE_TRAPSIG); + + if (mode != (editmode & LM_MODE)) { + tty_setedit(mode); + } + if (sig != (editmode & MODE_TRAPSIG)) { + tty_setsig(sig); + } + + set_termbuf(); + + if (!ack) { + (void) sprintf(nfrontp, "%c%c%c%c%c%c%c", IAC, + SB, TELOPT_LINEMODE, LM_MODE, + useeditmode|MODE_ACK, + IAC, SE); + nfrontp += 7; + } + + editmode = useeditmode; + } + + break; + + } /* end of case LM_MODE */ +#endif /* LINEMODE */ + + case TELOPT_NAWS: +#ifdef TIOCSWINSZ + { + struct winsize ws; + +#ifdef LINEMODE + /* + * Defer changing window size until after terminal is + * initialized. + */ + if (terminit() == 0) { + def_col = parm1; + def_row = parm1; + return; + } +#endif /* LINEMODE */ + + /* + * Change window size as requested by client. + */ + + ws.ws_col = parm1; + ws.ws_row = parm2; + (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); + } +#endif /* TIOCSWINSZ */ + + break; + + case TELOPT_TSPEED: + { +#ifdef LINEMODE + /* + * Defer changing the terminal speed. + */ + if (terminit() == 0) { + def_tspeed = parm1; + def_rspeed = parm2; + return; + } +#endif /* LINEMODE */ + /* + * Change terminal speed as requested by client. + */ + tty_tspeed(parm1); + tty_rspeed(parm2); + set_termbuf(); + + break; + + } /* end of case TELOPT_TSPEED */ + + default: + /* What? */ + break; + } /* end of switch */ + +#ifdef CRAY2 + /* + * Just in case of the likely event that we changed the pty state. + */ + rcv_ioctl(); +#endif /* CRAY2 */ + + netflush(); + +} /* end of clientstat */ + +#ifdef CRAY2 +termstat() +{ + needtermstat = 1; +} + +_termstat() +{ + needtermstat = 0; + init_termbuf(); + localstat(); + rcv_ioctl(); +} +#endif /* CRAY2 */ + +#ifdef LINEMODE +/* + * defer_terminit + * + * Some things should not be done until after the login process has started + * and all the pty modes are set to what they are supposed to be. This + * function is called when the pty state has been processed for the first time. + * It calls other functions that do things that were deferred in each module. + */ +defer_terminit() +{ + + /* + * local stuff that got deferred. + */ + if (def_tspeed != -1) { + clientstat(TELOPT_TSPEED, def_tspeed, def_rspeed); + def_tspeed = def_rspeed = 0; + } + +#ifdef TIOCSWINSZ + if (def_col || def_row) { + struct winsize ws; + + ws.ws_col = def_col; + ws.ws_row = def_row; + (void) ioctl(pty, TIOCSWINSZ, (char *)&ws); + } +#endif + + /* + * The only other module that currently defers anything. + */ + deferslc(); + +} /* end of defer_terminit */ + +/* + * terminit + * + * Returns true if the pty state has been processed yet. + */ +int terminit() +{ + return _terminit; + +} /* end of terminit */ +#endif /* LINEMODE */ diff --git a/usr/src/libexec/telnetd/utility.c b/usr/src/libexec/telnetd/utility.c new file mode 100644 index 0000000000..0aa1d19d22 --- /dev/null +++ b/usr/src/libexec/telnetd/utility.c @@ -0,0 +1,421 @@ +/* + * Copyright (c) 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +static char sccsid[] = "@(#)utility.c 5.1 (Berkeley) %G%"; +#endif /* not lint */ + + +#include "telnetd.h" + +/* + * utility functions performing io related tasks + */ + +/* + * 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. + */ + +void +ttloop() +{ + void netflush(); + + 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(); + } +} /* end of ttloop */ + +/* + * 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"); + } + if (FD_ISSET(s, &excepts)) { + return 1; + } else { + return 0; + } +} + +ptyflush() +{ + int n; + + if ((n = pfrontp - pbackp) > 0) + n = write(pty, pbackp, n); + if (n < 0) + return; + pbackp += n; + if (pbackp == pfrontp) + pbackp = pfrontp = ptyobuf; +} + +/* + * nextitem() + * + * Return the address of the next "item" in the TELNET data + * stream. This will be the address of the next character if + * the current address is a user data character, or it will + * be the address of the character following the TELNET command + * if the current address is a TELNET IAC ("I Am a Command") + * character. + */ +char * +nextitem(current) +char *current; +{ + if ((*current&0xff) != IAC) { + return current+1; + } + switch (*(current+1)&0xff) { + case DO: + case DONT: + case WILL: + case WONT: + return current+3; + case SB: /* loop forever looking for the SE */ + { + register char *look = current+2; + + for (;;) { + if ((*look++&0xff) == IAC) { + if ((*look++&0xff) == SE) { + return look; + } + } + } + } + default: + return current+2; + } +} /* end of nextitem */ + + +/* + * netclear() + * + * We are about to do a TELNET SYNCH operation. Clear + * the path to the network. + * + * Things are a bit tricky since we may have sent the first + * byte or so of a previous TELNET command into the network. + * So, we have to scan the network buffer from the beginning + * until we are up to where we want to be. + * + * A side effect of what we do, just to keep things + * simple, is to clear the urgent data pointer. The principal + * caller should be setting the urgent data pointer AFTER calling + * us in any case. + */ +netclear() +{ + register char *thisitem, *next; + char *good; +#define wewant(p) ((nfrontp > p) && ((*p&0xff) == IAC) && \ + ((*(p+1)&0xff) != EC) && ((*(p+1)&0xff) != EL)) + + thisitem = netobuf; + + while ((next = nextitem(thisitem)) <= nbackp) { + thisitem = next; + } + + /* Now, thisitem is first before/at boundary. */ + + good = netobuf; /* where the good bytes go */ + + while (nfrontp > thisitem) { + if (wewant(thisitem)) { + int length; + + next = thisitem; + do { + next = nextitem(next); + } while (wewant(next) && (nfrontp > next)); + length = next-thisitem; + bcopy(thisitem, good, length); + good += length; + thisitem = next; + } else { + thisitem = nextitem(thisitem); + } + } + + nbackp = netobuf; + nfrontp = good; /* next byte to be sent */ + neturg = 0; +} /* end of netclear */ + +/* + * netflush + * Send as much data as possible to the network, + * handling requests for urgent data. + */ +void +netflush() +{ + int n; + extern int not42; + + if ((n = nfrontp - nbackp) > 0) { + /* + * if no urgent data, or if the other side appears to be an + * old 4.2 client (and thus unable to survive TCP urgent data), + * write the entire buffer in non-OOB mode. + */ + if ((neturg == 0) || (not42 == 0)) { + n = write(net, nbackp, n); /* normal write */ + } else { + n = neturg - nbackp; + /* + * In 4.2 (and 4.3) systems, there is some question about + * what byte in a sendOOB operation is the "OOB" data. + * To make ourselves compatible, we only send ONE byte + * out of band, the one WE THINK should be OOB (though + * we really have more the TCP philosophy of urgent data + * rather than the Unix philosophy of OOB data). + */ + if (n > 1) { + n = send(net, nbackp, n-1, 0); /* send URGENT all by itself */ + } else { + n = send(net, nbackp, n, MSG_OOB); /* URGENT data */ + } + } + } + if (n < 0) { + if (errno == EWOULDBLOCK || errno == EINTR) + return; + cleanup(); + } + nbackp += n; + if (nbackp >= neturg) { + neturg = 0; + } + if (nbackp == nfrontp) { + nbackp = nfrontp = netobuf; + } + return; +} /* end of netflush */ + + +/* + * writenet + * + * Just a handy little function to write a bit of raw data to the net. + * It will force a transmit of the buffer if necessary + * + * arguments + * ptr - A pointer to a character string to write + * len - How many bytes to write + */ +writenet(ptr, len) +register char *ptr; +register int len; +{ + /* flush buffer if no room for new data) */ + if ((&netobuf[BUFSIZ] - nfrontp) < len) { + /* if this fails, don't worry, buffer is a little big */ + netflush(); + } + + bcopy(ptr, nfrontp, len); + nfrontp += len; + +} /* end of writenet */ + + +/* + * miscellaneous functions doing a variety of little jobs follow ... + */ + + +fatal(f, msg) + int f; + char *msg; +{ + char buf[BUFSIZ]; + + (void) sprintf(buf, "telnetd: %s.\r\n", msg); + (void) write(f, buf, (int)strlen(buf)); + sleep(1); /*XXX*/ + exit(1); +} + +fatalperror(f, msg) + int f; + char *msg; +{ + char buf[BUFSIZ]; + extern char *sys_errlist[]; + + (void) sprintf(buf, "%s: %s\r\n", msg, sys_errlist[errno]); + fatal(f, buf); +} + +char editedhost[32]; + +edithost(pat, host) + register char *pat; + register char *host; +{ + register char *res = editedhost; + char *strncpy(); + + if (!pat) + pat = ""; + while (*pat) { + switch (*pat) { + + case '#': + if (*host) + host++; + break; + + case '@': + if (*host) + *res++ = *host++; + break; + + default: + *res++ = *pat; + break; + } + if (res == &editedhost[sizeof editedhost - 1]) { + *res = '\0'; + return; + } + pat++; + } + if (*host) + (void) strncpy(res, host, + sizeof editedhost - (res - editedhost) -1); + else + *res = '\0'; + editedhost[sizeof editedhost - 1] = '\0'; +} + +static char *putlocation; + +putstr(s) +register char *s; +{ + + while (*s) + putchr(*s++); +} + +putchr(cc) +{ + *putlocation++ = cc; +} + +putf(cp, where) +register char *cp; +char *where; +{ + char *slash; +#ifndef NO_GETTYTAB + char datebuffer[60]; +#endif /* NO_GETTYTAB */ + extern char *rindex(); + + putlocation = where; + + while (*cp) { + if (*cp != '%') { + putchr(*cp++); + continue; + } + switch (*++cp) { + + case 't': + slash = rindex(line, '/'); + if (slash == (char *) 0) + putstr(line); + else + putstr(&slash[1]); + break; + + case 'h': + putstr(editedhost); + break; + +#ifndef NO_GETTYTAB + case 'd': + get_date(datebuffer); + putstr(datebuffer); + break; +#endif /* NO_GETTYTAB */ + + case '%': + putchr('%'); + break; + } + cp++; + } +} + +/*ARGSUSED*/ +#ifdef NO_GETTYTAB +getent(cp, name) +char *cp, *name; +{ + return(0); +} + +/*ARGSUSED*/ +char * +getstr(cp, cpp) +char *cp, **cpp; +{ + return(0); +} +#endif /* NO_GETTYTAB */ -- 2.20.1