Compare commits
71 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1081fd9235 | |||
| 72e09527b9 | |||
| 10d5c9cf3b | |||
| 4d3f7d384b | |||
| 1f516288ab | |||
| 55c5d0d226 | |||
| cd5de2fe5d | |||
| da027d4de1 | |||
| 3bcbf9c8c1 | |||
| 611ab3cbf3 | |||
| 6e2e530215 | |||
| 3d5848aff0 | |||
| 4223a4b92a | |||
| 2c090f3721 | |||
| 797ccf08c8 | |||
| 9739087594 | |||
| f41067c3be | |||
| 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 | |||
| f759c0e3cc | |||
| 0606891b78 | |||
| 899596fd5b | |||
| a21ba0cb9c | |||
| 8f03384964 | |||
| b314659125 | |||
| 42a4e2e062 | |||
| 0b402cdb6b | |||
| 302ed6d796 | |||
| bf11b1dd15 | |||
| 33de2e875c | |||
| eccd32fdfb | |||
| 089b733f27 | |||
| 41802ce7e9 | |||
| e51a98e4b2 | |||
| 9db6c5d83c | |||
| e3675b9e89 | |||
| cf6b334bcc | |||
| 09e96e72f7 | |||
| 688fc53caf | |||
| 4d1d6978e4 | |||
| 8f03aabe30 |
+4
-2
@@ -3,10 +3,12 @@
|
||||
/autom4te.cache/
|
||||
/compile
|
||||
/config.h.in
|
||||
/config.h.in~
|
||||
/config.guess
|
||||
/config.sub
|
||||
/configure
|
||||
/depcomp
|
||||
/install-sh
|
||||
/ltmain.sh
|
||||
/missing
|
||||
Makefile.in
|
||||
|
||||
@@ -20,7 +22,7 @@ Makefile
|
||||
|
||||
# build
|
||||
*.o
|
||||
/ttynvt
|
||||
/src/ttynvt
|
||||
|
||||
/ttynvt-*.tar.gz
|
||||
/ttynvt-*.tar.xz
|
||||
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
-kr -i4 --no-tabs --line-length79 -di16
|
||||
--braces-after-if-line --brace-indent0 --dont-cuddle-else --cuddle-do-while
|
||||
--blank-lines-after-declarations --blank-lines-after-procedures
|
||||
--break-after-boolean-operator
|
||||
--no-space-after-casts
|
||||
--else-endif-column0
|
||||
+13
-14
@@ -1,20 +1,19 @@
|
||||
# Build ttynvt
|
||||
#
|
||||
|
||||
MAINTAINERCLEANFILES = \
|
||||
aclocal.m4 autom4te.cache compile config.h.in config.h.in~ configure \
|
||||
depcomp install-sh missing \
|
||||
Makefile.in
|
||||
Makefile.in */Makefile.in
|
||||
|
||||
CFLAGS_OPT = -O3
|
||||
CFLAGS_WARN = -Wall -Wextra -Werror -Wno-unused-parameter
|
||||
SUBDIRS = lib src
|
||||
if BUILD_TESTTOOLS
|
||||
SUBDIRS += nvtsrv
|
||||
SUBDIRS += nvttest
|
||||
endif
|
||||
if BUILD_TEST
|
||||
SUBDIRS += test
|
||||
endif
|
||||
|
||||
bin_PROGRAMS = ttynvt
|
||||
|
||||
ttynvt_SOURCES = \
|
||||
ttynvt.c \
|
||||
telnet_basic.c telnet_rfc2217.c telnet.h telnet_param.h
|
||||
|
||||
ttynvt_CPPFLAGS = -I$(top_builddir) $(FUSE_CFLAGS) $(CFLAGS_OPT) $(CFLAGS_WARN)
|
||||
|
||||
ttynvt_LDADD = $(FUSE_LIBS)
|
||||
.PHONY: $(SUBDIRS) test
|
||||
$(SUBDIRS) test:
|
||||
$(MAKE) -C $@
|
||||
test: $(SUBDIRS)
|
||||
|
||||
+23
-8
@@ -1,23 +1,20 @@
|
||||
m4_define([pkg_version], [0.13])
|
||||
#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])
|
||||
AC_INIT([ttynvt],[0.17],[frj@thrane.eu])
|
||||
AM_INIT_AUTOMAKE([foreign dist-xz])
|
||||
|
||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||
|
||||
AC_USE_SYSTEM_EXTENSIONS
|
||||
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
|
||||
AC_PROG_INSTALL
|
||||
|
||||
AC_HEADER_STDC
|
||||
|
||||
define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl
|
||||
define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl
|
||||
define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl
|
||||
|
||||
LT_INIT()
|
||||
|
||||
AC_C_BIGENDIAN
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
@@ -29,9 +26,27 @@ PKG_PROG_PKG_CONFIG
|
||||
|
||||
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_FILES([
|
||||
Makefile
|
||||
lib/Makefile
|
||||
src/Makefile
|
||||
nvtsrv/Makefile
|
||||
nvttest/Makefile
|
||||
test/Makefile
|
||||
])
|
||||
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
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Logging functions
|
||||
*/
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "nvt_log.h"
|
||||
|
||||
#include <sys/syscall.h>
|
||||
#define gettid() (pid_t)syscall(__NR_gettid)
|
||||
|
||||
char log_level = 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)
|
||||
{
|
||||
struct timeval tv;
|
||||
unsigned int sod, hh, mm, ss;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
sod = tv.tv_sec % (24 * 3600);
|
||||
hh = sod / 3600;
|
||||
mm = (sod - hh * 3600) / 60;
|
||||
ss = sod - hh * 3600 - mm * 60;
|
||||
snprintf(buf, len, "%02d:%02d:%02d.%06d",
|
||||
hh, mm, ss, (unsigned int)tv.tv_usec);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
void nvt_log(int prio, const char *fmt, ...)
|
||||
{
|
||||
va_list arg;
|
||||
char buf[256], buft[64];
|
||||
const char *eol;
|
||||
int len;
|
||||
|
||||
if (prio > log_threshold)
|
||||
return;
|
||||
|
||||
va_start(arg, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, arg);
|
||||
|
||||
if (log_dest)
|
||||
{
|
||||
eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : "";
|
||||
fprintf(log_dest, "%s: [%d] %d: %s%s",
|
||||
_hms_txt(buft, sizeof(buft)), gettid(), prio, buf, eol);
|
||||
}
|
||||
else
|
||||
{
|
||||
syslog(prio, "[%d] %s", gettid(), buf);
|
||||
}
|
||||
|
||||
va_end(arg);
|
||||
}
|
||||
|
||||
#define BPL 16 /* Bytes per line */
|
||||
|
||||
void nvt_log_buf(int prio, const char *txt, const void *ptr_, unsigned int len)
|
||||
{
|
||||
unsigned int ix, i, imax;
|
||||
int l, n;
|
||||
char buf[128];
|
||||
const unsigned char *ptr = ptr_;
|
||||
|
||||
for (ix = 0; ix < len || (len == 0 && ix == 0); ix += BPL)
|
||||
{
|
||||
n = snprintf(buf, sizeof(buf), "%s: %04x:", txt, ix);
|
||||
if (n <= 0)
|
||||
return; /* Just playing it safe */
|
||||
imax = (len - ix < BPL) ? len - ix : BPL;
|
||||
for (i = 0; i < imax; i++)
|
||||
{
|
||||
l = snprintf(buf + n, sizeof(buf) - n, " %02x", *ptr++);
|
||||
if (l <= 0)
|
||||
return; /* Just playing it safe */
|
||||
n += l;
|
||||
}
|
||||
nvt_log(prio, "%s\n", buf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Logging functions
|
||||
*/
|
||||
#ifndef NVT_LOG_H
|
||||
#define NVT_LOG_H
|
||||
|
||||
#include <syslog.h>
|
||||
|
||||
#define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
|
||||
#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, ...);
|
||||
void nvt_log_buf(int prio, const char *txt,
|
||||
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;
|
||||
|
||||
#define DBG(...) \
|
||||
if (log_level > 0) nvt_log(LOG_DEBUG, __VA_ARGS__)
|
||||
#define DBG2(...) \
|
||||
if (log_level > 1) nvt_log(LOG_DEBUG, __VA_ARGS__)
|
||||
#define DBG2_BUF(txt, ptr, len) \
|
||||
if (log_level > 1) nvt_log_buf(LOG_DEBUG, txt, ptr, len)
|
||||
|
||||
#endif /* NVT_LOG_H */
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -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
|
||||
+246
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -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,254 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -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 -lpthread
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
bin_PROGRAMS = ttynvt
|
||||
|
||||
ttynvt_SOURCES = \
|
||||
ttynvt.c \
|
||||
telnet_basic.c telnet_client_rfc2217.c telnet.h telnet_param.h
|
||||
|
||||
ttynvt_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) $(FUSE_CFLAGS) $(CFLAGS_WARN)
|
||||
|
||||
ttynvt_LDADD = $(top_builddir)/lib/libnvt.la $(FUSE_LIBS)
|
||||
+12
-7
@@ -2,10 +2,10 @@
|
||||
* Copyright (c) 2018 Lars Thrane A/S
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Telnet interface
|
||||
* Telnet interface - client
|
||||
*/
|
||||
#ifndef TELNET_H
|
||||
#define TELNET_H
|
||||
#ifndef TELNET_CLIENT_H
|
||||
#define TELNET_CLIENT_H
|
||||
|
||||
typedef int (srv_tx_f) (void *cctx, const void *buf, int len);
|
||||
typedef int (srv_rx_f) (void *cctx, int timeout);
|
||||
@@ -21,7 +21,7 @@ typedef struct {
|
||||
|
||||
unsigned int bytes_rx; /* Rx byte count */
|
||||
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 telnet_ack[256]; /* telnet option ack counter */
|
||||
} 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,
|
||||
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_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);
|
||||
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*/
|
||||
#define TNS_STATE_DCD 0x80
|
||||
#define TNS_STATE_RI 0x40
|
||||
@@ -60,11 +64,12 @@ extern void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val);
|
||||
|
||||
/*RFC2217 control commands*/
|
||||
#define TNS_CTL_NOFLOW 1
|
||||
#define TNS_CTL_XONOFF 2
|
||||
#define TNS_CTL_XONOFF_OUT 2
|
||||
#define TNS_CTL_CTSRTS 3
|
||||
#define TNS_CTL_DTR_ON 8
|
||||
#define TNS_CTL_DTR_OFF 9
|
||||
#define TNS_CTL_RTS_ON 11
|
||||
#define TNS_CTL_RTS_OFF 12
|
||||
#define TNS_CTL_XONOFF_IN 15
|
||||
|
||||
#endif /*TELNET_H */
|
||||
#endif /* TELNET_CLIENT_H */
|
||||
@@ -2,12 +2,12 @@
|
||||
* Copyright (c) 2018 Lars Thrane A/S
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Telnet server - Telnet protocol handling
|
||||
* Telnet protocol handling - client
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "telnet.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];
|
||||
|
||||
@@ -68,7 +68,7 @@ static void _telnet_handle_req(tn_ctx_t * tcc, int cmd, int opt)
|
||||
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)
|
||||
{
|
||||
@@ -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;
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
unsigned char *pu;
|
||||
@@ -129,6 +129,10 @@ int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen)
|
||||
int np, nd;
|
||||
int b1, b2;
|
||||
|
||||
/* Skip telnet processing entirely in raw mode */
|
||||
if (tcc->mode_telnet < 0)
|
||||
return 0;
|
||||
|
||||
len = *plen;
|
||||
tcc->bytes_rx += len;
|
||||
|
||||
@@ -144,7 +148,7 @@ int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen)
|
||||
/* We have IAC (= 0xff) */
|
||||
np = rem - (p - s); /* N. chars available incl. IAC */
|
||||
if (np < 2)
|
||||
return 1;
|
||||
goto tn_more;
|
||||
|
||||
pu = (unsigned char *)p;
|
||||
b1 = pu[1];
|
||||
@@ -230,14 +234,14 @@ int telnet_rx(tn_ctx_t * tcc, char *buf, int *plen)
|
||||
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;
|
||||
int rem, nd;
|
||||
|
||||
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);
|
||||
else
|
||||
p = NULL;
|
||||
@@ -260,12 +264,18 @@ int telnet_tx(tn_ctx_t * tcc, const char *buf, int len)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int telnet_open(tn_ctx_t * tcc)
|
||||
int telnet_open(tn_ctx_t *tcc, int mode)
|
||||
{
|
||||
time_t t = time(NULL);;
|
||||
char buf[3];
|
||||
int res;
|
||||
|
||||
if (mode == TN_MODE_RAW)
|
||||
{
|
||||
tcc->mode_telnet = -1; /* Raw mode - no telnet processing */
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf[0] = TNC_FF_IAC;
|
||||
buf[1] = TNC_FB_WILL;
|
||||
buf[2] = TNO_CPCO;
|
||||
@@ -275,17 +285,17 @@ int telnet_open(tn_ctx_t * tcc)
|
||||
while (1)
|
||||
{
|
||||
res = tcc->srv_rx(tcc->cctx, 1000);
|
||||
if (res <= 0)
|
||||
return -1;
|
||||
if (res < 0)
|
||||
return errno;
|
||||
else if (tcc->mode_rfc2217)
|
||||
return 0;
|
||||
else if (t - time(NULL) > 2)
|
||||
return -1;
|
||||
else if (time(NULL) - t > 10)
|
||||
return EPROTO;
|
||||
}
|
||||
}
|
||||
|
||||
tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f * ftx,
|
||||
srv_rx_f * frx, srv_ms_f * fms)
|
||||
tn_ctx_t *telnet_ctx_init(void *cctx, srv_tx_f *ftx,
|
||||
srv_rx_f *frx, srv_ms_f *fms)
|
||||
{
|
||||
tn_ctx_t *tcc;
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
* Copyright (c) 2018 Lars Thrane A/S
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*
|
||||
* Telnet server - RFC-2217 handling
|
||||
* Telnet RFC-2217 handling - client
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "telnet.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 int sub;
|
||||
@@ -35,7 +32,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)
|
||||
return;
|
||||
@@ -43,9 +40,9 @@ void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd, const void *val, int 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;
|
||||
|
||||
if (!tcc->mode_rfc2217)
|
||||
return;
|
||||
@@ -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 */
|
||||
+360
-263
File diff suppressed because it is too large
Load Diff
-123
@@ -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 Binary Transmission [RFC856]
|
||||
* 1 Echo [RFC857]
|
||||
* 2 Reconnection [DDN Protocol Handbook, ... ]
|
||||
* 3 Suppress Go Ahead [RFC858]
|
||||
* 4 Approx Message Size Negotiation ["The Ethernet, ... ]
|
||||
* 5 Status [RFC859]
|
||||
* 6 Timing Mark [RFC860]
|
||||
* 7 Remote Controlled Trans and Echo [RFC726]
|
||||
* 8 Output Line Width [DDN Protocol Handbook, ... ]
|
||||
* 9 Output Page Size [DDN Protocol Handbook, ... ]
|
||||
* 10 Output Carriage-Return Disposition [RFC652]
|
||||
* 11 Output Horizontal Tab Stops [RFC653]
|
||||
* 12 Output Horizontal Tab Disposition [RFC654]
|
||||
* 13 Output Formfeed Disposition [RFC655]
|
||||
* 14 Output Vertical Tabstops [RFC656]
|
||||
* 15 Output Vertical Tab Disposition [RFC657]
|
||||
* 16 Output Linefeed Disposition [RFC658]
|
||||
* 17 Extended ASCII [RFC698]
|
||||
* 18 Logout [RFC727]
|
||||
* 19 Byte Macro [RFC735]
|
||||
* 20 Data Entry Terminal [RFC1043][RFC732]
|
||||
* 21 SUPDUP [RFC736][RFC734]
|
||||
* 22 SUPDUP Output [RFC749]
|
||||
* 23 Send Location [RFC779]
|
||||
* 24 Terminal Type [RFC1091]
|
||||
* 25 End of Record [RFC885]
|
||||
* 26 TACACS User Identification [RFC927]
|
||||
* 27 Output Marking [RFC933]
|
||||
* 28 Terminal Location Number [RFC946]
|
||||
* 29 Telnet 3270 Regime [RFC1041]
|
||||
* 30 X.3 PAD [RFC1053]
|
||||
* 31 Negotiate About Window Size [RFC1073]
|
||||
* 32 Terminal Speed [RFC1079]
|
||||
* 33 Remote Flow Control [RFC1372]
|
||||
* 34 Linemode [RFC1184]
|
||||
* 35 X Display Location [RFC1096]
|
||||
* 36 Environment Option [RFC1408]
|
||||
* 37 Authentication Option [RFC2941]
|
||||
* 38 Encryption Option [RFC2946]
|
||||
* 39 New Environment Option [RFC1572]
|
||||
* 40 TN3270E [RFC2355]
|
||||
* 41 XAUTH [Rob_Earhart]
|
||||
* 42 CHARSET [RFC2066]
|
||||
* 43 Telnet Remote Serial Port (RSP) [Robert_Barnes]
|
||||
* 44 Com Port Control Option [RFC2217]
|
||||
* 45 Telnet Suppress Local Echo [Wirt_Atmar]
|
||||
* 46 Telnet Start TLS [Michael_Boe]
|
||||
* 47 KERMIT [RFC2840]
|
||||
* 48 SEND-URL [David_Croft]
|
||||
* 49 FORWARD_X [Jeffrey_Altman]
|
||||
* 50-137 Unassigned [IANA]
|
||||
* 138 TELOPT PRAGMA LOGON [Steve_McGregory]
|
||||
* 139 TELOPT SSPI LOGON [Steve_McGregory]
|
||||
* 140 TELOPT PRAGMA HEARTBEAT [Steve_McGregory]
|
||||
* 141-254 Unassigned
|
||||
* 255 Extended-Options-List [RFC861]
|
||||
*/
|
||||
|
||||
#define TNO_BINARY 0 /* Binary Transmission */
|
||||
#define TNO_ECHO 1 /* Echo */
|
||||
#define TNO_SUPP_GA 3 /* Suppress Go Ahead */
|
||||
#define TNO_LOGOUT 18 /* Logout */
|
||||
#define TNO_TTYP 24 /* Terminal Type */
|
||||
#define TNO_NAWS 31 /* Negotiate About Window Size */
|
||||
#define TNO_NEO 39 /* New Environment Option */
|
||||
#define TNO_CPCO 44 /* RFC 2217 */
|
||||
#define TNO_KERMIT 47 /* Kermit */
|
||||
|
||||
#endif /* TELNET_PARAM_H */
|
||||
@@ -0,0 +1,48 @@
|
||||
# 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)
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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
@@ -0,0 +1,25 @@
|
||||
#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 */
|
||||
@@ -0,0 +1,6 @@
|
||||
#include "test.h"
|
||||
|
||||
TEST(Misc, dummy)
|
||||
{
|
||||
EXPECT_EQ(1, 1);
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user