/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
#ifndef lint
static char sccsid[] = "@(#)telnet.c 1.1 92/07/30 SMI"; /* from UCB 5.16 5/27/86 */
#endif not lint
/*
* User telnet program.
*
* Many of the FUNCTIONAL changes in this newest version of telnet
* were suggested by Dave Borman of Cray Research, Inc.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <netinet/in.h>
#define TELOPTS
#include <arpa/telnet.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <netdb.h>
#include <strings.h>
#include <varargs.h>
#ifndef FD_SETSIZE
/*
* The following is defined just in case someone should want to run
* this telnet on a 4.2 system.
*
*/
#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
#define strip(x) ((x)&0x7f)
char ttyobuf[2*BUFSIZ], *tfrontp = ttyobuf, *tbackp = ttyobuf;
#define TTYADD(c) { if (!(SYNCHing||flushout)) { *tfrontp++ = c; } }
#define TTYLOC() (tfrontp)
#define TTYMAX() (ttyobuf+sizeof ttyobuf-1)
#define TTYMIN() (netobuf)
#define TTYBYTES() (tfrontp-tbackp)
#define TTYROOM() (TTYMAX()-TTYLOC()+1)
char netobuf[2*BUFSIZ], *nfrontp = netobuf, *nbackp = netobuf;
#define NETADD(c) { *nfrontp++ = c; }
#define NET2ADD(c1,c2) { NETADD(c1); NETADD(c2); }
#define NETLOC() (nfrontp)
#define NETMAX() (netobuf+sizeof netobuf-1)
#define NETBYTES() (nfrontp-nbackp)
#define NETROOM() (NETMAX()-NETLOC()+1)
char *neturg = 0; /* one past last byte of urgent data */
char subbuffer[100], *subpointer, *subend; /* buffer for sub-options */
#define SB_CLEAR() subpointer = subbuffer;
#define SB_TERM() subend = subpointer;
#define SB_ACCUM(c) if (subpointer < (subbuffer+sizeof subbuffer)) { \
*subpointer++ = (c); \
}
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 };
struct cmd {
char *name; /* command name */
char *help; /* help string */
int (*handler)(); /* routine which executes command */
int dohelp; /* Should we give general help information? */
int needconnect; /* Do we need to be connected to execute? */
};
int connected;
int net;
int tout;
int showoptions = 0;
int debug = 0;
int crmod = 0;
int netdata = 0;
static FILE *NetTrace;
int telnetport = 1;
char *prompt;
char escape = CTRL(]);
char echoc = CTRL(E);
int SYNCHing = 0; /* we are in TELNET SYNCH mode */
int flushout = 0; /* flush output */
int autoflush = 0; /* flush output when interrupting? */
int autosynch = 0; /* send interrupt characters with SYNCH? */
int localchars = 0; /* we recognize interrupt/quit */
int donelclchars = 0; /* the user has set "localchars" */
int dontlecho = 0; /* do we suppress local echoing right now? */
int donelclflow = 0; /* the user has set "localflow" */
int localflow = 0; /* do xon/xoff flow control locally */
char line[200];
int margc;
char *margv[20];
jmp_buf toplevel;
jmp_buf peerdied;
extern int errno;
struct sockaddr_in sin;
struct cmd *getcmd();
struct tchars otc, ntc;
struct ltchars oltc, nltc;
struct sgttyb ottyb, nttyb;
int globalmode = 0;
int flushline = 1;
char *hostname;
char hnamebuf[32];
/*
* 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 */
gotDM; /* when did we last see a data mark */
} clocks;
#define settimer(x) clocks.x = clocks.system++
/*
* Various utility routines.
*/
char *ambiguous; /* special return value */
#define Ambiguous(t) ((t)&ambiguous)
char **
genget(name, table, next)
char *name; /* name to match */
char **table; /* name entry in table */
char **(*next)(); /* routine to return next entry in table */
{
register char *p, *q;
register char **c, **found;
register int nmatches, longest;
longest = 0;
nmatches = 0;
found = 0;
for (c = table; p = *c; c = (*next)(c)) {
for (q = name; *q == *p++; q++)
if (*q == 0) /* exact match? */
return (c);
if (!*q) { /* the name was a prefix */
if (q - name > longest) {
longest = q - name;
nmatches = 1;
found = c;
} else if (q - name == longest)
nmatches++;
}
}
if (nmatches > 1)
return Ambiguous(char **);
return (found);
}
/*
* Make a character string into a number.
*
* Todo: 1. Could take random integers (12, 0x12, 012, 0b1).
*/
special(s)
register char *s;
{
register char c;
char b;
switch (*s) {
case '^':
b = *++s;
if (b == '?') {
c = b | 0x40; /* DEL */
} else {
c = b & 0x1f;
}
break;
default:
c = *s;
break;
}
return c;
}
/*
* Construct a control character sequence
* for a special character.
*/
char *
control(c)
register int c;
{
static char buf[3];
if (c == 0x7f)
return ("^?");
if (c == '\377') {
return "off";
}
if (c >= 0x20) {
buf[0] = c;
buf[1] = 0;
} else {
buf[0] = '^';
buf[1] = '@'+c;
buf[2] = 0;
}
return (buf);
}
/*
* upcase()
*
* Upcase (in place) the argument.
*/
void
upcase(argument)
register char *argument;
{
register int c;
while (c = *argument) {
if (islower(c)) {
*argument = toupper(c);
}
argument++;
}
}
/*
* Check to see if any out-of-band data exists on a socket (for
* Telnet "synch" processing).
*/
int
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) {
perror("select");
quit();
}
if (FD_ISSET(s, &excepts)) {
return 1;
} else {
return 0;
}
}
/*
* netflush
* Send as much data as possible to the network,
* handling requests for urgent data.
*/
netflush(fd)
{
int n;
if ((n = nfrontp - nbackp) > 0) {
if (!neturg) {
n = write(fd, 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(fd, nbackp, n-1, 0); /* send URGENT all by itself */
} else {
n = send(fd, nbackp, n, MSG_OOB); /* URGENT data */
}
}
}
if (n < 0) {
if (errno != ENOBUFS && errno != EWOULDBLOCK) {
setcommandmode();
perror(hostname);
close(fd);
neturg = 0;
longjmp(peerdied, -1);
/*NOTREACHED*/
}
n = 0;
}
if (netdata && n) {
Dump('>', nbackp, n);
}
nbackp += n;
if (nbackp >= neturg) {
neturg = 0;
}
if (nbackp == nfrontp) {
nbackp = nfrontp = netobuf;
}
}
/*
* 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) {