23 Commits

Author SHA1 Message Date
Frank Rolsted Jensen 9cff495183 Support for software flow control 2021-10-28 11:35:11 +02:00
Kim Woelders f759c0e3cc Correct termios2 baud rate calculation
Based on suggestion by joluxer <linuxer@quantentunnel.de>.
2021-06-18 17:10:22 +02:00
Kim Woelders 0606891b78 Add indent profile + formatting tweak 2021-06-18 16:17:13 +02:00
joluxer 899596fd5b Use all baudrate for traditional termios known to linux kernel 5.10. 2021-06-18 16:17:13 +02:00
joluxer a21ba0cb9c Make some variable storage bit width agnostic for transport. 2021-06-18 16:17:13 +02:00
Kim Woelders 8f03384964 print_help(): Add missing options to run in foreground
Suggested by joluxer <linuxer@quantentunnel.de>.
2021-06-18 16:17:13 +02:00
Kim Woelders b314659125 Fix potentially incorrect speed check
We don't know if tio is termios or termios2.
2021-06-18 07:21:53 +02:00
Kim Woelders 42a4e2e062 ttynvt_ioctl(): Cleanups around TCSET.. handling
Avoid nested switches and unnecessary block level.
2021-06-18 07:21:53 +02:00
Kim Woelders 0b402cdb6b Remove pointless CFLAGS_OPT
It would normally be overridden by the default automake CFLAGS="-g -O2".
2021-06-14 18:35:10 +02:00
Kim Woelders 302ed6d796 Include asm/ioctls.h for TCSETSF2 and friends
Some targets appear to need that.
2021-01-20 18:34:53 +01:00
Kim Woelders bf11b1dd15 Add support for termios2 ioctls
Based on patch by Thomas Shaddack <shaddack@shaddack.mauriceward.com>
https://www.improwis.com/projects/sw_SerialOverIP_RFC2217/
2021-01-14 07:41:43 +01:00
Kim Woelders 33de2e875c Add a couple of debug logs
From patch by Thomas Shaddack <shaddack@shaddack.mauriceward.com>
https://www.improwis.com/projects/sw_SerialOverIP_RFC2217/
2021-01-14 07:40:40 +01:00
Kim Woelders eccd32fdfb telnet_param.h: Show hex codes too
Inspired by Thomas Shaddack <shaddack@shaddack.mauriceward.com>
https://www.improwis.com/projects/sw_SerialOverIP_RFC2217/
2021-01-14 07:19:05 +01:00
Kim Woelders 089b733f27 Indent 2021-01-14 07:18:48 +01:00
Kim Woelders 41802ce7e9 Include poll.h, not sys/poll.h
... the posixly correct location.
2020-10-08 17:36:55 +02:00
Kim Woelders e51a98e4b2 Handle CRTSCTS properly 2019-09-10 06:52:00 +02:00
Kim Woelders 9db6c5d83c Move logging functions to separate file 2019-09-05 11:36:49 +02:00
Kim Woelders e3675b9e89 Move ttynvt source to src/ 2019-09-05 11:36:49 +02:00
Kim Woelders cf6b334bcc ttynvt version 0.14 2019-03-21 08:52:05 +01:00
Kim Woelders 09e96e72f7 Add some more debug 2019-03-21 08:46:08 +01:00
Kim Woelders 688fc53caf Proper flow-control handling
Patch by FRJ.
2019-03-21 08:45:36 +01:00
Kim Woelders 4d1d6978e4 Don't clear readable flag on poll
When the slave becomes readable we set the pollin flag, and cleared it
in ttynvt_poll().
So if ttynvt_poll() is called twice, the readable flag was lost.

Now we clear it in ttynvt_read() if there actually are no more data to
be read.
2019-01-16 11:17:18 +01:00
Kim Woelders 8f03aabe30 Add timestamp when logging to stdout 2018-10-31 12:09:37 +01:00
13 changed files with 473 additions and 280 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ Makefile
# build
*.o
/ttynvt
/src/ttynvt
/ttynvt-*.tar.gz
/ttynvt-*.tar.xz
Vendored
+6
View File
@@ -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
+2 -15
View File
@@ -1,20 +1,7 @@
# 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
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)
SUBDIRS = src
+2 -1
View File
@@ -1,4 +1,4 @@
m4_define([pkg_version], [0.13])
m4_define([pkg_version], [0.14])
#m4_define([pkg_revision], [005])
m4_define([pkg_version], m4_ifdef([pkg_revision], [pkg_version.pkg_revision], [pkg_version]))
@@ -32,6 +32,7 @@ PKG_CHECK_MODULES(FUSE, fuse)
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
+12
View File
@@ -0,0 +1,12 @@
CFLAGS_WARN = -Wall -Wextra -Werror -Wno-unused-parameter
bin_PROGRAMS = ttynvt
ttynvt_SOURCES = \
ttynvt.c nvt_log.c nvt_log.h \
telnet_basic.c telnet_rfc2217.c telnet.h telnet_param.h
ttynvt_CPPFLAGS = -I$(top_builddir) $(FUSE_CFLAGS) $(CFLAGS_WARN)
ttynvt_LDADD = $(FUSE_LIBS)
+84
View File
@@ -0,0 +1,84 @@
/*
* 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;
char log_stdout = 0;
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;
va_start(arg, fmt);
len = vsnprintf(buf, sizeof(buf), fmt, arg);
if (log_stdout)
{
eol = (len > 0 && buf[len - 1] != '\n') ? "\n" : "";
printf("%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);
}
}
+26
View File
@@ -0,0 +1,26 @@
/*
* 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)
_PRF2_ void nvt_log(int prio, const char *fmt, ...);
void nvt_log_buf(int prio, const char *txt,
const void *ptr, unsigned int len);
extern char log_level;
extern char log_stdout;
#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 */
+2 -1
View File
@@ -60,11 +60,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 */
+123
View File
@@ -0,0 +1,123 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet parameters
*/
#ifndef TELNET_PARAM_H
#define TELNET_PARAM_H
/*
* Telnet commands
*/
#define TNC_F0_SE (0xf0) /* End of subnegotiation parameters */
#define TNC_F1_NOP (0xf1) /* No operation */
#define TNC_F2_MARK (0xf2) /* The data stream portion of a Synch.
This should always be accompanied
by a TCP Urgent notification. */
#define TNC_F3_BRK (0xf3) /* NVT character BRK. */
#define TNC_F4_IP (0xf4) /* The function IP (interrupt process) */
#define TNC_F5_AO (0xf5) /* The function AO (abort output) */
#define TNC_F6_AYT (0xf6) /* The function AYT (are you there) */
#define TNC_F7_EC (0xf7) /* The function EC (erase charceter) */
#define TNC_F8_EL (0xf8) /* The function EL (erase line) */
#define TNC_F9_GA (0xf9) /* The GA signal (go ahead) */
#define TNC_FA_SB (0xfa) /* Subnegotiation begin */
#define TNC_FB_WILL (0xfb) /* Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option. */
#define TNC_FC_WONT (0xfc) /* Indicates the refusal to perform,
or continue performing, the
indicated option. */
#define TNC_FD_DO (0xfd) /* Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option. */
#define TNC_FE_DONT (0xfe) /* Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option. */
#define TNC_FF_IAC (0xff) /* Interpret as Command / data byte */
/*
* Telnet options
*/
/*
* https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
*
* 0 00 Binary Transmission [RFC856]
* 1 01 Echo [RFC857]
* 2 02 Reconnection [DDN Protocol Handbook, ... ]
* 3 03 Suppress Go Ahead [RFC858]
* 4 04 Approx Message Size Negotiation ["The Ethernet, ..." ]
* 5 05 Status [RFC859]
* 6 06 Timing Mark [RFC860]
* 7 07 Remote Controlled Trans and Echo [RFC726]
* 8 08 Output Line Width [DDN Protocol Handbook, ... ]
* 9 09 Output Page Size [DDN Protocol Handbook, ... ]
* 10 0a Output Carriage-Return Disposition [RFC652]
* 11 0b Output Horizontal Tab Stops [RFC653]
* 12 0c Output Horizontal Tab Disposition [RFC654]
* 13 0d Output Formfeed Disposition [RFC655]
* 14 0e Output Vertical Tabstops [RFC656]
* 15 0f Output Vertical Tab Disposition [RFC657]
* 16 10 Output Linefeed Disposition [RFC658]
* 17 11 Extended ASCII [RFC698]
* 18 12 Logout [RFC727]
* 19 13 Byte Macro [RFC735]
* 20 14 Data Entry Terminal [RFC1043][RFC732]
* 21 15 SUPDUP [RFC736][RFC734]
* 22 16 SUPDUP Output [RFC749]
* 23 17 Send Location [RFC779]
* 24 18 Terminal Type [RFC1091]
* 25 19 End of Record [RFC885]
* 26 1a TACACS User Identification [RFC927]
* 27 1b Output Marking [RFC933]
* 28 1c Terminal Location Number [RFC946]
* 29 1d Telnet 3270 Regime [RFC1041]
* 30 1e X.3 PAD [RFC1053]
* 31 1f Negotiate About Window Size [RFC1073]
* 32 20 Terminal Speed [RFC1079]
* 33 21 Remote Flow Control [RFC1372]
* 34 22 Linemode [RFC1184]
* 35 23 X Display Location [RFC1096]
* 36 24 Environment Option [RFC1408]
* 37 25 Authentication Option [RFC2941]
* 38 26 Encryption Option [RFC2946]
* 39 27 New Environment Option [RFC1572]
* 40 28 TN3270E [RFC2355]
* 41 29 XAUTH [Rob_Earhart]
* 42 2a CHARSET [RFC2066]
* 43 2b Telnet Remote Serial Port (RSP) [Robert_Barnes]
* 44 2c Com Port Control Option [RFC2217]
* 45 2d Telnet Suppress Local Echo [Wirt_Atmar]
* 46 2e Telnet Start TLS [Michael_Boe]
* 47 2f KERMIT [RFC2840]
* 48 30 SEND-URL [David_Croft]
* 49 31 FORWARD_X [Jeffrey_Altman]
* 50-137 32-89 Unassigned [IANA]
* 138 8a TELOPT PRAGMA LOGON [Steve_McGregory]
* 139 8b TELOPT SSPI LOGON [Steve_McGregory]
* 140 8c TELOPT PRAGMA HEARTBEAT [Steve_McGregory]
* 141-254 8d-fe Unassigned
* 255 ff Extended-Options-List [RFC861]
*/
#define TNO_BINARY 0 /* 00 Binary Transmission */
#define TNO_ECHO 1 /* 01 Echo */
#define TNO_SUPP_GA 3 /* 03 Suppress Go Ahead */
#define TNO_LOGOUT 18 /* 12 Logout */
#define TNO_TTYP 24 /* 18 Terminal Type */
#define TNO_NAWS 31 /* 1f Negotiate About Window Size */
#define TNO_NEO 39 /* 27 New Environment Option */
#define TNO_CPCO 44 /* 2c RFC 2217 */
#define TNO_KERMIT 47 /* 2f Kermit */
#endif /* TELNET_PARAM_H */
+1 -1
View File
@@ -45,7 +45,7 @@ void telnet_rfc2217_cfg(tn_ctx_t * tcc, int cmd, const void *val, int nd)
void telnet_rfc2217_ctl(tn_ctx_t * tcc, unsigned int val)
{
unsigned char byte = val;
unsigned char byte = val;
if (!tcc->mode_rfc2217)
return;
+214 -138
View File
@@ -8,8 +8,10 @@
#include <errno.h>
#include <fuse_opt.h>
#include <netdb.h>
#include <poll.h>
#include <pthread.h>
#include <signal.h>
#include <stdint.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
@@ -17,15 +19,15 @@
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <asm/ioctls.h>
#include <asm/termbits.h>
#include <linux/sockios.h>
#include <linux/ppp-ioctl.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include "nvt_log.h"
#include "telnet.h"
#define NET_BUF_SIZE 1024
@@ -35,9 +37,6 @@
#define FD_MASTER 1
#define FD_SLAVE 2
#define _PRF_N_(no) __attribute__((__format__(__printf__, (no), (no) + 1)))
#define _PRF2_ _PRF_N_(2)
static struct ttynvt_param {
unsigned int major;
@@ -61,77 +60,28 @@ typedef struct {
struct fuse_pollhandle *ph;
volatile char n_fds;
volatile int rel_pending;
volatile int slave_suspended; /*waiting for application to read data */
volatile int master_suspended; /*server is not ready to receive data */
struct pollfd fds[3];
char net_buf[NET_BUF_SIZE];
int net_cnt;
struct termios tio;
struct termios2 tio;
int cmcr; /* Commanded by ioctl */
volatile int smcr; /* Status from modem */
int smcr_last;
volatile int pollin;
volatile int pollout;
volatile int error;
int epipe;
tn_ctx_t *tn;
} ttynvt_t;
#include <sys/syscall.h>
#define gettid() (pid_t)syscall(__NR_gettid)
_PRF2_ static void _log(int prio, const char *fmt, ...)
{
va_list arg;
char buf[256];
va_start(arg, fmt);
vsnprintf(buf, sizeof(buf), fmt, arg);
if (ttynvt_param.logstd)
printf("[%d] %d: %s", gettid(), prio, buf);
else
syslog(prio, "[%d] %s", gettid(), buf);
va_end(arg);
}
#define BPL 16 /* Bytes per line */
void _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;
}
_log(prio, "%s\n", buf);
}
}
#define DBG(...) \
if (ttynvt_param.debug > 0) _log(LOG_DEBUG, __VA_ARGS__)
#define DBG2(...) \
if (ttynvt_param.debug > 1) _log(LOG_DEBUG, __VA_ARGS__)
#define DBG2_BUF(txt, ptr, len) \
if (ttynvt_param.debug > 1) _log_buf(LOG_DEBUG, txt, ptr, len)
static void _fd_close(int fd)
{
if (fd >= 0)
@@ -187,9 +137,9 @@ static void ttynvt_release(fuse_req_t req, struct fuse_file_info *info)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
_log(LOG_INFO, "connection closed\n");
nvt_log(LOG_INFO, "connection closed\n");
tty->n_fds = 0;
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);
@@ -255,22 +205,26 @@ static void *_read_net(void *arg)
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
DBG2("%s: net_cnt=%d\n", __func__, tty->net_cnt);
while (tty->n_fds > 0)
while (!tty->rel_pending)
{
tty->fds[FD_NET].revents = tty->fds[FD_MASTER].revents =
tty->fds[FD_SLAVE].revents = 0;
res = poll(tty->fds, tty->n_fds, -1);
DBG2("%s: res=%d N=%d events-N/M/S=%#x/%#x/%#x\n", __func__,
res, tty->n_fds,
tty->fds[FD_NET].revents, tty->fds[FD_MASTER].revents,
tty->fds[FD_SLAVE].revents);
tty->fds[FD_SLAVE].events = tty->slave_suspended ? POLLPRI : POLLIN;
tty->fds[FD_MASTER].events = tty->master_suspended ? POLLPRI : POLLIN;
res = poll(tty->fds, 3, -1);
DBG2("%s: res=%d events-N/M/S=%x:%x/%x:%x/%x:%x SS=%d MS=%d\n",
__func__, res, tty->fds[FD_NET].events, tty->fds[FD_NET].revents,
tty->fds[FD_MASTER].events, tty->fds[FD_MASTER].revents,
tty->fds[FD_SLAVE].events, tty->fds[FD_SLAVE].revents,
tty->slave_suspended, tty->master_suspended);
if (res < 0)
{
if (errno == EINTR)
continue;
break;
}
if (tty->n_fds == 0)
if (tty->rel_pending)
break;
if (tty->fds[FD_NET].revents & POLLIN)
@@ -279,12 +233,12 @@ static void *_read_net(void *arg)
NET_BUF_SIZE - tty->net_cnt);
if (res < 0)
{
_log(LOG_ERR, "net read error: %m\n");
nvt_log(LOG_ERR, "net read error: %m\n");
break;
}
if (res == 0)
{
_log(LOG_INFO, "Connection closed by remote host\n");
nvt_log(LOG_INFO, "Connection closed by remote host\n");
break;
}
tty->net_cnt += res;
@@ -298,7 +252,7 @@ static void *_read_net(void *arg)
write(tty->fds[FD_MASTER].fd, tty->net_buf, tty->net_cnt);
if (res < 0)
{
_log(LOG_ERR, "master write error: %m\n");
nvt_log(LOG_ERR, "master write error: %m\n");
break;
}
tty->net_cnt = 0;
@@ -307,14 +261,20 @@ static void *_read_net(void *arg)
break;
}
if (tty->fds[FD_MASTER].revents & POLLIN)
if ((tty->fds[FD_MASTER].revents & POLLIN) && !tty->master_suspended)
{
res = read(tty->fds[FD_MASTER].fd, buf, TMP_BUF_SIZE);
DBG("%s: read res=%d pollout=%d\n", __func__, res, tty->pollout);
if (res < 0)
{
_log(LOG_ERR, "master read error: %m\n");
nvt_log(LOG_ERR, "master read error: %m\n");
break;
}
if (tty->pollout == 0)
{
tty->pollout = 1;
_notify(tty);
}
DBG2_BUF("TtyM in ", buf, res);
telnet_tx(tty->tn, buf, res);
}
@@ -322,7 +282,7 @@ static void *_read_net(void *arg)
if (tty->fds[FD_SLAVE].revents & POLLIN)
{
tty->pollin = 1;
tty->n_fds = 2; /* Disable polling of Slave */
tty->slave_suspended = 1; /* Disable polling of Slave */
_notify(tty);
}
}
@@ -346,14 +306,14 @@ static int _srv_connect(const char *host, unsigned int port)
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
_log(LOG_ERR, "Socket open failed: %m\n");
nvt_log(LOG_ERR, "Socket open failed: %m\n");
return sockfd;
}
server = gethostbyname(host);
if (server == NULL)
{
_log(LOG_ERR, "Cannot resolve host name: %m\n");
nvt_log(LOG_ERR, "Cannot resolve host name: %m\n");
close(sockfd);
return -1;
}
@@ -367,7 +327,7 @@ static int _srv_connect(const char *host, unsigned int port)
err = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if (err < 0)
{
_log(LOG_ERR, "Failed to connect to: %s:%u: %m\n", host, port);
nvt_log(LOG_ERR, "Failed to connect to: %s:%u: %m\n", host, port);
close(sockfd);
return -1;
}
@@ -442,6 +402,11 @@ static void _modem_status_cb(void *cctx, int status)
if (status & TNS_STATE_CTS)
mcr |= TIOCM_CTS;
if (tty->tio.c_cflag & CRTSCTS)
tty->master_suspended = (mcr & TIOCM_CTS) ? 0 : 1;
DBG("%s: tty=%p mcr=%x MS=%d\n", __func__, tty, mcr,
tty->master_suspended);
pthread_mutex_lock(&tty->tty_lock);
tty->smcr_last = tty->smcr;
tty->smcr = mcr;
@@ -474,6 +439,8 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
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);
if (!tty->tn)
{
@@ -542,14 +509,12 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
goto open_err;
}
tty->n_fds = 3; /* Initially poll Net, Master, and Slave */
info->fh = (uintptr_t) tty;
info->nonseekable = 1;
info->direct_io = 1;
_log(LOG_INFO, "Connected to server: %s:%d\n",
ttynvt_param.host, ttynvt_param.port);
nvt_log(LOG_INFO, "Connected to server: %s:%d\n",
ttynvt_param.host, ttynvt_param.port);
fuse_reply_open(req, info);
@@ -557,8 +522,8 @@ static void ttynvt_open(fuse_req_t req, struct fuse_file_info *info)
open_err:
_log(LOG_INFO, "Connection to server: %s:%d failed: %m\n",
ttynvt_param.host, ttynvt_param.port);
nvt_log(LOG_INFO, "Connection to server: %s:%d failed: %m\n",
ttynvt_param.host, ttynvt_param.port);
for (n = 0; n < 3; n++)
_fd_close(tty->fds[n].fd);
@@ -574,7 +539,7 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
char buf[TMP_BUF_SIZE];
int res;
int res, nr;
DBG2("%s\n", __func__);
@@ -583,7 +548,7 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
DBG2("%s: tty->error=%d\n", __func__, tty->error);
if (tty->epipe == 0)
{
_log(LOG_WARNING, "Rx EPIPE\n");
nvt_log(LOG_WARNING, "Rx EPIPE\n");
tty->epipe = 1;
fuse_reply_buf(req, NULL, 0);
}
@@ -601,8 +566,18 @@ ttynvt_read(fuse_req_t req, size_t size, off_t off,
res = _is_interrupted ? -2 : read(tty->fds[FD_SLAVE].fd, buf, size);
_is_interrupted = 0;
tty->ptid_read = 0;
tty->n_fds = 3; /* Enable polling of Slave */
tty->slave_suspended = 0; /* Enable polling of Slave */
fuse_req_interrupt_func(req, NULL, NULL);
if (res < (int)size)
{
tty->pollin = 0;
}
else
{
nr = 0;
ioctl(tty->fds[FD_SLAVE].fd, FIONREAD, &nr);
tty->pollin = nr > 0;
}
if (res < 0)
{
DBG2("%s: error: %m\n", __func__);
@@ -647,15 +622,24 @@ ttynvt_write(fuse_req_t req, const char *data, size_t size, off_t off,
fuse_reply_err(req, errno);
return;
}
else if (res != (int)size)
{
DBG("%s: caution: tty->pollout set to 0\n", __func__);
tty->pollout = 0;
}
fuse_reply_write(req, res);
}
static int baudrate(speed_t speed)
{
/**INDENT-OFF**/
return speed == B50 ? 50 :
speed == B75 ? 75 :
speed == B110 ? 110 :
speed == B134 ? 134 :
speed == B150 ? 150 :
speed == B200 ? 200 :
speed == B300 ? 300 :
speed == B600 ? 600 :
speed == B1200 ? 1200 :
@@ -666,7 +650,21 @@ static int baudrate(speed_t speed)
speed == B19200 ? 19200 :
speed == B38400 ? 38400 :
speed == B57600 ? 57600 :
speed == B115200 ? 115200 : speed == B230400 ? 230400 : 0;
speed == B115200 ? 115200 :
speed == B230400 ? 230400 :
speed == B460800 ? 460800 :
speed == B500000 ? 500000 :
speed == B576000 ? 576000 :
speed == B921600 ? 921600 :
speed == B1000000 ? 1000000 :
speed == B1152000 ? 1152000 :
speed == B1500000 ? 1500000 :
speed == B2000000 ? 2000000 :
speed == B2500000 ? 2500000 :
speed == B3000000 ? 3000000 :
speed == B3500000 ? 3500000 :
speed == B4000000 ? 4000000 : 0;
/**INDENT-ON**/
}
static const char pstr[] = { ' ', 'N', 'O', 'E', 'M', 'S' };
@@ -683,9 +681,13 @@ static const char *_ioctl_name(int cmd)
snprintf(buf, sizeof(buf), "0x%x", cmd);
return buf;
CASE(TCSETSF);
CASE(TCSETSF2);
CASE(TCSETSW);
CASE(TCSETSW2);
CASE(TCSETS);
CASE(TCSETS2);
CASE(TCGETS);
CASE(TCGETS2);
CASE(TIOCINQ);
CASE(TIOCOUTQ);
CASE(TCFLSH);
@@ -711,7 +713,7 @@ static const char *_ioctl_name(int cmd)
}
}
static unsigned int _tio_parity(const struct termios *tio)
static unsigned int _tio_parity(const struct termios2 *tio)
{
unsigned int par;
@@ -734,12 +736,20 @@ static unsigned int _tio_parity(const struct termios *tio)
return par;
}
static unsigned int _tio_baud(const struct termios *tio)
static unsigned int _tio_baud(const struct termios2 *tio)
{
return baudrate(tio->c_cflag & (CBAUD | CBAUDEX));
}
static unsigned int _tio_csize(const struct termios *tio)
static unsigned int _tio2_baud(const struct termios2 *tio2)
{
if ((tio2->c_cflag & CBAUD) == BOTHER)
return tio2->c_ospeed;
return _tio_baud(tio2);
}
static unsigned int _tio_csize(const struct termios2 *tio)
{
unsigned int csize;
@@ -755,7 +765,7 @@ static unsigned int _tio_csize(const struct termios *tio)
return csize;
}
static unsigned int _tio_stopb(const struct termios *tio)
static unsigned int _tio_stopb(const struct termios2 *tio)
{
unsigned int stopb;
@@ -767,7 +777,7 @@ static unsigned int _tio_stopb(const struct termios *tio)
return stopb;
}
static void _show_termios(const char *txt, const struct termios *tio)
static void _show_termios(const char *txt, const struct termios2 *tio)
{
DBG("%s: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n", txt,
_tio_baud(tio), pstr[_tio_parity(tio)], _tio_csize(tio),
@@ -776,6 +786,16 @@ static void _show_termios(const char *txt, const struct termios *tio)
tio->c_cflag & HUPCL ? '+' : '-', tio->c_cflag & CLOCAL ? '+' : '-');
}
static void _show_termios2(const char *txt, const struct termios2 *tio)
{
DBG("%s: %u%c%u%u %cCTSRTS %cCREAD %cHUPCL %cCLOCAL\n", txt,
_tio2_baud(tio), pstr[_tio_parity(tio)], _tio_csize(tio),
_tio_stopb(tio),
tio->c_cflag & CRTSCTS ? '+' : '-', tio->c_cflag & CREAD ? '+' : '-',
tio->c_cflag & HUPCL ? '+' : '-', tio->c_cflag & CLOCAL ? '+' : '-');
}
#define CHECK_BUF_IN(sz) \
if (in_bufsz < sz) do { in_bufsz = sz; goto retry_in; } while(0)
#define CHECK_BUF_OUT(sz) \
@@ -787,14 +807,14 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
const void *in_buf, size_t in_bufsz, size_t out_bufsz)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
struct termios *tio = &tty->tio;
struct termios2 *tio = &tty->tio;
struct winsize ws;
unsigned int tiocm = 0;
unsigned int tiocm, baud;
int mcr;
int val;
int res;
unsigned char byte;
unsigned int byte4;
uint8_t byte;
uint32_t byte4;
DBG("%s: cmd=%s arg=%p ibuf=%p:%u obuf=:%u\n", __func__,
_ioctl_name(cmd), arg, in_buf,
@@ -806,41 +826,82 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
case TCSETSW:
case TCSETS:
CHECK_BUF_IN(sizeof(struct termios));
memcpy(tio, in_buf, in_bufsz);
baud = _tio_baud(tio);
goto do_tcset;
case TCSETSF2:
case TCSETSW2:
case TCSETS2:
CHECK_BUF_IN(sizeof(struct termios2));
memcpy(tio, in_buf, in_bufsz);
baud = _tio2_baud(tio);
goto do_tcset;
do_tcset:
/* BEWARE of termios/termios2 buf structure difference! */
byte4 = htonl(baud);
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
byte = _tio_parity(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_PARITY, &byte, 1);
byte = _tio_stopb(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &byte, 1);
if (tio->c_cflag & CRTSCTS)
{
memcpy(tio, in_buf, in_bufsz);
byte4 = htonl(_tio_baud(tio));
telnet_rfc2217_cfg(tty->tn, TNS_SET_BAUDRATE, &byte4, 4);
byte = _tio_csize(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_DATASIZE, &byte, 1);
byte = _tio_parity(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_PARITY, &byte, 1);
byte = _tio_stopb(tio);
telnet_rfc2217_cfg(tty->tn, TNS_SET_STOPSIZE, &byte, 1);
if (tio->c_cflag & CRTSCTS)
{
tty->cmcr |= TIOCM_RTS;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
}
if (!(tty->cmcr & TIOCM_DTR) && _tio_baud(tio) != 0)
{
tty->cmcr |= TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
}
_show_termios("TCSETS", tio);
ioctl(tty->fds[FD_SLAVE].fd, cmd, tio);
if (cmd == TCSETSF)
tty->n_fds = 3; /* Re-enable polling of Slave */
fuse_reply_ioctl(req, 0, 0, 0);
tty->master_suspended = (tty->smcr & TIOCM_CTS) ? 0 : 1;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_CTSRTS);
}
else if ((tio->c_iflag & IXON) && (tio->c_iflag & IXOFF))
{
tty->master_suspended = 0;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_OUT);
telnet_rfc2217_ctl(tty->tn, TNS_CTL_XONOFF_IN);
}
else if (tio->c_iflag & IXON)
{
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
{
tty->master_suspended = 0;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_NOFLOW);
}
if (!(tty->cmcr & TIOCM_DTR) && baud != 0)
{
tty->cmcr |= TIOCM_DTR;
telnet_rfc2217_ctl(tty->tn, TNS_CTL_DTR_ON);
}
switch (cmd)
{
case TCSETSF:
case TCSETSW:
case TCSETS:
_show_termios("TCSETS", tio);
break;
case TCSETSF2:
case TCSETSW2:
case TCSETS2:
_show_termios2("TCSETS2", tio);
break;
}
ioctl(tty->fds[FD_SLAVE].fd, cmd, tio);
if (cmd == TCSETSF || cmd == TCSETSF2)
tty->slave_suspended = 0; /* Re-enable polling of Slave */
fuse_reply_ioctl(req, 0, 0, 0);
break;
case TCGETS:
CHECK_BUF_OUT(sizeof(struct termios));
@@ -848,6 +909,12 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
_show_termios("TCGETS", tio);
fuse_reply_ioctl(req, 0, tio, sizeof(struct termios));
break;
case TCGETS2:
CHECK_BUF_OUT(sizeof(struct termios2));
ioctl(tty->fds[FD_SLAVE].fd, TCGETS2, tio);
_show_termios2("TCGETS2", tio);
fuse_reply_ioctl(req, 0, tio, sizeof(struct termios2));
break;
case TIOCINQ: /* Get the number of bytes in the input
* buffer */
CHECK_BUF_OUT(sizeof(int));
@@ -861,19 +928,21 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
case TCFLSH:
switch ((size_t) arg)
switch ((size_t)arg)
{
default:
case TCIFLUSH:
byte = 1;
tty->n_fds = 3; /* Re-enable polling of Slave */
tty->slave_suspended = 0; /* Re-enable polling of Slave */
break;
case TCOFLUSH:
byte = 2;
tty->pollout = 1;
break;
case TCIOFLUSH:
byte = 3;
tty->n_fds = 3; /* Re-enable polling of Slave */
tty->slave_suspended = 0; /* Re-enable polling of Slave */
tty->pollout = 1;
break;
}
telnet_rfc2217_cfg(tty->tn, TNS_SET_PURGE, &byte, 1);
@@ -999,6 +1068,7 @@ ttynvt_ioctl(fuse_req_t req, int cmd, void *arg,
fuse_reply_ioctl(req, 0, &val, sizeof(int));
break;
default:
DBG("ERROR: Unsupported IOCTL\n");
fuse_reply_err(req, ENOSYS);
break;
@@ -1023,18 +1093,19 @@ ttynvt_poll(fuse_req_t req, struct fuse_file_info *info,
struct fuse_pollhandle *ph)
{
ttynvt_t *tty = (ttynvt_t *) (uintptr_t) info->fh;
int revents;
int revents = 0;
DBG2("%s: tty->pollin=%d tty->error/epipe=%d/%d\n",
__func__, tty->pollin, tty->error, tty->epipe);
_update_notify(tty, ph);
revents = POLLOUT;
if (tty->pollout)
{
revents = POLLOUT;
}
if (tty->pollin)
{
tty->pollin = 0;
revents |= POLLIN;
}
if (tty->error)
@@ -1082,6 +1153,8 @@ static void print_help(void)
{
fprintf(stderr, "ttynvt Usage:\n"
"\t-D level\tEnable debug\n"
"\t-d\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-M major, --maj=major\n"
"\t-m minor, --min=minor\n"
@@ -1122,6 +1195,9 @@ int main(int argc, char *argv[])
return 1;
}
log_level = ttynvt_param.debug;
log_stdout = ttynvt_param.logstd;
if (!ttynvt_param.dev_name)
ttynvt_param.dev_name = "ttyNVT0";
@@ -1164,14 +1240,14 @@ int main(int argc, char *argv[])
switch (cpid)
{
case -1:
_log(LOG_ERR, "fork(): %m\n");
nvt_log(LOG_ERR, "fork(): %m\n");
return -1;
case 0: /* Child */
DBG("Child proceeding...\n");
rc = cuse_lowlevel_main(args.argc, args.argv, &ci, &ttynvt_op, NULL);
if (rc != 0)
_log(LOG_ERR, "ttynvt failed rc=%d\n", rc);
nvt_log(LOG_ERR, "ttynvt failed rc=%d\n", rc);
else
DBG("Child exited\n");
return rc;
-123
View File
@@ -1,123 +0,0 @@
/*
* Copyright (c) 2018 Lars Thrane A/S
* SPDX-License-Identifier: GPL-2.0
*
* Telnet parameters
*/
#ifndef TELNET_PARAM_H
#define TELNET_PARAM_H
/*
* Telnet commands
*/
#define TNC_F0_SE (0xf0) /* End of subnegotiation parameters */
#define TNC_F1_NOP (0xf1) /* No operation */
#define TNC_F2_MARK (0xf2) /* The data stream portion of a Synch.
This should always be accompanied
by a TCP Urgent notification. */
#define TNC_F3_BRK (0xf3) /* NVT character BRK. */
#define TNC_F4_IP (0xf4) /* The function IP (interrupt process) */
#define TNC_F5_AO (0xf5) /* The function AO (abort output) */
#define TNC_F6_AYT (0xf6) /* The function AYT (are you there) */
#define TNC_F7_EC (0xf7) /* The function EC (erase charceter) */
#define TNC_F8_EL (0xf8) /* The function EL (erase line) */
#define TNC_F9_GA (0xf9) /* The GA signal (go ahead) */
#define TNC_FA_SB (0xfa) /* Subnegotiation begin */
#define TNC_FB_WILL (0xfb) /* Indicates the desire to begin
performing, or confirmation that
you are now performing, the
indicated option. */
#define TNC_FC_WONT (0xfc) /* Indicates the refusal to perform,
or continue performing, the
indicated option. */
#define TNC_FD_DO (0xfd) /* Indicates the request that the
other party perform, or
confirmation that you are expecting
the other party to perform, the
indicated option. */
#define TNC_FE_DONT (0xfe) /* Indicates the demand that the
other party stop performing,
or confirmation that you are no
longer expecting the other party
to perform, the indicated option. */
#define TNC_FF_IAC (0xff) /* Interpret as Command / data byte */
/*
* Telnet options
*/
/*
* https://www.iana.org/assignments/telnet-options/telnet-options.xhtml
*
* 0 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 */