#include "telnet_locl.h"
RCSID("$Id$");
#define strip(x) (eight ? (x) : ((x) & 0x7f))
static unsigned char subbuffer[SUBBUFSIZE],
*subpointer, *subend;
#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_PEEK() ((*subpointer)&0xff)
#define SB_EOF() (subpointer >= subend)
#define SB_LEN() (subend - subpointer)
char options[256];
char do_dont_resp[256];
char will_wont_resp[256];
int
eight = 3,
binary = 0,
autologin = 0,
skiprc = 0,
connected,
showoptions,
ISend,
debug = 0,
crmod,
netdata,
crlf,
telnetport,
wantencryption = 0,
SYNCHing,
flushout,
autoflush = 0,
autosynch,
localflow,
restartany,
localchars,
donelclchars,
donebinarytoggle,
dontlecho,
globalmode;
char *prompt = 0;
int scheduler_lockout_tty = 0;
cc_t escape;
cc_t rlogin;
#ifdef KLUDGELINEMODE
cc_t echoc;
#endif
#define TS_DATA 0
#define TS_IAC 1
#define TS_WILL 2
#define TS_WONT 3
#define TS_DO 4
#define TS_DONT 5
#define TS_CR 6
#define TS_SB 7
#define TS_SE 8
static int telrcv_state;
#ifdef OLD_ENVIRON
unsigned char telopt_environ = TELOPT_NEW_ENVIRON;
#else
# define telopt_environ TELOPT_NEW_ENVIRON
#endif
jmp_buf toplevel;
jmp_buf peerdied;
int flushline;
int linemode;
#ifdef KLUDGELINEMODE
int kludgelinemode = 1;
#endif
Clocks clocks;
static int is_unique(char *name, char **as, char **ae);
void
init_telnet(void)
{
env_init();
SB_CLEAR();
memset(options, 0, sizeof options);
connected = ISend = localflow = donebinarytoggle = 0;
#if defined(AUTHENTICATION) || defined(ENCRYPTION)
auth_encrypt_connect(connected);
#endif
restartany = -1;
SYNCHing = 0;
escape = CONTROL(']');
rlogin = _POSIX_VDISABLE;
#ifdef KLUDGELINEMODE
echoc = CONTROL('E');
#endif
flushline = 1;
telrcv_state = TS_DATA;
}
void
send_do(int c, int init)
{
if (init) {
if (((do_dont_resp[c] == 0) && my_state_is_do(c)) ||
my_want_state_is_do(c))
return;
set_my_want_state_do(c);
do_dont_resp[c]++;
}
NET2ADD(IAC, DO);
NETADD(c);
printoption("SENT", DO, c);
}
void
send_dont(int c, int init)
{
if (init) {
if (((do_dont_resp[c] == 0) && my_state_is_dont(c)) ||
my_want_state_is_dont(c))
return;
set_my_want_state_dont(c);
do_dont_resp[c]++;
}
NET2ADD(IAC, DONT);
NETADD(c);
printoption("SENT", DONT, c);
}
void
send_will(int c, int init)
{
if (init) {
if (((will_wont_resp[c] == 0) && my_state_is_will(c)) ||
my_want_state_is_will(c))
return;
set_my_want_state_will(c);
will_wont_resp[c]++;
}
NET2ADD(IAC, WILL);
NETADD(c);
printoption("SENT", WILL, c);
}
void
send_wont(int c, int init)
{
if (init) {
if (((will_wont_resp[c] == 0) && my_state_is_wont(c)) ||
my_want_state_is_wont(c))
return;
set_my_want_state_wont(c);
will_wont_resp[c]++;
}
NET2ADD(IAC, WONT);
NETADD(c);
printoption("SENT", WONT, c);
}
void
willoption(int option)
{
int new_state_ok = 0;
if (do_dont_resp[option]) {
--do_dont_resp[option];
if (do_dont_resp[option] && my_state_is_do(option))
--do_dont_resp[option];
}
if ((do_dont_resp[option] == 0) && my_want_state_is_dont(option)) {
switch (option) {
case TELOPT_ECHO:
case TELOPT_BINARY:
case TELOPT_SGA:
settimer(modenegotiated);
case TELOPT_STATUS:
#if defined(AUTHENTICATION)
case TELOPT_AUTHENTICATION:
#endif
#if defined(ENCRYPTION)
case TELOPT_ENCRYPT:
#endif
new_state_ok = 1;
break;
case TELOPT_TM:
if (flushout)
flushout = 0;
set_my_want_state_dont(option);
set_my_state_dont(option);
return;
case TELOPT_LINEMODE:
default:
break;
}
if (new_state_ok) {
set_my_want_state_do(option);
send_do(option, 0);
setconnmode(0);
} else {
do_dont_resp[option]++;
send_dont(option, 0);
}
}
set_my_state_do(option);
#if defined(ENCRYPTION)
if (option == TELOPT_ENCRYPT)
encrypt_send_support();
#endif
}
void
wontoption(int option)
{
if (do_dont_resp[option]) {
--do_dont_resp[option];
if (do_dont_resp[option] && my_state_is_dont(option))
--do_dont_resp[option];
}
if ((do_dont_resp[option] == 0) && my_want_state_is_do(option)) {
switch (option) {
#ifdef KLUDGELINEMODE
case TELOPT_SGA:
if (!kludgelinemode)
break;
#endif
case TELOPT_ECHO:
settimer(modenegotiated);
break;
case TELOPT_TM:
if (flushout)
flushout = 0;
set_my_want_state_dont(option);
set_my_state_dont(option);
return;
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
encrypt_not();
break;
#endif
default:
break;
}
set_my_want_state_dont(option);
if (my_state_is_do(option))
send_dont(option, 0);
setconnmode(0);
} else if (option == TELOPT_TM) {
if (flushout)
flushout = 0;
set_my_want_state_dont(option);
}
set_my_state_dont(option);
}
static void
dooption(int option)
{
int new_state_ok = 0;
if (will_wont_resp[option]) {
--will_wont_resp[option];
if (will_wont_resp[option] && my_state_is_will(option))
--will_wont_resp[option];
}
if (will_wont_resp[option] == 0) {
if (my_want_state_is_wont(option)) {
switch (option) {
case TELOPT_TM:
send_will(option, 0);
set_my_want_state_wont(TELOPT_TM);
set_my_state_wont(TELOPT_TM);
return;
case TELOPT_BINARY:
case TELOPT_NAWS:
case TELOPT_TSPEED:
case TELOPT_LFLOW:
case TELOPT_TTYPE:
case TELOPT_SGA:
#if defined(ENCRYPTION)
case TELOPT_ENCRYPT:
#endif
new_state_ok = 1;
break;
case TELOPT_NEW_ENVIRON:
#ifdef OLD_ENVIRON
if (my_state_is_will(TELOPT_OLD_ENVIRON))
send_wont(TELOPT_OLD_ENVIRON, 1);
goto env_common;
case TELOPT_OLD_ENVIRON:
if (my_state_is_will(TELOPT_NEW_ENVIRON))
break;
env_common:
telopt_environ = option;
#endif
new_state_ok = 1;
break;
#if defined(AUTHENTICATION)
case TELOPT_AUTHENTICATION:
if (autologin)
new_state_ok = 1;
break;
#endif
case TELOPT_XDISPLOC:
if (env_getvalue((unsigned char *)"DISPLAY"))
new_state_ok = 1;
break;
case TELOPT_LINEMODE:
#ifdef KLUDGELINEMODE
kludgelinemode = 0;
send_do(TELOPT_SGA, 1);
#endif
set_my_want_state_will(TELOPT_LINEMODE);
send_will(option, 0);
set_my_state_will(TELOPT_LINEMODE);
slc_init();
return;
case TELOPT_ECHO:
default:
break;
}
if (new_state_ok) {
set_my_want_state_will(option);
send_will(option, 0);
setconnmode(0);
} else {
will_wont_resp[option]++;
send_wont(option, 0);
}
} else {
switch (option) {
case TELOPT_LINEMODE:
#ifdef KLUDGELINEMODE
kludgelinemode = 0;
send_do(TELOPT_SGA, 1);
#endif
set_my_state_will(option);
slc_init();
send_do(TELOPT_SGA, 0);
return;
}
}
}
set_my_state_will(option);
}
static void
dontoption(int option)
{
if (will_wont_resp[option]) {
--will_wont_resp[option];
if (will_wont_resp[option] && my_state_is_wont(option))
--will_wont_resp[option];
}
if ((will_wont_resp[option] == 0) && my_want_state_is_will(option)) {
switch (option) {
case TELOPT_LINEMODE:
linemode = 0;
break;
#ifdef OLD_ENVIRON
case TELOPT_NEW_ENVIRON:
send_will(TELOPT_OLD_ENVIRON, 1);
telopt_environ = TELOPT_OLD_ENVIRON;
break;
#endif
#if 0
#ifdef ENCRYPTION
case TELOPT_ENCRYPT:
encrypt_not();
break;
#endif
#endif
}
set_my_want_state_wont(option);
if (my_state_is_will(option))
send_wont(option, 0);
setconnmode(0);
}
set_my_state_wont(option);
}
static char *name_unknown = "UNKNOWN";
static char *unknown[] = { 0, 0 };
static char **
mklist(char *buf, char *name)
{
int n;
char c, *cp, **argvp, *cp2, **argv, **avt;
if (name) {
if ((int)strlen(name) > 40) {
name = 0;
unknown[0] = name_unknown;
} else {
unknown[0] = name;
strupr(name);
}
} else
unknown[0] = name_unknown;
for (n = 1, cp = buf; *cp && *cp != ':'; cp++) {
if (*cp == '|')
n++;
}
argv = (char **)malloc((n+3)*sizeof(char *));
if (argv == 0)
return(unknown);
*argv = 0;
argvp = argv+1;
n = 0;
for (cp = cp2 = buf; (c = *cp); cp++) {
if (c == '|' || c == ':') {
*cp++ = '\0';
if (n || (cp - cp2 > 41))
;
else if (name && (strncasecmp(name, cp2, cp-cp2) == 0))
*argv = cp2;
else if (is_unique(cp2, argv+1, argvp))
*argvp++ = cp2;
if (c == ':')
break;
while ((c = *cp) == '|')
cp++;
cp2 = cp;
n = 0;
}
#undef ISASCII
#define ISASCII(c) (!((c)&0x80))
if ((c == ' ') || !ISASCII(c))
n = 1;
else if (islower((unsigned char)c))
*cp = toupper((unsigned char)c);
}
if ((argv[1] == buf) && (strlen(argv[1]) == 2)) {
--argvp;
for (avt = &argv[1]; avt < argvp; avt++)
*avt = *(avt+1);
*argvp++ = buf;
}
cp = *(argvp-1);
*argvp++ = cp;
*argvp = 0;
if (*argv == 0) {
if (name)
*argv = name;
else {
--argvp;
for (avt = argv; avt < argvp; avt++)
*avt = *(avt+1);
}
}
if (*argv)
return(argv);
else
return(unknown);
}
static int
is_unique(char *name, char **as, char **ae)
{
char **ap;
int n;
n = strlen(name) + 1;
for (ap = as; ap < ae; ap++)
if (strncasecmp(*ap, name, n) == 0)
return(0);
return (1);
}
static char termbuf[1024];
static int
telnet_setupterm(const char *tname, int fd, int *errp)
{
#ifdef HAVE_TGETENT
if (tgetent(termbuf, tname) == 1) {
termbuf[1023] = '\0';
if (errp)
*errp = 1;
return(0);
}
if (errp)
*errp = 0;
return(-1);
#else
strlcpy(termbuf, tname, sizeof(termbuf));
if(errp) *errp = 1;
return 0;
#endif
}
int resettermname = 1;
static char *
gettermname()
{
char *tname;
static char **tnamep = 0;
static char **next;
int err;
if (resettermname) {
resettermname = 0;
if (tnamep && tnamep != unknown)
free(tnamep);
if ((tname = (char *)env_getvalue((unsigned char *)"TERM")) &&
telnet_setupterm(tname, 1, &err) == 0) {
tnamep = mklist(termbuf, tname);
} else {
if (tname && ((int)strlen(tname) <= 40)) {
unknown[0] = tname;
strupr(tname);
} else
unknown[0] = name_unknown;
tnamep = unknown;
}
next = tnamep;
}
if (*next == 0)
next = tnamep;
return(*next++);
}
static void
suboption()
{
unsigned char subchar;
printsub('<', subbuffer, SB_LEN()+2);
switch (subchar = SB_GET()) {
case TELOPT_TTYPE:
if (my_want_state_is_wont(TELOPT_TTYPE))
return;
if (SB_EOF() || SB_GET() != TELQUAL_SEND) {
return;
} else {
char *name;
unsigned char temp[50];
int len;
name = gettermname();
len = strlen(name) + 4 + 2;
if (len < NETROOM()) {
snprintf((char *)temp, sizeof(temp),
"%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
TELQUAL_IS, name, IAC, SE);
ring_supply_data(&netoring, temp, len);
printsub('>', &temp[2], len-2);
} else {
ExitString("No room in buffer for terminal type.\n", 1);
}
}
break;
case TELOPT_TSPEED:
if (my_want_state_is_wont(TELOPT_TSPEED))
return;
if (SB_EOF())
return;
if (SB_GET() == TELQUAL_SEND) {
long output_speed, input_speed;
unsigned char temp[50];
int len;
TerminalSpeeds(&input_speed, &output_speed);
snprintf((char *)temp, sizeof(temp),
"%c%c%c%c%u,%u%c%c", IAC, SB, TELOPT_TSPEED,
TELQUAL_IS,
(unsigned)output_speed,
(unsigned)input_speed, IAC, SE);
len = strlen((char *)temp+4) + 4;
if (len < NETROOM()) {
ring_supply_data(&netoring, temp, len);
printsub('>', temp+2, len - 2);
}
else printf("lm_will: not enough room in buffer\n");
}
break;
case TELOPT_LFLOW:
if (my_want_state_is_wont(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;
}
setcommandmode();
setconnmode(0);
break;
case TELOPT_LINEMODE:
if (my_want_state_is_wont(TELOPT_LINEMODE))
return;
if (SB_EOF())
return;
switch (SB_GET()) {
case WILL:
lm_will(subpointer, SB_LEN());
break;
case WONT:
lm_wont(subpointer, SB_LEN());
break;
case DO:
lm_do(subpointer, SB_LEN());
break;
case DONT:
lm_dont(subpointer, SB_LEN());
break;
case LM_SLC:
slc(subpointer, SB_LEN());
break;
case LM_MODE:
lm_mode(subpointer, SB_LEN(), 0);
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;
}
env_opt(subpointer, SB_LEN());
break;
case TELOPT_XDISPLOC:
if (my_want_state_is_wont(TELOPT_XDISPLOC))
return;
if (SB_EOF())
return;
if (SB_GET() == TELQUAL_SEND) {
unsigned char temp[50], *dp;
int len;
if ((dp = env_getvalue((unsigned char *)"DISPLAY")) == NULL) {
send_wont(TELOPT_XDISPLOC, 1);
break;
}
snprintf((char *)temp, sizeof(temp),
"%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
TELQUAL_IS, dp, IAC, SE);
len = strlen((char *)temp+4) + 4;
if (len < NETROOM()) {
ring_supply_data(&netoring, temp, len);
printsub('>', temp+2, len - 2);
}
else printf("lm_will: not enough room in buffer\n");
}
break;
#if defined(AUTHENTICATION)
case TELOPT_AUTHENTICATION: {
if (!autologin)
break;
if (SB_EOF())
return;
switch(SB_GET()) {
case TELQUAL_IS:
if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
return;
auth_is(subpointer, SB_LEN());
break;
case TELQUAL_SEND:
if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
return;
auth_send(subpointer, SB_LEN());
break;
case TELQUAL_REPLY:
if (my_want_state_is_wont(TELOPT_AUTHENTICATION))
return;
auth_reply(subpointer, SB_LEN());
break;
case TELQUAL_NAME:
if (my_want_state_is_dont(TELOPT_AUTHENTICATION))
return;
auth_name(subpointer, SB_LEN());
break;
}
}
break;
#endif
#if defined(ENCRYPTION)
case TELOPT_ENCRYPT:
if (SB_EOF())
return;
switch(SB_GET()) {
case ENCRYPT_START:
if (my_want_state_is_dont(TELOPT_ENCRYPT))
return;
encrypt_start(subpointer, SB_LEN());
break;
case ENCRYPT_END:
if (my_want_state_is_dont(TELOPT_ENCRYPT))
return;
encrypt_end();
break;
case ENCRYPT_SUPPORT:
if (my_want_state_is_wont(TELOPT_ENCRYPT))
return;
encrypt_support(subpointer, SB_LEN());
break;
case ENCRYPT_REQSTART:
if (my_want_state_is_wont(TELOPT_ENCRYPT))
return;
encrypt_request_start(subpointer, SB_LEN());
break;
case ENCRYPT_REQEND:
if (my_want_state_is_wont(TELOPT_ENCRYPT))
return;
encrypt_request_end();
break;
case ENCRYPT_IS:
if (my_want_state_is_dont(TELOPT_ENCRYPT))
return;
encrypt_is(subpointer, SB_LEN());
break;
case ENCRYPT_REPLY:
if (my_want_state_is_wont(TELOPT_ENCRYPT))
return;
encrypt_reply(subpointer, SB_LEN());
break;
case ENCRYPT_ENC_KEYID:
if (my_want_state_is_dont(TELOPT_ENCRYPT))
return;
encrypt_enc_keyid(subpointer, SB_LEN());
break;
case ENCRYPT_DEC_KEYID:
if (my_want_state_is_wont(TELOPT_ENCRYPT))
return;
encrypt_dec_keyid(subpointer, SB_LEN());
break;
default:
break;
}
break;
#endif
default:
break;
}
}
static unsigned char str_lm[] = { IAC, SB, TELOPT_LINEMODE, 0, 0, IAC, SE };
void
lm_will(unsigned char *cmd, int len)
{
if (len < 1) {
printf("lm_will: no command!!!\n");
return;
}
switch(cmd[0]) {
case LM_FORWARDMASK:
default:
str_lm[3] = DONT;
str_lm[4] = cmd[0];
if (NETROOM() > sizeof(str_lm)) {
ring_supply_data(&netoring, str_lm, sizeof(str_lm));
printsub('>', &str_lm[2], sizeof(str_lm)-2);
}
else printf("lm_will: not enough room in buffer\n");
break;
}
}
void
lm_wont(unsigned char *cmd, int len)
{
if (len < 1) {
printf("lm_wont: no command!!!\n");
return;
}
switch(cmd[0]) {
case LM_FORWARDMASK:
default:
return;
}
}
void
lm_do(unsigned char *cmd, int len)
{
if (len < 1) {
printf("lm_do: no command!!!\n");
return;
}
switch(cmd[0]) {
case LM_FORWARDMASK:
default:
str_lm[3] = WONT;
str_lm[4] = cmd[0];
if (NETROOM() > sizeof(str_lm)) {
ring_supply_data(&netoring, str_lm, sizeof(str_lm));
printsub('>', &str_lm[2], sizeof(str_lm)-2);
}
else printf("lm_do: not enough room in buffer\n");
break;
}
}
void
lm_dont(unsigned char *cmd, int len)
{
if (len < 1) {
printf("lm_dont: no command!!!\n");
return;
}
switch(cmd[0]) {
case LM_FORWARDMASK:
default:
break;
}
}
static unsigned char str_lm_mode[] = {
IAC, SB, TELOPT_LINEMODE, LM_MODE, 0, IAC, SE
};
void
lm_mode(unsigned char *cmd, int len, int init)
{
if (len != 1)
return;
if ((linemode&MODE_MASK&~MODE_ACK) == *cmd)
return;
if (*cmd&MODE_ACK)
return;
linemode = *cmd&(MODE_MASK&~MODE_ACK);
str_lm_mode[4] = linemode;
if (!init)
str_lm_mode[4] |= MODE_ACK;
if (NETROOM() > sizeof(str_lm_mode)) {
ring_supply_data(&netoring, str_lm_mode, sizeof(str_lm_mode));
printsub('>', &str_lm_mode[2], sizeof(str_lm_mode)-2);
}
else printf("lm_mode: not enough room in buffer\n");
setconnmode(0);
}
struct spc {
cc_t val;
cc_t *valp;
char flags;
char mylevel;
} spc_data[NSLC+1];
#define SLC_IMPORT 0
#define SLC_EXPORT 1
#define SLC_RVALUE 2
static int slc_mode = SLC_EXPORT;
void
slc_init()
{
struct spc *spcp;
localchars = 1;
for (spcp = spc_data; spcp < &spc_data[NSLC+1]; spcp++) {
spcp->val = 0;
spcp->valp = 0;
spcp->flags = spcp->mylevel = SLC_NOSUPPORT;
}
#define initfunc(func, flags) { \
spcp = &spc_data[func]; \
if ((spcp->valp = tcval(func))) { \
spcp->val = *spcp->valp; \
spcp->mylevel = SLC_VARIABLE|flags; \
} else { \
spcp->val = 0; \
spcp->mylevel = SLC_DEFAULT; \
} \
}
initfunc(SLC_SYNCH, 0);
initfunc(SLC_AO, 0);
initfunc(SLC_AYT, 0);
initfunc(SLC_ABORT, SLC_FLUSHIN|SLC_FLUSHOUT);
initfunc(SLC_EOF, 0);
initfunc(SLC_SUSP, SLC_FLUSHIN);
initfunc(SLC_EC, 0);
initfunc(SLC_EL, 0);
initfunc(SLC_EW, 0);
initfunc(SLC_RP, 0);
initfunc(SLC_LNEXT, 0);
initfunc(SLC_XON, 0);
initfunc(SLC_XOFF, 0);
initfunc(SLC_FORW1, 0);
initfunc(SLC_FORW2, 0);
initfunc(SLC_IP, SLC_FLUSHIN|SLC_FLUSHOUT);
#undef initfunc
if (slc_mode == SLC_EXPORT)
slc_export();
else
slc_import(1);
}
void
slcstate()
{
printf("Special characters are %s values\n",
slc_mode == SLC_IMPORT ? "remote default" :
slc_mode == SLC_EXPORT ? "local" :
"remote");
}
void
slc_mode_export()
{
slc_mode = SLC_EXPORT;
if (my_state_is_will(TELOPT_LINEMODE))
slc_export();
}
void
slc_mode_import(int def)
{
slc_mode = def ? SLC_IMPORT : SLC_RVALUE;
if (my_state_is_will(TELOPT_LINEMODE))
slc_import(def);
}
unsigned char slc_import_val[] = {
IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_VARIABLE, 0, IAC, SE
};
unsigned char slc_import_def[] = {
IAC, SB, TELOPT_LINEMODE, LM_SLC, 0, SLC_DEFAULT, 0, IAC, SE
};
void
slc_import(int def)
{
if (NETROOM() > sizeof(slc_import_val)) {
if (def) {
ring_supply_data(&netoring, slc_import_def, sizeof(slc_import_def));
printsub('>', &slc_import_def[2], sizeof(slc_import_def)-2);
} else {
ring_supply_data(&netoring, slc_import_val, sizeof(slc_import_val));
printsub('>', &slc_import_val[2], sizeof(slc_import_val)-2);
}
}
else printf("slc_import: not enough room\n");
}
void
slc_export()
{
struct spc *spcp;
TerminalDefaultChars();
slc_start_reply();
for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
if (spcp->mylevel != SLC_NOSUPPORT) {
if (spcp->val == (cc_t)(_POSIX_VDISABLE))
spcp->flags = SLC_NOSUPPORT;
else
spcp->flags = spcp->mylevel;
if (spcp->valp)
spcp->val = *spcp->valp;
slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
}
}
slc_end_reply();
slc_update();
setconnmode(1);
}
void
slc(unsigned char *cp, int len)
{
struct spc *spcp;
int func,level;
slc_start_reply();
for (; len >= 3; len -=3, cp +=3) {
func = cp[SLC_FUNC];
if (func == 0) {
continue;
}
if (func > NSLC) {
if ((cp[SLC_FLAGS] & SLC_LEVELBITS) != SLC_NOSUPPORT)
slc_add_reply(func, SLC_NOSUPPORT, 0);
continue;
}
spcp = &spc_data[func];
level = cp[SLC_FLAGS]&(SLC_LEVELBITS|SLC_ACK);
if ((cp[SLC_VALUE] == (unsigned char)spcp->val) &&
((level&SLC_LEVELBITS) == (spcp->flags&SLC_LEVELBITS))) {
continue;
}
if (level == (SLC_DEFAULT|SLC_ACK)) {
cp[SLC_FLAGS] &= ~SLC_ACK;
}
if (level == ((spcp->flags&SLC_LEVELBITS)|SLC_ACK)) {
spcp->val = (cc_t)cp[SLC_VALUE];
spcp->flags = cp[SLC_FLAGS];
continue;
}
level &= ~SLC_ACK;
if (level <= (spcp->mylevel&SLC_LEVELBITS)) {
spcp->flags = cp[SLC_FLAGS]|SLC_ACK;
spcp->val = (cc_t)cp[SLC_VALUE];
}
if (level == SLC_DEFAULT) {
if ((spcp->mylevel&SLC_LEVELBITS) != SLC_DEFAULT)
spcp->flags = spcp->mylevel;
else
spcp->flags = SLC_NOSUPPORT;
}
slc_add_reply(func, spcp->flags, spcp->val);
}
slc_end_reply();
if (slc_update())
setconnmode(1);
}
void
slc_check()
{
struct spc *spcp;
slc_start_reply();
for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
if (spcp->valp && spcp->val != *spcp->valp) {
spcp->val = *spcp->valp;
if (spcp->val == (cc_t)(_POSIX_VDISABLE))
spcp->flags = SLC_NOSUPPORT;
else
spcp->flags = spcp->mylevel;
slc_add_reply(spcp - spc_data, spcp->flags, spcp->val);
}
}
slc_end_reply();
setconnmode(1);
}
unsigned char slc_reply[128];
unsigned char const * const slc_reply_eom = &slc_reply[sizeof(slc_reply)];
unsigned char *slc_replyp;
void
slc_start_reply()
{
slc_replyp = slc_reply;
*slc_replyp++ = IAC;
*slc_replyp++ = SB;
*slc_replyp++ = TELOPT_LINEMODE;
*slc_replyp++ = LM_SLC;
}
void
slc_add_reply(unsigned char func, unsigned char flags, cc_t value)
{
if (&slc_replyp[6+2] > slc_reply_eom)
return;
if ((*slc_replyp++ = func) == IAC)
*slc_replyp++ = IAC;
if ((*slc_replyp++ = flags) == IAC)
*slc_replyp++ = IAC;
if ((*slc_replyp++ = (unsigned char)value) == IAC)
*slc_replyp++ = IAC;
}
void
slc_end_reply()
{
int len;
if (&slc_replyp[2] > slc_reply_eom)
return;
*slc_replyp++ = IAC;
*slc_replyp++ = SE;
len = slc_replyp - slc_reply;
if (len <= 6)
return;
if (NETROOM() > len) {
ring_supply_data(&netoring, slc_reply, slc_replyp - slc_reply);
printsub('>', &slc_reply[2], slc_replyp - slc_reply - 2);
}
else printf("slc_end_reply: not enough room\n");
}
int
slc_update()
{
struct spc *spcp;
int need_update = 0;
for (spcp = &spc_data[1]; spcp < &spc_data[NSLC+1]; spcp++) {
if (!(spcp->flags&SLC_ACK))
continue;
spcp->flags &= ~SLC_ACK;
if (spcp->valp && (*spcp->valp != spcp->val)) {
*spcp->valp = spcp->val;
need_update = 1;
}
}
return(need_update);
}
#ifdef OLD_ENVIRON
# define old_env_var OLD_ENV_VAR
# define old_env_value OLD_ENV_VALUE
#endif
void
env_opt(unsigned char *buf, int len)
{
unsigned char *ep = 0, *epc = 0;
int i;
switch(buf[0]&0xff) {
case TELQUAL_SEND:
env_opt_start();
if (len == 1) {
env_opt_add(NULL);
} else for (i = 1; i < len; i++) {
switch (buf[i]&0xff) {
#ifdef OLD_ENVIRON
case OLD_ENV_VAR:
case OLD_ENV_VALUE:
#else
case NEW_ENV_VAR:
#endif
case ENV_USERVAR:
if (ep) {
*epc = 0;
env_opt_add(ep);
}
ep = epc = &buf[i+1];
break;
case ENV_ESC:
i++;
default:
if (epc)
*epc++ = buf[i];
break;
}
}
if (ep) {
*epc = 0;
env_opt_add(ep);
}
env_opt_end(1);
break;
case TELQUAL_IS:
case TELQUAL_INFO:
break;
default:
break;
}
}
#define OPT_REPLY_SIZE (2 * SUBBUFSIZE)
unsigned char *opt_reply;
unsigned char *opt_replyp;
unsigned char *opt_replyend;
void
env_opt_start()
{
if (opt_reply) {
void *tmp = realloc (opt_reply, OPT_REPLY_SIZE);
if (tmp != NULL) {
opt_reply = tmp;
} else {
free (opt_reply);
opt_reply = NULL;
}
} else
opt_reply = (unsigned char *)malloc(OPT_REPLY_SIZE);
if (opt_reply == NULL) {
printf("env_opt_start: malloc()/realloc() failed!!!\n");
opt_reply = opt_replyp = opt_replyend = NULL;
return;
}
opt_replyp = opt_reply;
opt_replyend = opt_reply + OPT_REPLY_SIZE;
*opt_replyp++ = IAC;
*opt_replyp++ = SB;
*opt_replyp++ = telopt_environ;
*opt_replyp++ = TELQUAL_IS;
}
void
env_opt_start_info()
{
env_opt_start();
if (opt_replyp)
opt_replyp[-1] = TELQUAL_INFO;
}
void
env_opt_add(unsigned char *ep)
{
unsigned char *vp, c;
if (opt_reply == NULL)
return;
if (ep == NULL || *ep == '\0') {
env_default(1, 0);
while ((ep = env_default(0, 0)))
env_opt_add(ep);
env_default(1, 1);
while ((ep = env_default(0, 1)))
env_opt_add(ep);
return;
}
vp = env_getvalue(ep);
if (opt_replyp + (vp ? 2 * strlen((char *)vp) : 0) +
2 * strlen((char *)ep) + 6 > opt_replyend)
{
int len;
void *tmp;
opt_replyend += OPT_REPLY_SIZE;
len = opt_replyend - opt_reply;
tmp = realloc(opt_reply, len);
if (tmp == NULL) {
printf("env_opt_add: realloc() failed!!!\n");
opt_reply = opt_replyp = opt_replyend = NULL;
return;
}
opt_reply = tmp;
opt_replyp = opt_reply + len - (opt_replyend - opt_replyp);
opt_replyend = opt_reply + len;
}
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 (;;) {
while ((c = *ep++)) {
if (opt_replyp + (2 + 2) > opt_replyend)
return;
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;
}
if ((ep = vp)) {
if (opt_replyp + (1 + 2 + 2) > opt_replyend)
return;
#ifdef OLD_ENVIRON
if (telopt_environ == TELOPT_OLD_ENVIRON)
*opt_replyp++ = old_env_value;
else
#endif
*opt_replyp++ = NEW_ENV_VALUE;
vp = NULL;
} else
break;
}
}
int
opt_welldefined(char *ep)
{
if ((strcmp(ep, "USER") == 0) ||
(strcmp(ep, "DISPLAY") == 0) ||
(strcmp(ep, "PRINTER") == 0) ||
(strcmp(ep, "SYSTEMTYPE") == 0) ||
(strcmp(ep, "JOB") == 0) ||
(strcmp(ep, "ACCT") == 0))
return(1);
return(0);
}
void
env_opt_end(int emptyok)
{
int len;
if (opt_replyp + 2 > opt_replyend)
return;
len = opt_replyp + 2 - opt_reply;
if (emptyok || len > 6) {
*opt_replyp++ = IAC;
*opt_replyp++ = SE;
if (NETROOM() > len) {
ring_supply_data(&netoring, opt_reply, len);
printsub('>', &opt_reply[2], len - 2);
}
else printf("slc_end_reply: not enough room\n");
}
if (opt_reply) {
free(opt_reply);
opt_reply = opt_replyp = opt_replyend = NULL;
}
}
int
telrcv(void)
{
int c;
int scc;
unsigned char *sbp = NULL;
int count;
int returnValue = 0;
scc = 0;
count = 0;
while (TTYROOM() > 2) {
if (scc == 0) {
if (count) {
ring_consumed(&netiring, count);
returnValue = 1;
count = 0;
}
sbp = netiring.consume;
scc = ring_full_consecutive(&netiring);
if (scc == 0) {
break;
}
}
c = *sbp++ & 0xff, scc--; count++;
#if defined(ENCRYPTION)
if (decrypt_input)
c = (*decrypt_input)(c);
#endif
switch (telrcv_state) {
case TS_CR:
telrcv_state = TS_DATA;
if (c == '\0') {
break;
}
else if ((c == '\n') && my_want_state_is_dont(TELOPT_ECHO) && !crmod) {
TTYADD(c);
break;
}
case TS_DATA:
if (c == IAC) {
telrcv_state = TS_IAC;
break;
}
if ((c == '\r') && my_want_state_is_dont(TELOPT_BINARY)) {
if (scc > 0) {
c = *sbp&0xff;
#if defined(ENCRYPTION)
if (decrypt_input)
c = (*decrypt_input)(c);
#endif
if (c == 0) {
sbp++, scc--; count++;
TTYADD('\r');
} else if (my_want_state_is_dont(TELOPT_ECHO) &&
(c == '\n')) {
sbp++, scc--; count++;
TTYADD('\n');
} else {
#if defined(ENCRYPTION)
if (decrypt_input)
(*decrypt_input)(-1);
#endif
TTYADD('\r');
if (crmod) {
TTYADD('\n');
}
}
} else {
telrcv_state = TS_CR;
TTYADD('\r');
if (crmod) {
TTYADD('\n');
}
}
} else {
TTYADD(c);
}
continue;
case TS_IAC:
process_iac:
switch (c) {
case WILL:
telrcv_state = TS_WILL;
continue;
case WONT:
telrcv_state = TS_WONT;
continue;
case DO:
telrcv_state = TS_DO;
continue;
case DONT:
telrcv_state = TS_DONT;
continue;
case DM:
printoption("RCVD", IAC, DM);
SYNCHing = 1;
ttyflush(1);
SYNCHing = stilloob();
settimer(gotDM);
break;
case SB:
SB_CLEAR();
telrcv_state = TS_SB;
continue;
case IAC:
TTYADD(IAC);
break;
case NOP:
case GA:
default:
printoption("RCVD", IAC, c);
break;
}
telrcv_state = TS_DATA;
continue;
case TS_WILL:
printoption("RCVD", WILL, c);
willoption(c);
telrcv_state = TS_DATA;
continue;
case TS_WONT:
printoption("RCVD", WONT, c);
wontoption(c);
telrcv_state = TS_DATA;
continue;
case TS_DO:
printoption("RCVD", DO, c);
dooption(c);
if (c == TELOPT_NAWS) {
sendnaws();
} else if (c == TELOPT_LFLOW) {
localflow = 1;
setcommandmode();
setconnmode(0);
}
telrcv_state = TS_DATA;
continue;
case TS_DONT:
printoption("RCVD", DONT, c);
dontoption(c);
flushline = 1;
setconnmode(0);
telrcv_state = TS_DATA;
continue;
case TS_SB:
if (c == IAC) {
telrcv_state = TS_SE;
} else {
SB_ACCUM(c);
}
continue;
case TS_SE:
if (c != SE) {
if (c != IAC) {
SB_ACCUM(IAC);
SB_ACCUM(c);
subpointer -= 2;
SB_TERM();
printoption("In SUBOPTION processing, RCVD", IAC, c);
suboption();
telrcv_state = TS_IAC;
goto process_iac;
}
SB_ACCUM(c);
telrcv_state = TS_SB;
} else {
SB_ACCUM(IAC);
SB_ACCUM(SE);
subpointer -= 2;
SB_TERM();
suboption();
telrcv_state = TS_DATA;
}
}
}
if (count)
ring_consumed(&netiring, count);
return returnValue||count;
}
static int bol = 1, local = 0;
int
rlogin_susp(void)
{
if (local) {
local = 0;
bol = 1;
command(0, "z\n", 2);
return(1);
}
return(0);
}
static int
telsnd()
{
int tcc;
int count;
int returnValue = 0;
unsigned char *tbp = NULL;
tcc = 0;
count = 0;
while (NETROOM() > 2) {
int sc;
int c;
if (tcc == 0) {
if (count) {
ring_consumed(&ttyiring, count);
returnValue = 1;
count = 0;
}
tbp = ttyiring.consume;
tcc = ring_full_consecutive(&ttyiring);
if (tcc == 0) {
break;
}
}
c = *tbp++ & 0xff, sc = strip(c), tcc--; count++;
if (rlogin != _POSIX_VDISABLE) {
if (bol) {
bol = 0;
if (sc == rlogin) {
local = 1;
continue;
}
} else if (local) {
local = 0;
if (sc == '.' || c == termEofChar) {
bol = 1;
command(0, "close\n", 6);
continue;
}
if (sc == termSuspChar) {
bol = 1;
command(0, "z\n", 2);
continue;
}
if (sc == escape) {
command(0, (char *)tbp, tcc);
bol = 1;
count += tcc;
tcc = 0;
flushline = 1;
break;
}
if (sc != rlogin) {
++tcc;
--tbp;
--count;
c = sc = rlogin;
}
}
if ((sc == '\n') || (sc == '\r'))
bol = 1;
} else if (sc == escape) {
if (tcc && strip(*tbp) == escape) {
tbp++;
tcc--;
count++;
bol = 0;
} else {
command(0, (char *)tbp, tcc);
bol = 1;
count += tcc;
tcc = 0;
flushline = 1;
break;
}
} else
bol = 0;
#ifdef KLUDGELINEMODE
if (kludgelinemode && (globalmode&MODE_EDIT) && (sc == echoc)) {
if (tcc > 0 && strip(*tbp) == echoc) {
tcc--; tbp++; count++;
} else {
dontlecho = !dontlecho;
settimer(echotoggle);
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 (MODE_LOCAL_CHARS(globalmode)) {
NETADD('\r');
}
NETADD('\n');
bol = flushline = 1;
break;
case '\r':
if (!crlf) {
NET2ADD('\r', '\0');
} else {
NET2ADD('\r', '\n');
}
bol = flushline = 1;
break;
case IAC:
NET2ADD(IAC, IAC);
break;
default:
NETADD(c);
break;
}
} else if (c == IAC) {
NET2ADD(IAC, IAC);
} else {
NETADD(c);
}
}
if (count)
ring_consumed(&ttyiring, count);
return returnValue||count;
}
int
Scheduler(int block)
{
int returnValue;
int netin, netout, netex, ttyin, ttyout;
netout = ring_full_count(&netoring) &&
(flushline ||
(my_want_state_is_wont(TELOPT_LINEMODE)
#ifdef KLUDGELINEMODE
&& (!kludgelinemode || my_want_state_is_do(TELOPT_SGA))
#endif
) ||
my_want_state_is_will(TELOPT_BINARY));
ttyout = ring_full_count(&ttyoring);
ttyin = ring_empty_count(&ttyiring);
netin = !ISend && ring_empty_count(&netiring);
netex = !SYNCHing;
if (scheduler_lockout_tty) {
ttyin = ttyout = 0;
}
returnValue = process_rings(netin, netout, netex, ttyin, ttyout, !block);
if (ring_full_count(&ttyiring)) {
returnValue |= telsnd();
}
if (ring_full_count(&netiring)) {
returnValue |= telrcv();
}
return returnValue;
}
extern int auth_has_failed;
void
my_telnet(char *user)
{
int printed_encrypt = 0;
sys_telnet_init();
#if defined(AUTHENTICATION) || defined(ENCRYPTION)
{
static char local_host[256] = { 0 };
if (!local_host[0]) {
gethostname(local_host, sizeof(local_host));
local_host[sizeof(local_host)-1] = 0;
}
auth_encrypt_init(local_host, hostname, "TELNET", 0);
auth_encrypt_user(user);
}
#endif
if (telnetport) {
#if defined(AUTHENTICATION)
if (autologin)
send_will(TELOPT_AUTHENTICATION, 1);
#endif
#if defined(ENCRYPTION)
send_do(TELOPT_ENCRYPT, 1);
send_will(TELOPT_ENCRYPT, 1);
#endif
send_do(TELOPT_SGA, 1);
send_will(TELOPT_TTYPE, 1);
send_will(TELOPT_NAWS, 1);
send_will(TELOPT_TSPEED, 1);
send_will(TELOPT_LFLOW, 1);
send_will(TELOPT_LINEMODE, 1);
send_will(TELOPT_NEW_ENVIRON, 1);
send_do(TELOPT_STATUS, 1);
if (env_getvalue((unsigned char *)"DISPLAY"))
send_will(TELOPT_XDISPLOC, 1);
if (binary)
tel_enter_binary(binary);
}
#ifdef ENCRYPTION
if (telnetport && wantencryption) {
time_t timeout = time(0) + 60;
send_do(TELOPT_ENCRYPT, 1);
send_will(TELOPT_ENCRYPT, 1);
while (1) {
if (my_want_state_is_wont(TELOPT_AUTHENTICATION)) {
if (wantencryption == -1) {
break;
} else {
printf("\nServer refused to negotiate authentication,\n");
printf("which is required for encryption.\n");
Exit(1);
}
}
if (auth_has_failed) {
printf("\nAuthentication negotiation has failed,\n");
printf("which is required for encryption.\n");
Exit(1);
}
if (my_want_state_is_dont(TELOPT_ENCRYPT) ||
my_want_state_is_wont(TELOPT_ENCRYPT)) {
printf("\nServer refused to negotiate encryption.\n");
Exit(1);
}
if (encrypt_is_encrypting())
break;
if (time(0) > timeout) {
printf("\nEncryption could not be enabled.\n");
Exit(1);
}
if (printed_encrypt == 0) {
printed_encrypt = 1;
printf("Waiting for encryption to be negotiated...\n");
TerminalNewMode(getconnmode()|MODE_TRAPSIG);
intr_waiting = 1;
}
if (intr_happened) {
printf("\nUser interrupt.\n");
Exit(1);
}
if (telnet_spin()) {
printf("\nServer disconnected.\n");
Exit(1);
}
}
if (printed_encrypt) {
printf("Encryption negotiated.\n");
intr_waiting = 0;
setconnmode(0);
}
}
#endif
for (;;) {
int schedValue;
while ((schedValue = Scheduler(0)) != 0) {
if (schedValue == -1) {
setcommandmode();
return;
}
}
if (Scheduler(1) == -1) {
setcommandmode();
return;
}
}
}
static void
netclear()
{
#if 0
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)) <= netobuf.send) {
thisitem = next;
}
good = netobuf;
while (netoring.add > thisitem) {
if (wewant(thisitem)) {
int length;
next = thisitem;
do {
next = nextitem(next);
} while (wewant(next) && (nfrontp > next));
length = next-thisitem;
memmove(good, thisitem, length);
good += length;
thisitem = next;
} else {
thisitem = nextitem(thisitem);
}
}
#endif
}
static void
doflush()
{
NET2ADD(IAC, DO);
NETADD(TELOPT_TM);
flushline = 1;
flushout = 1;
ttyflush(1);
printoption("SENT", DO, TELOPT_TM);
}
void
xmitAO(void)
{
NET2ADD(IAC, AO);
printoption("SENT", IAC, AO);
if (autoflush) {
doflush();
}
}
void
xmitEL(void)
{
NET2ADD(IAC, EL);
printoption("SENT", IAC, EL);
}
void
xmitEC(void)
{
NET2ADD(IAC, EC);
printoption("SENT", IAC, EC);
}
int
dosynch()
{
netclear();
NETADD(IAC);
setneturg();
NETADD(DM);
printoption("SENT", IAC, DM);
return 1;
}
int want_status_response = 0;
int
get_status()
{
unsigned char tmp[16];
unsigned char *cp;
if (my_want_state_is_dont(TELOPT_STATUS)) {
printf("Remote side does not support STATUS option\n");
return 0;
}
cp = tmp;
*cp++ = IAC;
*cp++ = SB;
*cp++ = TELOPT_STATUS;
*cp++ = TELQUAL_SEND;
*cp++ = IAC;
*cp++ = SE;
if (NETROOM() >= cp - tmp) {
ring_supply_data(&netoring, tmp, cp-tmp);
printsub('>', tmp+2, cp - tmp - 2);
}
++want_status_response;
return 1;
}
void
intp(void)
{
NET2ADD(IAC, IP);
printoption("SENT", IAC, IP);
flushline = 1;
if (autoflush) {
doflush();
}
if (autosynch) {
dosynch();
}
}
void
sendbrk(void)
{
NET2ADD(IAC, BREAK);
printoption("SENT", IAC, BREAK);
flushline = 1;
if (autoflush) {
doflush();
}
if (autosynch) {
dosynch();
}
}
void
sendabort(void)
{
NET2ADD(IAC, ABORT);
printoption("SENT", IAC, ABORT);
flushline = 1;
if (autoflush) {
doflush();
}
if (autosynch) {
dosynch();
}
}
void
sendsusp(void)
{
NET2ADD(IAC, SUSP);
printoption("SENT", IAC, SUSP);
flushline = 1;
if (autoflush) {
doflush();
}
if (autosynch) {
dosynch();
}
}
void
sendeof(void)
{
NET2ADD(IAC, xEOF);
printoption("SENT", IAC, xEOF);
}
void
sendayt(void)
{
NET2ADD(IAC, AYT);
printoption("SENT", IAC, AYT);
}
void
sendnaws()
{
long rows, cols;
unsigned char tmp[16];
unsigned char *cp;
if (my_state_is_wont(TELOPT_NAWS))
return;
#undef PUTSHORT
#define PUTSHORT(cp, x) { if ((*cp++ = ((x)>>8)&0xff) == IAC) *cp++ = IAC; \
if ((*cp++ = ((x))&0xff) == IAC) *cp++ = IAC; }
if (TerminalWindowSize(&rows, &cols) == 0) {
return;
}
cp = tmp;
*cp++ = IAC;
*cp++ = SB;
*cp++ = TELOPT_NAWS;
PUTSHORT(cp, cols);
PUTSHORT(cp, rows);
*cp++ = IAC;
*cp++ = SE;
if (NETROOM() >= cp - tmp) {
ring_supply_data(&netoring, tmp, cp-tmp);
printsub('>', tmp+2, cp - tmp - 2);
}
}
void
tel_enter_binary(int rw)
{
if (rw&1)
send_do(TELOPT_BINARY, 1);
if (rw&2)
send_will(TELOPT_BINARY, 1);
}
void
tel_leave_binary(int rw)
{
if (rw&1)
send_dont(TELOPT_BINARY, 1);
if (rw&2)
send_wont(TELOPT_BINARY, 1);
}