#ifndef lint
#if 0
static const char sccsid[] = "@(#)state.c 8.2 (Berkeley) 12/15/93";
#endif
static const char rcsid[] =
"$FreeBSD: src/libexec/telnetd/state.c,v 1.13 2001/07/20 15:14:03 ru Exp $";
#endif
#include <stdarg.h>
#include "telnetd.h"
#if defined(AUTHENTICATION)
#include <libtelnet/auth.h>
#endif
unsigned char doopt[] = { IAC, DO, '%', 'c', 0 };
unsigned char dont[] = { IAC, DONT, '%', 'c', 0 };
unsigned char will[] = { IAC, WILL, '%', 'c', 0 };
unsigned char wont[] = { IAC, WONT, '%', 'c', 0 };
int not42 = 1;
unsigned char subbuffer[512], *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)
#define SB_LEN() (subend - subpointer)
#ifdef ENV_HACK
unsigned char *subsave;
#define SB_SAVE() subsave = subpointer;
#define SB_RESTORE() subpointer = subsave;
#endif
#define TS_DATA 0
#define TS_IAC 1
#define TS_CR 2
#define TS_SB 3
#define TS_SE 4
#define TS_WILL 5
#define TS_WONT 6
#define TS_DO 7
#define TS_DONT 8
void
telrcv()
{
register int c;
static int state = TS_DATA;
#if defined(CRAY2) && defined(UNICOS5)
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;
if ((c == 0) || (c == '\n')) {
break;
}
case TS_DATA:
if (c == IAC) {
state = TS_IAC;
break;
}
if ((c == '\r') && his_state_is_wont(TELOPT_BINARY)) {
int nc = *netip;
#ifdef LINEMODE
if (linemode && (ncc > 0) && (('\n' == nc) ||
((0 == nc) && tty_iscrnl())) ) {
netip++; ncc--;
c = '\n';
} else
#endif
{
state = TS_CR;
}
}
*pfrontp++ = c;
break;
case TS_IAC:
gotiac: switch (c) {
case IP:
DIAG(TD_OPTIONS,
printoption("td: recv IAC", c));
interrupt();
break;
case BREAK:
DIAG(TD_OPTIONS,
printoption("td: recv IAC", c));
sendbrk();
break;
case AYT:
DIAG(TD_OPTIONS,
printoption("td: recv IAC", c));
recv_ayt();
break;
case AO:
{
DIAG(TD_OPTIONS,
printoption("td: recv IAC", c));
ptyflush();
init_termbuf();
if (slctab[SLC_AO].sptr &&
*slctab[SLC_AO].sptr != (cc_t)(_POSIX_VDISABLE)) {
*pfrontp++ =
(unsigned char)*slctab[SLC_AO].sptr;
}
netclear();
output_data("%c%c", IAC, DM);
neturg = nfrontp-1;
DIAG(TD_OPTIONS,
printoption("td: send IAC", DM));
break;
}
case EC:
case EL:
{
cc_t ch;
DIAG(TD_OPTIONS,
printoption("td: recv IAC", c));
ptyflush();
init_termbuf();
if (c == EC)
ch = *slctab[SLC_EC].sptr;
else
ch = *slctab[SLC_EL].sptr;
if (ch != (cc_t)(_POSIX_VDISABLE))
*pfrontp++ = (unsigned char)ch;
break;
}
case DM:
DIAG(TD_OPTIONS,
printoption("td: recv IAC", c));
SYNCHing = stilloob(net);
settimer(gotDM);
break;
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 (his_state_is_will(TELOPT_EOR))
doeof();
break;
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) {
SB_ACCUM(IAC);
SB_ACCUM(c);
subpointer -= 2;
SB_TERM();
suboption();
state = TS_IAC;
goto gotiac;
}
SB_ACCUM(c);
state = TS_SB;
} else {
SB_ACCUM(IAC);
SB_ACCUM(SE);
subpointer -= 2;
SB_TERM();
suboption();
state = TS_DATA;
}
break;
case TS_WILL:
willoption(c);
state = TS_DATA;
continue;
case TS_WONT:
wontoption(c);
state = TS_DATA;
continue;
case TS_DO:
dooption(c);
state = TS_DATA;
continue;
case TS_DONT:
dontoption(c);
state = TS_DATA;
continue;
default:
syslog(LOG_ERR, "panic state=%d", state);
printf("telnetd: panic state=%d\n", state);
exit(1);
}
}
#if defined(CRAY2) && defined(UNICOS5)
if (!linemode) {
char xptyobuf[BUFSIZ+NETSLOP];
char xbuf2[BUFSIZ];
register char *cp;
int n = pfrontp - opfrontp, oc;
memmove(xptyobuf, opfrontp, 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
}
void
send_do(option, init)
int option, init;
{
if (init) {
if ((do_dont_resp[option] == 0 && his_state_is_will(option)) ||
his_want_state_is_will(option))
return;
if (option == TELOPT_TM)
set_his_want_state_wont(option);
else
set_his_want_state_will(option);
do_dont_resp[option]++;
}
output_data((const char *)doopt, option);
DIAG(TD_OPTIONS, printoption("td: send do", option));
}
#ifdef AUTHENTICATION
extern void auth_request();
#endif
#ifdef LINEMODE
extern void doclientstat();
#endif
void
willoption(option)
int option;
{
int changeok = 0;
void (*func)() = 0;
DIAG(TD_OPTIONS, printoption("td: recv will", option));
if (do_dont_resp[option]) {
do_dont_resp[option]--;
if (do_dont_resp[option] && his_state_is_will(option))
do_dont_resp[option]--;
}
if (do_dont_resp[option] == 0) {
if (his_want_state_is_wont(option)) {
switch (option) {
case TELOPT_BINARY:
init_termbuf();
tty_binaryin(1);
set_termbuf();
changeok++;
break;
case TELOPT_ECHO:
not42 = 0;
break;
case TELOPT_TM:
#if defined(LINEMODE) && defined(KLUDGELINEMODE)
if (lmodetype < KLUDGE_LINEMODE) {
lmodetype = KLUDGE_LINEMODE;
clientstat(TELOPT_LINEMODE, WILL, 0);
send_wont(TELOPT_SGA, 1);
} else if (lmodetype == NO_AUTOKLUDGE) {
lmodetype = KLUDGE_OK;
}
#endif
return;
case TELOPT_LFLOW:
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:
case TELOPT_XDISPLOC:
case TELOPT_NEW_ENVIRON:
case TELOPT_OLD_ENVIRON:
changeok++;
break;
#ifdef LINEMODE
case TELOPT_LINEMODE:
# ifdef KLUDGELINEMODE
lmodetype = REAL_LINEMODE;
# endif
func = doclientstat;
changeok++;
break;
#endif
#ifdef AUTHENTICATION
case TELOPT_AUTHENTICATION:
func = auth_request;
changeok++;
break;
#endif
default:
break;
}
if (changeok) {
set_his_want_state_will(option);
send_do(option, 0);
} else {
do_dont_resp[option]++;
send_dont(option, 0);
}
} else {
switch (option) {
case TELOPT_ECHO:
not42 = 0;
send_dont(option, 1);
break;
#ifdef LINEMODE
case TELOPT_LINEMODE:
# ifdef KLUDGELINEMODE
lmodetype = REAL_LINEMODE;
# endif
func = doclientstat;
break;
#endif
#ifdef AUTHENTICATION
case TELOPT_AUTHENTICATION:
func = auth_request;
break;
#endif
case TELOPT_LFLOW:
func = flowstat;
break;
}
}
}
set_his_state_will(option);
if (func)
(*func)();
}
void
send_dont(option, init)
int option, init;
{
if (init) {
if ((do_dont_resp[option] == 0 && his_state_is_wont(option)) ||
his_want_state_is_wont(option))
return;
set_his_want_state_wont(option);
do_dont_resp[option]++;
}
output_data((const char *)dont, option);
DIAG(TD_OPTIONS, printoption("td: send dont", option));
}
void
wontoption(option)
int option;
{
DIAG(TD_OPTIONS, printoption("td: recv wont", option));
if (do_dont_resp[option]) {
do_dont_resp[option]--;
if (do_dont_resp[option] && his_state_is_wont(option))
do_dont_resp[option]--;
}
if (do_dont_resp[option] == 0) {
if (his_want_state_is_will(option)) {
switch (option) {
case TELOPT_ECHO:
not42 = 1;
break;
case TELOPT_BINARY:
init_termbuf();
tty_binaryin(0);
set_termbuf();
break;
#ifdef LINEMODE
case TELOPT_LINEMODE:
# ifdef KLUDGELINEMODE
if (lmodetype != REAL_LINEMODE)
break;
lmodetype = KLUDGE_LINEMODE;
# endif
clientstat(TELOPT_LINEMODE, WONT, 0);
break;
#endif
case TELOPT_TM:
set_his_want_state_wont(TELOPT_TM);
return;
case TELOPT_LFLOW:
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;
#if defined(AUTHENTICATION)
case TELOPT_AUTHENTICATION:
auth_finished(0, AUTH_REJECT);
break;
#endif
case TELOPT_TTYPE:
settimer(ttypesubopt);
break;
case TELOPT_TSPEED:
settimer(tspeedsubopt);
break;
case TELOPT_XDISPLOC:
settimer(xdisplocsubopt);
break;
case TELOPT_OLD_ENVIRON:
settimer(oenvironsubopt);
break;
case TELOPT_NEW_ENVIRON:
settimer(environsubopt);
break;
default:
break;
}
set_his_want_state_wont(option);
if (his_state_is_will(option))
send_dont(option, 0);
} else {
switch (option) {
case TELOPT_TM:
#if defined(LINEMODE) && defined(KLUDGELINEMODE)
if (lmodetype < NO_AUTOKLUDGE) {
lmodetype = NO_LINEMODE;
clientstat(TELOPT_LINEMODE, WONT, 0);
send_will(TELOPT_SGA, 1);
send_will(TELOPT_ECHO, 1);
}
#endif
break;
#if defined(AUTHENTICATION)
case TELOPT_AUTHENTICATION:
auth_finished(0, AUTH_REJECT);
break;
#endif
default:
break;
}
}
}
set_his_state_wont(option);
}
void
send_will(option, init)
int option, init;
{
if (init) {
if ((will_wont_resp[option] == 0 && my_state_is_will(option))||
my_want_state_is_will(option))
return;
set_my_want_state_will(option);
will_wont_resp[option]++;
}
output_data((const char *)will, option);
DIAG(TD_OPTIONS, printoption("td: send will", option));
}
#if !defined(LINEMODE) || !defined(KLUDGELINEMODE)
int turn_on_sga = 0;
#endif
void
dooption(option)
int option;
{
int changeok = 0;
DIAG(TD_OPTIONS, printoption("td: recv do", option));
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) && (my_want_state_is_wont(option))) {
switch (option) {
case TELOPT_ECHO:
#ifdef LINEMODE
# ifdef KLUDGELINEMODE
if (lmodetype == NO_LINEMODE)
# else
if (his_state_is_wont(TELOPT_LINEMODE))
# endif
#endif
{
init_termbuf();
tty_setecho(1);
set_termbuf();
}
changeok++;
break;
case TELOPT_BINARY:
init_termbuf();
tty_binaryout(1);
set_termbuf();
changeok++;
break;
case TELOPT_SGA:
#if defined(LINEMODE) && defined(KLUDGELINEMODE)
if (lmodetype == KLUDGE_LINEMODE) {
clientstat(TELOPT_LINEMODE, WONT, 0);
if (linemode)
break;
}
#else
turn_on_sga = 0;
#endif
changeok++;
break;
case TELOPT_STATUS:
changeok++;
break;
case TELOPT_TM:
send_will(option, 0);
set_my_want_state_wont(option);
set_my_state_wont(option);
return;
case TELOPT_LOGOUT:
set_my_want_state_will(TELOPT_LOGOUT);
send_will(TELOPT_LOGOUT, 0);
set_my_state_will(TELOPT_LOGOUT);
(void)netflush();
cleanup(0);
break;
case TELOPT_LINEMODE:
case TELOPT_TTYPE:
case TELOPT_NAWS:
case TELOPT_TSPEED:
case TELOPT_LFLOW:
case TELOPT_XDISPLOC:
#ifdef TELOPT_ENVIRON
case TELOPT_NEW_ENVIRON:
#endif
case TELOPT_OLD_ENVIRON:
default:
break;
}
if (changeok) {
set_my_want_state_will(option);
send_will(option, 0);
} else {
will_wont_resp[option]++;
send_wont(option, 0);
}
}
set_my_state_will(option);
}
void
send_wont(option, init)
int option, init;
{
if (init) {
if ((will_wont_resp[option] == 0 && my_state_is_wont(option)) ||
my_want_state_is_wont(option))
return;
set_my_want_state_wont(option);
will_wont_resp[option]++;
}
output_data((const char *)wont, option);
DIAG(TD_OPTIONS, printoption("td: send wont", option));
}
void
dontoption(option)
int option;
{
DIAG(TD_OPTIONS, printoption("td: recv dont", 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_BINARY:
init_termbuf();
tty_binaryout(0);
set_termbuf();
break;
case TELOPT_ECHO:
#ifdef LINEMODE
# ifdef KLUDGELINEMODE
if ((lmodetype != REAL_LINEMODE) &&
(lmodetype != KLUDGE_LINEMODE))
# else
if (his_state_is_wont(TELOPT_LINEMODE))
# endif
#endif
{
init_termbuf();
tty_setecho(0);
set_termbuf();
}
break;
case TELOPT_SGA:
#if defined(LINEMODE) && defined(KLUDGELINEMODE)
if ((lmodetype == KLUDGE_LINEMODE) ||
(lmodetype == KLUDGE_OK)) {
lmodetype = KLUDGE_LINEMODE;
clientstat(TELOPT_LINEMODE, WILL, 0);
}
break;
#else
set_my_want_state_wont(option);
if (my_state_is_will(option))
send_wont(option, 0);
set_my_state_wont(option);
if (turn_on_sga ^= 1)
send_will(option, 1);
return;
#endif
default:
break;
}
set_my_want_state_wont(option);
if (my_state_is_will(option))
send_wont(option, 0);
}
set_my_state_wont(option);
}
#ifdef ENV_HACK
int env_ovar = -1;
int env_ovalue = -1;
#else
# define env_ovar OLD_ENV_VAR
# define env_ovalue OLD_ENV_VALUE
#endif
void
suboption()
{
register int subchar;
DIAG(TD_OPTIONS, {netflush(); printsub('<', subpointer, SB_LEN()+2);});
subchar = SB_GET();
switch (subchar) {
case TELOPT_TSPEED: {
register int xspeed, rspeed;
if (his_state_is_wont(TELOPT_TSPEED))
break;
settimer(tspeedsubopt);
if (SB_EOF() || SB_GET() != TELQUAL_IS)
return;
xspeed = atoi((char *)subpointer);
while (SB_GET() != ',' && !SB_EOF());
if (SB_EOF())
return;
rspeed = atoi((char *)subpointer);
clientstat(TELOPT_TSPEED, xspeed, rspeed);
break;
}
case TELOPT_TTYPE: {
static char terminalname[41];
if (his_state_is_wont(TELOPT_TTYPE))
break;
settimer(ttypesubopt);
if (SB_EOF() || SB_GET() != TELQUAL_IS) {
return;
}
terminaltype = terminalname;
while ((terminaltype < (terminalname + sizeof terminalname-1)) &&
!SB_EOF()) {
register int c;
c = SB_GET();
if (isupper(c)) {
c = tolower(c);
}
*terminaltype++ = c;
}
*terminaltype = 0;
terminaltype = terminalname;
break;
}
case TELOPT_NAWS: {
register int xwinsize, ywinsize;
if (his_state_is_wont(TELOPT_NAWS))
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;
}
#ifdef LINEMODE
case TELOPT_LINEMODE: {
register int request;
if (his_state_is_wont(TELOPT_LINEMODE))
break;
if (SB_EOF())
break;
request = SB_GET();
if (SB_EOF())
break;
if (request == LM_SLC) {
start_slc(1);
do_opt_slc(subpointer, subend - subpointer);
(void) end_slc(0);
break;
} else if (request == LM_MODE) {
if (SB_EOF())
return;
useeditmode = SB_GET();
clientstat(LM_MODE, 0, 0);
break;
}
if (SB_EOF())
break;
switch (SB_GET()) {
case LM_FORWARDMASK:
default:
break;
}
break;
}
#endif
case TELOPT_STATUS: {
int mode;
if (SB_EOF())
break;
mode = SB_GET();
switch (mode) {
case TELQUAL_SEND:
if (my_state_is_will(TELOPT_STATUS))
send_status();
break;
case TELQUAL_IS:
break;
default:
break;
}
break;
}
case TELOPT_XDISPLOC: {
if (SB_EOF() || SB_GET() != TELQUAL_IS)
return;
settimer(xdisplocsubopt);
subpointer[SB_LEN()] = '\0';
(void)setenv("DISPLAY", (char *)subpointer, 1);
break;
}
#ifdef TELOPT_NEW_ENVIRON
case TELOPT_NEW_ENVIRON:
#endif
case TELOPT_OLD_ENVIRON: {
register int c;
register char *cp, *varp, *valp;
if (SB_EOF())
return;
c = SB_GET();
if (c == TELQUAL_IS) {
if (subchar == TELOPT_OLD_ENVIRON)
settimer(oenvironsubopt);
else
settimer(environsubopt);
} else if (c != TELQUAL_INFO) {
return;
}
#ifdef TELOPT_NEW_ENVIRON
if (subchar == TELOPT_NEW_ENVIRON) {
while (!SB_EOF()) {
c = SB_GET();
if ((c == NEW_ENV_VAR) || (c == ENV_USERVAR))
break;
}
} else
#endif
{
#ifdef ENV_HACK
if (env_ovar < 0) {
register int last = -1;
int empty = 0;
int got_var = 0, got_value = 0, got_uservar = 0;
SB_SAVE();
while (!SB_EOF()) {
c = SB_GET();
switch(c) {
case OLD_ENV_VAR:
if (last < 0 || last == OLD_ENV_VAR
|| (empty && (last == OLD_ENV_VALUE)))
goto env_ovar_ok;
got_var++;
last = OLD_ENV_VAR;
break;
case OLD_ENV_VALUE:
if (last < 0 || last == OLD_ENV_VALUE
|| (empty && (last == OLD_ENV_VAR)))
goto env_ovar_wrong;
got_value++;
last = OLD_ENV_VALUE;
break;
case ENV_USERVAR:
if (last != ENV_USERVAR)
got_uservar++;
if (empty) {
if (last == OLD_ENV_VALUE)
goto env_ovar_ok;
if (last == OLD_ENV_VAR)
goto env_ovar_wrong;
}
last = ENV_USERVAR;
break;
case ENV_ESC:
if (!SB_EOF())
c = SB_GET();
default:
empty = 0;
continue;
}
empty = 1;
}
if (empty) {
if (last == OLD_ENV_VALUE)
goto env_ovar_ok;
if (last == OLD_ENV_VAR)
goto env_ovar_wrong;
}
if (got_uservar + got_var == got_value) {
env_ovar_ok:
env_ovar = OLD_ENV_VAR;
env_ovalue = OLD_ENV_VALUE;
} else if (got_uservar + got_value == got_var) {
env_ovar_wrong:
env_ovar = OLD_ENV_VALUE;
env_ovalue = OLD_ENV_VAR;
DIAG(TD_OPTIONS,
output_data("ENVIRON VALUE and VAR are reversed!\r\n"));
}
}
SB_RESTORE();
#endif
while (!SB_EOF()) {
c = SB_GET();
if ((c == env_ovar) || (c == ENV_USERVAR))
break;
}
}
if (SB_EOF())
return;
cp = varp = (char *)subpointer;
valp = 0;
while (!SB_EOF()) {
c = SB_GET();
if (subchar == TELOPT_OLD_ENVIRON) {
if (c == env_ovar)
c = NEW_ENV_VAR;
else if (c == env_ovalue)
c = NEW_ENV_VALUE;
}
switch (c) {
case NEW_ENV_VALUE:
*cp = '\0';
cp = valp = (char *)subpointer;
break;
case NEW_ENV_VAR:
case ENV_USERVAR:
*cp = '\0';
if (valp)
(void)setenv(varp, valp, 1);
else
unsetenv(varp);
cp = varp = (char *)subpointer;
valp = 0;
break;
case ENV_ESC:
if (SB_EOF())
break;
c = SB_GET();
default:
*cp++ = c;
break;
}
}
*cp = '\0';
if (valp)
(void)setenv(varp, valp, 1);
else
unsetenv(varp);
break;
}
#if defined(AUTHENTICATION)
case TELOPT_AUTHENTICATION:
if (SB_EOF())
break;
switch(SB_GET()) {
case TELQUAL_SEND:
case TELQUAL_REPLY:
break;
case TELQUAL_IS:
auth_is(subpointer, SB_LEN());
break;
case TELQUAL_NAME:
auth_name(subpointer, SB_LEN());
break;
}
break;
#endif
default:
break;
}
}
void
doclientstat()
{
clientstat(TELOPT_LINEMODE, WILL, 0);
}
#define ADD(c) *ncp++ = c
#define ADD_DATA(c) { *ncp++ = c; if (c == SE) *ncp++ = c; }
void
send_status()
{
unsigned char statusbuf[256];
register unsigned char *ncp;
register unsigned char i;
ncp = statusbuf;
netflush();
ADD(IAC);
ADD(SB);
ADD(TELOPT_STATUS);
ADD(TELQUAL_IS);
for (i = 0; i < (unsigned char)NTELOPTS; i++) {
if (my_want_state_is_will(i)) {
ADD(WILL);
ADD_DATA(i);
if (i == IAC)
ADD(IAC);
}
if (his_want_state_is_will(i)) {
ADD(DO);
ADD_DATA(i);
if (i == IAC)
ADD(IAC);
}
}
if (his_want_state_is_will(TELOPT_LFLOW)) {
ADD(SB);
ADD(TELOPT_LFLOW);
if (flowmode) {
ADD(LFLOW_ON);
} else {
ADD(LFLOW_OFF);
}
ADD(SE);
if (restartany >= 0) {
ADD(SB);
ADD(TELOPT_LFLOW);
if (restartany) {
ADD(LFLOW_RESTART_ANY);
} else {
ADD(LFLOW_RESTART_XON);
}
ADD(SE);
ADD(SB);
}
}
#ifdef LINEMODE
if (his_want_state_is_will(TELOPT_LINEMODE)) {
unsigned 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
ADD(IAC);
ADD(SE);
output_datalen(statusbuf, ncp - statusbuf);
netflush();
DIAG(TD_OPTIONS,
{printsub('>', statusbuf, ncp - statusbuf); netflush();});
}
int
output_data(const char *format, ...)
{
va_list args;
int len;
char *buf;
va_start(args, format);
if ((len = vasprintf(&buf, format, args)) == -1)
return -1;
output_datalen(buf, len);
va_end(args);
free(buf);
return (len);
}
void
output_datalen(const char *buf, int len)
{
int remaining, copied;
remaining = BUFSIZ - (nfrontp - netobuf);
while (len > 0) {
if ((len > BUFSIZ ? BUFSIZ : len) > remaining) {
netflush();
remaining = BUFSIZ - (nfrontp - netobuf);
}
copied = remaining > len ? len : remaining;
memmove(nfrontp, buf, copied);
nfrontp += copied;
len -= copied;
remaining -= copied;
buf += copied;
}
return;
}