* Copyright (c) 1983 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)telnet.c 5.5 (Berkeley) %G%";
#define strip(x) ((x)&0177)
char ttyobuf
[BUFSIZ
], *tfrontp
= ttyobuf
, *tbackp
= ttyobuf
;
char netobuf
[BUFSIZ
], *nfrontp
= netobuf
, *nbackp
= netobuf
;
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 tn(), quit(), suspend(), bye(), help();
int setescape(), status(), toggle(), setoptions();
int setcrmod(), setdebug(), sendesc(), ayt(), intp();
#define HELPINDENT (sizeof ("connect"))
char *name
; /* command name */
char *help
; /* help string */
int (*handler
)(); /* routine which executes command */
char openhelp
[] = "connect to a site";
char closehelp
[] = "close current connection";
char quithelp
[] = "exit telnet";
char zhelp
[] = "suspend telnet";
char debughelp
[] = "toggle debugging";
char escapehelp
[] = "set escape character";
char statushelp
[] = "print status information";
char helphelp
[] = "print help information";
char optionshelp
[] = "toggle viewing of options processing";
char crmodhelp
[] = "toggle mapping of received carriage returns";
char sendeschelp
[] = "send escape character";
char aythelp
[] = "send \"Are You There\"";
char intphelp
[] = "send \"Interrupt Process\"";
{ "open", openhelp
, tn
},
{ "close", closehelp
, bye
},
{ "quit", quithelp
, quit
},
{ "escape", escapehelp
, setescape
},
{ "status", statushelp
, status
},
{ "options", optionshelp
, setoptions
},
{ "crmod", crmodhelp
, setcrmod
},
{ "debug", debughelp
, setdebug
},
{ "interrupt", intphelp
, intp
},
{ "passthru", sendeschelp
, sendesc
},
{ "help", helphelp
, help
},
sp
= getservbyname("telnet", "tcp");
fprintf(stderr
, "telnet: tcp/telnet: unknown service\n");
ioctl(0, TIOCGETP
, (char *)&ottyb
);
ioctl(0, TIOCGETC
, (char *)&otc
);
ioctl(0, TIOCGLTC
, (char *)&oltc
);
if (argc
> 1 && !strcmp(argv
[1], "-d")) {
if (setjmp(toplevel
) != 0)
register struct hostent
*host
= 0;
printf("?Already connected to %s\n", hostname
);
strcpy(line
, "Connect ");
gets(&line
[strlen(line
)]);
printf("usage: %s host-name [port]\n", argv
[0]);
sin
.sin_addr
.s_addr
= inet_addr(argv
[1]);
if (sin
.sin_addr
.s_addr
!= -1) {
sin
.sin_family
= AF_INET
;
strcpy(hnamebuf
, argv
[1]);
host
= gethostbyname(argv
[1]);
sin
.sin_family
= host
->h_addrtype
;
bcopy(host
->h_addr_list
[0], (caddr_t
)&sin
.sin_addr
,
printf("%s: unknown host\n", argv
[1]);
sin
.sin_port
= sp
->s_port
;
sin
.sin_port
= atoi(argv
[2]);
sp
= getservbyname(argv
[2], "tcp");
sin
.sin_port
= sp
->s_port
;
printf("%s: bad port number\n", argv
[2]);
sin
.sin_port
= atoi(argv
[2]);
sin
.sin_port
= htons(sin
.sin_port
);
net
= socket(AF_INET
, SOCK_STREAM
, 0);
perror("telnet: socket");
setsockopt(net
, SOL_SOCKET
, SO_DEBUG
, &debug
, sizeof(debug
)) < 0)
perror("setsockopt (SO_DEBUG)");
signal(SIGPIPE
, deadpeer
);
while (connect(net
, (caddr_t
)&sin
, sizeof (sin
)) < 0) {
if (host
&& host
->h_addr_list
[1]) {
fprintf(stderr
, "telnet: connect to address %s: ",
inet_ntoa(sin
.sin_addr
));
bcopy(host
->h_addr_list
[0], (caddr_t
)&sin
.sin_addr
,
fprintf(stderr
, "Trying %s...\n",
inet_ntoa(sin
.sin_addr
));
net
= socket(AF_INET
, SOCK_STREAM
, 0);
perror("telnet: socket");
perror("telnet: connect");
call(status
, "status", 0);
if (setjmp(peerdied
) == 0)
fprintf(stderr
, "Connection closed by foreign host.\n");
* Print status about the connection.
printf("Connected to %s.\n", hostname
);
printf("No connection.\n");
printf("Escape character is '%s'.\n", control(escape
));
register char **argp
= margv
;
while (*cp
!= '\0' && !isspace(*cp
))
/* reget parameters in case they were changed */
ioctl(0, TIOCGETP
, (char *)&ottyb
);
ioctl(0, TIOCGETC
, (char *)&otc
);
ioctl(0, TIOCGLTC
, (char *)&oltc
);
printf("Connection closed.\n");
for (op
= hisopts
; op
< &hisopts
[256]; op
++)
printf("Commands may be abbreviated. Commands are:\n\n");
for (c
= cmdtab
; c
->name
; c
++)
printf("%-*s\t%s\n", HELPINDENT
, c
->name
, c
->help
);
if (c
== (struct cmd
*)-1)
printf("?Ambiguous help command %s\n", arg
);
else if (c
== (struct cmd
*)0)
printf("?Invalid help command %s\n", arg
);
* Call routine with argc, argv set from args (terminated by 0).
for (argc
= 0, argp
= &args
; *argp
++ != 0; argc
++)
struct tchars notc
= { -1, -1, -1, -1, -1, -1 };
struct ltchars noltc
= { -1, -1, -1, -1, -1, -1 };
sb
.sg_flags
&= ~(ECHO
|CRMOD
);
sb
.sg_flags
|= ECHO
|CRMOD
;
sb
.sg_erase
= sb
.sg_kill
= -1;
ioctl(fileno(stdin
), TIOCSLTC
, (char *)ltc
);
ioctl(fileno(stdin
), TIOCSETC
, (char *)tc
);
ioctl(fileno(stdin
), TIOCSETP
, (char *)&sb
);
ioctl(fileno(stdin
), FIONBIO
, &onoff
);
ioctl(fileno(stdout
), FIONBIO
, &onoff
);
char sibuf
[BUFSIZ
], *sbp
;
char tibuf
[BUFSIZ
], *tbp
;
* Select from tty and network...
int tin
= fileno(stdin
), tout
= fileno(stdout
);
if (telnetport
&& !hisopts
[TELOPT_SGA
])
int ibits
= 0, obits
= 0;
select(16, &ibits
, &obits
, 0, 0);
if (ibits
== 0 && obits
== 0) {
* Something to read from the network...
scc
= read(s
, sibuf
, sizeof (sibuf
));
if (scc
< 0 && errno
== EWOULDBLOCK
)
* Something to read from the tty...
if (ibits
& (1 << tin
)) {
tcc
= read(tin
, tibuf
, sizeof (tibuf
));
if (tcc
< 0 && errno
== EWOULDBLOCK
)
if ((&netobuf
[BUFSIZ
] - nfrontp
) < 2)
c
= *tbp
++ & 0377, tcc
--;
if (strip(c
) == escape
) {
if (!hisopts
[TELOPT_ECHO
])
if (hisopts
[TELOPT_ECHO
])
if ((obits
& (1 << s
)) && (nfrontp
- nbackp
) > 0)
if ((obits
& (1 << tout
)) && (tfrontp
- tbackp
) > 0)
if (c
== (struct cmd
*)-1) {
printf("?Ambiguous command\n");
printf("?Invalid command\n");
(*c
->handler
)(margc
, margv
);
* Telnet receiver states for fsm
static int state
= TS_DATA
;
c
= *sbp
++ & 0377, scc
--;
* This hack is needed since we can't set
* CRMOD on output only. Machines like MULTICS
* like to send \r without \n; since we must
* turn off CRMOD to get proper input, the mapping
ioctl(fileno(stdout
), TIOCFLUSH
, 0);
printoption("RCVD", will
, c
, !hisopts
[c
]);
printoption("RCVD", wont
, c
, hisopts
[c
]);
printoption("RCVD", doopt
, c
, !myopts
[c
]);
printoption("RCVD", dont
, c
, myopts
[c
]);
sprintf(nfrontp
, wont
, c
);
nfrontp
+= sizeof (wont
) - 2;
printoption("SENT", wont
, c
);
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (dont
) - 2;
printoption("SENT", fmt
, option
);
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (doopt
) - 2;
printoption("SENT", fmt
, option
);
sprintf(nfrontp
, fmt
, option
);
nfrontp
+= sizeof (doopt
) - 2;
printoption("SENT", fmt
, option
);
* Set the escape character.
printf("new escape character: ");
printf("Escape character is '%s'.\n", control(escape
));
showoptions
= !showoptions
;
printf("%s show option processing.\n", showoptions
? "Will" : "Won't");
printf("%s map carriage return on output.\n", crmod
? "Will" : "Won't");
printf("%s turn on socket level debugging.\n",
debug
? "Will" : "Won't");
setsockopt(net
, SOL_SOCKET
, SO_DEBUG
, &debug
, sizeof(debug
)) < 0)
perror("setsockopt (SO_DEBUG)");
* Construct a control character sequence
* for a special character.
register struct cmd
*c
, *found
;
register int nmatches
, longest
;
for (c
= cmdtab
; p
= c
->name
; c
++) {
for (q
= name
; *q
== *p
++; q
++)
if (*q
== 0) /* exact match? */
if (!*q
) { /* the name was a prefix */
if (q
- name
> longest
) {
} else if (q
- name
== longest
)
return ((struct cmd
*)-1);
if ((n
= tfrontp
- tbackp
) > 0)
n
= write(fd
, tbackp
, n
);
tbackp
= tfrontp
= ttyobuf
;
if ((n
= nfrontp
- nbackp
) > 0)
n
= write(fd
, nbackp
, n
);
if (errno
!= ENOBUFS
&& errno
!= EWOULDBLOCK
) {
nbackp
= nfrontp
= netobuf
;
printoption(direction
, fmt
, option
, what
)
printf("%s ", direction
);
if (option
< TELOPT_SUPDUP
)
printf("%s %s", fmt
, telopts
[option
]);
printf("%s %d", fmt
, option
);
printf(" (%s)\r\n", what
? "reply" : "don't reply");