1 Commits

Author SHA1 Message Date
Frank Rolsted Jensen eaf30f067c Clear pollin flag when input buffer is flushed
The pollin flag must be cleared when the input buffer (pty slave) is
flushed. This ensures that the POLLIN event is not signaled to the application,
when there is not data to read.
2021-10-27 15:44:09 +02:00
26 changed files with 192 additions and 2100 deletions
+1 -3
View File
@@ -3,12 +3,10 @@
/autom4te.cache/ /autom4te.cache/
/compile /compile
/config.h.in /config.h.in
/config.guess /config.h.in~
/config.sub
/configure /configure
/depcomp /depcomp
/install-sh /install-sh
/ltmain.sh
/missing /missing
Makefile.in Makefile.in
+1 -13
View File
@@ -4,16 +4,4 @@ MAINTAINERCLEANFILES = \
depcomp install-sh missing \ depcomp install-sh missing \
Makefile.in */Makefile.in Makefile.in */Makefile.in
SUBDIRS = lib src SUBDIRS = src
if BUILD_TESTTOOLS
SUBDIRS += nvtsrv
SUBDIRS += nvttest
endif
if BUILD_TEST
SUBDIRS += test
endif
.PHONY: $(SUBDIRS) test
$(SUBDIRS) test:
$(MAKE) -C $@
test: $(SUBDIRS)
+8 -22
View File
@@ -1,20 +1,23 @@
AC_INIT([ttynvt],[0.17],[frj@thrane.eu]) m4_define([pkg_version], [0.14])
AM_INIT_AUTOMAKE([foreign dist-xz]) #m4_define([pkg_revision], [005])
m4_define([pkg_version], m4_ifdef([pkg_revision], [pkg_version.pkg_revision], [pkg_version]))
AC_INIT([ttynvt],[pkg_version],[frj@thrane.eu])
AM_INIT_AUTOMAKE([foreign dist-xz])
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
AC_USE_SYSTEM_EXTENSIONS AC_USE_SYSTEM_EXTENSIONS
AC_PROG_CC AC_PROG_CC
AC_PROG_CXX
AC_PROG_INSTALL AC_PROG_INSTALL
AC_HEADER_STDC
define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl
define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl
define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl
LT_INIT()
AC_C_BIGENDIAN AC_C_BIGENDIAN
AC_C_CONST AC_C_CONST
AC_C_INLINE AC_C_INLINE
@@ -26,27 +29,10 @@ PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES(FUSE, fuse) PKG_CHECK_MODULES(FUSE, fuse)
AC_ARG_ENABLE(testtools,
AS_HELP_STRING([--enable-testtools], [Build test tools @<:@default=no@:>@]),,
enable_testtools=no)
AM_CONDITIONAL([BUILD_TESTTOOLS], [test "x$enable_testtools" = "xyes"])
AM_CONDITIONAL(BUILD_TEST, false)
CFLAGS_WARN="$CFLAGS_WARN -Wall -Wextra -Werror -Wno-unused-parameter"
CFLAGS_WARN="$CFLAGS_WARN -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes"
CFLAGS_WARN="$CFLAGS_WARN -Waggregate-return -Wpointer-arith -Wshadow -Wwrite-strings"
CFLAGS_WARN="$CFLAGS_WARN -Wcast-align -Wcast-qual"
AC_SUBST(CFLAGS_WARN)
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([ AC_CONFIG_FILES([
Makefile Makefile
lib/Makefile
src/Makefile src/Makefile
nvtsrv/Makefile
nvttest/Makefile
test/Makefile
]) ])
AC_OUTPUT AC_OUTPUT
-6
View File
@@ -1,6 +0,0 @@
noinst_LTLIBRARIES = libnvt.la
libnvt_la_CFLAGS = $(CFLAGS_WARN)
libnvt_la_SOURCES = nvt_log.c nvt_log.h
libnvt_la_SOURCES += nvt_socket.c nvt_socket.h
-217
View File
@@ -1,217 +0,0 @@
/*
* Socket interface API
*/
#include <netdb.h> /* getaddrinfo() */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include "nvt_log.h"
#include "nvt_socket.h"
typedef struct {
unsigned short type; // socket type
unsigned short slen; // sockaddr length
union {
struct sockaddr gen;
struct sockaddr_in ipv4;
struct sockaddr_in6 ipv6;
struct sockaddr_un un;
};
} sa_t;
static int _sa_host_lookup(sa_t *sa, const char *host, const char *port)
{
struct addrinfo hint, *adrinf = NULL, *p;
int ret = -1;
memset(&hint, 0, sizeof(struct addrinfo));
if (getaddrinfo(host, port, &hint, &adrinf) != 0)
{
nvt_log(LOG_ERR, "Cannot resolve host name: %m\n");
return -1;
}
// Priority is given to the use of IPv4
// This code must be modified if the preffered protocol is changed or varied.
for (p = adrinf; p; p = p->ai_next)
{
if (p->ai_family == AF_INET)
{
sa->slen = sizeof(sa->ipv4);
memcpy(&sa->ipv4, p->ai_addr, sizeof(struct sockaddr_in));
ret = 0;
goto exit_host_lookup;
}
}
for (p = adrinf; p; p = p->ai_next)
{
if (p->ai_family == AF_INET6)
{
sa->slen = sizeof(sa->ipv6);
memcpy(&sa->ipv6, p->ai_addr, sizeof(struct sockaddr_in6));
ret = 0;
goto exit_host_lookup;
}
}
exit_host_lookup:
freeaddrinfo(adrinf);
return ret;
}
static int _sa_addr_parse(sa_t *sa, const char *addr)
{
ssize_t host_len;
char host[64];
const char *port;
memset(sa, 0, sizeof(sa_t));
if (strncmp(addr, "unix:", 5) == 0)
{ // case of unix domain sockets ...
addr += 5; // skip "unix:" prefix
if (strlen(addr) > sizeof(sa->un.sun_path) - 1)
{
nvt_log(LOG_ERR, "UNIX domain socket path is too long.\n");
return -1;
}
sa->slen = sizeof(sa->un);
sa->un.sun_family = AF_UNIX;
strcpy(sa->un.sun_path, addr);
sa->type = SOCK_STREAM;
return 0;
}
if (!(port = strrchr(addr, ':')))
{
nvt_log(LOG_ERR, "bad server address format.\n");
return -1;
}
host_len = (port++) - addr;
// *port indicates the last ':', its next is 1st char of the port number
if (host_len >= 2 && addr[0] == '[' && addr[host_len - 1] == ']')
{
// IPv6 addresses are enclosed in pair of square brackets.
addr += 1;
host_len -= 2;
}
else
{
if (strchr(addr, ':') != port - 1)
{
nvt_log(LOG_ERR,
"addresses containing colons must be enclosed in square brackets.\n");
return -1;
}
}
if ((ssize_t) sizeof(host) <= host_len)
{
nvt_log(LOG_ERR, "host name is too long.\n");
return -1;
}
strncpy(host, addr, host_len);
host[host_len] = '\0'; // strncpy doesn't add null char
if (_sa_host_lookup(sa, host, port) != 0)
return -1;
sa->type = SOCK_STREAM;
return 0;
}
int socket_create_client(const char *addr)
{
sa_t sa;
int fd, err;
if (_sa_addr_parse(&sa, addr) != 0)
return -1;
fd = socket(sa.gen.sa_family, sa.type, 0);
if (fd < 0)
{
nvt_log(LOG_ERR, "%s: Could not find address: '%s'", __func__, addr);
return fd;
}
err = connect(fd, &sa.gen, sa.slen);
if (err < 0)
{
nvt_log(LOG_ERR, "Failed to connect to: %s: %m\n", addr);
close(fd);
return -1;
}
return fd;
}
int socket_create_server(const char *addr)
{
sa_t sa;
int err, fd, opt;
err = _sa_addr_parse(&sa, addr);
if (err != 0)
{
nvt_log(LOG_ERR, "%s: Could not find address: '%s'", __func__, addr);
return -1;
}
fd = socket(sa.gen.sa_family, sa.type, 0);
if (fd < 0)
{
nvt_log(LOG_ERR, "%s: *** socket(%s): %m", __func__, addr);
return -1;
}
if (sa.gen.sa_family == AF_INET || sa.gen.sa_family == AF_INET6)
{
/* Avoid TIME_WAIT state on socket close */
opt = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}
err = bind(fd, &sa.gen, sa.slen);
if (err != 0)
{
nvt_log(LOG_ERR, "%s: *** bind(%s): %m", __func__, addr);
close(fd);
return -1;
}
if (sa.type == SOCK_STREAM)
{
err = listen(fd, 5);
if (err != 0)
{
nvt_log(LOG_ERR, "%s: *** listen(%s): %m", __func__, addr);
close(fd);
return -1;
}
}
return fd;
}
int socket_create_server_client(int sfd)
{
int cfd;
socklen_t slen;
slen = 0;
cfd = accept(sfd, NULL, &slen);
if (cfd < 0)
{
nvt_log(LOG_ERR, "%s: *** accept(): %m", __func__);
return -1;
}
return cfd;
}
-12
View File
@@ -1,12 +0,0 @@
/*
* Socket functions
*/
#ifndef NVT_SOCKET_H
#define NVT_SOCKET_H
int socket_create_client(const char *addr);
int socket_create_server(const char *addr);
int socket_create_server_client(int sfd);
#endif /* NVT_SOCKET_H */
-10
View File
@@ -1,10 +0,0 @@
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
-246
View File
@@ -1,246 +0,0 @@
/*
* RFC2217 test server
*/
#include <poll.h>
#include <signal.h>
#include <stdbool.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"
/**INDENT-OFF**/
static const char help_text[] =
"Usage: nvtsrv OPTIONS... HOST:PORT\n"
"OPTIONS:\n"
" -d : Enable debug\n"
" -e : Echo received data\n"
" -q : Quiet\n"
;
/**INDENT-ON**/
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 _usage(int rc)
{
printf(help_text);
exit(rc);
}
static void _tn_cli_close(void)
{
int i = 2;
nvt_log(LOG_INFO, "%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;
DBG2_BUF("Tx", buf, len);
if (fd_cli < 0)
{
nvt_log(LOG_NOTICE, "Write fd is closed - drop Tx\n");
return;
}
nw = write(fd_cli, buf, len);
if (nw != len)
{
nvt_log(LOG_ERR, "Write failed %d != %d\n", nw, len);
_tn_cli_close();
}
}
static void _tn_ss(void *cctx, int signal, int value)
{
DBG("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, int len)
{
int val;
if (line[0] == '*' && len > 2)
{
/* Special features */
if (line[1] == 'm')
{
sscanf(line + 2, "%x", &val);
telnet_rfc2217_change_modemstate(tcc, val);
}
}
else
{
/* Send all input */
_tn_tx(NULL, line, len);
}
}
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)
{
int opt;
char buf[128];
int rc, nr, i, len;
int debug, quiet;
bool do_echo;
debug = quiet = 0;
do_echo = false;
while ((opt = getopt(argc, argv, "deq")) != -1)
{
switch (opt)
{
default: /* '?' */
_usage(0);
break;
case 'd':
debug += 1;
break;
case 'e': /* Echo received data */
do_echo = true;
break;
case 'q':
quiet = 1;
break;
}
}
argc -= optind;
argv += optind;
if (argc <= 0)
_usage(1);
nvt_log_level(quiet, debug);
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)
{
nvt_log(LOG_ERR, "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;
DBG2_BUF("Con Rx", buf, nr);
_nvtsrv_cmd(dd.tnct, buf, nr);
}
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;
DBG2_BUF("Rx", buf, nr);
len = nr;
rc = telnet_rx(dd.tnct, buf, &len);
if (do_echo)
{
/* Echo received */
_tn_tx(NULL, buf, len);
}
}
}
i = 1;
if (pfds[i].revents)
{
if (pfds[2].fd >= 0)
{
nvt_log(LOG_NOTICE, "Client already connected on fd=%d\n",
pfds[2].fd);
break;
}
pfds[2].fd = socket_create_server_client(pfds[i].fd);
nvt_log(LOG_INFO, "%d: fd=%d: Connected\n", 2, pfds[2].fd);
pfds[2].events = POLLIN;
nfds = 3;
_nvtsrv_init();
}
}
return 0;
}
-63
View File
@@ -1,63 +0,0 @@
/*
* 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
@@ -1,275 +0,0 @@
/*
* 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
@@ -1,123 +0,0 @@
/*
* 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 */
-254
View File
@@ -1,254 +0,0 @@
/*
* 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;
DBG("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;
DBG("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;
}
-9
View File
@@ -1,9 +0,0 @@
bin_PROGRAMS = nvttest
nvttest_SOURCES = \
nvttest.c
nvttest_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(CFLAGS_WARN)
nvttest_LDADD = $(top_builddir)/lib/libnvt.la -lpthread
-220
View File
@@ -1,220 +0,0 @@
/*
* ttynvt stress tool
*/
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <asm/termbits.h>
#include "lib/nvt_log.h"
/**INDENT-OFF**/
static const char help_text[] =
"Usage: nvttest OPTIONS...\n"
"OPTIONS:\n"
" -D name : Specify device name (/dev/name, default /dev/ttyNVT0)\n"
" -d : Enable debug\n"
" -n loops : Test loops per thread\n"
" -q : Quiet\n"
" -r : Insert random delay in loops\n"
" -t threads : N. threads\n"
;
/**INDENT-ON**/
static char dev[64] = "/dev/ttyNVT0";
typedef struct {
int nthread;
int ithread;
int nloop;
bool opt_rand_delay;
} topts_t;
typedef struct {
pthread_t thr;
topts_t opts;
int errors;
} thr_t;
static void _usage(int rc)
{
printf(help_text);
exit(rc);
}
static void *_worker(void *arg)
{
thr_t *thr = arg;
int iloop;
int fd, err, nw;
struct termios ios;
for (iloop = 0; iloop < thr->opts.nloop; iloop++)
{
nvt_log(LOG_INFO, "Thread %d/%d run %d/%d\n",
thr->opts.ithread, thr->opts.nthread,
iloop + 1, thr->opts.nloop);
{
fd = open(dev, O_RDWR);
if (fd < 0)
{
nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev);
thr->errors++;
break;
}
memset(&ios, 0, sizeof(ios));
err = ioctl(fd, TCGETS, &ios);
if (err != 0)
{
nvt_log(LOG_ERR, "ioctl(TCGETS) failed: %m\n");
thr->errors++;
break;
}
err = ioctl(fd, TCSETS, &ios);
if (err != 0)
{
nvt_log(LOG_ERR, "ioctl(TCSETS) failed: %m\n");
thr->errors++;
break;
}
close(fd);
}
{
int i, len;
char buf[1024];
for (i = 0; i < (int)sizeof(buf); i++)
buf[i] = '0' + i % 10;
fd = open(dev, O_RDWR);
if (fd < 0)
{
nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev);
thr->errors++;
break;
}
for (i = 0; i < 10; i++)
{
len = rand() & 0xff;
nw = write(fd, buf, len);
if (nw < 0)
{
nvt_log(LOG_ERR, "write failed: %m\n");
thr->errors++;
break;
}
if (nw < len)
{
nvt_log(LOG_ERR, "write short: %u/%u\n", nw, len);
thr->errors++;
break;
}
}
close(fd);
}
if (thr->opts.opt_rand_delay)
usleep(rand() & 0xfff);
}
return NULL;
}
int main(int argc, char **argv)
{
int err, errors;
int opt;
int ithr, nthr;
thr_t *threads, *thr;
topts_t topts = { };
int debug, quiet;
struct stat st;
debug = quiet = 0;
nthr = 1;
topts.nloop = 1;
while ((opt = getopt(argc, argv, "D:dn:qrt:")) != -1)
{
switch (opt)
{
default: /* '?' */
_usage(1);
break;
case 'D':
snprintf(dev, sizeof(dev), "/dev/%s", optarg);
break;
case 'd':
debug += 1;
break;
case 'n':
topts.nloop = atoi(optarg);
break;
case 'q':
quiet = 1;
break;
case 'r':
topts.opt_rand_delay = true;
break;
case 't':
nthr = atoi(optarg);
break;
}
}
if (stat(dev, &st) || !S_ISCHR(st.st_mode))
{
fprintf(stderr, "Invalid device file: '%s'\n", dev);
exit(EXIT_FAILURE);
}
nvt_log_level(quiet, debug);
nvt_log_dest(NVT_LOG_STDOUT);
topts.nthread = nthr;
threads = calloc(nthr, sizeof(thr_t));
for (ithr = 0; ithr < nthr; ithr++)
{
nvt_log(LOG_INFO, "Create worker thread %d/%d\n", ithr + 1, nthr);
thr = &threads[ithr];
thr->opts = topts;
thr->opts.ithread = ithr + 1;
err = pthread_create(&thr->thr, NULL, _worker, thr);
if (err)
{
nvt_log(LOG_ERR, "Failed to create thread %d/%d\n", ithr, nthr);
break;
}
}
errors = 0;
for (ithr = 0; ithr < nthr; ithr++)
{
thr = &threads[ithr];
if (thr->thr == 0)
break;
pthread_join(thr->thr, NULL);
errors += thr->errors;
nvt_log(LOG_INFO, "Joined worker thread %d/%d (err=%d)\n",
ithr + 1, nthr, thr->errors);
}
return errors ? 1 : 0;
}
+6 -4
View File
@@ -1,10 +1,12 @@
CFLAGS_WARN = -Wall -Wextra -Werror -Wno-unused-parameter
bin_PROGRAMS = ttynvt bin_PROGRAMS = ttynvt
ttynvt_SOURCES = \ ttynvt_SOURCES = \
ttynvt.c \ ttynvt.c nvt_log.c nvt_log.h \
telnet_basic.c telnet_client_rfc2217.c telnet.h telnet_param.h telnet_basic.c telnet_rfc2217.c telnet.h telnet_param.h
ttynvt_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(FUSE_CFLAGS) $(CFLAGS_WARN) ttynvt_CPPFLAGS = -I$(top_builddir) $(FUSE_CFLAGS) $(CFLAGS_WARN)
ttynvt_LDADD = $(top_builddir)/lib/libnvt.la $(FUSE_LIBS) ttynvt_LDADD = $(FUSE_LIBS)
+4 -34
View File
@@ -14,34 +14,7 @@
#define gettid() (pid_t)syscall(__NR_gettid) #define gettid() (pid_t)syscall(__NR_gettid)
char log_level = 0; char log_level = 0;
char log_stdout = 0;
static char log_threshold = LOG_DEBUG;
static FILE *log_dest = NULL;
void nvt_log_dest(int dest)
{
switch (dest)
{
default:
case NVT_LOG_SYSLOG:
log_dest = NULL;
break;
case NVT_LOG_STDOUT:
log_dest = stdout;
break;
case NVT_LOG_STDERR:
log_dest = stderr;
break;
}
}
void nvt_log_level(int quiet, int level)
{
if (quiet)
log_threshold = LOG_NOTICE;
log_level = level;
}
static const char *_hms_txt(char *buf, unsigned int len) static const char *_hms_txt(char *buf, unsigned int len)
{ {
@@ -67,17 +40,14 @@ void nvt_log(int prio, const char *fmt, ...)
const char *eol; const char *eol;
int len; int len;
if (prio > log_threshold)
return;
va_start(arg, fmt); va_start(arg, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, arg); len = vsnprintf(buf, sizeof(buf), fmt, arg);
if (log_dest) if (log_stdout)
{ {
eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : ""; eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : "";
fprintf(log_dest, "%s: [%d] %d: %s%s", printf("%s: [%d] %d: %s%s",
_hms_txt(buft, sizeof(buft)), gettid(), prio, buf, eol); _hms_txt(buft, sizeof(buft)), gettid(), prio, buf, eol);
} }
else else
{ {
+1 -6
View File
@@ -9,17 +9,12 @@
#define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1))) #define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
#define _PRF2_ _PRF_N_(2) #define _PRF2_ _PRF_N_(2)
void nvt_log_dest(int dest);
void nvt_log_level(int quiet, int level);
_PRF2_ void nvt_log(int prio, const char *fmt, ...); _PRF2_ void nvt_log(int prio, const char *fmt, ...);
void nvt_log_buf(int prio, const char *txt, void nvt_log_buf(int prio, const char *txt,
const void *ptr, unsigned int len); const void *ptr, unsigned int len);
#define NVT_LOG_SYSLOG 0
#define NVT_LOG_STDOUT 1
#define NVT_LOG_STDERR 2
extern char log_level; extern char log_level;
extern char log_stdout;
#define DBG(...) \ #define DBG(...) \
if (log_level > 0) nvt_log(LOG_DEBUG, __VA_ARGS__) if (log_level > 0) nvt_log(LOG_DEBUG, __VA_ARGS__)
+7 -12
View File
@@ -2,10 +2,10 @@
* Copyright (c) 2018 Lars Thrane A/S * Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0 * SPDX-License-Identifier: GPL-2.0
* *
* Telnet interface - client * Telnet interface
*/ */
#ifndef TELNET_CLIENT_H #ifndef TELNET_H
#define TELNET_CLIENT_H #define TELNET_H
typedef int (srv_tx_f) (void *cctx, const void *buf, int len); typedef int (srv_tx_f) (void *cctx, const void *buf, int len);
typedef int (srv_rx_f) (void *cctx, int timeout); typedef int (srv_rx_f) (void *cctx, int timeout);
@@ -21,7 +21,7 @@ typedef struct {
unsigned int bytes_rx; /* Rx byte count */ unsigned int bytes_rx; /* Rx byte count */
unsigned int off_rx; /* Rx buffer offset */ unsigned int off_rx; /* Rx buffer offset */
signed char mode_telnet; /* Telnet mode */ unsigned char mode_telnet; /* Telnet mode */
unsigned char mode_rfc2217; /* RFC-2217 mode */ unsigned char mode_rfc2217; /* RFC-2217 mode */
unsigned char telnet_ack[256]; /* telnet option ack counter */ unsigned char telnet_ack[256]; /* telnet option ack counter */
} tn_ctx_t; } tn_ctx_t;
@@ -29,7 +29,7 @@ typedef struct {
extern tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f * ftx, srv_rx_f * frx, extern tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f * ftx, srv_rx_f * frx,
srv_ms_f * fss); srv_ms_f * fss);
extern int telnet_open(tn_ctx_t * tcc, int mode); extern int telnet_open(tn_ctx_t * tcc);
extern int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen); 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 int telnet_tx(tn_ctx_t * tcc, const char *buf, int len);
@@ -44,10 +44,6 @@ extern void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd,
const void *val, int nd); const void *val, int nd);
extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val); extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val);
/* Telnet mode */
#define TN_MODE_RAW 0 /* Telnet processing off */
#define TN_MODE_TELNET 1 /* Telnet mode */
/*RFC2217 modem state*/ /*RFC2217 modem state*/
#define TNS_STATE_DCD 0x80 #define TNS_STATE_DCD 0x80
#define TNS_STATE_RI 0x40 #define TNS_STATE_RI 0x40
@@ -64,12 +60,11 @@ extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val);
/*RFC2217 control commands*/ /*RFC2217 control commands*/
#define TNS_CTL_NOFLOW 1 #define TNS_CTL_NOFLOW 1
#define TNS_CTL_XONOFF_OUT 2 #define TNS_CTL_XONOFF 2
#define TNS_CTL_CTSRTS 3 #define TNS_CTL_CTSRTS 3
#define TNS_CTL_DTR_ON 8 #define TNS_CTL_DTR_ON 8
#define TNS_CTL_DTR_OFF 9 #define TNS_CTL_DTR_OFF 9
#define TNS_CTL_RTS_ON 11 #define TNS_CTL_RTS_ON 11
#define TNS_CTL_RTS_OFF 12 #define TNS_CTL_RTS_OFF 12
#define TNS_CTL_XONOFF_IN 15
#endif /* TELNET_CLIENT_H */ #endif /*TELNET_H */
+17 -27
View File
@@ -2,12 +2,12 @@
* Copyright (c) 2018 Lars Thrane A/S * Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0 * SPDX-License-Identifier: GPL-2.0
* *
* Telnet protocol handling - client * Telnet server - Telnet protocol handling
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <errno.h>
#include "telnet.h" #include "telnet.h"
#include "telnet_param.h" #include "telnet_param.h"
@@ -39,7 +39,7 @@ static const unsigned char tn_opt[256] = {
}; };
static void _telnet_handle_req(tn_ctx_t *tcc, int cmd, int opt) static void _telnet_handle_req(tn_ctx_t * tcc, int cmd, int opt)
{ {
char buf[3]; char buf[3];
@@ -68,7 +68,7 @@ static void _telnet_handle_req(tn_ctx_t *tcc, int cmd, int opt)
tcc->srv_tx(tcc->cctx, buf, 3); tcc->srv_tx(tcc->cctx, buf, 3);
} }
static void _telnet_handle_resp(tn_ctx_t *tcc, int cmd, int opt) static void _telnet_handle_resp(tn_ctx_t * tcc, int cmd, int opt)
{ {
if (opt == TNO_CPCO) if (opt == TNO_CPCO)
{ {
@@ -77,7 +77,7 @@ static void _telnet_handle_resp(tn_ctx_t *tcc, int cmd, int opt)
} }
} }
void telnet_set_opt(tn_ctx_t *tcc, int opt, int prm, const void *val, int len) void telnet_set_opt(tn_ctx_t * tcc, int opt, int prm, const void *val, int len)
{ {
unsigned char buf[128], *pu = buf; unsigned char buf[128], *pu = buf;
const unsigned char *pv; const unsigned char *pv;
@@ -107,7 +107,7 @@ void telnet_set_opt(tn_ctx_t *tcc, int opt, int prm, const void *val, int len)
tcc->srv_tx(tcc->cctx, buf, len); tcc->srv_tx(tcc->cctx, buf, len);
} }
static void _telnet_handle_opt(tn_ctx_t *tcc, int opt, static void _telnet_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd) unsigned char *pu, int nd)
{ {
switch (opt) switch (opt)
@@ -121,7 +121,7 @@ static void _telnet_handle_opt(tn_ctx_t *tcc, int opt,
} }
} }
int telnet_rx(tn_ctx_t *tcc, char *buf, int *plen) int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen)
{ {
char *s, *p; char *s, *p;
unsigned char *pu; unsigned char *pu;
@@ -129,10 +129,6 @@ int telnet_rx(tn_ctx_t *tcc, char *buf, int *plen)
int np, nd; int np, nd;
int b1, b2; int b1, b2;
/* Skip telnet processing entirely in raw mode */
if (tcc->mode_telnet < 0)
return 0;
len = *plen; len = *plen;
tcc->bytes_rx += len; tcc->bytes_rx += len;
@@ -148,7 +144,7 @@ int telnet_rx(tn_ctx_t *tcc, char *buf, int *plen)
/* We have IAC (= 0xff) */ /* We have IAC (= 0xff) */
np = rem - (p - s); /* N. chars available incl. IAC */ np = rem - (p - s); /* N. chars available incl. IAC */
if (np < 2) if (np < 2)
goto tn_more; return 1;
pu = (unsigned char *)p; pu = (unsigned char *)p;
b1 = pu[1]; b1 = pu[1];
@@ -234,14 +230,14 @@ int telnet_rx(tn_ctx_t *tcc, char *buf, int *plen)
return 0; return 0;
} }
int telnet_tx(tn_ctx_t *tcc, const char *buf, int len) int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
{ {
const char *s, *p; const char *s, *p;
int rem, nd; int rem, nd;
for (s = buf, rem = len; rem > 0; s = p) for (s = buf, rem = len; rem > 0; s = p)
{ {
if (tcc->mode_telnet > 0) if (tcc->mode_telnet)
p = memchr(s, TNC_FF_IAC, rem); p = memchr(s, TNC_FF_IAC, rem);
else else
p = NULL; p = NULL;
@@ -264,18 +260,12 @@ int telnet_tx(tn_ctx_t *tcc, const char *buf, int len)
return 0; return 0;
} }
int telnet_open(tn_ctx_t *tcc, int mode) int telnet_open(tn_ctx_t * tcc)
{ {
time_t t = time(NULL);; time_t t = time(NULL);;
char buf[3]; char buf[3];
int res; int res;
if (mode == TN_MODE_RAW)
{
tcc->mode_telnet = -1; /* Raw mode - no telnet processing */
return 0;
}
buf[0] = TNC_FF_IAC; buf[0] = TNC_FF_IAC;
buf[1] = TNC_FB_WILL; buf[1] = TNC_FB_WILL;
buf[2] = TNO_CPCO; buf[2] = TNO_CPCO;
@@ -285,17 +275,17 @@ int telnet_open(tn_ctx_t *tcc, int mode)
while (1) while (1)
{ {
res = tcc->srv_rx(tcc->cctx, 1000); res = tcc->srv_rx(tcc->cctx, 1000);
if (res < 0) if (res <= 0)
return errno; return -1;
else if (tcc->mode_rfc2217) else if (tcc->mode_rfc2217)
return 0; return 0;
else if (time(NULL) - t > 10) else if (t - time(NULL) > 2)
return EPROTO; return -1;
} }
} }
tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f *ftx, tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f * ftx,
srv_rx_f *frx, srv_ms_f *fms) srv_rx_f * frx, srv_ms_f * fms)
{ {
tn_ctx_t *tcc; tn_ctx_t *tcc;
@@ -2,12 +2,15 @@
* Copyright (c) 2018 Lars Thrane A/S * Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0 * SPDX-License-Identifier: GPL-2.0
* *
* Telnet RFC-2217 handling - client * Telnet server - RFC-2217 handling
*/ */
#include <stddef.h>
#include "telnet.h" #include "telnet.h"
#include "telnet_param.h" #include "telnet_param.h"
void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt, void telnet_rfc2217_handle_opt(tn_ctx_t * tcc, int opt,
unsigned char *pu, int nd) unsigned char *pu, int nd)
{ {
unsigned int sub; unsigned int sub;
@@ -32,7 +35,7 @@ void telnet_rfc2217_handle_opt(tn_ctx_t *tcc, int opt,
} }
} }
void telnet_rfc2217_cfg(tn_ctx_t *tcc, int cmd, const void *val, int nd) void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd, const void *val, int nd)
{ {
if (!tcc->mode_rfc2217) if (!tcc->mode_rfc2217)
return; return;
@@ -40,7 +43,7 @@ void telnet_rfc2217_cfg(tn_ctx_t *tcc, int cmd, const void *val, int nd)
telnet_set_opt(tcc, TNO_CPCO, cmd, val, nd); telnet_set_opt(tcc, TNO_CPCO, cmd, val, nd);
} }
void telnet_rfc2217_ctl(tn_ctx_t *tcc, unsigned int val) void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val)
{ {
unsigned char byte = val; unsigned char byte = val;
+140 -175
View File
@@ -7,6 +7,7 @@
#include <cuse_lowlevel.h> #include <cuse_lowlevel.h>
#include <errno.h> #include <errno.h>
#include <fuse_opt.h> #include <fuse_opt.h>
#include <netdb.h>
#include <poll.h> #include <poll.h>
#include <pthread.h> #include <pthread.h>
#include <signal.h> #include <signal.h>
@@ -20,12 +21,13 @@
#include <unistd.h> #include <unistd.h>
#include <asm/ioctls.h> #include <asm/ioctls.h>
#include <asm/termbits.h> #include <asm/termbits.h>
#include <linux/sockios.h>
#include <linux/ppp-ioctl.h> #include <linux/ppp-ioctl.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h>
#include "lib/nvt_log.h" #include "nvt_log.h"
#include "lib/nvt_socket.h"
#include "telnet.h" #include "telnet.h"
#define NET_BUF_SIZE 1024 #define NET_BUF_SIZE 1024
@@ -40,14 +42,12 @@ static struct ttynvt_param {
unsigned int major; unsigned int major;
unsigned int minor; unsigned int minor;
const char *dev_name; const char *dev_name;
const char *server; char *server;
unsigned int quiet;
unsigned int debug; unsigned int debug;
int logstd; int logstd;
int raw; /* No telnet processing */
const char *close_delay;
/* Derived */ /* Derived */
unsigned int close_delay_us; char host[64];
unsigned int port;
/* Picked up */ /* Picked up */
int foreground; int foreground;
} ttynvt_param; } ttynvt_param;
@@ -82,8 +82,11 @@ typedef struct {
tn_ctx_t *tn; tn_ctx_t *tn;
} ttynvt_t; } ttynvt_t;
static void _fd_close(int fd)
static pthread_mutex_t access_lock = PTHREAD_MUTEX_INITIALIZER; {
if (fd >= 0)
close(fd);
}
static volatile char _is_interrupted = 0; static volatile char _is_interrupted = 0;
@@ -114,20 +117,6 @@ static void ttynvt_interrupted(fuse_req_t req, void *data)
pthread_kill(thread, SIGUSR2); pthread_kill(thread, SIGUSR2);
} }
static void _ttynvt_sleep_us(unsigned int tus)
{
struct timespec ts;
ts.tv_sec = tus / 1000000;
tus -= ts.tv_sec * 1000000;
ts.tv_nsec = tus * 1000;
while (nanosleep(&ts, &ts))
;
}
static int _check_dev(void) static int _check_dev(void)
{ {
int rc; int rc;
@@ -144,13 +133,31 @@ static int _check_dev(void)
} }
static void _fd_close(int fd) static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
{ {
if (fd >= 0) ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
close(fd);
nvt_log(LOG_INFO, "connection closed\n");
tty->rel_pending = 1;
_fd_close(tty->fds[FD_NET].fd);
_fd_close(tty->fds[FD_MASTER].fd);
_fd_close(tty->fds[FD_SLAVE].fd);
pthread_cancel(tty->ptid_poll);
pthread_join(tty->ptid_poll, NULL);
pthread_mutex_destroy(&tty->tty_lock);
pthread_mutex_destroy(&tty->poll_lock);
if (tty->ph)
fuse_pollhandle_destroy(tty->ph);
free(tty->tn);
free(tty);
fuse_reply_err(req, 0);
} }
static void _notify(ttynvt_t *tty) static void _notify(ttynvt_t * tty)
{ {
pthread_mutex_lock(&tty->poll_lock); pthread_mutex_lock(&tty->poll_lock);
if (tty->ph) if (tty->ph)
@@ -162,7 +169,7 @@ static void _notify(ttynvt_t *tty)
pthread_mutex_unlock(&tty->poll_lock); pthread_mutex_unlock(&tty->poll_lock);
} }
static void _update_notify(ttynvt_t *tty, struct fuse_pollhandle *ph) static void _update_notify(ttynvt_t * tty, struct fuse_pollhandle *ph)
{ {
pthread_mutex_lock(&tty->poll_lock); pthread_mutex_lock(&tty->poll_lock);
struct fuse_pollhandle *tmp_ph = tty->ph; struct fuse_pollhandle *tmp_ph = tty->ph;
@@ -173,7 +180,7 @@ static void _update_notify(ttynvt_t *tty, struct fuse_pollhandle *ph)
fuse_pollhandle_destroy(tmp_ph); fuse_pollhandle_destroy(tmp_ph);
} }
static int _net_read_check_exit(ttynvt_t *tty) static int _net_read_check_exit(ttynvt_t * tty)
{ {
if ((tty->tio.c_cflag & CLOCAL) == 0 && if ((tty->tio.c_cflag & CLOCAL) == 0 &&
(tty->smcr_last & TIOCM_CD) != 0 && (tty->smcr & TIOCM_CD) == 0) (tty->smcr_last & TIOCM_CD) != 0 && (tty->smcr & TIOCM_CD) == 0)
@@ -231,7 +238,7 @@ static void *_read_net(void *arg)
} }
if (res == 0) if (res == 0)
{ {
nvt_log(LOG_NOTICE, "Connection closed by remote host\n"); nvt_log(LOG_INFO, "Connection closed by remote host\n");
break; break;
} }
tty->net_cnt += res; tty->net_cnt += res;
@@ -290,6 +297,44 @@ static void *_read_net(void *arg)
return 0; return 0;
} }
static int _srv_connect(const char *host, unsigned int port)
{
struct sockaddr_in serveraddr;
struct hostent *server;
int sockfd, err;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
nvt_log(LOG_ERR, "Socket open failed: %m\n");
return sockfd;
}
server = gethostbyname(host);
if (server == NULL)
{
nvt_log(LOG_ERR, "Cannot resolve host name: %m\n");
close(sockfd);
return -1;
}
bzero((char *)&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serveraddr.sin_addr.s_addr,
server->h_length);
serveraddr.sin_port = htons(port);
err = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (err < 0)
{
nvt_log(LOG_ERR, "Failed to connect to: %s:%u: %m\n", host, port);
close(sockfd);
return -1;
}
return sockfd;
}
static int _srv_write(void *cctx, const void *buf, int len) static int _srv_write(void *cctx, const void *buf, int len)
{ {
ttynvt_t *tty = cctx; ttynvt_t *tty = cctx;
@@ -373,65 +418,47 @@ static void ttynvt_init(void *userdata, struct fuse_conn_info *conn)
set_signal_handler(); set_signal_handler();
} }
static ttynvt_t *_ttynvt_ctx_create(void)
{
ttynvt_t *tty;
int i;
tty = calloc(1, sizeof(ttynvt_t));
if (!tty)
return NULL;
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
if (!tty->tn)
{
free(tty);
return NULL;
}
for (i = 0; i < 3; i++)
tty->fds[i].fd = -1;
tty->pollout = 1;
return tty;
}
static void _ttynvt_ctx_destroy(ttynvt_t *tty)
{
if (tty)
free(tty->tn);
free(tty);
}
static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info) static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
{ {
ttynvt_t *tty; ttynvt_t *tty;
char *slave_name; char *slave_name;
struct termios ios; struct termios ios;
int sockfd;
int fd; int fd;
int res; int res;
int n; int n;
pthread_mutex_lock(&access_lock); DBG("%s\n", __func__);
nvt_log(LOG_INFO, "Connecting to server: %s\n", ttynvt_param.server); tty = calloc(1, sizeof(*tty));
tty = _ttynvt_ctx_create();
if (!tty) if (!tty)
{ {
fuse_reply_err(req, ENOMEM); fuse_reply_err(req, ENOMEM);
return; return;
} }
for (n = 0; n < 3; n++)
tty->fds[n].fd = -1;
fd = socket_create_client(ttynvt_param.server); tty->pollout = 1;
if (fd < 0)
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
if (!tty->tn)
{ {
errno = EHOSTUNREACH; free(tty);
goto open_err; fuse_reply_err(req, ENOMEM);
return;
} }
tty->fds[FD_NET].fd = fd; sockfd = _srv_connect(ttynvt_param.host, ttynvt_param.port);
if (sockfd < 0)
{
free(tty->tn);
free(tty);
fuse_reply_err(req, EHOSTUNREACH);
return;
}
tty->fds[FD_NET].fd = sockfd;
tty->fds[FD_NET].events = POLLIN; tty->fds[FD_NET].events = POLLIN;
tty->net_cnt = 0; tty->net_cnt = 0;
@@ -468,16 +495,14 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
tty->fds[FD_SLAVE].fd = fd; tty->fds[FD_SLAVE].fd = fd;
tty->fds[FD_SLAVE].events = POLLIN; tty->fds[FD_SLAVE].events = POLLIN;
res = res = telnet_open(tty->tn);
telnet_open(tty->tn, ttynvt_param.raw ? TN_MODE_RAW : TN_MODE_TELNET); if (res < 0)
if (res != 0)
{ {
errno = res; errno = EPROTO;
goto open_err; goto open_err;
} }
if ((res = pthread_mutex_init(&tty->tty_lock, NULL) < 0) ||
if ((res = pthread_mutex_init(&tty->tty_lock, NULL)) || (res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) ||
(res = pthread_mutex_init(&tty->poll_lock, NULL)) ||
(res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty))) (res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty)))
{ {
errno = res; errno = res;
@@ -488,23 +513,24 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
info->nonseekable = 1; info->nonseekable = 1;
info->direct_io = 1; info->direct_io = 1;
nvt_log(LOG_INFO, "Connected to server: %s\n", ttynvt_param.server); nvt_log(LOG_INFO, "Connected to server: %s:%d\n",
ttynvt_param.host, ttynvt_param.port);
fuse_reply_open(req, info); fuse_reply_open(req, info);
return; return;
open_err: open_err:
nvt_log(LOG_INFO, "Connection to server: %s:%d failed: %m\n",
ttynvt_param.host, ttynvt_param.port);
for (n = 0; n < 3; n++) for (n = 0; n < 3; n++)
_fd_close(tty->fds[n].fd); _fd_close(tty->fds[n].fd);
_ttynvt_ctx_destroy(tty); free(tty->tn);
free(tty);
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
nvt_log(LOG_ERR, "Connection to server: %s failed: %m\n",
ttynvt_param.server);
pthread_mutex_unlock(&access_lock);
} }
static void static void
@@ -691,12 +717,10 @@ static unsigned int _tio_parity(const struct termios2 *tio)
{ {
unsigned int par; unsigned int par;
if (!(tio->c_cflag & PARENB)) if (tio->c_cflag & PARENB && tio->c_iflag & IGNPAR)
{
par = 1; par = 1;
}
#ifdef CMSPAR #ifdef CMSPAR
else if (tio->c_iflag & CMSPAR) else if (tio->c_cflag & PARENB && tio->c_iflag & CMSPAR)
{ {
if (tio->c_cflag & PARODD) if (tio->c_cflag & PARODD)
par = 4; par = 4;
@@ -704,14 +728,10 @@ static unsigned int _tio_parity(const struct termios2 *tio)
par = 5; par = 5;
} }
#endif #endif
else if (tio->c_cflag & PARODD) else if (tio->c_cflag & PARENB)
{
par = 2;
}
else
{
par = 3; par = 3;
} else
par = 1;
return par; return par;
} }
@@ -800,12 +820,6 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
_ioctl_name(cmd), arg, in_buf, _ioctl_name(cmd), arg, in_buf,
(unsigned int)in_bufsz, (unsigned int)out_bufsz); (unsigned int)in_bufsz, (unsigned int)out_bufsz);
if (ttynvt_param.raw)
{
fuse_reply_ioctl(req, 0, 0, 0);
return;
}
switch (cmd) switch (cmd)
{ {
case TCSETSF: case TCSETSF:
@@ -824,12 +838,8 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
goto do_tcset; goto do_tcset;
do_tcset: do_tcset:
/* BEWARE of termios/termios2 buf structure difference! */ /* BEWARE of termios/termios2 buf structure difference! */
byte4 = htonl(baud);
if (baud != 0) telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
{
byte4 = htonl(baud);
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
}
byte = _tio_csize(tio); byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1); telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
@@ -845,14 +855,6 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
tty->master_suspended = (tty->smcr & TIOCM_CTS) ? 0 : 1; tty->master_suspended = (tty->smcr & TIOCM_CTS) ? 0 : 1;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS); telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
} }
else if (tio->c_iflag & (IXON | IXOFF))
{
tty->master_suspended = 0;
if (tio->c_iflag & IXON)
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_OUT);
if (tio->c_iflag & IXOFF)
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_IN);
}
else else
{ {
tty->master_suspended = 0; tty->master_suspended = 0;
@@ -942,7 +944,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
break; break;
case TIOCMBIS: case TIOCMBIS:
CHECK_BUF_IN(sizeof(int)); CHECK_BUF_IN(sizeof(int));
tiocm = *(const unsigned int *)in_buf; tiocm = *(unsigned int *)in_buf;
fuse_reply_ioctl(req, 0, 0, 0); fuse_reply_ioctl(req, 0, 0, 0);
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm); DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
if (tiocm & TIOCM_RTS) if (tiocm & TIOCM_RTS)
@@ -958,7 +960,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
goto show_mctl; goto show_mctl;
case TIOCMBIC: case TIOCMBIC:
CHECK_BUF_IN(sizeof(int)); CHECK_BUF_IN(sizeof(int));
tiocm = *(const unsigned int *)in_buf; tiocm = *(unsigned int *)in_buf;
fuse_reply_ioctl(req, 0, 0, 0); fuse_reply_ioctl(req, 0, 0, 0);
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm); DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
if (tiocm & TIOCM_RTS) if (tiocm & TIOCM_RTS)
@@ -974,7 +976,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
goto show_mctl; goto show_mctl;
case TIOCMSET: case TIOCMSET:
CHECK_BUF_IN(sizeof(int)); CHECK_BUF_IN(sizeof(int));
tiocm = *(const unsigned int *)in_buf; tiocm = *(unsigned int *)in_buf;
fuse_reply_ioctl(req, 0, 0, 0); fuse_reply_ioctl(req, 0, 0, 0);
DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm); DBG("%s: cmd=%s tiocm=%#x\n", __func__, _ioctl_name(cmd), tiocm);
tty->cmcr &= ~(TIOCM_RTS | TIOCM_DTR); tty->cmcr &= ~(TIOCM_RTS | TIOCM_DTR);
@@ -1004,7 +1006,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
break; break;
case TIOCSWINSZ: case TIOCSWINSZ:
CHECK_BUF_IN(sizeof(struct winsize)); CHECK_BUF_IN(sizeof(struct winsize));
ws = *(const struct winsize *)in_buf; ws = *(struct winsize *)in_buf;
res = ioctl(tty->fds[FD_SLAVE].fd, TIOCSWINSZ, &ws); res = ioctl(tty->fds[FD_SLAVE].fd, TIOCSWINSZ, &ws);
if (res < 0) if (res < 0)
{ {
@@ -1028,7 +1030,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
break; break;
case TIOCSETD: case TIOCSETD:
CHECK_BUF_IN(sizeof(int)); CHECK_BUF_IN(sizeof(int));
val = *(const int *)in_buf; val = *(int *)in_buf;
res = ioctl(tty->fds[FD_SLAVE].fd, TIOCSETD, &val); res = ioctl(tty->fds[FD_SLAVE].fd, TIOCSETD, &val);
if (res < 0) if (res < 0)
fuse_reply_err(req, errno); fuse_reply_err(req, errno);
@@ -1105,38 +1107,6 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
fuse_reply_poll(req, revents); fuse_reply_poll(req, revents);
} }
static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
DBG("%s\n", __func__);
tty->rel_pending = 1;
pthread_cancel(tty->ptid_poll);
pthread_join(tty->ptid_poll, NULL);
pthread_mutex_destroy(&tty->tty_lock);
pthread_mutex_destroy(&tty->poll_lock);
_fd_close(tty->fds[FD_NET].fd);
_fd_close(tty->fds[FD_MASTER].fd);
_fd_close(tty->fds[FD_SLAVE].fd);
if (ttynvt_param.close_delay_us)
_ttynvt_sleep_us(ttynvt_param.close_delay_us);
if (tty->ph)
fuse_pollhandle_destroy(tty->ph);
_ttynvt_ctx_destroy(tty);
fuse_reply_err(req, 0);
nvt_log(LOG_INFO, "Disconnected from server: %s\n", ttynvt_param.server);
pthread_mutex_unlock(&access_lock);
}
static const struct cuse_lowlevel_ops ttynvt_op = { static const struct cuse_lowlevel_ops ttynvt_op = {
.init = ttynvt_init, .init = ttynvt_init,
.open = ttynvt_open, .open = ttynvt_open,
@@ -1158,9 +1128,6 @@ static const struct fuse_opt ttynvt_opts[] = {
TTYNET_OPT("--min=%u", minor, 1), TTYNET_OPT("--min=%u", minor, 1),
TTYNET_OPT("-n %s", dev_name, 0), TTYNET_OPT("-n %s", dev_name, 0),
TTYNET_OPT("--name=%s", dev_name, 0), TTYNET_OPT("--name=%s", dev_name, 0),
TTYNET_OPT("-q", quiet, 1),
TTYNET_OPT("-r", raw, 1),
TTYNET_OPT("-s %s", close_delay, 0),
TTYNET_OPT("-S %s", server, 0), TTYNET_OPT("-S %s", server, 0),
TTYNET_OPT("--server=%s", server, 0), TTYNET_OPT("--server=%s", server, 0),
FUSE_OPT_KEY("-h", 0), FUSE_OPT_KEY("-h", 0),
@@ -1174,17 +1141,12 @@ static void print_help(void)
"\t-D level\tEnable debug\n" "\t-D level\tEnable debug\n"
"\t-d\t\tStay in foreground, do not detach\n" "\t-d\t\tStay in foreground, do not detach\n"
"\t-f\t\tStay in foreground, do not detach\n" "\t-f\t\tStay in foreground, do not detach\n"
"\t-E\t\tPrint debug to stderr (default is syslog)\n" "\t-E\t\tPrint debug to stdout (default is syslog)\n"
"\t-M major, --maj=major\n" "\t-M major, --maj=major\n"
"\t-m minor, --min=minor\n" "\t-m minor, --min=minor\n"
"\t-n name, --name=name (default: ttyNVT0)\n" "\t-n name, --name=name (default: ttyNVT0)\n"
"\t\tDevice will be created in /dev/$name.\n" "\t\tDevice will be created in /dev/$name.\n"
"\t-q\t\tBe quiet (suppress informational messages)\n" "\t-S server, --server=host:port\n");
"\t-r\t\tDisable telnet processing\n"
"\t-s delay \t\tDelay at close in ms (use u suffix for us)\n"
"\t-S server, --server=host:port\n"
"\t\t\t[ipv6addr]:port (connect via IPv6)\n"
"\t\t\tunix:/path/to.socket (connect via UNIX domain socket)\n");
} }
static int static int
@@ -1219,9 +1181,8 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
nvt_log_level(ttynvt_param.quiet, ttynvt_param.debug); log_level = ttynvt_param.debug;
if (ttynvt_param.logstd) log_stdout = ttynvt_param.logstd;
nvt_log_dest(NVT_LOG_STDERR);
if (!ttynvt_param.dev_name) if (!ttynvt_param.dev_name)
ttynvt_param.dev_name = "ttyNVT0"; ttynvt_param.dev_name = "ttyNVT0";
@@ -1232,17 +1193,21 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
snprintf(dev_name, sizeof(dev_name), "DEVNAME=%s", ttynvt_param.dev_name); rc = sscanf(ttynvt_param.server, "%63[^:]:%u", ttynvt_param.host,
&ttynvt_param.port);
if (ttynvt_param.close_delay) if (rc <= 1)
{ {
char ch = 'm'; fprintf(stderr, "Bad server specification: '%s'\n",
ttynvt_param.server);
sscanf(ttynvt_param.close_delay, "%u%c", return 1;
&ttynvt_param.close_delay_us, &ch);
if (ch != 'u')
ttynvt_param.close_delay_us *= 1000; // To milliseconds
} }
if (ttynvt_param.port == 0)
{
fprintf(stderr, "Bad server port: %u\n", ttynvt_param.port);
return 1;
}
snprintf(dev_name, sizeof(dev_name), "DEVNAME=%s", ttynvt_param.dev_name);
struct cuse_info ci = { struct cuse_info ci = {
.dev_major = ttynvt_param.major, .dev_major = ttynvt_param.major,
-48
View File
@@ -1,48 +0,0 @@
# Unit test makefile
#
.NOTPARALLEL:
noinst_PROGRAMS = $(GTESTS)
GTEST_LIBS = -lgtest -lstdc++
GTESTS = test_misc test_nvttest
AM_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
AM_CFLAGS += $(CFLAGS_ASAN)
AM_CXXFLAGS = $(AM_CFLAGS)
AM_CPPFLAGS = -I $(top_builddir)
AM_CPPFLAGS += -D TOP_SRC_DIR='"$(top_srcdir)"' -D TOP_BLD_DIR='"$(top_builddir)"'
AM_CPPFLAGS += -D BUILD_TEST=1
LIBS += $(GTEST_LIBS)
TEST_COMMON = test.cpp test.h
test_misc_SOURCES = $(TEST_COMMON) test_misc.cpp
test_misc_LDADD = $(LIBS)
test_nvttest_SOURCES = $(TEST_COMMON) test_nvttest.cpp
test_nvttest_LDADD = $(LIBS)
TESTS_RUN = $(addprefix run-, $(GTESTS))
VG_PROG = valgrind --leak-check=full
all-local: run
.PHONY: run $(TESTS_RUN)
run: $(TESTS_RUN)
$(TESTS_RUN): run-%: %
# $(TEST_ENV) ./.libs/$* $(RUN_OPTS)
$(TEST_ENV) ./$* $(RUN_OPTS)
TESTS_RUN_VG = $(addprefix run-vg-, $(GTESTS))
.PHONY: run-vg $(TESTS_RUN_VG)
run-vg: $(TESTS_RUN_VG)
$(TESTS_RUN_VG): run-vg-%: %
# $(TEST_ENV) $(VG_PROG) ./.libs/$* $(RUN_OPTS)
$(TEST_ENV) $(VG_PROG) ./$* $(RUN_OPTS)
-70
View File
@@ -1,70 +0,0 @@
/*
* General test stuff
*
* - The main() function
* - Some printing functions
*/
#include "test.h"
int debug = 0;
static bool ttyout = false;
int main(int argc, char **argv)
{
const char *s;
ttyout = isatty(STDOUT_FILENO);
::testing::InitGoogleTest(&argc, argv);
for (argc--, argv++; argc > 0; argc--, argv++)
{
s = argv[0];
if (*s++ != '-')
break;
again:
switch (*s++)
{
case 'd':
debug++;
goto again;
}
}
return RUN_ALL_TESTS();
}
#include <stdarg.h>
void _pr_text(const char *col, const char *fmt, va_list args)
{
char fmtx[1024];
if (ttyout)
snprintf(fmtx, sizeof(fmtx), "%s[ ] - %s%s\n",
col, fmt, COL_RST);
else
snprintf(fmtx, sizeof(fmtx), "[ ] - %s\n", fmt);
fmt = fmtx;
vprintf(fmt, args);
}
void pr_text(const char *col, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
_pr_text(col, fmt, args);
va_end(args);
}
void pr_info(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
_pr_text(COL_YEL, fmt, args);
va_end(args);
}
-25
View File
@@ -1,25 +0,0 @@
#ifndef TEST_H
#define TEST_H 1
#include <gtest/gtest.h>
#include "config.h"
#define D(...) do{ if (debug) printf(__VA_ARGS__); }while(0)
#define D2(...) do{ if (debug > 1) printf(__VA_ARGS__); }while(0)
extern int debug;
#define COL_RST "\x1B[0m"
#define COL_RED "\x1B[31m"
#define COL_GRN "\x1B[32m"
#define COL_YEL "\x1B[33m"
#define COL_BLU "\x1B[34m"
#define COL_MAG "\x1B[35m"
#define COL_CYN "\x1B[36m"
#define COL_WHT "\x1B[37m"
void pr_text(const char *col, const char *fmt, ...);
void pr_info(const char *fmt, ...);
#endif /* TEST_H */
-6
View File
@@ -1,6 +0,0 @@
#include "test.h"
TEST(Misc, dummy)
{
EXPECT_EQ(1, 1);
}
-216
View File
@@ -1,216 +0,0 @@
#include "test.h"
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#define NVT_NAME "ttyNVT99"
#define NVT_DEV "/dev/" NVT_NAME
#define NVT_SRV TOP_BLD_DIR "/nvtsrv/nvtsrv"
#define NVT_TEST TOP_BLD_DIR "/nvttest/nvttest"
#define NVT_OPTS "-D" NVT_NAME
#define LAUNCH_NVTTEST(...) proc_launch(NVT_TEST, NVT_OPTS, __VA_ARGS__)
pid_t proc_spawn(char **argv)
{
pid_t pid;
char buf[1024];
for (int i = 0, len = 0; argv[i]; i++)
len += snprintf(buf + len, sizeof(buf) - len, "%s ", argv[i]);
pr_info("Launching: %s", buf);
pid = fork();
if (pid > 0)
{
pr_info("... ok, pid = %d", pid);
return pid;
}
// Child
execv(argv[0], argv);
pr_info("Launch failed: %m");
EXPECT_EQ(1, 0);
exit(1);
}
void proc_waitfor(const char *name, pid_t pid)
{
int err, wstatus;
pr_info("Wait for %s (pid %d) to finish", name, pid);
wstatus = 0;
err = waitpid(pid, &wstatus, 0);
if (err > 0)
pr_info("... ok, status = %x", wstatus);
else
pr_info("... error: %m");
EXPECT_GT(err, 0);
EXPECT_EQ(WEXITSTATUS(wstatus), 0);
}
static pid_t proc_launch(const char *prog, ...)
{
char *argv[16], *arg;
int i;
pid_t pid;
va_list args;
va_start(args, prog);
arg = (char *)prog;
i = 0;
argv[i++] = arg;
for (; arg != NULL;)
{
arg = va_arg(args, char *);
argv[i++] = arg;
}
va_end(args);
pid = proc_spawn(argv);
return pid;
}
bool ttynvt_check()
{
struct stat st;
bool ok;
ok = stat(NVT_DEV, &st) == 0 && S_ISCHR(st.st_mode);
if (!ok)
{
fprintf(stderr, "\n%s"
"************************************************************\n"
"*** Invalid device file: '%s'\n"
"*** Ensure that ttynvt is launched correcly, e.g.:\n"
"# ttynvt -d -E -S localhost:1234 -n %s\n"
"*** ttynvt must run as root, and the device and server names\n"
"*** must be as shown above.\n"
"************************************************************\n"
"%s\n", COL_CYN, NVT_DEV, NVT_NAME, COL_RST);
}
return ok;
}
TEST(Test1, t1)
{
int err;
pid_t pid_srv, pid;
ASSERT_TRUE(ttynvt_check());
pid_srv = proc_launch(NVT_SRV, "-de", "localhost:1234", NULL);
ASSERT_GT(pid_srv, 0);
usleep(100000);
pid = LAUNCH_NVTTEST(NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
pr_info("Terminate nvtsrv");
err = kill(pid_srv, SIGTERM);
EXPECT_EQ(err, 0);
proc_waitfor("nvtsrv", pid_srv);
}
/* Using fixture */
/**INDENT-OFF**/
class Test2:public::testing::Test {
pid_t pid_srv;
protected:
Test2() { }
virtual ~Test2() { }
virtual void SetUp() {
// Ensure ttynvt is running
ASSERT_TRUE(ttynvt_check());
// NB! Quiet nvtsrv
pid_srv = proc_launch(NVT_SRV, "-qe", "localhost:1234", NULL);
ASSERT_GT(pid_srv, 0);
usleep(100000);
}
virtual void TearDown() {
int err;
pr_info("Terminate nvtsrv");
err = kill(pid_srv, SIGTERM);
EXPECT_EQ(err, 0);
proc_waitfor("nvtsrv", pid_srv);
}
};
/**INDENT-ON**/
TEST_F(Test2, t1)
{
pid_t pid;
pid = LAUNCH_NVTTEST(NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}
// Multiple threads
TEST_F(Test2, t2)
{
pid_t pid;
// NB! Quiet nvttest
pid = LAUNCH_NVTTEST("-t5", "-q", NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}
// Multiple loops
TEST_F(Test2, t3)
{
pid_t pid;
// NB! Quiet nvttest
pid = LAUNCH_NVTTEST("-n5", "-q", NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}
// Multiple threads and loops
TEST_F(Test2, t4)
{
pid_t pid;
// NB! Quiet nvttest
pid = LAUNCH_NVTTEST("-n5", "-t5", "-q", NULL);
ASSERT_GT(pid, 0);
proc_waitfor("nvttest", pid);
}