/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Copyright (c) 1988, 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef lint
#endif /* not lint */
#include <netdb.h>
#include <curses.h>
#include <signal.h>
/*
* among other things, telnet.h #defines 'DO', which is a variable
* declared in curses.h.
*/
#include <ctype.h>
#include "ring.h"
#include "defines.h"
#include "externs.h"
#include "types.h"
#include "general.h"
#include "auth.h"
#include "encrypt.h"
/* Buffer for sub-options */
static unsigned char *subpointer;
static unsigned char *subend;
*subpointer++ = (c); \
}
int eight = 0;
int skiprc = 0;
int connected;
int showoptions;
int debug = 0;
int crmod;
int telnetport;
int globalmode;
/* spin while waiting for authentication */
int encrypt_flag = 0;
int forwardable_flag = 0;
int forward_flag = 0;
char *prompt = 0;
#ifdef KLUDGELINEMODE
#endif
/*
* Telnet receiver states for fsm
*/
#define TS_DATA 0
static int telrcv_state;
#ifdef OLD_ENVIRON
#else
#endif
static int flushline;
int linemode;
/* effect since before initial negotiations */
#ifdef KLUDGELINEMODE
#endif
/*
* The following are some clocks used to decide how to interpret
* the relationship between various variables.
*/
#ifdef notdef
{ "telnet command mode", COMMAND_LINE },
{ "character-at-a-time mode", 0 },
{ "3270 mode", 0 },
};
#endif
static void willoption(int);
static void wontoption(int);
static void lm_will(unsigned char *, int);
static void lm_wont(unsigned char *, int);
static void lm_do(unsigned char *, int);
static void lm_dont(unsigned char *, int);
static void slc_init(void);
static void slc_import(int);
static void slc_export(void);
static void slc_start_reply(size_t);
static void slc_add_reply(unsigned char, unsigned char, cc_t);
static void slc_end_reply(void);
static void slc(unsigned char *, int);
static int slc_update(void);
static void env_opt(unsigned char *, int);
static void env_opt_start(void);
static void sendeof(void);
static int is_unique(register char *, register char **, register char **);
/*
* Initialize telnet environment.
*/
int
{
if (env_init() == 0)
return (0);
SB_CLEAR();
restartany = -1;
SYNCHing = 0;
/* Don't change NetTrace */
#ifdef KLUDGELINEMODE
#endif
flushline = 1;
return (1);
}
#ifdef notdef
#include <varargs.h>
/*VARARGS*/
static void
{
char *ptr;
char *format;
char *string;
int i;
while ((i = *format++) != 0) {
if (i == '%') {
i = *format++;
switch (i) {
case 'c':
break;
case 's':
break;
case 0:
ExitString("printring: trailing %%.\n",
/*NOTREACHED*/
default:
ExitString("printring: unknown format "
"character.\n", EXIT_FAILURE);
/*NOTREACHED*/
}
} else {
*ptr++ = i;
}
}
}
#endif
/*
* These routines are in charge of sending option negotiations
* to the other side.
*
* The basic idea is that we send the negotiation if either side
* is in disagreement as to what the current state should be.
*/
void
register int c, init;
{
if (init) {
if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
return;
do_dont_resp[c]++;
}
NETADD(c);
}
void
register int c, init;
{
if (init) {
if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
return;
do_dont_resp[c]++;
}
NETADD(c);
}
void
register int c, init;
{
if (init) {
if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
return;
will_wont_resp[c]++;
}
NETADD(c);
}
void
register int c, init;
{
if (init) {
if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
return;
will_wont_resp[c]++;
}
NETADD(c);
}
static void
int option;
{
int new_state_ok = 0;
if (do_dont_resp[option]) {
--do_dont_resp[option];
--do_dont_resp[option];
}
switch (option) {
case TELOPT_ECHO:
case TELOPT_SGA:
break;
}
/* FALLTHROUGH */
case TELOPT_BINARY:
/* FALLTHROUGH */
case TELOPT_STATUS:
case TELOPT_AUTHENTICATION:
/* FALLTHROUGH */
case TELOPT_ENCRYPT:
new_state_ok = 1;
break;
case TELOPT_TM:
if (flushout)
flushout = 0;
/*
* Special case for TM. If we get back a WILL,
* pretend we got back a WONT.
*/
case TELOPT_LINEMODE:
default:
break;
}
if (new_state_ok) {
setconnmode(0); /* possibly set new tty mode */
} else {
do_dont_resp[option]++;
}
}
if (option == TELOPT_ENCRYPT)
}
static void
int option;
{
if (do_dont_resp[option]) {
--do_dont_resp[option];
--do_dont_resp[option];
}
switch (option) {
#ifdef KLUDGELINEMODE
case TELOPT_SGA:
if (!kludgelinemode)
break;
/* FALLTHROUGH */
#endif
case TELOPT_ECHO:
break;
case TELOPT_TM:
if (flushout)
flushout = 0;
default:
break;
}
if (my_state_is_do(option))
setconnmode(0); /* Set new tty mode */
/*
* Special case for TM.
*/
if (flushout)
flushout = 0;
}
}
static void
int option;
{
int new_state_ok = 0;
if (will_wont_resp[option]) {
--will_wont_resp[option];
--will_wont_resp[option];
}
if (will_wont_resp[option] == 0) {
if (my_want_state_is_wont(option)) {
switch (option) {
case TELOPT_TM:
/*
* Special case for TM. We send a WILL,
* but pretend we sent WONT.
*/
return;
case TELOPT_BINARY: /* binary mode */
case TELOPT_NAWS: /* window size */
case TELOPT_TSPEED: /* terminal speed */
case TELOPT_LFLOW: /* local flow control */
case TELOPT_TTYPE: /* terminal type option */
case TELOPT_SGA: /* no big deal */
case TELOPT_ENCRYPT: /* encryption variable option */
new_state_ok = 1;
break;
case TELOPT_NEW_ENVIRON:
/* New environment variable option */
#ifdef OLD_ENVIRON
/* turn off the old */
goto env_common;
case TELOPT_OLD_ENVIRON:
/* Old environment variable option */
/* Don't enable if new one is in use! */
break;
#endif
new_state_ok = 1;
break;
case TELOPT_AUTHENTICATION:
if (autologin)
new_state_ok = 1;
break;
case TELOPT_XDISPLOC: /* X Display location */
if (env_getvalue((unsigned char *)"DISPLAY"))
new_state_ok = 1;
break;
case TELOPT_LINEMODE:
#ifdef KLUDGELINEMODE
kludgelinemode = 0;
#endif
slc_init();
return;
case TELOPT_ECHO: /* We're never going to echo... */
default:
break;
}
if (new_state_ok) {
setconnmode(0); /* Set new tty mode */
} else {
will_wont_resp[option]++;
}
} else {
/*
* Handle options that need more things done after the
* other side has acknowledged the option.
*/
switch (option) {
case TELOPT_LINEMODE:
#ifdef KLUDGELINEMODE
kludgelinemode = 0;
#endif
slc_init();
send_do(TELOPT_SGA, 0);
return;
}
}
}
}
static void
int option;
{
if (will_wont_resp[option]) {
--will_wont_resp[option];
--will_wont_resp[option];
}
switch (option) {
case TELOPT_LINEMODE:
linemode = 0; /* put us back to the default state */
break;
#ifdef OLD_ENVIRON
case TELOPT_NEW_ENVIRON:
/*
* The new environ option wasn't recognized, try
* the old one.
*/
break;
#endif
}
/* we always accept a DONT */
if (my_state_is_will(option))
setconnmode(0); /* Set new tty mode */
}
}
/*
* Given a buffer returned by tgetent(), this routine will turn
* the pipe seperated list of names in the buffer into an array
* of pointers to null terminated names. We toss out any bad,
* duplicate, or verbose names (names with spaces).
*/
static char *unknown[] = { 0, 0 };
static char **
{
register int n;
if (name) {
name = 0;
unknown[0] = name_unknown;
} else {
}
} else
unknown[0] = name_unknown;
/*
* Count up the number of names.
*/
if (*cp == '|')
n++;
}
/*
* Allocate an array to put the name pointers into
*/
if (argv == 0)
return (unknown);
/*
* Fill up the array of pointers to names.
*/
*argv = 0;
n = 0;
if (c == '|' || c == ':') {
*cp++ = '\0';
/*
* Skip entries that have spaces or are over 40
* characters long. If this is our environment
* name, then put it up front. Otherwise, as
* long as this is not a duplicate name (case
* insensitive) add it to the list.
*/
/* EMPTY */;
if (c == ':')
break;
/*
* Skip multiple delimiters. Reset cp2 to
* the beginning of the next name. Reset n,
* the flag for names with spaces.
*/
while ((c = *cp) == '|')
cp++;
n = 0;
}
/*
* Skip entries with spaces or non-ascii values.
* Convert lower case letters to upper case.
*/
if ((c == ' ') || !isascii(c))
n = 1;
else if (islower(c))
}
/*
* Check for an old V6 2 character name. If the second
* name points to the beginning of the buffer, and is
* only 2 characters long, move it to the end of the array.
*/
--argvp;
}
/*
* Duplicate last name, for TTYPE option, and null
* terminate the array. If we didn't find a match on
* our terminal name, put that name at the beginning.
*/
*argvp = 0;
if (*argv == 0) {
if (name)
else {
--argvp;
}
}
if (*argv)
return (argv);
else
return (unknown);
}
static int
{
register char **ap;
register int n;
return (0);
return (1);
}
extern char ttytype[];
static char *
gettermname(void)
{
char *tname;
static char **tnamep = 0;
static char **next;
int err;
if (resettermname) {
resettermname = 0;
} else {
} else
unknown[0] = name_unknown;
}
}
if (*next == 0)
return (*next++);
}
/*
* suboption()
*
* Look at the sub-option buffer, and try to be helpful to the other
* side.
*
* Currently we recognize:
*
* Terminal type, send request.
* Terminal speed (send request).
* Local flow control (is request).
* Linemode
*/
static void
{
unsigned char subchar;
case TELOPT_TTYPE:
return;
return;
} else {
char *name;
name = gettermname();
} else {
ExitString("No room in buffer for "
"terminal type.\n", EXIT_FAILURE);
/*NOTREACHED*/
}
}
break;
case TELOPT_TSPEED:
return;
if (SB_EOF())
return;
if (SB_GET() == TELQUAL_SEND) {
}
else
(void) printf(
"telnet: not enough room in buffer "
"for terminal speed option reply\n");
}
break;
case TELOPT_LFLOW:
return;
if (SB_EOF())
return;
switch (SB_GET()) {
case LFLOW_RESTART_ANY:
restartany = 1;
break;
case LFLOW_RESTART_XON:
restartany = 0;
break;
case LFLOW_ON:
localflow = 1;
break;
case LFLOW_OFF:
localflow = 0;
break;
default:
return;
}
setconnmode(0);
break;
case TELOPT_LINEMODE:
return;
if (SB_EOF())
return;
switch (SB_GET()) {
case WILL:
break;
case WONT:
break;
case DO:
break;
case DONT:
break;
case LM_SLC:
break;
case LM_MODE:
break;
default:
break;
}
break;
#ifdef OLD_ENVIRON
case TELOPT_OLD_ENVIRON:
#endif
case TELOPT_NEW_ENVIRON:
if (SB_EOF())
return;
switch (SB_PEEK()) {
case TELQUAL_IS:
case TELQUAL_INFO:
if (my_want_state_is_dont(subchar))
return;
break;
case TELQUAL_SEND:
if (my_want_state_is_wont(subchar)) {
return;
}
break;
default:
return;
}
break;
case TELOPT_XDISPLOC:
return;
if (SB_EOF())
return;
if (SB_GET() == TELQUAL_SEND) {
NULL) {
/*
* Something happened, we no longer have a
* DISPLAY variable. So, turn off the option.
*/
break;
}
}
else
(void) printf(
"telnet: not enough room in buffer"
" for display location option reply\n");
}
break;
case TELOPT_AUTHENTICATION: {
if (!autologin)
break;
if (SB_EOF())
return;
switch (SB_GET()) {
case TELQUAL_SEND:
return;
break;
case TELQUAL_REPLY:
return;
break;
}
}
break;
case TELOPT_ENCRYPT:
if (SB_EOF())
return;
switch (SB_GET()) {
case ENCRYPT_START:
return;
break;
case ENCRYPT_END:
return;
encrypt_end();
break;
case ENCRYPT_SUPPORT:
return;
break;
case ENCRYPT_REQSTART:
return;
break;
case ENCRYPT_REQEND:
return;
/*
* We can always send an REQEND so that we cannot
* get stuck encrypting. We should only get this
* if we have been able to get in the correct mode
* anyhow.
*/
break;
case ENCRYPT_IS:
return;
break;
case ENCRYPT_REPLY:
return;
break;
case ENCRYPT_ENC_KEYID:
return;
break;
case ENCRYPT_DEC_KEYID:
return;
break;
default:
break;
}
break;
default:
break;
}
}
static void
unsigned char *cmd;
int len;
{
if (len < 1) {
/* Should not happen... */
(void) printf(
"telnet: command missing from linemode WILL request\n");
return;
}
switch (cmd[0]) {
case LM_FORWARDMASK: /* We shouldn't ever get this... */
default:
}
else
(void) printf("telnet: not enough room in buffer for"
"reply to linemode WILL request\n");
break;
}
}
static void
unsigned char *cmd;
int len;
{
if (len < 1) {
/* Should not happen... */
(void) printf(
"telnet: command missing from linemode WONT request\n");
return;
}
switch (cmd[0]) {
case LM_FORWARDMASK: /* We shouldn't ever get this... */
default:
/* We are always DONT, so don't respond */
return;
}
}
static void
unsigned char *cmd;
int len;
{
if (len < 1) {
/* Should not happen... */
(void) printf(
"telnet: command missing from linemode DO request\n");
return;
}
switch (cmd[0]) {
case LM_FORWARDMASK:
default:
}
else
(void) printf("telnet: not enough room in buffer for"
"reply to linemode DO request\n");
break;
}
}
static void
unsigned char *cmd;
int len;
{
if (len < 1) {
/* Should not happen... */
(void) printf(
"telnet: command missing from linemode DONT request\n");
return;
}
switch (cmd[0]) {
case LM_FORWARDMASK:
default:
/* we are always WONT, so don't respond */
break;
}
}
static unsigned char str_lm_mode[] = {
};
void
unsigned char *cmd;
{
if (len != 1)
return;
return;
if (!init)
if (NETROOM() > sizeof (str_lm_mode)) {
}
else
(void) printf("telnet: not enough room in buffer for"
"reply to linemode request\n");
setconnmode(0); /* set changed mode */
}
/*
* slc()
* Handle special character suboption of LINEMODE.
*/
static struct spc {
#define SLC_IMPORT 0
static void
slc_init()
{
localchars = 1;
}
} else { \
} \
}
/* No BRK */
/* No EOR */
#ifdef USE_TERMIO
/* No FORW2 */
#endif
if (slc_mode == SLC_EXPORT)
slc_export();
else
slc_import(1);
}
void
slcstate()
{
(void) printf("Special characters are %s values\n",
"remote");
}
void
{
slc_export();
}
void
int def;
{
}
static unsigned char slc_import_val[] = {
};
static unsigned char slc_import_def[] = {
};
static void
int def;
{
if (NETROOM() > sizeof (slc_import_val)) {
if (def) {
sizeof (slc_import_def));
sizeof (slc_import_def)-2);
} else {
sizeof (slc_import_val));
sizeof (slc_import_val)-2);
}
}
else
(void) printf(
"telnet: not enough room in buffer for slc import"
" request\n");
}
/*
* The SLC reply consists of: IAC, SB, TELOPT_LINEMODE, LM_SLC,
* SLC triplets[], IAC, SE. i.e. it has a 'wrapper' of 6 control characters.
*/
static void
{
else
}
}
(void) slc_update();
}
static void
register unsigned char *cp;
int len;
{
if (func == 0) {
/*
* Client side: always ignore 0 function.
*/
continue;
}
continue;
}
continue;
}
/*
* This is an error condition, the SLC_ACK
* bit should never be set for the SLC_DEFAULT
* level. Our best guess to recover is to
* ignore the SLC_ACK bit.
*/
}
continue;
}
}
if (level == SLC_DEFAULT) {
else
}
}
if (slc_update())
}
void
{
else
}
}
setconnmode(1);
}
static void
{
/*
* SLC triplets may contain escaped characters, allow for
* worst case by allocating 2 bytes for every character.
*/
return;
}
*slc_replyp++ = IAC;
*slc_replyp++ = SB;
*slc_replyp++ = TELOPT_LINEMODE;
*slc_replyp++ = LM_SLC;
}
static void
{
*slc_replyp++ = IAC;
*slc_replyp++ = IAC;
*slc_replyp++ = IAC;
}
static void
{
register int len;
*slc_replyp++ = IAC;
*slc_replyp++ = SE;
if (len <= SLC_WRAPPER_SIZE)
return;
}
else
(void) printf("telnet: not enough room in buffer for slc end "
"reply\n");
}
static int
{
int need_update = 0;
continue;
need_update = 1;
}
}
return (need_update);
}
#ifdef OLD_ENVIRON
#ifdef ENV_HACK
/*
* the definitions of VALUE and VAR reversed. To ensure
* maximum interoperability, we assume that the server is
* an older BSD server, until proven otherwise. The newer
* BSD servers should be able to handle either definition,
* so it is better to use the wrong values if we don't
* know what type of server it is.
*/
#else
#endif
#endif
static void
register unsigned char *buf;
register int len;
{
register int i;
switch (buf[0]&0xff) {
case TELQUAL_SEND:
if (len == 1) {
} else for (i = 1; i < len; i++) {
switch (buf[i]&0xff) {
#ifdef OLD_ENVIRON
case OLD_ENV_VAR:
#ifdef ENV_HACK
if (telopt_environ == TELOPT_OLD_ENVIRON &&
env_auto) {
/* Server has the same definitions */
}
/* FALLTHROUGH */
#endif
case OLD_ENV_VALUE:
/*
* Although OLD_ENV_VALUE is not legal, we will
* still recognize it, just in case it is an
* old server that has VAR & VALUE mixed up...
*/
/* FALLTHROUGH */
#else
case NEW_ENV_VAR:
#endif
case ENV_USERVAR:
if (ep) {
*epc = 0;
}
break;
case ENV_ESC:
i++;
/*FALLTHROUGH*/
default:
if (epc)
break;
}
}
if (ep) {
*epc = 0;
}
env_opt_end(1);
break;
case TELQUAL_IS:
case TELQUAL_INFO:
/* Ignore for now. We shouldn't get it anyway. */
break;
default:
break;
}
}
static unsigned char *opt_reply;
static unsigned char *opt_replyp;
static unsigned char *opt_replyend;
/*
* The opt reply consists of: IAC, SB, telopt_environ, TELQUAL_IS,
* value, IAC, SE. i.e. it has a 'wrapper' of 6 control characters.
*/
static void
{
(void) printf(
"telnet: error allocating environment option memory\n");
return;
}
*opt_replyp++ = IAC;
*opt_replyp++ = SB;
*opt_replyp++ = telopt_environ;
*opt_replyp++ = TELQUAL_IS;
}
void
{
if (opt_replyp)
}
void
register unsigned char *ep;
{
register unsigned char *vp, c;
int opt_reply_size;
int opt_reply_used;
return; /* XXX */
/* Send user defined variables first. */
(void) env_default(1, 0);
while (ep = env_default(0, 0))
/* Now add the list of well know variables. */
return;
}
/*
* Calculate space required for opt_reply and allocate more if required.
* Assume worst case that every character is escaped, so needs 2 bytes.
*/
(void) printf(
"telnet: can't allocate environment option "
"reply\n");
return;
}
}
if (opt_welldefined((char *)ep))
#ifdef OLD_ENVIRON
if (telopt_environ == TELOPT_OLD_ENVIRON)
*opt_replyp++ = old_env_var;
else
#endif
*opt_replyp++ = NEW_ENV_VAR;
else
*opt_replyp++ = ENV_USERVAR;
for (;;) {
switch (c&0xff) {
case IAC:
*opt_replyp++ = IAC;
break;
case NEW_ENV_VAR:
case NEW_ENV_VALUE:
case ENV_ESC:
case ENV_USERVAR:
*opt_replyp++ = ENV_ESC;
break;
}
*opt_replyp++ = c;
}
#ifdef OLD_ENVIRON
if (telopt_environ == TELOPT_OLD_ENVIRON)
*opt_replyp++ = old_env_value;
else
#endif
*opt_replyp++ = NEW_ENV_VALUE;
} else
break;
}
}
int
char *ep;
{
return (1);
return (0);
}
void
register int emptyok;
{
register int len;
*opt_replyp++ = IAC;
*opt_replyp++ = SE;
}
else
(void) printf("telnet: not enough room in buffer for "
"environment option end reply\n");
}
if (opt_reply) {
}
}
int
telrcv()
{
register int c;
register int scc;
register unsigned char *sbp;
int count;
int returnValue = 0;
int min_room = 0;
scc = 0;
count = 0;
if (scc == 0) {
if (count) {
returnValue = 1;
count = 0;
}
if (scc == 0) {
/* No more data coming in */
break;
}
}
if (decrypt_input)
c = (*decrypt_input)(c);
switch (telrcv_state) {
case TS_CR:
if (c == '\0') {
break; /* Ignore \0 after CR */
} else if ((c == '\n') &&
TTYADD(c);
break;
}
/* FALLTHROUGH */
case TS_DATA:
if (c == IAC) {
break;
}
/*
* The 'crmod' hack (see following) 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 is done here (sigh).
*/
if ((c == '\r') &&
if (scc > 0) {
c = *sbp&0xff;
if (decrypt_input)
c = (*decrypt_input)(c);
if (c == 0) {
/* a "true" CR */
TTYADD('\r');
} else if (my_want_state_is_dont(
TELOPT_ECHO) && (c == '\n')) {
TTYADD('\n');
} else {
if (decrypt_input)
(*decrypt_input)(-1);
TTYADD('\r');
if (crmod) {
TTYADD('\n');
}
}
} else {
TTYADD('\r');
if (crmod) {
TTYADD('\n');
}
}
} else {
TTYADD(c);
}
continue;
case TS_IAC:
switch (c) {
case WILL:
continue;
case WONT:
continue;
case DO:
continue;
case DONT:
continue;
case DM:
/*
* We may have missed an urgent notification,
* so make sure we flush whatever is in the
* buffer currently.
*/
SYNCHing = 1;
/* This will not return. */
fatal_tty_error("write");
}
break;
case SB:
SB_CLEAR();
continue;
case IAC:
break;
case NOP:
case GA:
default:
break;
}
continue;
case TS_WILL:
willoption(c);
continue;
case TS_WONT:
wontoption(c);
continue;
case TS_DO:
dooption(c);
if (c == TELOPT_NAWS) {
sendnaws();
} else if (c == TELOPT_LFLOW) {
localflow = 1;
setconnmode(0);
}
continue;
case TS_DONT:
dontoption(c);
flushline = 1;
setconnmode(0); /* set new tty mode (maybe) */
continue;
case TS_SB:
if (c == IAC) {
} else {
SB_ACCUM(c);
}
continue;
case TS_SE:
if (c != SE) {
if (c != IAC) {
/*
* This is an error. We only expect to get
* "IAC IAC" or "IAC SE". Several things may
* have happend. An IAC was not doubled, the
* IAC SE was left off, or another option got
* inserted into the suboption are all possibilities.
* If we assume that the IAC was not doubled,
* and really the IAC SE was left off, we could
* get into an infinate loop here. So, instead,
* we terminate the suboption, and process the
* partial suboption if we can.
*/
SB_ACCUM(c);
subpointer -= 2;
SB_TERM();
printoption("In SUBOPTION processing, "
"RCVD", IAC, c);
suboption(); /* handle sub-option */
goto process_iac;
}
SB_ACCUM(c);
} else {
subpointer -= 2;
SB_TERM();
suboption(); /* handle sub-option */
}
}
}
if (count)
return (returnValue||count);
}
int
{
if (local) {
local = 0;
bol = 1;
return (1);
}
return (0);
}
static int
telsnd()
{
int tcc;
int count;
int returnValue = 0;
unsigned char *tbp;
tcc = 0;
count = 0;
while (NETROOM() > 2) {
register int sc;
register int c;
if (tcc == 0) {
if (count) {
returnValue = 1;
count = 0;
}
if (tcc == 0) {
break;
}
}
if (rlogin != _POSIX_VDISABLE) {
if (bol) {
bol = 0;
local = 1;
continue;
}
} else if (local) {
local = 0;
bol = 1;
continue;
}
if (sc == termSuspChar) {
bol = 1;
continue;
}
bol = 1;
tcc = 0;
flushline = 1;
break;
}
++tcc;
--tbp;
--count;
}
}
bol = 1;
/*
* Double escape is a pass through of a single
* escape character.
*/
tbp++;
tcc--;
count++;
bol = 0;
} else {
bol = 1;
tcc = 0;
flushline = 1;
break;
}
} else
bol = 0;
#ifdef KLUDGELINEMODE
} else {
setconnmode(0);
flushline = 1;
break;
}
}
#endif
if (MODE_LOCAL_CHARS(globalmode)) {
if (TerminalSpecialChars(sc) == 0) {
bol = 1;
break;
}
}
if (my_want_state_is_wont(TELOPT_BINARY)) {
switch (c) {
case '\n':
/*
* If we are in CRMOD mode (\r ==> \n)
* on our local machine, then probably
* a newline (unix) is CRLF (TELNET).
*/
if (MODE_LOCAL_CHARS(globalmode)) {
NETADD('\r');
}
NETADD('\n');
break;
case '\r':
if (!crlf) {
} else {
}
break;
case IAC:
break;
default:
NETADD(c);
break;
}
} else if (c == IAC) {
} else {
NETADD(c);
}
}
if (count)
}
/*
* Scheduler()
*
* Try to do something.
*
* If we do something useful, return 1; else return 0.
*
*/
int
int block; /* should we block in the select ? */
{
/*
* One wants to be a bit careful about setting returnValue
* to one, since a one implies we did some useful work,
* and therefore probably won't be called to block next
* time (TN3270 mode only).
*/
int returnValue;
/* Decide which rings should be processed */
(flushline ||
#ifdef KLUDGELINEMODE
#endif
/* XXX */) ||
if (scheduler_lockout_tty) {
}
/* Call to system code to process rings */
!block);
/* Now, look at the input rings, looking for work to do. */
if (ring_full_count(&ttyiring)) {
returnValue |= telsnd();
} else {
/*
* If ttyiring is empty, check to see if there is a real EOF
* pending. If so, we can maybe do the EOF write now.
*/
if (eof_pending) {
eof_pending = 0;
sendeof();
}
}
if (ring_full_count(&netiring)) {
returnValue |= telrcv();
}
return (returnValue);
}
/*
* Select from tty and network...
*/
void
char *user;
{
{
if (!local_host[0]) {
}
}
if (autologin)
if (telnetport || wantencryption) {
}
if (telnetport) {
if (!reqd_linemode)
if (!reqd_linemode)
if (env_getvalue((unsigned char *)"DISPLAY"))
if (eight)
}
/*
* Note: we assume a tie to the authentication option here. This
* is necessary so that authentication fails, we don't spin
* forever.
*/
if (wantencryption) {
extern boolean_t auth_has_failed;
for (;;) {
"\nServer refused to negotiate "
"authentication, which is required\n"
"for encryption. Good-bye.\n\r"));
}
if (auth_has_failed) {
"\nAuthentication negotation has failed, "
"which is required for\n"
"encryption. Good-bye.\n\r"));
}
if (my_want_state_is_dont(TELOPT_ENCRYPT) ||
"\nServer refused to negotiate encryption. "
"Good-bye.\n\r"));
}
if (encrypt_is_encrypting())
break;
"\nEncryption could not be enabled. "
"Good-bye.\n\r"));
}
if (printed_encrypt == B_FALSE) {
"Waiting for encryption to be negotiated...\n"));
/*
* Turn on MODE_TRAPSIG and then turn off localchars
* so that ^C will cause telnet to exit.
*/
intr_waiting = 1;
}
if (intr_happened) {
"\nUser requested an interrupt. Good-bye.\n\r"));
}
telnet_spin();
}
if (printed_encrypt) {
intr_waiting = 0;
setconnmode(0);
}
}
for (;;) {
int schedValue;
while ((schedValue = Scheduler(0)) != 0) {
if (schedValue == -1) {
return;
}
}
return;
}
}
}
#if 0 /* XXX - this not being in is a bug */
/*
* 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.
*/
static char *
char *current;
{
return (current+1);
}
case DO:
case DONT:
case WILL:
case WONT:
return (current+3);
case SB: /* loop forever looking for the SE */
{
for (;;) {
return (look);
}
}
}
}
default:
return (current+2);
}
}
#endif /* 0 */
/*
* 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.
*/
static void
netclear()
{
#if 0 /* XXX */
char *good;
}
int length;
do {
} else {
}
}
#endif /* 0 */
}
/*
* These routines add various telnet commands to the data stream.
*/
/*
* doflush - Send do timing mark (for network connection flush) & then
* get rid of anything in the output buffer. Return -1 if there was a
* non-EWOULDBLOCK error on the tty flush, and otherwise return 0.
*/
static int
doflush()
{
flushline = 1;
flushout = 1;
/* Drop pending tty output */
return (-1);
/* do printoption AFTER flush, otherwise the output gets tossed... */
return (0);
}
int
xmitAO()
{
if (autoflush) {
if (doflush() == -1)
return (-1);
}
return (0);
}
void
xmitEL()
{
}
void
xmitEC()
{
}
int
dosynch()
{
netclear(); /* clear the path to the network */
setneturg();
return (1);
}
int want_status_response = 0;
int
{
register unsigned char *cp;
if (my_want_state_is_dont(TELOPT_STATUS)) {
(void) printf("Remote side does not support STATUS option\n");
return (0);
}
*cp++ = TELOPT_STATUS;
*cp++ = TELQUAL_SEND;
}
return (1);
}
void
intp()
{
flushline = 1;
if (autoflush) {
/* Ignore return as we're ending off anyway. */
(void) doflush();
}
if (autosynch) {
(void) dosynch();
}
}
int
sendbrk()
{
flushline = 1;
if (autoflush) {
if (doflush() == -1)
return (-1);
}
if (autosynch) {
(void) dosynch();
}
return (0);
}
void
{
flushline = 1;
if (autoflush) {
/*
* Since sendabort() gets called while aborting,
* ignore the doflush() return
*/
(void) doflush();
}
if (autosynch) {
(void) dosynch();
}
}
void
sendsusp()
{
flushline = 1;
if (autoflush) {
if (doflush() == -1) {
/* The following will not return. */
fatal_tty_error("write");
}
}
if (autosynch) {
(void) dosynch();
}
}
static void
sendeof()
{
}
/*
* Send a window size update to the remote system.
*/
void
sendnaws()
{
register unsigned char *cp;
if (my_state_is_wont(TELOPT_NAWS))
return;
return;
}
*cp++ = TELOPT_NAWS;
}
}
void
int rw;
{
if (rw&1)
if (rw&2)
}
void
int rw;
{
if (rw&1)
if (rw&2)
}