/******************************************************************************
* TinTin++ *
* Copyright (C) 2004 (See CREDITS file) *
* *
* This program is protected under the GNU GPL (See COPYING) *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the Free Software *
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
******************************************************************************/
/******************************************************************************
* (T)he K(I)cki(N) (T)ickin D(I)kumud Clie(N)t *
* *
* coded by Igor van den Hoven 2004 *
******************************************************************************/
#include "tintin.h"
#include "telnet.h"
/* what we see from the remote end */
char iac_do_naws[] = { IAC, DO, TELOPT_NAWS };
char iac_will_naws[] = { IAC, WILL, TELOPT_NAWS };
char iac_do_sga[] = { IAC, DO, TELOPT_SGA };
char iac_will_sga[] = { IAC, WILL, TELOPT_SGA };
char iac_sb_tspeed[] = { IAC, SB, TELOPT_TSPEED, ENV_SEND, IAC, SE };
char iac_dont_ttype[] = { IAC, DONT, TELOPT_TTYPE };
char iac_sb_ttype[] = { IAC, SB, TELOPT_TTYPE, ENV_SEND, IAC, SE };
char iac_do_echo[] = { IAC, DO, TELOPT_ECHO };
char iac_wont_echo[] = { IAC, WONT, TELOPT_ECHO };
char iac_will_echo[] = { IAC, WILL, TELOPT_ECHO };
char iac_will_mssp[] = { IAC, WILL, TELOPT_MSSP };
char iac_sb_mssp[] = { IAC, SB, TELOPT_MSSP };
char iac_sb_msdp[] = { IAC, SB, TELOPT_MSDP };
char iac_sb_gmcp[] = { IAC, SB, TELOPT_GMCP };
char iac_sb_new_environ[] = { IAC, SB, TELOPT_NEW_ENVIRON };
char iac_sb_zmp[] = { IAC, SB, TELOPT_ZMP };
char iac_sb_mccp1[] = { IAC, SB, TELOPT_MCCP1, WILL, SE };
char iac_will_mccp2[] = { IAC, WILL, TELOPT_MCCP2 };
char iac_sb_mccp2[] = { IAC, SB, TELOPT_MCCP2, IAC, SE };
char iac_eor[] = { IAC, EOR };
char iac_ga[] = { IAC, GA };
struct iac_type
{
int size;
char * code;
int (* func) (struct session *ses, int cplen, unsigned char *cpsrc);
};
struct iac_type iac_table [] =
{
{ 3, iac_do_sga, &send_will_sga },
{ 3, iac_will_sga, &send_do_sga },
{ 3, iac_do_naws, &send_sb_naws },
{ 3, iac_will_naws, &send_sb_naws },
{ 3, iac_do_echo, &send_echo_will },
{ 3, iac_will_echo, &send_echo_off },
{ 3, iac_wont_echo, &send_echo_on },
{ 3, iac_will_mccp2, &send_do_mccp2 },
{ 3, iac_will_mssp, &send_do_mssp },
{ 3, iac_sb_mssp, &recv_sb_mssp },
{ 3, iac_sb_msdp, &recv_sb_msdp },
{ 3, iac_sb_gmcp, &recv_sb_gmcp },
{ 3, iac_sb_new_environ, &recv_sb_new_environ },
{ 6, iac_sb_tspeed, &recv_sb_tspeed },
{ 3, iac_dont_ttype, &recv_dont_ttype },
{ 6, iac_sb_ttype, &recv_sb_ttype },
{ 3, iac_sb_zmp, &recv_sb_zmp },
{ 5, iac_sb_mccp1, &init_mccp },
{ 5, iac_sb_mccp2, &init_mccp },
{ 2, iac_eor, &mark_prompt },
{ 2, iac_ga, &mark_prompt },
{ 0, NULL, NULL }
};
void telopt_debug(struct session *ses, char *format, ...)
{
char buf[BUFFER_SIZE];
va_list args;
if (HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG))
{
va_start(args, format);
vsprintf(buf, format, args);
va_end(args);
tintin_puts(ses, buf);
}
}
int translate_telopts(struct session *ses, unsigned char *src, int cplen)
{
int skip, cnt;
unsigned char *cpdst;
unsigned char *cpsrc;
if (cplen == 0)
{
return 0;
}
push_call("translate_telopts(%p,%p,%d)",ses,src,cplen);
if (ses->mccp)
{
ses->mccp->next_in = src;
ses->mccp->avail_in = cplen;
ses->mccp->next_out = gtd->mccp_buf;
ses->mccp->avail_out = gtd->mccp_len;
inflate:
switch (inflate(ses->mccp, Z_SYNC_FLUSH))
{
case Z_OK:
if (ses->mccp->avail_out == 0)
{
gtd->mccp_len *= 2;
gtd->mccp_buf = (unsigned char *) realloc(gtd->mccp_buf, gtd->mccp_len);
ses->mccp->avail_out = gtd->mccp_len / 2;
ses->mccp->next_out = gtd->mccp_buf + gtd->mccp_len / 2;
goto inflate;
}
cplen = ses->mccp->next_out - gtd->mccp_buf;
cpsrc = gtd->mccp_buf;
break;
case Z_STREAM_END:
tintin_printf2(ses, "");
tintin_printf2(ses, "#COMPRESSION END, DISABLING MCCP.");
cpsrc = src + (cplen - ses->mccp->avail_in);
cplen = ses->mccp->avail_in;
inflateEnd(ses->mccp);
free(ses->mccp);
ses->mccp = NULL;
break;
default:
tintin_puts2(ses, "");
tintin_puts2(ses, "#COMPRESSION ERROR, DISABLING MCCP.");
send_dont_mccp2(ses, 0, NULL);
inflateEnd(ses->mccp);
free(ses->mccp);
ses->mccp = NULL;
cpsrc = src;
cplen = 0;
break;
}
}
else
{
cpsrc = src;
}
if (HAS_BIT(ses->flags, SES_FLAG_LOGLEVEL) && ses->logfile)
{
fwrite(cpsrc, 1, cplen, ses->logfile);
fflush(ses->logfile);
}
if (ses->read_len + cplen >= ses->read_max)
{
ses->read_max = ses->read_len + cplen + 1000;
ses->read_buf = (unsigned char *) realloc(ses->read_buf, ses->read_max);
}
memcpy(ses->read_buf + ses->read_len, cpsrc, cplen);
cpsrc = ses->read_buf;
cplen = ses->read_len + cplen;
if (gtd->mud_output_len + cplen >= gtd->mud_output_max)
{
gtd->mud_output_max = gtd->mud_output_len + cplen + 1000;
gtd->mud_output_buf = (char *) realloc(gtd->mud_output_buf, gtd->mud_output_max);
}
cpdst = (unsigned char *) gtd->mud_output_buf + gtd->mud_output_len;
while (cplen > 0)
{
if (*cpsrc == IAC)
{
skip = 2;
if (HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG))
{
switch(cpsrc[1])
{
case NOP:
case DM:
case BREAK:
case IP:
case AO:
case AYT:
case EC:
case EL:
case IAC:
case GA:
case EOR:
tintin_printf2(ses, "RCVD IAC %s", TELCMD(cpsrc[1]));
break;
case DO:
case DONT:
case WILL:
case WONT:
if (cplen > 2)
{
tintin_printf2(ses, "RCVD IAC %s %s", TELCMD(cpsrc[1]), telopt_table[cpsrc[2]].name);
}
else
{
tintin_printf2(ses, "RCVD IAC %s %d (BAD TELOPT)", TELCMD(cpsrc[1]), cpsrc[2]);
}
break;
case SB:
if (cplen > 2)
{
tintin_printf2(ses, "RCVD IAC SB %s", telopt_table[cpsrc[2]].name);
}
break;
default:
if (TELCMD_OK(cpsrc[1]))
{
tintin_printf2(ses, "RCVD IAC %s %d", TELCMD(cpsrc[1]), cpsrc[2]);
}
else
{
tintin_printf2(ses, "RCVD IAC %d %d", cpsrc[1], cpsrc[2]);
}
break;
}
}
for (cnt = 0 ; iac_table[cnt].code ; cnt++)
{
if (cplen < iac_table[cnt].size && !memcmp(cpsrc, iac_table[cnt].code, cplen))
{
skip = iac_table[cnt].size;
break;
}
if (cplen >= iac_table[cnt].size && !memcmp(cpsrc, iac_table[cnt].code, iac_table[cnt].size))
{
skip = iac_table[cnt].func(ses, cplen, cpsrc);
if (iac_table[cnt].func == init_mccp)
{
pop_call();
return translate_telopts(ses, cpsrc + skip, cplen - skip);
}
break;
}
}
if (iac_table[cnt].code == NULL && cplen > 1)
{
switch (cpsrc[1])
{
case SE:
case NOP:
case DM:
case BREAK:
case IP:
case AO:
case AYT:
case EC:
case EL:
case GA:
case EOR:
skip = 2;
break;
case WILL:
if (cplen > 2)
{
if (!check_all_events(ses, 1, 0, "IAC WILL %s", telopt_table[cpsrc[2]].name))
{
if (!HAS_BIT(ses->telopt_flag[cpsrc[2] / 32], 1 << cpsrc[2] % 32))
{
if (telopt_table[cpsrc[2]].want)
{
skip = send_do_telopt(ses, cplen, cpsrc);
}
else
{
skip = send_dont_telopt(ses, cplen, cpsrc);
}
SET_BIT(ses->telopt_flag[cpsrc[2] / 32], 1 << cpsrc[2] % 32);
}
}
}
skip = 3;
break;
case DO:
if (cplen > 2)
{
if (!check_all_events(ses, 1, 0, "IAC DO %s", telopt_table[cpsrc[2]].name))
{
if (!HAS_BIT(ses->telopt_flag[cpsrc[2] / 32], 1 << cpsrc[2] % 32))
{
if (telopt_table[cpsrc[2]].want)
{
skip = send_will_telopt(ses, cplen, cpsrc);
}
else
{
skip = send_wont_telopt(ses, cplen, cpsrc);
}
SET_BIT(ses->telopt_flag[cpsrc[2] / 32], 1 << cpsrc[2] % 32);
}
}
}
skip = 3;
break;
case WONT:
case DONT:
if (cplen > 2)
{
check_all_events(ses, 2, 0, "IAC %s %s", TELCMD(cpsrc[1]), telopt_table[cpsrc[2]].name);
DEL_BIT(ses->telopt_flag[cpsrc[2] / 32], 1 << cpsrc[2] % 32);
}
skip = 3;
break;
case SB:
skip = recv_sb(ses, cplen, cpsrc);
break;
case IAC:
gtd->mud_output_len++;
*cpdst++ = 0xFF;
skip = 2;
break;
default:
tintin_printf(NULL, "#IAC BAD TELOPT %d", cpsrc[1]);
skip = 1;
break;
}
}
if (skip <= cplen)
{
cplen -= skip;
cpsrc += skip;
}
else
{
memcpy(ses->read_buf, cpsrc, cplen);
gtd->mud_output_buf[gtd->mud_output_len] = 0;
pop_call();
return cplen;
}
}
#ifdef BIG5
else if (*cpsrc & 0x80)
{
*cpdst++ = *cpsrc++;
gtd->mud_output_len++;
cplen--;
if (cplen)
{
*cpdst++ = *cpsrc++;
gtd->mud_output_len++;
cplen--;
}
}
#endif
else
{
/*
skip '\0' and '\r' in text input
*/
switch (*cpsrc)
{
case '\0':
cpsrc++;
cplen--;
continue;
case '\r':
if (cplen > 1 && cpsrc[1] == '\n')
{
cpsrc++;
cplen--;
continue;
}
break;
case '\n':
if (HAS_BIT(ses->telopts, TELOPT_FLAG_PROMPT))
{
DEL_BIT(ses->telopts, TELOPT_FLAG_PROMPT);
}
*cpdst++ = *cpsrc++;
gtd->mud_output_len++;
cplen--;
while (*cpsrc == '\r')
{
cpsrc++;
cplen--;
}
continue;
default:
if (HAS_BIT(ses->telopts, TELOPT_FLAG_PROMPT))
{
DEL_BIT(ses->telopts, TELOPT_FLAG_PROMPT);
/*
Fix up non vt muds
*/
if (HAS_BIT(ses->flags, SES_FLAG_SPLIT) || !IS_SPLIT(ses))
{
*cpdst++ = '\n';
gtd->mud_output_len++;
}
}
break;
}
*cpdst++ = *cpsrc++;
gtd->mud_output_len++;
cplen--;
}
}
gtd->mud_output_buf[gtd->mud_output_len] = 0;
pop_call();
return 0;
}
/*
SGA
*/
int send_do_sga(struct session *ses, int cplen, unsigned char *cpsrc)
{
SET_BIT(ses->telopts, TELOPT_FLAG_SGA);
if (!HAS_BIT(ses->telopt_flag[TELOPT_SGA / 32], 1 << TELOPT_SGA % 32))
{
SET_BIT(ses->telopt_flag[TELOPT_SGA / 32], 1 << TELOPT_SGA % 32);
socket_printf(ses, 3, "%c%c%c", IAC, DO, TELOPT_SGA);
telopt_debug(ses, "SENT IAC DO %s", telopt_table[TELOPT_SGA].name);
}
return 3;
}
int send_will_sga(struct session *ses, int cplen, unsigned char *cpsrc)
{
if (!HAS_BIT(ses->telopt_flag[TELOPT_SGA / 32], 1 << TELOPT_SGA % 32))
{
SET_BIT(ses->telopt_flag[TELOPT_SGA / 32], 1 << TELOPT_SGA % 32);
socket_printf(ses, 3, "%c%c%c", IAC, WILL, TELOPT_SGA);
telopt_debug(ses, "SENT IAC WILL %s", telopt_table[TELOPT_SGA].name);
}
return 3;
}
int mark_prompt(struct session *ses, int cplen, unsigned char *cpsrc)
{
SET_BIT(ses->telopts, TELOPT_FLAG_PROMPT);
return 2;
}
/*
TTYPE
*/
int recv_dont_ttype(struct session *ses, int cplen, unsigned char *cpsrc)
{
if (!check_all_events(ses, 0, 0, "IAC DONT TERMINAL TYPE"))
{
DEL_BIT(ses->telopts, TELOPT_FLAG_TTYPE);
DEL_BIT(ses->telopt_flag[cpsrc[2] / 32], 1 << cpsrc[2] % 32);
}
return 3;
}
int recv_sb_ttype(struct session *ses, int cplen, unsigned char *cpsrc)
{
if (!check_all_events(ses, 0, 1, "IAC SB TERMINAL TYPE", ntos(cpsrc[3])))
{
if (HAS_BIT(ses->telopts, TELOPT_FLAG_TTYPE) && getenv("TERM"))
{
socket_printf(ses, 6 + strlen(getenv("TERM")), "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 0, getenv("TERM"), IAC, SE);
telopt_debug(ses, "SENT IAC SB TTYPE %s", getenv("TERM"));
}
else
{
socket_printf(ses, 14, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE, 0, "TINTIN++", IAC, SE);
telopt_debug(ses, "SENT IAC SB TTYPE %s", "TINTIN++");
SET_BIT(ses->telopts, TELOPT_FLAG_TTYPE);
}
}
return 6;
}
/*
TSPEED
*/
int recv_sb_tspeed(struct session *ses, int cplen, unsigned char *cpsrc)
{
SET_BIT(ses->telopts, TELOPT_FLAG_TSPEED);
if (!check_all_events(ses, 0, 1, "IAC SB TSPEED", ntos(cpsrc[3])))
{
socket_printf(ses, 17, "%c%c%c%c%s%c%c", IAC, SB, TELOPT_TSPEED, 0, "38400,38400", IAC, SE);
telopt_debug(ses, "SENT IAC SB 0 %s 38400,38400 IAC SB", telopt_table[TELOPT_TSPEED].name);
}
return 6;
}
/*
NAWS
*/
int send_will_naws(struct session *ses, int cplen, unsigned char *cpsrc)
{
SET_BIT(ses->telopt_flag[TELOPT_NAWS / 32], 1 << TELOPT_NAWS % 32);
socket_printf(ses, 3, "%c%c%c", IAC, WILL, TELOPT_NAWS);
telopt_debug(ses, "SENT IAC WILL NAWS");
return 3;
}
int send_sb_naws(struct session *ses, int cplen, unsigned char *cpsrc)
{
int rows;
if (!HAS_BIT(ses->telopt_flag[TELOPT_NAWS / 32], 1 << TELOPT_NAWS % 32))
{
send_will_naws(ses, cplen, cpsrc);
}
rows = HAS_BIT(ses->flags, SES_FLAG_SPLIT) ? ses->bot_row - ses->top_row + 1 : ses->rows;
socket_printf(ses, 9, "%c%c%c%c%c%c%c%c%c", IAC, SB, TELOPT_NAWS, ses->cols / 256, ses->cols % 256, rows / 256, rows % 256, IAC, SE);
SET_BIT(ses->telopts, TELOPT_FLAG_NAWS);
telopt_debug(ses, "SENT IAC SB NAWS %d %d %d %d", ses->cols / 256, ses->cols % 256, ses->rows / 256, ses->rows % 256);
return 3;
}
/*
ECHO
*/
int send_echo_on(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, DONT, TELOPT_ECHO);
SET_BIT(ses->telopts, TELOPT_FLAG_ECHO);
telopt_debug(ses, "SENT IAC DONT ECHO");
return 3;
}
int send_echo_off(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, DO, TELOPT_ECHO);
DEL_BIT(ses->telopts, TELOPT_FLAG_ECHO);
telopt_debug(ses, "SENT IAC DO ECHO");
return 3;
}
int send_echo_will(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, WILL, TELOPT_ECHO);
DEL_BIT(ses->telopts, TELOPT_FLAG_ECHO);
telopt_debug(ses, "SENT IAC WILL ECHO");
return 3;
}
/*
IP
*/
int send_ip(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 5, "%c%c%c%c%c", IAC, IP, IAC, DO, TELOPT_TIMINGMARK);
telopt_debug(ses, "SENT IAC IP");
telopt_debug(ses, "SENT IAC DO TIMING MARK");
return 3;
}
/*
Automatic telopt handling
*/
int send_wont_telopt(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, WONT, cpsrc[2]);
telopt_debug(ses, "SENT IAC WONT %s", telopt_table[cpsrc[2]].name);
return 3;
}
int send_dont_telopt(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, DONT, cpsrc[2]);
telopt_debug(ses, "SENT IAC DONT %s", telopt_table[cpsrc[2]].name);
return 3;
}
int send_will_telopt(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, WILL, cpsrc[2]);
telopt_debug(ses, "SENT IAC WILL %s", telopt_table[cpsrc[2]].name);
return 3;
}
int send_do_telopt(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, DO, cpsrc[2]);
telopt_debug(ses, "SENT IAC DO %s", telopt_table[cpsrc[2]].name);
return 3;
}
/*
MSSP (Mud Server Status Protocol)
*/
int send_do_mssp(struct session *ses, int cplen, unsigned char *cpsrc)
{
if (!check_all_events(ses, 0, 0, "IAC WILL MSSP"))
{
if (HAS_BIT(ses->telopts, TELOPT_FLAG_DEBUG))
{
socket_printf(ses, 3, "%c%c%c", IAC, DO, TELOPT_MSSP);
telopt_debug(ses, "SENT IAC DO MSSP");
}
}
return 3;
}
int recv_sb_mssp(struct session *ses, int cplen, unsigned char *src)
{
char var[BUFFER_SIZE], val[BUFFER_SIZE];
char *pto;
int i;
var[0] = val[0] = i = 0;
if (skip_sb(ses, cplen, src) > cplen)
{
return cplen + 1;
}
while (i < cplen && src[i] != SE)
{
switch (src[i])
{
case MSSP_VAR:
i++;
pto = var;
while (i < cplen && src[i] >= 3 && src[i] != IAC)
{
*pto++ = src[i++];
}
*pto = 0;
break;
case MSSP_VAL:
i++;
pto = val;
while (i < cplen && src[i] >= 3 && src[i] != IAC)
{
*pto++ = src[i++];
}
*pto = 0;
telopt_debug(ses, "MSSP VAR %-20s VAL %s", var, val);
check_all_events(ses, 0, 2, "IAC SB MSSP", var, val);
break;
default:
i++;
break;
}
}
telopt_debug(ses, "IAC SB MSSP IAC SE");
check_all_events(ses, 0, 0, "IAC SB MSSP IAC SE");
return UMIN(i + 1, cplen);
}
/*
MSDP (Mud Server Data Protocol)
*/
#define MSDP_STATE_NEW 0
#define MSDP_STATE_VAR 1
#define MSDP_STATE_VAL 2
int recv_sb_msdp(struct session *ses, int cplen, unsigned char *src)
{
char buf[BUFFER_SIZE], var[BUFFER_SIZE], val[BUFFER_SIZE];
char *pto, *ptarr[1000];
int i, nest = 0, state[1000];
var[0] = val[0] = i = state[nest] = 0;
if (skip_sb(ses, cplen, src) > cplen)
{
return cplen + 1;
}
while (i < cplen && src[i] != SE)
{
switch (src[i])
{
case MSDP_VAR:
i++;
pto = buf;
while (i < cplen && src[i] > 4 && src[i] != IAC)
{
*pto++ = src[i++];
}
*pto = 0;
substitute(ses, buf, var, SUB_SEC);
state[nest] = 0;
break;
case MSDP_VAL:
pto = val;
while (i < cplen)
{
switch (src[i])
{
case MSDP_VAR:
if (state[nest] >= 2)
{
*pto++ = '}';
}
if (nest == 0)
{
goto end_val;
}
if (state[nest] >= 1)
{
*pto++ = '}';
}
*pto++ = '{';
state[nest] = 0;
break;
case MSDP_VAL:
if (state[nest] == 0)
{
if (nest)
{
*pto++ = '}';
*pto++ = '{';
}
ptarr[nest] = pto;
state[nest]++;
}
else if (state[nest] == 1)
{
sprintf(buf, "{1}{%.*s}{2}{", (int) (pto - ptarr[nest]), ptarr[nest]);
sprintf(ptarr[nest], "%s", buf);
pto += 9;
state[nest]++;
}
else
{
pto += sprintf(pto, "}{%d}{", ++state[nest]);
}
break;
case MSDP_OPEN:
if (++nest > 1)
{
*pto++ = '{';
}
state[nest] = 0;
break;
case MSDP_CLOSE:
if (state[nest] >= 2)
{
*pto++ = '}';
}
if (state[nest] >= 1)
{
*pto++ = '}';
}
if (--nest > 0)
{
*pto++ = '}';
}
break;
case IAC:
if (state[nest] >= 2)
{
*pto++ = '}';
}
goto end_val;
case '\\':
*pto++ = '\\';
*pto++ = '\\';
break;
case '{':
*pto++ = '\\';
*pto++ = 'x';
*pto++ = '7';
*pto++ = 'B';
break;
case '}':
*pto++ = '\\';
*pto++ = 'x';
*pto++ = '7';
*pto++ = 'D';
break;
case COMMAND_SEPARATOR:
*pto++ = '\\';
*pto++ = COMMAND_SEPARATOR;
break;
default:
*pto++ = src[i];
break;
}
i++;
}
end_val:
*pto = 0;
telopt_debug(ses, "IAC SB MSDP VAR %-20s VAL %s", var, val);
check_all_events(ses, 1, 2, "IAC SB MSDP %s", var, var, val);
check_all_events(ses, 0, 2, "IAC SB MSDP", var, val);
state[nest] = 0;
break;
default:
i++;
break;
}
}
telopt_debug(ses, "IAC SB MSDP IAC SE");
check_all_events(ses, 0, 0, "IAC SB MSDP IAC SE");
return UMIN(i + 1, cplen);
}
/*
NEW-ENVIRON
*/
int recv_sb_new_environ(struct session *ses, int cplen, unsigned char *src)
{
char buf[BUFFER_SIZE], var[BUFFER_SIZE], val[BUFFER_SIZE];
char *pto;
int i, x;
var[0] = val[0] = 0;
if (skip_sb(ses, cplen, src) > cplen)
{
return cplen + 1;
}
telopt_debug(ses, "IAC SB NEW-ENVIRON %d %d", src[3], src[4]);
i = 4;
x = -1;
while (i < cplen && src[i] != SE)
{
switch (src[i])
{
case ENV_VAR:
case ENV_USR:
x = src[i];
i++;
pto = buf;
while (i < cplen && src[i] >= 4 && src[i] != IAC)
{
*pto++ = src[i++];
}
*pto = 0;
substitute(ses, buf, var, SUB_SEC);
if (src[3] == ENV_SEND)
{
telopt_debug(ses, "IAC SB NEW-ENVIRON SEND %s", x ? "VAR" : "USR");
check_all_events(ses, 1, 2, "IAC SB NEW-ENVIRON SEND %s", x ? "VAR" : "USR", var, "");
}
break;
case ENV_VAL:
i++;
pto = buf;
while (i < cplen && src[i] >= 3 && src[i] != IAC)
{
*pto++ = src[i++];
}
*pto = 0;
substitute(ses, buf, val, SUB_SEC);
telopt_debug(ses, "IAC SB NEW-ENVIRON %s %s", src[3] ? "IS" : "INFO", x ? "VAR" : "USR");
check_all_events(ses, 2, 2, "IAC SB NEW-ENVIRON %s %s", src[i] ? "IS" : "INFO", x ? "VAR" : "USR", var, val);
break;
case IAC:
if (x == -1)
{
telopt_debug(ses, "IAC SB NEW-ENVIRON %s", src[3] == 0 ? "IS" : src[3] == 1 ? "SEND" : "INFO");
check_all_events(ses, 1, 2, "IAC SB NEW-ENVIRON %s", src[3] == 0 ? "IS" : src[3] == 1 ? "SEND" : "INFO", var, val);
}
i++;
break;
default:
i++;
break;
}
}
telopt_debug(ses, "IAC SB NEW-ENVIRON IAC SE");
check_all_events(ses, 0, 0, "IAC SB NEW-ENVIRON IAC SE");
return i + 1;
}
int recv_sb_zmp(struct session *ses, int cplen, unsigned char *src)
{
char buf[BUFFER_SIZE], var[BUFFER_SIZE], val[BUFFER_SIZE];
char *pto;
int i, x;
var[0] = val[0] = x = 0;
if (skip_sb(ses, cplen, src) > cplen)
{
return cplen + 1;
}
i = 3;
while (i < cplen && src[i] != SE)
{
switch (src[i])
{
case IAC:
i++;
break;
default:
pto = buf;
while (i < cplen && src[i])
{
*pto++ = src[i++];
}
*pto = src[i++];
substitute(ses, buf, x ? val : var, SUB_SEC);
if (x++)
{
telopt_debug(ses, "IAC SB ZMP %s", var);
check_all_events(ses, 1, 1, "IAC SB ZMP %s", var, val);
}
break;
}
}
telopt_debug(ses, "IAC SB ZMP %s IAC SE", var);
check_all_events(ses, 1, 0, "IAC SB ZMP %s IAC SE", var);
return UMIN(i + 1, cplen);
}
int recv_sb_gmcp(struct session *ses, int cplen, unsigned char *src)
{
char mod[BUFFER_SIZE], val[BUFFER_SIZE], json[BUFFER_SIZE], *pto;
int i, state[1000], nest, type;
push_call("recv_sb_gmcp(%p,%d,%p)",ses,cplen,src);
if (skip_sb(ses, cplen, src) > cplen)
{
pop_call();
return cplen + 1;
}
mod[0] = val[0] = state[0] = nest = type = 0;
i = 3;
pto = mod;
// space out
while (i < cplen && src[i] == ' ')
{
i++;
}
// grab module
while (i < cplen && src[i] != IAC)
{
if (src[i] == ' ' || src[i] == '{' || src[i] == '[')
{
break;
}
*pto++ = src[i++];
}
*pto = 0;
// parse JSON content
pto = val;
while (i < cplen && src[i] != IAC)
{
switch (src[i])
{
case ' ':
i++;
break;
case '{':
if (nest != 0)
{
*pto++ = '{';
}
i++;
state[++nest] = 0;
break;
case '}':
nest--;
i++;
if (nest != 0)
{
*pto++ = '}';
}
break;
case '[':
if (nest != 0)
{
*pto++ = '{';
}
i++;
state[++nest] = 1;
pto += sprintf(pto, "{%d}", state[nest]);
break;
case ']':
nest--;
i++;
if (nest != 0)
{
*pto++ = '}';
}
break;
case ':':
i++;
break;
case ',':
i++;
if (state[nest])
{
pto += sprintf(pto, "{%d}", ++state[nest]);
}
break;
case '"':
i++;
if (nest)
{
*pto++ = '{';
}
type = 1;
while (i < cplen && src[i] != IAC && type == 1)
{
switch (src[i])
{
case '\\':
i++;
if (i < cplen && src[i] == '"')
{
*pto++ = src[i++];
}
else
{
*pto++ = '\\';
*pto++ = '\\';
}
break;
case '"':
i++;
type = 0;
break;
case '{':
i++;
*pto++ = '\\';
*pto++ = 'x';
*pto++ = '7';
*pto++ = 'B';
break;
case '}':
i++;
*pto++ = '\\';
*pto++ = 'x';
*pto++ = '7';
*pto++ = 'D';
break;
case COMMAND_SEPARATOR:
i++;
*pto++ = '\\';
*pto++ = COMMAND_SEPARATOR;
break;
default:
*pto++ = src[i++];
break;
}
}
if (nest)
{
*pto++ = '}';
}
break;
default:
if (nest)
{
*pto++ = '{';
}
type = 1;
while (i < cplen && src[i] != IAC && type == 1)
{
switch (src[i])
{
case '}':
case ']':
case ',':
case ':':
type = 0;
break;
case ' ':
i++;
break;
default:
*pto++ = src[i++];
break;
}
}
if (nest)
{
*pto++ = '}';
}
break;
}
}
*pto = 0;
// Raw json data for debugging purposes.
pto = json;
i = 3;
while (i < cplen && src[i] != IAC)
{
switch (src[i])
{
case '\\':
i++;
*pto++ = '\\';
*pto++ = '\\';
break;
case '{':
i++;
*pto++ = '\\';
*pto++ = 'x';
*pto++ = '7';
*pto++ = 'B';
break;
case '}':
i++;
*pto++ = '\\';
*pto++ = 'x';
*pto++ = '7';
*pto++ = 'D';
break;
case COMMAND_SEPARATOR:
i++;
*pto++ = '\\';
*pto++ = COMMAND_SEPARATOR;
break;
default:
*pto++ = src[i++];
break;
}
}
*pto = 0;
while (i < cplen && src[i] != SE)
{
i++;
}
telopt_debug(ses, "IAC SB GMCP %s IAC SE", mod);
check_all_events(ses, 1, 2, "IAC SB GMCP %s IAC SE", mod, val, json);
pop_call();
return UMIN(i + 1, cplen);
}
/*
MCCP
*/
int send_do_mccp2(struct session *ses, int cplen, unsigned char *cpsrc)
{
if (HAS_BIT(ses->flags, SES_FLAG_MCCP))
{
socket_printf(ses, 3, "%c%c%c", IAC, DO, TELOPT_MCCP2);
telopt_debug(ses, "SENT IAC DO MCCP2");
}
else
{
socket_printf(ses, 3, "%c%c%c", IAC, WONT, TELOPT_MCCP2);
telopt_debug(ses, "SENT IAC WONT MCCP2 (MCCP HAS BEEN DISABLED)");
}
return 3;
}
int send_dont_mccp2(struct session *ses, int cplen, unsigned char *cpsrc)
{
socket_printf(ses, 3, "%c%c%c", IAC, DONT, TELOPT_MCCP2);
telopt_debug(ses, "SENT DONT MCCP2");
return 3;
}
int init_mccp(struct session *ses, int cplen, unsigned char *cpsrc)
{
if (ses->mccp)
{
return 5;
}
ses->mccp = (z_stream *) calloc(1, sizeof(z_stream));
ses->mccp->data_type = Z_ASCII;
ses->mccp->zalloc = zlib_alloc;
ses->mccp->zfree = zlib_free;
ses->mccp->opaque = NULL;
if (inflateInit(ses->mccp) != Z_OK)
{
tintin_puts2(ses, "#FAILED TO INITIALIZE MCCP.");
send_dont_mccp2(ses, 0, NULL);
free(ses->mccp);
ses->mccp = NULL;
}
else
{
telopt_debug(ses, "MCCP INITIALIZED.");
}
return 5;
}
void *zlib_alloc(void *opaque, unsigned int items, unsigned int size)
{
return calloc(items, size);
}
void zlib_free(void *opaque, void *address)
{
free(address);
}
void init_telnet_session(struct session *ses)
{
// send_do_sga(ses, 0, NULL);
// send_will_naws(ses, 0, NULL);
}
/*
Returns the length of a telnet subnegotiation
*/
int skip_sb(struct session *ses, int cplen, unsigned char *cpsrc)
{
int i;
for (i = 1 ; i < cplen ; i++)
{
if (cpsrc[i] == SE && cpsrc[i-1] == IAC)
{
return i + 1;
}
}
telopt_debug(ses, "SKIP SB (%d)", cplen);
return cplen + 1;
}
int recv_sb(struct session *ses, int cplen, unsigned char *cpsrc)
{
char *pt1, *pt2, var1[BUFFER_SIZE], var2[BUFFER_SIZE];
int i;
if (skip_sb(ses, cplen, cpsrc) > cplen)
{
return cplen + 1;
}
pt1 = var1;
pt2 = var2;
for (i = 3 ; i < cplen ; i++)
{
if (cpsrc[i] == IAC && i + 1 < cplen && cpsrc[i+1] == SE)
{
break;
}
else
{
*pt1++ = cpsrc[i];
sprintf(pt2, "%03d ", cpsrc[i]);
pt2 += 4;
}
}
*pt1 = 0;
*pt2 = 0;
check_all_events(ses, 1, 2, "IAC SB %s", telopt_table[cpsrc[2]].name, var1, var2);
return i + 2;
}