257 lines
8.8 KiB
C
257 lines
8.8 KiB
C
/*
|
|
* Copyright (c) 2018 Lars Thrane A/S
|
|
* SPDX-License-Identifier: GPL-2.0
|
|
*
|
|
* Telnet RFC-2217 handling - server
|
|
*/
|
|
#include <arpa/inet.h>
|
|
|
|
#include "lib/nvt_log.h"
|
|
#include "telnet.h"
|
|
#include "telnet_param.h"
|
|
|
|
void telnet_rfc2217_init(tn_ctx_t *tcc, int when)
|
|
{
|
|
switch (when)
|
|
{
|
|
default: /* Ignore */
|
|
break;
|
|
case TNI_INIT: /* Init context */
|
|
tcc->baudrate = 115200;
|
|
tcc->datasize = 8; /* 8 bit */
|
|
tcc->parity = 1; /* None */
|
|
tcc->stopsize = 1; /* 1 bit */
|
|
|
|
tcc->linestate = 0;
|
|
tcc->modemstate = 0;
|
|
|
|
tcc->linestate_mask = 0;
|
|
tcc->modemstate_mask = 0xff;
|
|
|
|
tcc->dtr_state = 0;
|
|
tcc->rts_state = 0;
|
|
break;
|
|
case TNI_ON: /* RFC-2217 activated */
|
|
tcc->mode_rfc2217 = 1;
|
|
telnet_rfc2217_change_modemstate(tcc, tcc->modemstate);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt,
|
|
unsigned char *pu, int nd)
|
|
{
|
|
unsigned int sub, vali, valo, val4;
|
|
|
|
sub = pu[0];
|
|
switch (sub)
|
|
{
|
|
default:
|
|
// case 20-127: /* Available for Future Use */
|
|
nvt_log(LOG_WARNING, "Unhandled SB: Opt/prm = %d/%d", opt, sub);
|
|
break;
|
|
case 0: /* SIGNATURE */
|
|
telnet_reply_opt(tcc, opt, 100 + sub, "LT", 2);
|
|
DBG("RFC2217: Set signature");
|
|
break;
|
|
case 1: /* SET-BAUDRATE */
|
|
if (nd < 4)
|
|
break; /* Incorrect */
|
|
vali = (pu[1] << 24) | (pu[2] << 16) | (pu[3] << 8) | pu[4];
|
|
if (vali != 0) /* Set value */
|
|
tcc->baudrate = vali;
|
|
valo = tcc->baudrate;
|
|
DBG("RFC2217: Set baud %u -> %u", vali, valo);
|
|
val4 = htonl(valo);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &val4, 4);
|
|
break;
|
|
case 2: /* SET-DATASIZE */
|
|
valo = vali = pu[1];
|
|
#if 0 /* Request only */
|
|
if (vali != 0) /* Set value */
|
|
tcc->datasize = vali;
|
|
else /* Request value */
|
|
#endif
|
|
valo = tcc->datasize;
|
|
DBG("RFC2217: Set data %u -> %u", vali, valo);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
break;
|
|
case 3: /* SET-PARITY */
|
|
valo = vali = pu[1];
|
|
#if 0 /* Request only */
|
|
if (vali != 0) /* Set value */
|
|
tcc->parity = vali;
|
|
else
|
|
#endif
|
|
valo = tcc->parity;
|
|
DBG("RFC2217: Set parity %u -> %u", vali, valo);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
break;
|
|
case 4: /* SET-STOPSIZE */
|
|
valo = vali = pu[1];
|
|
#if 0 /* Request only */
|
|
if (vali != 0) /* Set value */
|
|
tcc->stopsize = vali;
|
|
else
|
|
#endif
|
|
valo = tcc->stopsize;
|
|
DBG("RFC2217: Set stop %u -> %u", vali, valo);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
break;
|
|
case 5: /* SET-CONTROL */
|
|
vali = pu[1];
|
|
valo = 0;
|
|
switch (vali)
|
|
{
|
|
default:
|
|
nvt_log(LOG_WARNING, "Unexpected SB: Opt/prm/val = %d/%d/%u",
|
|
opt, sub, vali);
|
|
break;
|
|
|
|
case 0: /* Request Com Port Flow Control Setting
|
|
(outbound/both) */
|
|
case 1: /* Use No Flow Control (outbound/both) */
|
|
valo = 1; /* No flow control */
|
|
goto do_ctl_reply;
|
|
case 2: /* Use XON/XOFF Flow Control (outbound/both) */
|
|
valo = 1; /* No flow control */
|
|
nvt_log(LOG_WARNING, "RFC2217: Flow O %d -> %d", vali, valo);
|
|
goto do_ctl_reply;
|
|
case 3: /* Use HARDWARE Flow Control (outbound/both) */
|
|
valo = 3; /* HW flow control */
|
|
goto do_ctl_reply;
|
|
|
|
case 4: /* Request BREAK State */
|
|
valo = 6; /* BREAK off */
|
|
nvt_log(LOG_WARNING, "RFC2217: Break st %d -> %d", vali, valo);
|
|
goto do_ctl_reply;
|
|
case 5: /* Set BREAK State ON */
|
|
case 6: /* Set BREAK State OFF */
|
|
valo = 6; /* BREAK off */
|
|
goto do_ctl_reply;
|
|
|
|
case 7: /* Request DTR Signal State */
|
|
valo = (tcc->dtr_state) ? 8 : 9;
|
|
goto do_ctl_reply;
|
|
case 8: /* Set DTR Signal State ON */
|
|
case 9: /* Set DTR Signal State OFF */
|
|
valo = vali;
|
|
tcc->dtr_state = (valo == 8) ? 1 : 0;
|
|
nvt_log(LOG_INFO, "RFC2217: Set DTR %s",
|
|
tcc->dtr_state ? "on" : "off");
|
|
goto do_ctl_reply;
|
|
|
|
case 10: /* Request RTS Signal State */
|
|
valo = (tcc->rts_state) ? 11 : 12;
|
|
goto do_ctl_reply;
|
|
case 11: /* Set RTS Signal State ON */
|
|
case 12: /* Set RTS Signal State OFF */
|
|
valo = vali;
|
|
tcc->rts_state = (valo == 11) ? 1 : 0;
|
|
nvt_log(LOG_INFO, "RFC2217: Set RTS %s",
|
|
tcc->rts_state ? "on" : "off");
|
|
goto do_ctl_reply;
|
|
|
|
case 13: /* Request Com Port Flow Control Setting (inbound) */
|
|
case 14: /* Use No Flow Control (inbound) */
|
|
valo = 14; /* No flow control */
|
|
goto do_ctl_reply;
|
|
case 16: /* Use HARDWARE Flow Control (inbound) */
|
|
valo = 16; /* HW flow control */
|
|
goto do_ctl_reply;
|
|
case 15: /* Use XON/XOFF Flow Control (inbound) */
|
|
case 17: /* Use DCD Flow Control (outbound/both) */
|
|
case 18: /* Use DTR Flow Control (inbound) */
|
|
case 19: /* Use DSR Flow Control (outbound/both) */
|
|
valo = 14; /* No flow control */
|
|
nvt_log(LOG_WARNING, "RFC2217: Flow I %d -> %d", vali, valo);
|
|
goto do_ctl_reply;
|
|
|
|
do_ctl_reply:
|
|
DBG("RFC2217: Set control %u -> %u", vali, valo);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
switch (vali)
|
|
{
|
|
default: /* Do noting */
|
|
break;
|
|
case 8: /* Set DTR Signal State ON */
|
|
case 9: /* Set DTR Signal State OFF */
|
|
if (tcc->cli_ss)
|
|
tcc->cli_ss(tcc->cctx, TNS_DTR, tcc->dtr_state);
|
|
break;
|
|
case 11: /* Set RTS Signal State ON */
|
|
case 12: /* Set RTS Signal State OFF */
|
|
if (tcc->cli_ss)
|
|
tcc->cli_ss(tcc->cctx, TNS_RTS, tcc->rts_state);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
#if 0 /* Server -> client only */
|
|
case 6: /* NOTIFY-LINESTATE */
|
|
case 7: /* NOTIFY-MODEMSTATE */
|
|
break;
|
|
#endif
|
|
case 8: /* FLOWCONTROL-SUSPEND */
|
|
case 9: /* FLOWCONTROL-RESUME */
|
|
nvt_log(LOG_WARNING, "RFC2217: Flowcontrol %s",
|
|
(sub == 8) ? "suspend" : "resume");
|
|
/* NB! Do not reply */
|
|
break;
|
|
case 10: /* SET-LINESTATE-MASK */
|
|
valo = vali = pu[1];
|
|
tcc->linestate_mask = vali;
|
|
DBG("RFC2217: Set linestate mask 0x%02x", vali);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
break;
|
|
case 11: /* SET-MODEMSTATE-MASK */
|
|
valo = vali = pu[1];
|
|
tcc->modemstate_mask = vali;
|
|
DBG("RFC2217: Set modemstate mask 0x%02x", vali);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
break;
|
|
case 12: /* PURGE-DATA */
|
|
valo = vali = pu[1];
|
|
DBG("RFC2217: Purge %d", vali);
|
|
telnet_reply_opt(tcc, opt, 100 + sub, &valo, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void telnet_rfc2217_change_linestate(tn_ctx_t *tcc, int mask)
|
|
{
|
|
unsigned char buf[1];
|
|
|
|
if (!tcc->mode_rfc2217)
|
|
goto done;
|
|
|
|
buf[0] = mask & tcc->linestate_mask;
|
|
buf[0] &= 0xf0; /* Drop change bits */
|
|
DBG("RFC2217: Notify linestate 0x%02x->0x%02x: 0x%02x",
|
|
tcc->linestate, mask, buf[0]);
|
|
|
|
telnet_reply_opt(tcc, TNO_CPCO, 100 + 6, buf, 1);
|
|
|
|
done:
|
|
tcc->linestate = mask;
|
|
}
|
|
|
|
void telnet_rfc2217_change_modemstate(tn_ctx_t *tcc, int mask)
|
|
{
|
|
unsigned char buf[1];
|
|
|
|
if (!tcc->mode_rfc2217)
|
|
goto done;
|
|
|
|
buf[0] = mask & tcc->modemstate_mask;
|
|
DBG("RFC2217: Notify modemstate 0x%02x->0x%02x: 0x%02x",
|
|
tcc->modemstate, mask, buf[0]);
|
|
|
|
telnet_reply_opt(tcc, TNO_CPCO, 100 + 7, buf, 1);
|
|
|
|
done:
|
|
tcc->modemstate = mask;
|
|
}
|