nvtsrv: Add test server

This commit is contained in:
Kim Woelders
2019-08-30 04:21:54 +02:00
parent d0bf492b10
commit 4e14ef1956
8 changed files with 957 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
bin_PROGRAMS = nvtsrv
nvtsrv_SOURCES = \
nvtsrv.c \
telnet_basic.c telnet_server_rfc2217.c telnet.h telnet_param.h
nvtsrv_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(CFLAGS_WARN)
nvtsrv_LDADD = $(top_builddir)/lib/libnvt.la
+221
View File
@@ -0,0 +1,221 @@
/*
* RFC2217 test server
*/
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "lib/nvt_log.h"
#include "lib/nvt_socket.h"
#include "telnet.h"
#define MATCH(s1, s2) (strcmp(s1, s2) == 0)
struct {
unsigned char mline;
tn_ctx_t *tnct;
} dd;
struct pollfd pfds[3];
int nfds;
#define fd_srv pfds[1].fd
#define fd_cli pfds[2].fd
static void _nvtsrv_usage(void)
{
printf("Usage:\n" " nvtsrv host:port\n");
exit(0);
}
static void _tn_cli_close(void)
{
int i = 2;
printf("%d: fd=%d: Closed\n", i, pfds[i].fd);
close(pfds[i].fd);
pfds[i].fd = -1;
nfds = 2;
}
static void _tn_tx(void *cctx, const void *buf, int len)
{
int nw;
nvt_log_buf(LOG_INFO, "Tx", buf, len);
if (fd_cli < 0)
{
printf("Write fd is closed - drop Tx\n");
return;
}
nw = write(fd_cli, buf, len);
if (nw != len)
{
printf("Write failed %d != %d\n", nw, len);
_tn_cli_close();
}
}
static void _tn_ss(void *cctx, int signal, int value)
{
printf("RFC2217 signal %d: %02x", signal, value);
}
static void _nvtsrv_init(void)
{
free(dd.tnct);
memset(&dd, 0, sizeof(dd));
dd.tnct = telnet_ctx_init(NULL, _tn_tx, _tn_ss);
}
static void _nvtsrv_cmd(tn_ctx_t * tcc, const char *line)
{
char cmd[8];
const char *args;
int len, val;
cmd[0] = '\0';
len = 0;
sscanf(line, "%7s %n", cmd, &len);
args = line + len;
if (MATCH(cmd, "w"))
{
_tn_tx(NULL, args, strlen(args));
}
else if (MATCH(cmd, "m"))
{
sscanf(args, "%x", &val);
telnet_rfc2217_change_modemstate(tcc, val);
}
}
static void _nvtsrv_set_signal_handlers(void)
{
struct sigaction sa = {
.sa_handler = SIG_IGN,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
sigaction(SIGPIPE, &sa, NULL);
}
int main(int argc, char **argv)
{
const char *s;
char buf[128];
int rc, nr, i, len;
for (;;)
{
s = *argv++;
argc--;
if (*s != '-')
break;
}
if (argc <= 0)
_nvtsrv_usage();
nvt_log_level(0, 10);
nvt_log_dest(NVT_LOG_STDOUT);
_nvtsrv_set_signal_handlers();
memset(pfds, 0, sizeof(pfds));
pfds[0].fd = STDIN_FILENO;
pfds[0].events = POLLIN;
pfds[1].fd = socket_create_server(argv[0]);
pfds[1].events = POLLIN;
pfds[2].fd = -1;
pfds[2].events = POLLIN;
nfds = 2;
for (;;)
{
for (i = 0; i < 3; i++)
pfds[i].revents = 0;
rc = poll(pfds, nfds, -1);
if (rc < 0)
{
printf("poll failed: %m\n");
break;
}
i = 0;
if (pfds[i].revents)
{
nr = read(pfds[i].fd, buf, sizeof(buf) - 1);
if (nr <= 0)
break;
buf[nr] = 0;
len = strcspn(buf, "\n\r");
buf[len] = 0;
// printf("%d: fd=%d: r %d: '%.*s'\n", i, pfds[i].fd, nr, nr, buf);
_nvtsrv_cmd(dd.tnct, buf);
}
i = 2;
if (pfds[i].revents)
{
nr = 0;
if (pfds[i].revents & POLLIN)
{
nr = read(pfds[i].fd, buf, sizeof(buf));
if (nr <= 0)
{
_tn_cli_close();
continue;
}
}
if (pfds[i].revents & (POLLERR | POLLHUP))
{
_tn_cli_close();
}
if (nr > 0)
{
buf[nr] = 0;
len = strcspn(buf, "\n\r");
buf[len] = 0;
nvt_log_buf(LOG_INFO, "Rx", buf, nr);
// printf("%d: fd=%d: r %d: '%.*s'\n", i, pfds[i].fd, nr, nr, buf);
len = nr;
rc = telnet_rx(dd.tnct, buf, &len);
printf("%d: fd=%d: r %d: '%.*s'\n", i, pfds[i].fd, rc, len,
buf);
}
}
i = 1;
if (pfds[i].revents)
{
if (pfds[2].fd >= 0)
{
printf("Client already connected on fd=%d\n", pfds[2].fd);
break;
}
pfds[2].fd = socket_create_server_client(pfds[i].fd);
printf("%d: fd=%d: Connected\n", 2, pfds[2].fd);
pfds[2].events = POLLIN;
nfds = 3;
_nvtsrv_init();
}
}
return 0;
}
+63
View File
@@ -0,0 +1,63 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet interface - server
*/
#ifndef TELNET_SERVER_H
#define TELNET_SERVER_H
typedef void (cli_tx_f) (void *cctx, const void *buf, int len);
typedef void (cli_ss_f) (void *cctx, int signal, int value);
/* Telnet context */
typedef struct {
void *cctx; /* Client context */
cli_tx_f *cli_tx; /* Write to client */
cli_ss_f *cli_ss; /* Signal from client */
unsigned int bytes_rx; /* Rx byte count */
unsigned int off_rx; /* Rx buffer offset */
unsigned char mode_telnet; /* Telnet mode */
unsigned char mode_rfc2217; /* RFC-2217 mode */
unsigned int baudrate;
unsigned char datasize;
unsigned char parity;
unsigned char stopsize;
unsigned char linestate;
unsigned char modemstate;
unsigned char linestate_mask;
unsigned char modemstate_mask;
char dtr_state;
char rts_state;
} tn_ctx_t;
extern tn_ctx_t *telnet_ctx_init(void *cctx, cli_tx_f * ftx, cli_ss_f * fss);
extern int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen);
extern int telnet_tx(tn_ctx_t * tcc, const char *buf, int len);
extern void telnet_reply_opt(tn_ctx_t * tcc, int opt, int prm,
const void *val, int len);
extern void telnet_rfc2217_init(tn_ctx_t * tcc, int when);
extern void telnet_rfc2217_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd);
extern void telnet_rfc2217_change_linestate(tn_ctx_t * tcc, int mask);
extern void telnet_rfc2217_change_modemstate(tn_ctx_t * tcc, int mask);
/* Init modes */
#define TNI_INIT 0 /* Init context */
#define TNI_ON 1 /* Option activated */
/* Signals that may be passed to cli_ss_f() */
#define TNS_DTR 1
#define TNS_RTS 2
#endif /* TELNET_SERVER_H */
+275
View File
@@ -0,0 +1,275 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet protocol handling - server
*/
#include <stdlib.h>
#include <string.h>
#include "lib/nvt_log.h"
#include "telnet.h"
#include "telnet_param.h"
/* Telnet commands */
static const char *const tn_cmd[] = {
"SE", "NOP", "MARK", "BRK", "IP", "AO", "AYT", "EC",
"EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC"
};
#define OPT_NACK 0 /* Reject (DONT/WONT) */
#define OPT_IGNORE 1 /* Ignore the command (?) */
#define OPT_ACK 2 /* Acknowledge (DO /WILL) */
/* Telnet option support */
static const unsigned char tn_opt[256] = {
/* *INDENT-OFF* */
[TNO_BINARY] = OPT_ACK,
[TNO_SUPP_GA] = OPT_ACK,
// [TNO_TTYP] = OPT_NACK,
// [TNO_NAWS] = OPT_NACK,
// [TNO_NEO] = OPT_NACK,
[TNO_CPCO] = OPT_ACK,
// [TNO_KERMIT] = OPT_NACK,
/* *INDENT-ON* */
};
static void _telnet_handle_req(tn_ctx_t * tcc, int cmd, int opt)
{
char buf[3];
switch (tn_opt[opt])
{
default:
case OPT_IGNORE:
return;
case OPT_ACK:
cmd = (cmd == TNC_FB_WILL) ? TNC_FD_DO : TNC_FB_WILL;
break;
case OPT_NACK:
cmd = (cmd == TNC_FB_WILL) ? TNC_FE_DONT : TNC_FC_WONT;
break;
}
buf[0] = TNC_FF_IAC;
buf[1] = cmd;
buf[2] = opt;
DBG("TNo: Op2 %02x %02x: %-4s %d", cmd, opt, tn_cmd[cmd & 0xf], opt);
tcc->cli_tx(tcc->cctx, buf, 3);
if (opt == TNO_CPCO && (cmd == TNC_FB_WILL || cmd == TNC_FD_DO))
telnet_rfc2217_init(tcc, TNI_ON);
}
void telnet_reply_opt(tn_ctx_t * tcc, int opt, int prm,
const void *val, int len)
{
unsigned char buf[128], *pu = buf;
const unsigned char *pv;
int i;
*pu++ = TNC_FF_IAC;
*pu++ = TNC_FA_SB;
*pu++ = opt;
*pu++ = prm;
if (len > 0 && len <= 32)
{
pv = val;
for (i = 0; i < len; i++)
{
*pu++ = *pv;
if (*pv++ == TNC_FF_IAC)
*pu++ = TNC_FF_IAC;
}
}
*pu++ = TNC_FF_IAC;
*pu++ = TNC_F0_SE;
len = pu - buf;
DBG2_BUF("opto", buf, len);
tcc->cli_tx(tcc->cctx, buf, len);
}
static void _telnet_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd)
{
DBG2_BUF("opti", pu, nd);
switch (opt)
{
default:
nvt_log(LOG_WARNING, "Unexpected SB: Opt = %d", opt);
break;
case TNO_CPCO: /* RFC 2217 */
tcc->mode_rfc2217 = 1;
telnet_rfc2217_handle_opt(tcc, opt, pu, nd);
break;
}
}
int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen)
{
char *s, *p;
unsigned char *pu;
int len, rem;
int np, nd;
int b1, b2;
/* Drop telnet processing entirely if we haven't seen any telnet
* negotiation in the beginning */
if (tcc->bytes_rx >= 32 && !tcc->mode_telnet)
return 0;
len = *plen;
tcc->bytes_rx += len;
if (len < (int)tcc->off_rx)
tcc->off_rx = 0;
for (s = buf + tcc->off_rx, rem = len - tcc->off_rx; rem > 0; s = p)
{
DBG2_BUF("****", s, rem);
p = memchr(s, TNC_FF_IAC, rem);
if (!p)
break;
/* We have IAC (= 0xff) */
np = rem - (p - s); /* N. chars available incl. IAC */
if (np < 2)
goto tn_more;
pu = (unsigned char *)p;
b1 = pu[1];
switch (b1)
{
default:
case TNC_F0_SE:
case TNC_F1_NOP:
case TNC_F2_MARK:
case TNC_F3_BRK:
case TNC_F4_IP:
case TNC_F5_AO:
case TNC_F6_AYT:
case TNC_F7_EC:
case TNC_F8_EL:
case TNC_F9_GA:
DBG("TNi: Op1 %02x : %s", b1, tn_cmd[b1 & 0xf]);
nd = 2; /* Drop 2 bytes */
goto tn_drop;
case TNC_FB_WILL:
case TNC_FC_WONT:
case TNC_FD_DO:
case TNC_FE_DONT:
if (np < 3)
goto tn_more; /* Need more */
tcc->mode_telnet = 1; /* Any such sequence activates TN */
b2 = pu[2];
DBG("TNi: Op2 %02x %02x: %-4s %d", b1, b2, tn_cmd[b1 & 0xf], b2);
if (b1 == TNC_FB_WILL || b1 == TNC_FD_DO)
_telnet_handle_req(tcc, b1, b2);
nd = 3; /* Drop 3 bytes */
goto tn_drop;
case TNC_FA_SB:
for (nd = 2; nd < np - 1; nd++)
{
if (pu[nd] == TNC_FF_IAC && pu[nd + 1] == TNC_F0_SE)
{
nd -= 2;
if (nd <= 0)
break;
b2 = pu[2];
tcc->mode_telnet = 1; /* Any such sequence activates TN */
DBG("TNi: Op2 %02x %02x: %-4s %d",
b1, b2, tn_cmd[b1 & 0xf], b2);
_telnet_handle_opt(tcc, b2, pu + 3, nd - 1);
nd += 4;
goto tn_drop;
}
}
if (np < 16)
goto tn_more; /* Not done */
/* This does not look right */
nvt_log_buf(LOG_WARNING, "tnrx", p, np);
rem = 0; /* Quit loop */
break;
case TNC_FF_IAC:
p++;
nd = 1; /* Drop 1 bytes */
goto tn_drop;
tn_drop:
if (!tcc->mode_telnet)
goto tn_break;
DBG2_BUF("drop", p, nd);
rem -= nd + (p - s);
len -= nd;
memmove(p, p + nd, rem);
break;
tn_break:
rem = 0; /* Quit loop */
break;
tn_more: /* Request more data */
tcc->off_rx = len - np;
*plen = len;
return 1;
}
}
tcc->off_rx = 0;
*plen = len;
return 0;
}
int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
{
const char *s, *p;
int rem, nd;
for (s = buf, rem = len; rem > 0; s = p)
{
if (tcc->mode_telnet)
p = memchr(s, TNC_FF_IAC, rem);
else
p = NULL;
if (!p)
{
/* Send the rest */
nd = rem;
tcc->cli_tx(tcc->cctx, s, nd);
}
else
{
nd = p - s + 1;
tcc->cli_tx(tcc->cctx, s, nd);
tcc->cli_tx(tcc->cctx, p, 1); /* One TNC_FF_IAC */
p++;
}
rem -= nd;
}
return 0;
}
tn_ctx_t *telnet_ctx_init(void *cctx, cli_tx_f * ftx, cli_ss_f * fss)
{
tn_ctx_t *tcc;
tcc = calloc(1, sizeof(tn_ctx_t));
tcc->cctx = cctx;
tcc->cli_tx = ftx; /* Write to client */
tcc->cli_ss = fss; /* Set state (line/modem) */
telnet_rfc2217_init(tcc, TNI_INIT);
return tcc;
}
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet parameters
*/
#ifndef TELNET_PARAM_H
#define TELNET_PARAM_H
/*
* Telnet commands
*/
#define TNC_F0_SE (0xf0) /* End of subnegotiation parameters */
#define TNC_F1_NOP (0xf1) /* No operation */
#define TNC_F2_MARK (0xf2) /* The data stream portion of a Synch.
This should always be accompanied
by a TCP Urgent notification. */
#define TNC_F3_BRK (0xf3) /* NVT character BRK. */
#define TNC_F4_IP (0xf4) /* The function IP (interrupt process) */
#define TNC_F5_AO (0xf5) /* The function AO (abort output) */
#define TNC_F6_AYT (0xf6) /* The function AYT (are you there) */
#define TNC_F7_EC (0xf7) /* The function EC (erase charceter) */
#define TNC_F8_EL (0xf8) /* The function EL (erase line) */
#define TNC_F9_GA (0xf9) /* The GA signal (go ahead) */
#define TNC_FA_SB (0xfa) /* Subnegotiation begin */
#define TNC_FB_WILL (0xfb) /* Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option. */
#define TNC_FC_WONT (0xfc) /* Indicates the refusal to perform,
or continue performing, the
indicated option. */
#define TNC_FD_DO (0xfd) /* Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option. */
#define TNC_FE_DONT (0xfe) /* Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option. */
#define TNC_FF_IAC (0xff) /* Interpret as Command / data byte */
/*
* Telnet options
*/
/*
* https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
*
* 0 00 Binary Transmission [RFC856]
* 1 01 Echo [RFC857]
* 2 02 Reconnection [DDN Protocol Handbook, ... ]
* 3 03 Suppress Go Ahead [RFC858]
* 4 04 Approx Message Size Negotiation ["The Ethernet, ..." ]
* 5 05 Status [RFC859]
* 6 06 Timing Mark [RFC860]
* 7 07 Remote Controlled Trans and Echo [RFC726]
* 8 08 Output Line Width [DDN Protocol Handbook, ... ]
* 9 09 Output Page Size [DDN Protocol Handbook, ... ]
* 10 0a Output Carriage-Return Disposition [RFC652]
* 11 0b Output Horizontal Tab Stops [RFC653]
* 12 0c Output Horizontal Tab Disposition [RFC654]
* 13 0d Output Formfeed Disposition [RFC655]
* 14 0e Output Vertical Tabstops [RFC656]
* 15 0f Output Vertical Tab Disposition [RFC657]
* 16 10 Output Linefeed Disposition [RFC658]
* 17 11 Extended ASCII [RFC698]
* 18 12 Logout [RFC727]
* 19 13 Byte Macro [RFC735]
* 20 14 Data Entry Terminal [RFC1043][RFC732]
* 21 15 SUPDUP [RFC736][RFC734]
* 22 16 SUPDUP Output [RFC749]
* 23 17 Send Location [RFC779]
* 24 18 Terminal Type [RFC1091]
* 25 19 End of Record [RFC885]
* 26 1a TACACS User Identification [RFC927]
* 27 1b Output Marking [RFC933]
* 28 1c Terminal Location Number [RFC946]
* 29 1d Telnet 3270 Regime [RFC1041]
* 30 1e X.3 PAD [RFC1053]
* 31 1f Negotiate About Window Size [RFC1073]
* 32 20 Terminal Speed [RFC1079]
* 33 21 Remote Flow Control [RFC1372]
* 34 22 Linemode [RFC1184]
* 35 23 X Display Location [RFC1096]
* 36 24 Environment Option [RFC1408]
* 37 25 Authentication Option [RFC2941]
* 38 26 Encryption Option [RFC2946]
* 39 27 New Environment Option [RFC1572]
* 40 28 TN3270E [RFC2355]
* 41 29 XAUTH [Rob_Earhart]
* 42 2a CHARSET [RFC2066]
* 43 2b Telnet Remote Serial Port (RSP) [Robert_Barnes]
* 44 2c Com Port Control Option [RFC2217]
* 45 2d Telnet Suppress Local Echo [Wirt_Atmar]
* 46 2e Telnet Start TLS [Michael_Boe]
* 47 2f KERMIT [RFC2840]
* 48 30 SEND-URL [David_Croft]
* 49 31 FORWARD_X [Jeffrey_Altman]
* 50-137 32-89 Unassigned [IANA]
* 138 8a TELOPT PRAGMA LOGON [Steve_McGregory]
* 139 8b TELOPT SSPI LOGON [Steve_McGregory]
* 140 8c TELOPT PRAGMA HEARTBEAT [Steve_McGregory]
* 141-254 8d-fe Unassigned
* 255 ff Extended-Options-List [RFC861]
*/
#define TNO_BINARY 0 /* 00 Binary Transmission */
#define TNO_ECHO 1 /* 01 Echo */
#define TNO_SUPP_GA 3 /* 03 Suppress Go Ahead */
#define TNO_LOGOUT 18 /* 12 Logout */
#define TNO_TTYP 24 /* 18 Terminal Type */
#define TNO_NAWS 31 /* 1f Negotiate About Window Size */
#define TNO_NEO 39 /* 27 New Environment Option */
#define TNO_CPCO 44 /* 2c RFC 2217 */
#define TNO_KERMIT 47 /* 2f Kermit */
#endif /* TELNET_PARAM_H */
+256
View File
@@ -0,0 +1,256 @@
/*
* 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;
}