Compare commits
38 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c80995256 | |||
| 299cfcabb5 | |||
| b47cb98a69 | |||
| cb6509861f | |||
| 2f0cea6f73 | |||
| 015da8a55e | |||
| 4fce33a5d7 | |||
| b2115e5fea | |||
| b25eb00bc5 | |||
| b6faf87c3a | |||
| 674ed45ebf | |||
| f02de1ae99 | |||
| b65d44abe5 | |||
| d1f46f7dbe | |||
| 20c393c053 | |||
| 02d90b7634 | |||
| 4e14ef1956 | |||
| d0bf492b10 | |||
| 84c54c5504 | |||
| a978e86c82 | |||
| a49f95a300 | |||
| ac58d1a470 | |||
| cc038383fe | |||
| ad077571f8 | |||
| 8bf4b7705d | |||
| cac5be0ddc | |||
| 3ab1dd5cc7 | |||
| 94bd59e1e0 | |||
| 5fb11097e1 | |||
| 895cfffc6b | |||
| 6e5c44eb29 | |||
| 05b9b13b04 | |||
| 9e99ec69b4 | |||
| 72f0058e27 | |||
| 50a6b600bf | |||
| 465f31ba87 | |||
| 90c5387613 | |||
| 6bf10a6901 |
+3
-1
@@ -3,10 +3,12 @@
|
|||||||
/autom4te.cache/
|
/autom4te.cache/
|
||||||
/compile
|
/compile
|
||||||
/config.h.in
|
/config.h.in
|
||||||
/config.h.in~
|
/config.guess
|
||||||
|
/config.sub
|
||||||
/configure
|
/configure
|
||||||
/depcomp
|
/depcomp
|
||||||
/install-sh
|
/install-sh
|
||||||
|
/ltmain.sh
|
||||||
/missing
|
/missing
|
||||||
Makefile.in
|
Makefile.in
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -4,4 +4,8 @@ MAINTAINERCLEANFILES = \
|
|||||||
depcomp install-sh missing \
|
depcomp install-sh missing \
|
||||||
Makefile.in */Makefile.in
|
Makefile.in */Makefile.in
|
||||||
|
|
||||||
SUBDIRS = src
|
SUBDIRS = lib src
|
||||||
|
if BUILD_TESTTOOLS
|
||||||
|
SUBDIRS += nvtsrv
|
||||||
|
SUBDIRS += nvttest
|
||||||
|
endif
|
||||||
|
|||||||
+18
-7
@@ -1,9 +1,6 @@
|
|||||||
m4_define([pkg_version], [0.14])
|
AC_INIT([ttynvt],[0.16],[frj@thrane.eu])
|
||||||
#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])
|
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
|
||||||
@@ -12,12 +9,12 @@ AC_PROG_CC
|
|||||||
|
|
||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
|
|
||||||
AC_HEADER_STDC
|
|
||||||
|
|
||||||
define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl
|
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
|
||||||
@@ -29,10 +26,24 @@ 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"])
|
||||||
|
|
||||||
|
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
|
||||||
])
|
])
|
||||||
AC_OUTPUT
|
AC_OUTPUT
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
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
|
||||||
@@ -14,7 +14,34 @@
|
|||||||
#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)
|
||||||
{
|
{
|
||||||
@@ -40,14 +67,17 @@ 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_stdout)
|
if (log_dest)
|
||||||
{
|
{
|
||||||
eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : "";
|
eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : "";
|
||||||
printf("%s: [%d] %d: %s%s",
|
fprintf(log_dest, "%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
|
||||||
{
|
{
|
||||||
@@ -9,12 +9,17 @@
|
|||||||
#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__)
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* Socket interface API
|
||||||
|
*/
|
||||||
|
#include <netdb.h> /* getaddrinfo() */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.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;
|
||||||
|
};
|
||||||
|
} sa_t;
|
||||||
|
|
||||||
|
|
||||||
|
static int _sa_host_lookup(sa_t * sa, const char *host)
|
||||||
|
{
|
||||||
|
struct hostent *server;
|
||||||
|
|
||||||
|
server = gethostbyname(host);
|
||||||
|
if (server == NULL)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_ERR, "Cannot resolve host name: %m\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa->slen = sizeof(sa->ipv4);
|
||||||
|
|
||||||
|
sa->ipv4.sin_family = AF_INET;
|
||||||
|
memcpy(&sa->ipv4.sin_addr.s_addr, server->h_addr, server->h_length);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _sa_addr_parse(sa_t * sa, const char *addr)
|
||||||
|
{
|
||||||
|
char host[64], port[64];
|
||||||
|
|
||||||
|
memset(sa, 0, sizeof(sa_t));
|
||||||
|
|
||||||
|
sscanf(addr, "%63[^:]:%63s", host, port);
|
||||||
|
|
||||||
|
if (_sa_host_lookup(sa, host) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
sa->type = SOCK_STREAM;
|
||||||
|
sa->ipv4.sin_port = htons(atoi(port));
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
* 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 */
|
||||||
@@ -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
|
||||||
+240
@@ -0,0 +1,240 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
|
||||||
|
#define HELP \
|
||||||
|
"Usage: nvtsrv -deq\n"
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_INFO, "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: /* '?' */
|
||||||
|
fprintf(stderr, HELP);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
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)
|
||||||
|
_nvtsrv_usage();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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 */
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
bin_PROGRAMS = nvttest
|
||||||
|
|
||||||
|
nvttest_SOURCES = \
|
||||||
|
nvttest.c
|
||||||
|
|
||||||
|
nvttest_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(CFLAGS_WARN)
|
||||||
|
|
||||||
|
nvttest_LDADD = $(top_builddir)/lib/libnvt.la
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* ttynvt stress tool
|
||||||
|
*/
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <asm/termbits.h>
|
||||||
|
|
||||||
|
#include "lib/nvt_log.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define HELP \
|
||||||
|
"Usage: nvttest -dqr [-n loops] [-t threads]\n"
|
||||||
|
|
||||||
|
static const char *dev = "/dev/ttyNVT0";
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int nthread;
|
||||||
|
int ithread;
|
||||||
|
int nloop;
|
||||||
|
bool opt_rand_delay;
|
||||||
|
} topts_t;
|
||||||
|
|
||||||
|
static void *_worker(void *arg)
|
||||||
|
{
|
||||||
|
topts_t *topts = arg;
|
||||||
|
int iloop;
|
||||||
|
int fd, err, nw;
|
||||||
|
struct termios ios;
|
||||||
|
|
||||||
|
for (iloop = 0; iloop < topts->nloop; iloop++)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_INFO, "Thread %d/%d run %d/%d\n",
|
||||||
|
topts->ithread, topts->nthread, iloop + 1, topts->nloop);
|
||||||
|
{
|
||||||
|
fd = open(dev, O_RDWR);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_ERR, "Open '%s' failed: %m\n", dev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&ios, 0, sizeof(ios));
|
||||||
|
err = ioctl(fd, TCGETS, &ios);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_ERR, "ioctl(TCGETS) failed: %m\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
err = ioctl(fd, TCSETS, &ios);
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_ERR, "ioctl(TCSETS) failed: %m\n");
|
||||||
|
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);
|
||||||
|
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");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (nw < len)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_ERR, "write short: %u/%u\n", nw, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topts->opt_rand_delay)
|
||||||
|
usleep(rand() & 0xfff);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
int opt;
|
||||||
|
int ithr, nthr;
|
||||||
|
topts_t topts = { }, *popts;
|
||||||
|
pthread_t *ptids;
|
||||||
|
int debug, quiet;
|
||||||
|
|
||||||
|
debug = quiet = 0;
|
||||||
|
nthr = 1;
|
||||||
|
topts.nloop = 1;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "dn:qrt:")) != -1)
|
||||||
|
{
|
||||||
|
switch (opt)
|
||||||
|
{
|
||||||
|
default: /* '?' */
|
||||||
|
fprintf(stderr, HELP);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nvt_log_level(quiet, debug);
|
||||||
|
nvt_log_dest(NVT_LOG_STDOUT);
|
||||||
|
|
||||||
|
topts.nthread = nthr;
|
||||||
|
|
||||||
|
ptids = malloc(nthr * sizeof(pthread_t));
|
||||||
|
|
||||||
|
for (ithr = 0; ithr < nthr; ithr++)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_INFO, "Create worker thread %d/%d\n", ithr + 1, nthr);
|
||||||
|
|
||||||
|
popts = malloc(sizeof(topts_t));
|
||||||
|
*popts = topts;
|
||||||
|
popts->ithread = ithr + 1;
|
||||||
|
|
||||||
|
err = pthread_create(&ptids[ithr], NULL, _worker, popts);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_ERR, "Failed to create thread %d/%d\n", ithr, nthr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ithr = 0; ithr < nthr; ithr++)
|
||||||
|
{
|
||||||
|
if (ptids[ithr] == 0)
|
||||||
|
break;
|
||||||
|
pthread_join(ptids[ithr], NULL);
|
||||||
|
nvt_log(LOG_INFO, "Joined worker thread %d/%d\n", ithr + 1, nthr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
+4
-6
@@ -1,12 +1,10 @@
|
|||||||
|
|
||||||
CFLAGS_WARN = -Wall -Wextra -Werror -Wno-unused-parameter
|
|
||||||
|
|
||||||
bin_PROGRAMS = ttynvt
|
bin_PROGRAMS = ttynvt
|
||||||
|
|
||||||
ttynvt_SOURCES = \
|
ttynvt_SOURCES = \
|
||||||
ttynvt.c nvt_log.c nvt_log.h \
|
ttynvt.c \
|
||||||
telnet_basic.c telnet_rfc2217.c telnet.h telnet_param.h
|
telnet_basic.c telnet_client_rfc2217.c telnet.h telnet_param.h
|
||||||
|
|
||||||
ttynvt_CPPFLAGS = -I$(top_builddir) $(FUSE_CFLAGS) $(CFLAGS_WARN)
|
ttynvt_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(FUSE_CFLAGS) $(CFLAGS_WARN)
|
||||||
|
|
||||||
ttynvt_LDADD = $(FUSE_LIBS)
|
ttynvt_LDADD = $(top_builddir)/lib/libnvt.la $(FUSE_LIBS)
|
||||||
|
|||||||
+10
-6
@@ -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
|
* Telnet interface - client
|
||||||
*/
|
*/
|
||||||
#ifndef TELNET_H
|
#ifndef TELNET_CLIENT_H
|
||||||
#define TELNET_H
|
#define TELNET_CLIENT_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 */
|
||||||
unsigned char mode_telnet; /* Telnet mode */
|
signed 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);
|
extern int telnet_open(tn_ctx_t * tcc, int mode);
|
||||||
|
|
||||||
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,6 +44,10 @@ 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
|
||||||
@@ -68,4 +72,4 @@ extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val);
|
|||||||
#define TNS_CTL_RTS_OFF 12
|
#define TNS_CTL_RTS_OFF 12
|
||||||
#define TNS_CTL_XONOFF_IN 15
|
#define TNS_CTL_XONOFF_IN 15
|
||||||
|
|
||||||
#endif /*TELNET_H */
|
#endif /* TELNET_CLIENT_H */
|
||||||
|
|||||||
+14
-5
@@ -2,9 +2,8 @@
|
|||||||
* 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 server - Telnet protocol handling
|
* Telnet protocol handling - client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
@@ -129,6 +128,10 @@ 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;
|
||||||
|
|
||||||
@@ -144,7 +147,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)
|
||||||
return 1;
|
goto tn_more;
|
||||||
|
|
||||||
pu = (unsigned char *)p;
|
pu = (unsigned char *)p;
|
||||||
b1 = pu[1];
|
b1 = pu[1];
|
||||||
@@ -237,7 +240,7 @@ int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
|
|||||||
|
|
||||||
for (s = buf, rem = len; rem > 0; s = p)
|
for (s = buf, rem = len; rem > 0; s = p)
|
||||||
{
|
{
|
||||||
if (tcc->mode_telnet)
|
if (tcc->mode_telnet > 0)
|
||||||
p = memchr(s, TNC_FF_IAC, rem);
|
p = memchr(s, TNC_FF_IAC, rem);
|
||||||
else
|
else
|
||||||
p = NULL;
|
p = NULL;
|
||||||
@@ -260,12 +263,18 @@ int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int telnet_open(tn_ctx_t * tcc)
|
int telnet_open(tn_ctx_t * tcc, int mode)
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
|||||||
@@ -2,11 +2,8 @@
|
|||||||
* 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 server - RFC-2217 handling
|
* Telnet RFC-2217 handling - client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
#include "telnet.h"
|
#include "telnet.h"
|
||||||
#include "telnet_param.h"
|
#include "telnet_param.h"
|
||||||
|
|
||||||
+268
-172
@@ -7,7 +7,6 @@
|
|||||||
#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>
|
||||||
@@ -21,13 +20,12 @@
|
|||||||
#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 "nvt_log.h"
|
#include "lib/nvt_log.h"
|
||||||
|
#include "lib/nvt_socket.h"
|
||||||
#include "telnet.h"
|
#include "telnet.h"
|
||||||
|
|
||||||
#define NET_BUF_SIZE 1024
|
#define NET_BUF_SIZE 1024
|
||||||
@@ -42,12 +40,14 @@ static struct ttynvt_param {
|
|||||||
unsigned int major;
|
unsigned int major;
|
||||||
unsigned int minor;
|
unsigned int minor;
|
||||||
const char *dev_name;
|
const char *dev_name;
|
||||||
char *server;
|
const 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 */
|
||||||
char host[64];
|
unsigned int close_delay_us;
|
||||||
unsigned int port;
|
|
||||||
/* Picked up */
|
/* Picked up */
|
||||||
int foreground;
|
int foreground;
|
||||||
} ttynvt_param;
|
} ttynvt_param;
|
||||||
@@ -55,14 +55,17 @@ static struct ttynvt_param {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
pthread_mutex_t tty_lock;
|
pthread_mutex_t tty_lock;
|
||||||
pthread_mutex_t poll_lock;
|
pthread_mutex_t poll_lock;
|
||||||
|
pthread_mutex_t read_lock;
|
||||||
|
pthread_mutex_t write_lock;
|
||||||
|
pthread_mutex_t ioctl_lock;
|
||||||
pthread_t ptid_poll;
|
pthread_t ptid_poll;
|
||||||
pthread_t ptid_read;
|
pthread_t ptid_read;
|
||||||
|
|
||||||
struct fuse_pollhandle *ph;
|
struct fuse_pollhandle *ph;
|
||||||
|
|
||||||
volatile int rel_pending;
|
volatile int rel_pending;
|
||||||
volatile int slave_suspended; /*waiting for application to read data */
|
volatile int slave_suspended; /* Waiting for application to read data */
|
||||||
volatile int master_suspended; /*server is not ready to receive data */
|
volatile int master_suspended; /* Server is not ready to receive data */
|
||||||
struct pollfd fds[3];
|
struct pollfd fds[3];
|
||||||
|
|
||||||
char net_buf[NET_BUF_SIZE];
|
char net_buf[NET_BUF_SIZE];
|
||||||
@@ -80,13 +83,20 @@ typedef struct {
|
|||||||
int epipe;
|
int epipe;
|
||||||
|
|
||||||
tn_ctx_t *tn;
|
tn_ctx_t *tn;
|
||||||
|
|
||||||
|
int seq;
|
||||||
|
struct ttynvt_cli_s *cli;
|
||||||
} ttynvt_t;
|
} ttynvt_t;
|
||||||
|
|
||||||
static void _fd_close(int fd)
|
static ttynvt_t *ptty = NULL;
|
||||||
{
|
|
||||||
if (fd >= 0)
|
typedef struct ttynvt_cli_s {
|
||||||
close(fd);
|
int id;
|
||||||
}
|
ttynvt_t *tty;
|
||||||
|
struct ttynvt_cli_s *next;
|
||||||
|
} ttynvt_cli_t;
|
||||||
|
|
||||||
|
static pthread_mutex_t access_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
static volatile char _is_interrupted = 0;
|
static volatile char _is_interrupted = 0;
|
||||||
|
|
||||||
@@ -117,6 +127,19 @@ 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;
|
||||||
@@ -132,29 +155,10 @@ static int _check_dev(void)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _fd_close(int fd)
|
||||||
static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
|
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
if (fd >= 0)
|
||||||
|
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)
|
||||||
@@ -238,7 +242,7 @@ static void *_read_net(void *arg)
|
|||||||
}
|
}
|
||||||
if (res == 0)
|
if (res == 0)
|
||||||
{
|
{
|
||||||
nvt_log(LOG_INFO, "Connection closed by remote host\n");
|
nvt_log(LOG_NOTICE, "Connection closed by remote host\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tty->net_cnt += res;
|
tty->net_cnt += res;
|
||||||
@@ -297,44 +301,6 @@ 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;
|
||||||
@@ -418,51 +384,94 @@ static void ttynvt_init(void *userdata, struct fuse_conn_info *conn)
|
|||||||
set_signal_handler();
|
set_signal_handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
|
static ttynvt_t *_ttynvt_ctx_create(void)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty;
|
ttynvt_t *tty;
|
||||||
char *slave_name;
|
int i;
|
||||||
struct termios ios;
|
|
||||||
int sockfd;
|
|
||||||
int fd;
|
|
||||||
int res;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
DBG("%s\n", __func__);
|
tty = calloc(1, sizeof(ttynvt_t));
|
||||||
|
|
||||||
tty = calloc(1, sizeof(*tty));
|
|
||||||
if (!tty)
|
if (!tty)
|
||||||
{
|
return NULL;
|
||||||
fuse_reply_err(req, ENOMEM);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (n = 0; n < 3; n++)
|
|
||||||
tty->fds[n].fd = -1;
|
|
||||||
|
|
||||||
tty->pollout = 1;
|
|
||||||
|
|
||||||
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
|
tty->tn = telnet_ctx_init(tty, _srv_write, _srv_read, _modem_status_cb);
|
||||||
if (!tty->tn)
|
if (!tty->tn)
|
||||||
{
|
{
|
||||||
free(tty);
|
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)
|
||||||
|
{
|
||||||
|
ttynvt_t *tty = ptty;
|
||||||
|
ttynvt_cli_t *tty_cli;
|
||||||
|
char *slave_name;
|
||||||
|
struct termios ios;
|
||||||
|
int fd;
|
||||||
|
int res;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
|
||||||
|
tty_cli = calloc(1, sizeof(ttynvt_cli_t));
|
||||||
|
if (!tty_cli)
|
||||||
|
{
|
||||||
fuse_reply_err(req, ENOMEM);
|
fuse_reply_err(req, ENOMEM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sockfd = _srv_connect(ttynvt_param.host, ttynvt_param.port);
|
pthread_mutex_lock(&access_lock);
|
||||||
if (sockfd < 0)
|
|
||||||
|
if (tty == NULL)
|
||||||
|
tty = _ttynvt_ctx_create();
|
||||||
|
if (!tty)
|
||||||
{
|
{
|
||||||
free(tty->tn);
|
fuse_reply_err(req, ENOMEM);
|
||||||
free(tty);
|
free(tty_cli);
|
||||||
fuse_reply_err(req, EHOSTUNREACH);
|
pthread_mutex_unlock(&access_lock);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tty->fds[FD_NET].fd = sockfd;
|
tty_cli->id = tty->seq++;
|
||||||
|
tty_cli->tty = tty;
|
||||||
|
tty_cli->next = tty->cli;
|
||||||
|
|
||||||
|
tty->cli = tty_cli;
|
||||||
|
|
||||||
|
nvt_log(LOG_INFO, "Client open (%d)\n", tty_cli->id);
|
||||||
|
|
||||||
|
if (ptty)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ptty = tty;
|
||||||
|
|
||||||
|
nvt_log(LOG_INFO, "Connecting to server: %s\n", ttynvt_param.server);
|
||||||
|
|
||||||
|
fd = socket_create_client(ttynvt_param.server);
|
||||||
|
if (fd < 0)
|
||||||
|
{
|
||||||
|
errno = EHOSTUNREACH;
|
||||||
|
goto open_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty->fds[FD_NET].fd = fd;
|
||||||
tty->fds[FD_NET].events = POLLIN;
|
tty->fds[FD_NET].events = POLLIN;
|
||||||
tty->net_cnt = 0;
|
tty->net_cnt = 0;
|
||||||
|
|
||||||
if ((fd = open("/dev/ptmx", O_RDWR)) < 0) /* open master */
|
if ((fd = open("/dev/ptmx", O_RDWR)) < 0) /* Open master */
|
||||||
goto open_err;
|
goto open_err;
|
||||||
|
|
||||||
tty->fds[FD_MASTER].fd = fd;
|
tty->fds[FD_MASTER].fd = fd;
|
||||||
@@ -480,11 +489,11 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
|
|||||||
if (ioctl(fd, TCSETS, &ios) < 0)
|
if (ioctl(fd, TCSETS, &ios) < 0)
|
||||||
goto open_err;
|
goto open_err;
|
||||||
|
|
||||||
grantpt(fd); /* change permission of slave */
|
grantpt(fd); /* Change permission of slave */
|
||||||
unlockpt(fd); /* unlock slave */
|
unlockpt(fd); /* Unlock slave */
|
||||||
slave_name = ptsname(fd); /* get name of slave */
|
slave_name = ptsname(fd); /* Get name of slave */
|
||||||
|
|
||||||
if ((fd = open(slave_name, O_RDWR | O_NOCTTY)) < 0) /* open slave */
|
if ((fd = open(slave_name, O_RDWR | O_NOCTTY)) < 0) /* Open slave */
|
||||||
goto open_err;
|
goto open_err;
|
||||||
|
|
||||||
ioctl(fd, TCGETS, &tty->tio);
|
ioctl(fd, TCGETS, &tty->tio);
|
||||||
@@ -495,53 +504,65 @@ 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 = telnet_open(tty->tn);
|
res =
|
||||||
|
telnet_open(tty->tn, ttynvt_param.raw ? TN_MODE_RAW : TN_MODE_TELNET);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
errno = EPROTO;
|
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) < 0) ||
|
||||||
(res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) ||
|
(res = pthread_mutex_init(&tty->poll_lock, NULL) < 0) ||
|
||||||
|
(res = pthread_mutex_init(&tty->ioctl_lock, NULL) < 0) ||
|
||||||
|
(res = pthread_mutex_init(&tty->read_lock, NULL) < 0) ||
|
||||||
|
(res = pthread_mutex_init(&tty->write_lock, NULL) < 0) ||
|
||||||
(res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty)))
|
(res = pthread_create(&tty->ptid_poll, NULL, &_read_net, tty)))
|
||||||
{
|
{
|
||||||
errno = res;
|
errno = res;
|
||||||
goto open_err;
|
goto open_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->fh = (uintptr_t) tty;
|
nvt_log(LOG_INFO, "Connected to server: %s\n", ttynvt_param.server);
|
||||||
|
|
||||||
|
out:
|
||||||
|
info->fh = (uintptr_t) tty_cli;
|
||||||
info->nonseekable = 1;
|
info->nonseekable = 1;
|
||||||
info->direct_io = 1;
|
info->direct_io = 1;
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&access_lock);
|
||||||
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);
|
||||||
|
|
||||||
free(tty->tn);
|
free(tty_cli);
|
||||||
free(tty);
|
_ttynvt_ctx_destroy(tty);
|
||||||
|
ptty = NULL;
|
||||||
|
|
||||||
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
|
||||||
ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
||||||
struct fuse_file_info *info)
|
struct fuse_file_info *info)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||||
|
ttynvt_t *tty = tty_cli->tty;
|
||||||
char buf[TMP_BUF_SIZE];
|
char buf[TMP_BUF_SIZE];
|
||||||
int res, nr;
|
int res, nr;
|
||||||
|
|
||||||
DBG2("%s\n", __func__);
|
pthread_mutex_lock(&tty->read_lock);
|
||||||
|
|
||||||
|
DBG2("%s (%d)\n", __func__, tty_cli->id);
|
||||||
|
|
||||||
if (tty->error)
|
if (tty->error)
|
||||||
{
|
{
|
||||||
@@ -555,7 +576,7 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
|||||||
else
|
else
|
||||||
fuse_reply_err(req, EBADFD);
|
fuse_reply_err(req, EBADFD);
|
||||||
|
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size > TMP_BUF_SIZE)
|
if (size > TMP_BUF_SIZE)
|
||||||
@@ -582,36 +603,42 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
|
|||||||
{
|
{
|
||||||
DBG2("%s: error: %m\n", __func__);
|
DBG2("%s: error: %m\n", __func__);
|
||||||
fuse_reply_err(req, errno);
|
fuse_reply_err(req, errno);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBG2("%s: fd=%d: sz=%u/%u: '%.*s'\n", __func__,
|
DBG2("%s (%d): fd=%d: sz=%u/%u: '%.*s'\n", __func__,
|
||||||
tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
|
tty_cli->id, tty->fds[FD_SLAVE].fd, res, (int)size, res, buf);
|
||||||
|
|
||||||
fuse_reply_buf(req, buf, res);
|
fuse_reply_buf(req, buf, res);
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&tty->read_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
||||||
struct fuse_file_info *info)
|
struct fuse_file_info *info)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||||
|
ttynvt_t *tty = tty_cli->tty;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
DBG2("%s: fd=%d: sz=%u: '%.*s'\n", __func__,
|
pthread_mutex_lock(&tty->write_lock);
|
||||||
tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
|
|
||||||
|
DBG2("%s (%d): fd=%d: sz=%u: '%.*s'\n", __func__,
|
||||||
|
tty_cli->id, tty->fds[FD_SLAVE].fd, (int)size, (int)size, data);
|
||||||
|
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
{
|
{
|
||||||
fuse_reply_write(req, 0);
|
fuse_reply_write(req, 0);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tty->error)
|
if (tty->error)
|
||||||
{
|
{
|
||||||
DBG2("%s: tty->error=%d\n", __func__, tty->error);
|
DBG2("%s: tty->error=%d\n", __func__, tty->error);
|
||||||
fuse_reply_err(req, EPIPE);
|
fuse_reply_err(req, EPIPE);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = write(tty->fds[FD_SLAVE].fd, data, size);
|
res = write(tty->fds[FD_SLAVE].fd, data, size);
|
||||||
@@ -620,7 +647,7 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
|||||||
DBG2("%s: error: %m\n", __func__);
|
DBG2("%s: error: %m\n", __func__);
|
||||||
tty->error = 1;
|
tty->error = 1;
|
||||||
fuse_reply_err(req, errno);
|
fuse_reply_err(req, errno);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
else if (res != (int)size)
|
else if (res != (int)size)
|
||||||
{
|
{
|
||||||
@@ -629,6 +656,9 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
|
|||||||
}
|
}
|
||||||
|
|
||||||
fuse_reply_write(req, res);
|
fuse_reply_write(req, res);
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&tty->write_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int baudrate(speed_t speed)
|
static int baudrate(speed_t speed)
|
||||||
@@ -717,10 +747,10 @@ static unsigned int _tio_parity(const struct termios2 *tio)
|
|||||||
{
|
{
|
||||||
unsigned int par;
|
unsigned int par;
|
||||||
|
|
||||||
if (tio->c_cflag & PARENB && tio->c_iflag & IGNPAR)
|
if ((tio->c_cflag & PARENB) && (tio->c_iflag & IGNPAR))
|
||||||
par = 1;
|
par = 1;
|
||||||
#ifdef CMSPAR
|
#ifdef CMSPAR
|
||||||
else if (tio->c_cflag & PARENB && 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;
|
||||||
@@ -806,7 +836,8 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
struct fuse_file_info *info, unsigned int flags,
|
struct fuse_file_info *info, unsigned int flags,
|
||||||
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
|
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||||
|
ttynvt_t *tty = tty_cli->tty;
|
||||||
struct termios2 *tio = &tty->tio;
|
struct termios2 *tio = &tty->tio;
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
unsigned int tiocm, baud;
|
unsigned int tiocm, baud;
|
||||||
@@ -816,10 +847,18 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
uint32_t byte4;
|
uint32_t byte4;
|
||||||
|
|
||||||
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
|
pthread_mutex_lock(&tty->ioctl_lock);
|
||||||
_ioctl_name(cmd), arg, in_buf,
|
|
||||||
|
DBG("%s (%d): cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
|
||||||
|
tty_cli->id, _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);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
switch (cmd)
|
switch (cmd)
|
||||||
{
|
{
|
||||||
case TCSETSF:
|
case TCSETSF:
|
||||||
@@ -855,21 +894,13 @@ 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) && (tio->c_iflag & IXOFF))
|
else if (tio->c_iflag & (IXON | IXOFF))
|
||||||
{
|
{
|
||||||
tty->master_suspended = 0;
|
tty->master_suspended = 0;
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_OUT);
|
if (tio->c_iflag & IXON)
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_IN);
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_OUT);
|
||||||
}
|
if (tio->c_iflag & IXOFF)
|
||||||
else if (tio->c_iflag & IXON)
|
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_IN);
|
||||||
{
|
|
||||||
tty->master_suspended = 0;
|
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_OUT);
|
|
||||||
}
|
|
||||||
else if (tio->c_iflag & IXOFF)
|
|
||||||
{
|
|
||||||
tty->master_suspended = 0;
|
|
||||||
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_IN);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -934,6 +965,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
case TCIFLUSH:
|
case TCIFLUSH:
|
||||||
byte = 1;
|
byte = 1;
|
||||||
tty->slave_suspended = 0; /* Re-enable polling of Slave */
|
tty->slave_suspended = 0; /* Re-enable polling of Slave */
|
||||||
|
tty->pollin = 0;
|
||||||
break;
|
break;
|
||||||
case TCOFLUSH:
|
case TCOFLUSH:
|
||||||
byte = 2;
|
byte = 2;
|
||||||
@@ -943,6 +975,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
byte = 3;
|
byte = 3;
|
||||||
tty->slave_suspended = 0; /* Re-enable polling of Slave */
|
tty->slave_suspended = 0; /* Re-enable polling of Slave */
|
||||||
tty->pollout = 1;
|
tty->pollout = 1;
|
||||||
|
tty->pollin = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &byte, 1);
|
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &byte, 1);
|
||||||
@@ -958,7 +991,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 = *(unsigned int *)in_buf;
|
tiocm = *(const 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 +1007,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 = *(unsigned int *)in_buf;
|
tiocm = *(const 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)
|
||||||
@@ -990,7 +1023,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 = *(unsigned int *)in_buf;
|
tiocm = *(const 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);
|
||||||
@@ -1014,18 +1047,18 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
if (res < 0)
|
if (res < 0)
|
||||||
{
|
{
|
||||||
fuse_reply_err(req, errno);
|
fuse_reply_err(req, errno);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
||||||
break;
|
break;
|
||||||
case TIOCSWINSZ:
|
case TIOCSWINSZ:
|
||||||
CHECK_BUF_IN(sizeof(struct winsize));
|
CHECK_BUF_IN(sizeof(struct winsize));
|
||||||
ws = *(struct winsize *)in_buf;
|
ws = *(const 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)
|
||||||
{
|
{
|
||||||
fuse_reply_err(req, errno);
|
fuse_reply_err(req, errno);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
fuse_reply_ioctl(req, 0, &ws, sizeof(struct winsize));
|
||||||
break;
|
break;
|
||||||
@@ -1044,7 +1077,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 = *(int *)in_buf;
|
val = *(const 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);
|
||||||
@@ -1086,17 +1119,21 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out:
|
||||||
|
pthread_mutex_unlock(&tty->ioctl_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
|
ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
|
||||||
struct fuse_pollhandle *ph)
|
struct fuse_pollhandle *ph)
|
||||||
{
|
{
|
||||||
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
|
ttynvt_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||||
|
ttynvt_t *tty = tty_cli->tty;
|
||||||
int revents = 0;
|
int revents = 0;
|
||||||
|
|
||||||
DBG2("%s: tty->pollin=%d tty->error/epipe=%d/%d\n",
|
DBG2("%s (%d): tty->pollin=%d tty->error/epipe=%d/%d\n",
|
||||||
__func__, tty->pollin, tty->error, tty->epipe);
|
__func__, tty_cli->id, tty->pollin, tty->error, tty->epipe);
|
||||||
|
|
||||||
_update_notify(tty, ph);
|
_update_notify(tty, ph);
|
||||||
|
|
||||||
@@ -1121,6 +1158,62 @@ 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_cli_t *tty_cli = (ttynvt_cli_t *) (uintptr_t) info->fh;
|
||||||
|
ttynvt_t *tty = tty_cli->tty;
|
||||||
|
ttynvt_cli_t *tmp, *prev;
|
||||||
|
|
||||||
|
DBG("%s\n", __func__);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&access_lock);
|
||||||
|
|
||||||
|
for (tmp = tty->cli, prev = NULL; tmp; prev = tmp, tmp = tmp->next)
|
||||||
|
if (tmp == tty_cli)
|
||||||
|
{
|
||||||
|
nvt_log(LOG_INFO, "Client release (%d)\n", tmp->id);
|
||||||
|
if (prev)
|
||||||
|
prev->next = tmp->next;
|
||||||
|
else
|
||||||
|
tty->cli = tmp->next;
|
||||||
|
free(tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tty->cli)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
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);
|
||||||
|
pthread_mutex_destroy(&tty->ioctl_lock);
|
||||||
|
pthread_mutex_destroy(&tty->read_lock);
|
||||||
|
pthread_mutex_destroy(&tty->write_lock);
|
||||||
|
|
||||||
|
if (ttynvt_param.close_delay_us)
|
||||||
|
_ttynvt_sleep_us(ttynvt_param.close_delay_us);
|
||||||
|
|
||||||
|
_fd_close(tty->fds[FD_NET].fd);
|
||||||
|
_fd_close(tty->fds[FD_MASTER].fd);
|
||||||
|
_fd_close(tty->fds[FD_SLAVE].fd);
|
||||||
|
|
||||||
|
if (tty->ph)
|
||||||
|
fuse_pollhandle_destroy(tty->ph);
|
||||||
|
|
||||||
|
_ttynvt_ctx_destroy(tty);
|
||||||
|
ptty = NULL;
|
||||||
|
|
||||||
|
nvt_log(LOG_INFO, "Disconnected from server: %s\n", ttynvt_param.server);
|
||||||
|
|
||||||
|
out:
|
||||||
|
fuse_reply_err(req, 0);
|
||||||
|
|
||||||
|
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,
|
||||||
@@ -1142,6 +1235,9 @@ 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),
|
||||||
@@ -1155,11 +1251,14 @@ 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 stdout (default is syslog)\n"
|
"\t-E\t\tPrint debug to stderr (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-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-S server, --server=host:port\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1195,8 +1294,9 @@ int main(int argc, char *argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_level = ttynvt_param.debug;
|
nvt_log_level(ttynvt_param.quiet, ttynvt_param.debug);
|
||||||
log_stdout = ttynvt_param.logstd;
|
if (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";
|
||||||
@@ -1207,22 +1307,18 @@ int main(int argc, char *argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = sscanf(ttynvt_param.server, "%63[^:]:%u", ttynvt_param.host,
|
|
||||||
&ttynvt_param.port);
|
|
||||||
if (rc <= 1)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Bad server specification: '%s'\n",
|
|
||||||
ttynvt_param.server);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
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);
|
snprintf(dev_name, sizeof(dev_name), "DEVNAME=%s", ttynvt_param.dev_name);
|
||||||
|
|
||||||
|
if (ttynvt_param.close_delay)
|
||||||
|
{
|
||||||
|
char ch = 'm';
|
||||||
|
|
||||||
|
sscanf(ttynvt_param.close_delay, "%u%c",
|
||||||
|
&ttynvt_param.close_delay_us, &ch);
|
||||||
|
if (ch != 'u')
|
||||||
|
ttynvt_param.close_delay_us *= 1000; // To milliseconds
|
||||||
|
}
|
||||||
|
|
||||||
struct cuse_info ci = {
|
struct cuse_info ci = {
|
||||||
.dev_major = ttynvt_param.major,
|
.dev_major = ttynvt_param.major,
|
||||||
.dev_minor = ttynvt_param.minor,
|
.dev_minor = ttynvt_param.minor,
|
||||||
|
|||||||
Reference in New Issue
Block a user