blob: edb5c3cc5f5ca8c5053b008ada24644a6d35df8f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Wind River Systems, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
* You may elect to redistribute this code under either of these licenses.
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
/*
* This module implements PortForward service.
* The service allows a client to open a port and read/write to/from
* this port using a stream.
* The type of ports supported is OS specific but currently UDP, TCP and
* serial ports are supported.
*/
#include <tcf/config.h>
#include <tcf/framework/mdep-inet.h>
#include <tcf/framework/mdep-threads.h>
#include <assert.h>
#include <errno.h>
#include <tcf/framework/myalloc.h>
#include <tcf/framework/context.h>
#include <tcf/services/portforward_service.h>
#include <tcf/framework/errors.h>
#include <tcf/framework/json.h>
#include <tcf/framework/trace.h>
#include <tcf/framework/exceptions.h>
#include <tcf/framework/asyncreq.h>
#include <tcf/services/streamsservice.h>
#if SERVICE_PortForward
#if defined(_WRS_KERNEL)
#define ENABLE_PortForward_Serial 0
#define ENABLE_PortForward_UDP 0
#elif defined(WIN32)
#define ENABLE_PortForward_Serial 1
#define ENABLE_PortForward_UDP 0
#elif defined(__linux__)
#define ENABLE_PortForward_Serial 1
#define ENABLE_PortForward_UDP 1
#else
#define ENABLE_PortForward_Serial 0
#define ENABLE_PortForward_UDP 0
#endif
#ifdef WIN32
#ifndef ECONNREFUSED
#define ECONNREFUSED WSAECONNREFUSED
#endif
#endif
#if ENABLE_PortForward_Serial
#if !defined(WIN32)
#include <fcntl.h>
#include <ctype.h>
#if defined(__linux__)
#include <linux/serial.h>
#endif
#include <termios.h>
#endif
#endif
#define PORTFW_TCP_PORT 0
#define PORTFW_UDP_PORT 1
#define PORTFW_SER_PORT 2
#define IN_BUF_SIZE 32*1024
#define OUT_BUF_SIZE 32*1024
#define PORTFW_SERIAL_FC_NONE 0
#define PORTFW_SERIAL_FC_XON 1
#define PORTFW_SERIAL_FC_RTS 2
#define PORTFW_SERIAL_FC_DSR 3
#define PORTFW_SERIAL_PARITY_NONE 0
#define PORTFW_SERIAL_PARITY_ODD 1
#define PORTFW_SERIAL_PARITY_EVEN 2
#define PORTFW_SERIAL_PARITY_MARK 3
#define PORTFW_SERIAL_PARITY_SPACE 4
#if ENABLE_PortForward_Serial
typedef struct BaudConst {
int baud_const;
int baud_rate;
} BaudConst;
typedef struct DataBitConst
{
int data_bits_const;
int data_bits;
} DataBitConst;
#ifdef WIN32
static BaudConst baud_table[] =
{ { CBR_110, 110 }, { CBR_300, 300 }, { CBR_600, 600 }, { CBR_1200, 1200 },
{ CBR_2400, 2400 }, { CBR_4800, 4800 },
{ CBR_9600, 9600 }, { CBR_14400, 14400},
{ CBR_19200, 19200 }, { CBR_38400, 38400 },
{ CBR_57600, 57600 }, { CBR_115200, 115200 },
{ CBR_128000, 128000 }, { CBR_256000, 256000 },
{ 0, 0 }
};
#else /* WIN32 */
static BaudConst baud_table[] =
{ { B150, 150 }, { B300, 300 }, { B600, 600 }, { B1200, 1200 },
{ B1800, 1800 }, { B2400, 2400 }, { B4800, 4800 },
{ B9600, 9600 }, { B19200, 19200 }, { B38400, 38400 },
#ifdef B57600
{ B57600, 57600 },
#endif
#ifdef B115200
{ B115200, 115200 },
#endif
#ifdef B230400
{ B230400, 230400, },
#endif
#ifdef B46080
{ B460800, 460800 },
#endif
{ 0, 0 }
};
static DataBitConst data_bit_table[] =
{ {CS8, 8}, {CS7, 7}, {CS6, 6}, {CS5, 5}, {0,0} };
#endif /* WIN32 */
#endif /* ENABLE_PortForward_Serial */
typedef struct PortFwConfig PortFwConfig;
typedef void (*ConnectCallBack)(PortFwConfig * /* config */, int /* error */);
typedef int (*UserSendReqFunc)(PortFwConfig * /* config */, void * /* buffer */, size_t /* length */);
typedef int (*UserRecvReqFunc)(PortFwConfig * /* config */, void * /* buffer */, size_t /* length */);
typedef void (*UserCloseFunc)(PortFwConfig * /* config */);
typedef struct PortConnectInfo {
AsyncReqInfo cnct_req;
ConnectCallBack cnct_callback;
/* UDP/TCP specific parameters */
struct sockaddr * addr_buf;
int addr_len;
int sock;
/* Non network parameters */
UserSendReqFunc send_req_func;
UserRecvReqFunc recv_req_func;
UserCloseFunc close_func;
#if ENABLE_PortForward_Serial
/* Serial line specific parameters */
/* Syntax of the param string for serial line is:
* <baud rate>[-<data bits>[-<parity>[-<stop bits>[-<flow control>]]]]
* eg: 9600-8-N-1-N or 9600-8-N-1 or 9600
*/
struct {
/* Serial line specific parameters */
int stop_bits; /* number of stop bits (1 or 2), 1 is default */
int data_bits; /* number of data bits (between 5 & 8), 8 is default */
int parity; /* parity, no parity is the default */
int flow_ctrl; /* flow control; no flow control is the dfault */
int baud_rate; /* baud rate, default to 115200 */
#ifdef WIN32
HANDLE handle; /* serial device handle */
OVERLAPPED write_overlapped; /* overlap struct for write ops */
OVERLAPPED read_overlapped; /* overlap struct for read ops */
COMMTIMEOUTS saved_timeout; /* saved serial line device timeout */
DCB saved_dcb; /* saved serial line configuration */
#else
int fd;
#endif
} serial;
#endif
} PortConnectInfo;
struct PortFwConfig {
LINK link;
char id[256];
Channel * channel;
char token[256];
int verbose;
int port_type; /* port type */
char * port_config; /* port config string (tcp:<port number>, udp:<port number>, serial:<device>) */
char * port_params; /* port parameters */
char * client_data; /* client data */
int connected;
VirtualStream * in_vstream;
VirtualStream * out_vstream;
char outbuf[OUT_BUF_SIZE];
size_t outbuf_len;
char inbuf[IN_BUF_SIZE];
size_t inbuf_pos;
size_t inbuf_len;
AsyncReqInfo send_req;
int send_in_progress;
AsyncReqInfo recv_req;
int recv_in_progress;
char in_stream_id[256];
char out_stream_id[256];
int shutdown_in_progress;
PortConnectInfo port_info;
};
#define link2pfwp(A) ((PortFwConfig *)((char *)(A) - offsetof(PortFwConfig, link)))
static const char * PortForward = "PortForward";
static LINK portfw_config_list = TCF_LIST_INIT(portfw_config_list);
static const char * portfw_inv_config_err = "Unable to find specified PortForward Remote configuration";
static TCFBroadcastGroup * broadcast_group = NULL;
static PortFwConfig * find_portfw_config(const char * name);
static void write_portfw_config(OutputStream * out, PortFwConfig * config);
static void write_port_to_stream(void * arg);
static int log_portfw;
static void set_socket_options(int sock) {
int snd_buf = OUT_BUF_SIZE;
int rcv_buf = IN_BUF_SIZE;
struct linger optval;
int i = 1;
/*
* set SO_LINGER & SO_REUSEADDR socket options so that it closes the
* connections gracefully, when required to close.
*/
optval.l_onoff = 1;
optval.l_linger = 0;
if (setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &optval,
sizeof(optval)) != 0) {
trace(LOG_ALWAYS, "Unable to set SO_LINGER socket option: %s",
errno_to_str(errno));
};
#if !(defined(_WIN32) || defined(__CYGWIN__))
{
const int i = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &i, sizeof(i))
< 0) {
trace(LOG_ALWAYS, "Unable to set SO_REUSEADDR socket option: ",
errno_to_str(errno));
}
}
#endif
/* Set TCP_NODELAY socket option to optimize communication */
i = 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &i, sizeof(i))
< 0) {
trace(LOG_ALWAYS, "Can't set TCP_NODELAY option on a socket: %s",
errno_to_str(errno));
}
if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *) &i, sizeof(i))
< 0) {
trace(LOG_ALWAYS, "Can't set SO_KEEPALIVE option on a socket: %s",
errno_to_str(errno));
}
if (setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char *) &snd_buf,
sizeof(snd_buf)) < 0) {
trace(LOG_ALWAYS, "setsockopt(SOL_SOCKET,SO_SNDBUF,%d) error: %s",
snd_buf, errno_to_str(errno));
}
if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char *) &rcv_buf,
sizeof(rcv_buf)) < 0) {
trace(LOG_ALWAYS, "setsockopt(SOL_SOCKET,SO_RCVBUF,%d) error: %s",
rcv_buf, errno_to_str(errno));
}
}
static void tcp_connect_done(void * args) {
PortFwConfig * config = (PortFwConfig *)((AsyncReqInfo *)args)->client_data;
PortConnectInfo * info = &config->port_info;
loc_free(info->cnct_req.u.con.addr);
if (info->cnct_req.error) {
info->cnct_callback(config, info->cnct_req.error);
}
else {
config->connected = 1;
set_socket_options(info->sock);
info->cnct_callback(config, 0);
}
}
static int tcp_connect(PortFwConfig * config, ConnectCallBack callback) {
int error = 0;
struct addrinfo hints;
struct addrinfo * reslist = NULL;
char * dev_string = loc_strdup(config->port_config);
struct sockaddr * addr_buf = NULL;
int addr_len;
int sock;
char * host_str;
char * port_str;
host_str = strchr(dev_string, ':');
if (host_str == NULL) {
error = EINVAL;
}
else {
host_str++;
port_str = strchr(host_str, ':');
if (port_str == NULL) {
error = EINVAL;
}
}
if (!error) {
port_str[0] = '\0';
port_str++;
if (strlen(host_str) == 0) host_str = NULL;
}
if (!error) {
memset(&hints, 0, sizeof hints);
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
error = loc_getaddrinfo(host_str, port_str, &hints, &reslist);
if (error) error = set_gai_errno(error);
if (!error) {
struct addrinfo * res;
for (res = reslist; res != NULL; res = res->ai_next) {
addr_len = res->ai_addrlen;
addr_buf = (struct sockaddr *)loc_alloc(res->ai_addrlen);
memcpy(addr_buf, res->ai_addr, res->ai_addrlen);
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock < 0) {
error = errno;
}
else {
error = 0;
break;
}
}
loc_freeaddrinfo(reslist);
}
}
if (!error && addr_buf == NULL) error = ENOENT;
if (error) {
callback(config, error);
}
else {
PortConnectInfo * info = &config->port_info;
info->addr_buf = addr_buf;
info->addr_len = addr_len;
info->sock = sock;
info->cnct_callback = callback;
info->cnct_req.client_data = config;
info->cnct_req.done = tcp_connect_done;
info->cnct_req.type = AsyncReqConnect;
info->cnct_req.u.con.sock = info->sock;
info->cnct_req.u.con.addr = info->addr_buf;
info->cnct_req.u.con.addrlen = info->addr_len;
async_req_post(&info->cnct_req);
}
loc_free(dev_string);
return 0;
}
#if ENABLE_PortForward_UDP
/* UDP PORT SUPPORT */
static void udp_connect_done(void * args) {
PortFwConfig * config = (PortFwConfig *)((AsyncReqInfo *)args)->client_data;
PortConnectInfo * info = &config->port_info;
loc_free(info->cnct_req.u.con.addr);
if (info->cnct_req.error) {
info->cnct_callback(config, info->cnct_req.error);
}
else {
config->connected = 1;
info->cnct_callback(config, 0);
}
}
static int udp_connect(PortFwConfig * config, ConnectCallBack callback) {
int error = 0;
struct addrinfo hints;
struct addrinfo * reslist = NULL;
char * dev_string = loc_strdup(config->port_config);
struct sockaddr * addr_buf = NULL;
int addr_len;
int sock;
char * host_str;
char * port_str;
host_str = strchr(dev_string, ':');
if (host_str == NULL) {
error = EINVAL;
}
else {
host_str++;
port_str = strchr(host_str, ':');
if (port_str == NULL) {
error = EINVAL;
}
}
if (!error) {
port_str[0] = '\0';
port_str++;
if (strlen(host_str) == 0) host_str = NULL;
}
if (!error) {
memset(&hints, 0, sizeof hints);
hints.ai_family = PF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;
error = loc_getaddrinfo(host_str, port_str, &hints, &reslist);
if (error) error = set_gai_errno(error);
if (!error) {
struct addrinfo * res;
for (res = reslist; res != NULL; res = res->ai_next) {
addr_len = res->ai_addrlen;
addr_buf = (struct sockaddr *)loc_alloc(res->ai_addrlen);
memcpy(addr_buf, res->ai_addr, res->ai_addrlen);
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock < 0) {
error = errno;
}
else {
error = 0;
break;
}
}
loc_freeaddrinfo(reslist);
}
}
if (!error && addr_buf == NULL) error = ENOENT;
if (error) {
callback(config, error);
}
else {
PortConnectInfo * info = &config->port_info;
info->addr_buf = addr_buf;
info->addr_len = addr_len;
info->sock = sock;
info->cnct_callback = callback;
info->cnct_req.client_data = config;
info->cnct_req.done = udp_connect_done;
info->cnct_req.type = AsyncReqConnect;
info->cnct_req.u.con.sock = info->sock;
info->cnct_req.u.con.addr = info->addr_buf;
info->cnct_req.u.con.addrlen = info->addr_len;
async_req_post(&info->cnct_req);
}
loc_free(dev_string);
return 0;
}
#endif
static void disconnect_port(PortFwConfig * config) {
if (config->port_info.sock != -1) {
/* It seems we need to use shutdown to unblock threads blocked on recv/send */
if (config->connected) shutdown(config->port_info.sock, SHUT_RDWR);
if (closesocket(config->port_info.sock) == -1) perror ("closesocket");
config->port_info.sock = -1;
if (config->verbose) fprintf(stderr, "Connection with %s closed\n", config->port_config);
}
else if (config->port_info.close_func) {
config->port_info.close_func(config);
}
config->connected = 0;
}
#if ENABLE_PortForward_Serial
/* Serial line support */
static int serial_parse_params(PortFwConfig * config) {
char * ptr;
char * s;
char * param = NULL;
config->port_info.serial.baud_rate = 115200;
config->port_info.serial.stop_bits = 1;
config->port_info.serial.data_bits = 8;
config->port_info.serial.parity = PORTFW_SERIAL_PARITY_NONE;
config->port_info.serial.flow_ctrl = PORTFW_SERIAL_FC_NONE;
if (config->port_params == NULL) return 0;
s = config->port_params;
/* BAUD RATE */
while (*s && *s != '-') s++;
if (s != config->port_params) {
param = loc_strndup(config->port_params, s - config->port_params);
config->port_info.serial.baud_rate = atoi(param);
loc_free(param);
}
/* DATA BITS */
if (*s) s++;
ptr = s;
while (*s && *s != '-') s++;
if (s != ptr) {
param = loc_strndup(ptr, s - ptr);
config->port_info.serial.data_bits = atoi(param);
loc_free(param);
}
/* PARITY */
if (*s) s++;
ptr = s;
while (*s && *s != '-') s++;
if (s != ptr) {
param = loc_strndup(ptr, s - ptr);
if (strlen(param) != 1) {
errno = EINVAL;
loc_free(param);
return -1;
}
if (param[0] == 'O' || param[0] == 'o') config->port_info.serial.parity = PORTFW_SERIAL_PARITY_ODD;
else if (param[0] == 'E' || param[0] == 'e') config->port_info.serial.parity = PORTFW_SERIAL_PARITY_EVEN;
else if (param[0] == 'N' || param[0] == 'n') config->port_info.serial.parity = PORTFW_SERIAL_PARITY_NONE;
else if (param[0] == 'S' || param[0] == 's') config->port_info.serial.parity = PORTFW_SERIAL_PARITY_SPACE;
else if (param[0] == 'M' || param[0] == 'm') config->port_info.serial.parity = PORTFW_SERIAL_PARITY_MARK;
else {
errno = EINVAL;
loc_free(param);
return -1;
}
loc_free(param);
}
/* STOP BITS */
if (*s) s++;
ptr = s;
while (*s && *s != '-') s++;
if (s != ptr) {
param = loc_strndup(ptr, s - ptr);
config->port_info.serial.stop_bits = atoi(param);
loc_free(param);
}
/* FLOW CONTROL */
if (*s) s++;
ptr = s;
while (*s && *s != '-') s++;
if (s != ptr) {
param = loc_strndup(ptr, s - ptr);
if (strlen(param) != 1) {
errno = EINVAL;
loc_free(param);
return -1;
}
if (param[0] == 'N' || param[0] == 'n') config->port_info.serial.flow_ctrl = PORTFW_SERIAL_FC_NONE;
else if (param[0] == 'S' || param[0] == 's') config->port_info.serial.flow_ctrl = PORTFW_SERIAL_FC_XON;
else if (param[0] == 'R' || param[0] == 'r') config->port_info.serial.flow_ctrl = PORTFW_SERIAL_FC_RTS;
else if (param[0] == 'D' || param[0] == 'd') config->port_info.serial.flow_ctrl = PORTFW_SERIAL_FC_DSR;
else {
errno = EINVAL;
loc_free(param);
return -1;
}
loc_free(param);
}
if (config->port_info.serial.data_bits < 5 || config->port_info.serial.data_bits > 8) {
errno = EINVAL;
return -1;
}
if (config->port_info.serial.stop_bits < 1 || config->port_info.serial.stop_bits > 2) {
errno = EINVAL;
return -1;
}
return 0;
}
static void serial_connect_done(void * args) {
PortFwConfig * config = (PortFwConfig *)((AsyncReqInfo *)args)->client_data;
PortConnectInfo * info = &config->port_info;
if (info->cnct_req.error) {
info->cnct_callback(config, info->cnct_req.error);
}
else {
config->connected = 1;
info->cnct_callback(config, 0);
}
}
static int serial_connect_req_func(void * reqdata) {
PortFwConfig * config = (PortFwConfig *)reqdata;
PortConnectInfo * info = &config->port_info;
char * dev_name = config->port_config + strlen("serial:");
int ix;
int baud_const = 0;
#ifdef WIN32
DCB dcb = {0};
COMMTIMEOUTS timeouts;
HANDLE handle;
char com_dev_name[128];
#endif
trace(log_portfw, "Try to open serial device %s", dev_name);
for (ix = 0; baud_table[ix].baud_rate != 0; ix++) {
if (baud_table[ix].baud_rate == info->serial.baud_rate)
baud_const = baud_table[ix].baud_const;
}
#ifdef WIN32
if (baud_const == 0) baud_const = info->serial.baud_rate;
#else
if (baud_const == 0) {
trace(log_portfw, "ERROR: Invalid baud rate");
errno = EINVAL;
return -1;
}
#endif
#ifdef WIN32
snprintf(com_dev_name, sizeof(com_dev_name), "\\\\.\\%s", dev_name);
handle = CreateFile (com_dev_name,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
(void *)NULL);
if (handle == INVALID_HANDLE_VALUE) {
set_win32_errno(GetLastError());
trace(log_portfw, "ERROR: Unable to open open serial device %s: %d", dev_name, GetLastError());
return -1;
}
info->serial.read_overlapped.hEvent = CreateEvent (
NULL, /* security attributes */
1, /* manual reset (required by GetOverlappedResult()) */
0, /* non signalled initial state */
NULL /* event name */);
if (info->serial.read_overlapped.hEvent == NULL) {
set_win32_errno(GetLastError());
CloseHandle (handle);
return -1;
}
info->serial.write_overlapped.hEvent = CreateEvent (
NULL, /* security attributes */
1, /* manual reset (required by GetOverlappedResult()) */
0, /* non signalled initial state */
NULL /* event name */);
if (info->serial.write_overlapped.hEvent == NULL) {
set_win32_errno(GetLastError());
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (handle);
return -1;
}
info->serial.saved_dcb.DCBlength= sizeof(DCB);
if (!GetCommState(handle, &info->serial.saved_dcb))
{
set_win32_errno(GetLastError());
CloseHandle (handle);
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (info->serial.write_overlapped.hEvent);
return -1;
}
dcb = info->serial.saved_dcb;
dcb.BaudRate = baud_const;
dcb.ByteSize = info->serial.data_bits;
if (info->serial.parity == PORTFW_SERIAL_PARITY_ODD) dcb.Parity = ODDPARITY;
else if (info->serial.parity == PORTFW_SERIAL_PARITY_EVEN) dcb.Parity = EVENPARITY;
else if (info->serial.parity == PORTFW_SERIAL_PARITY_NONE) dcb.Parity = NOPARITY;
else if (info->serial.parity == PORTFW_SERIAL_PARITY_SPACE) dcb.Parity = SPACEPARITY;
else if (info->serial.parity == PORTFW_SERIAL_PARITY_MARK) dcb.Parity = MARKPARITY;
else {
CloseHandle (handle);
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (info->serial.write_overlapped.hEvent);
trace(log_portfw, "ERROR: Invalid parity");
errno = EINVAL;
return -1;
}
if (info->serial.stop_bits == 1)
dcb.StopBits = ONESTOPBIT;
else if (info->serial.stop_bits == 2)
dcb.StopBits = TWOSTOPBITS;
else {
CloseHandle (handle);
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (info->serial.write_overlapped.hEvent);
trace(log_portfw, "ERROR: Invalid stop bits number");
errno = EINVAL;
return -1;
}
/* Documented unsupported configurations */
if (info->serial.stop_bits == 2 &&
info->serial.data_bits == 5)
{
CloseHandle (handle);
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (info->serial.write_overlapped.hEvent);
errno = EINVAL;
trace(log_portfw, "ERROR: Invalid stop bits number");
return -1;
}
dcb.fOutX = 0;
dcb.fInX = 0;
if (info->serial.flow_ctrl == PORTFW_SERIAL_FC_NONE) {
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = 0;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
}
#ifdef SUPPORT_FLOW_CTRL
else if (info->serial.flow_ctrl == PORTFW_SERIAL_FC_XON) {
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = 0;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
dcb.fOutX = dcb.fInX = 1;
}
else if (info->serial.flow_ctrl == PORTFW_SERIAL_FC_RTS) {
dcb.fOutxCtsFlow = 1;
dcb.fOutxDsrFlow = 0;
dcb.fDtrControl = DTR_CONTROL_DISABLE;
dcb.fDsrSensitivity = 0;
dcb.fRtsControl = RTS_CONTROL_ENABLE;
}
else if (info->serial.flow_ctrl == PORTFW_SERIAL_FC_DSR) {
dcb.fOutxCtsFlow = 0;
dcb.fOutxDsrFlow = 1;
dcb.fDtrControl = DTR_CONTROL_ENABLE;
dcb.fDsrSensitivity = 1;
dcb.fRtsControl = RTS_CONTROL_DISABLE;
}
#endif /* SUPPORT_FLOW_CTRL */
else {
CloseHandle (handle);
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (info->serial.write_overlapped.hEvent);
trace(log_portfw, "ERROR: Invalid flow control");
errno = EINVAL;
return -1;
}
dcb.fAbortOnError = 0;
if (!SetCommState(handle, &dcb)) {
set_win32_errno(GetLastError());
CloseHandle (handle);
CloseHandle (info->serial.read_overlapped.hEvent);
CloseHandle (info->serial.write_overlapped.hEvent);
trace(log_portfw, "Unable to set comm state: %d", errno);
return -1;
}
GetCommTimeouts (handle, &info->serial.saved_timeout);
timeouts.ReadIntervalTimeout = MAXDWORD;
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD;
timeouts.ReadTotalTimeoutConstant = 2000; /* set a timeout of 2s max */
timeouts.WriteTotalTimeoutMultiplier = 15; /* max = 15ms per character */
timeouts.WriteTotalTimeoutConstant = 200; /* const: 200ms */
SetCommTimeouts (handle, &timeouts);
info->serial.handle = handle;
#else /* WIN32 */
struct termios termIo;
#if defined(__linux__)
struct serial_struct serial;
#endif
int data_bits_const = CS8;
for (ix = 0; data_bit_table[ix].data_bits != 0; ix++) {
if (data_bit_table[ix].data_bits == info->serial.data_bits)
data_bits_const = data_bit_table[ix].data_bits_const;
}
#if defined(__APPLE__)
if ((info->serial.fd = open(dev_name, O_RDWR|O_NONBLOCK, 0)) < 0) {
#else
if ((info->serial.fd = open(dev_name, O_RDWR, 0)) < 0) {
#endif
trace(log_portfw, "Unable to open serial port: %d", errno);
return -1;
}
if (tcgetattr(info->serial.fd, &termIo) == -1) {
trace(log_portfw, "Unable to get serial port attributes: %d", errno);
close(info->serial.fd);
info->serial.fd = -1;
return -1;
}
termIo.c_iflag = 0; /* no input procesing */
termIo.c_oflag = 0; /* no output procesing */
termIo.c_cflag = CREAD | data_bits_const | CLOCAL;
if (info->serial.stop_bits == 2) termIo.c_cflag |= CSTOPB;
if (info->serial.parity == PORTFW_SERIAL_PARITY_ODD) termIo.c_cflag |= PARODD;
else if (info->serial.parity == PORTFW_SERIAL_PARITY_EVEN) termIo.c_cflag |= PARENB;
else if (info->serial.parity != PORTFW_SERIAL_PARITY_NONE) {
errno = EINVAL;
close(info->serial.fd);
info->serial.fd = -1;
trace(log_portfw, "ERROR: Invalid parity");
return -1;
}
termIo.c_lflag = 0; /* no local processing */
/* time out value (<val> * 0.1s) */
termIo.c_cc[VMIN] = 0;
termIo.c_cc[VTIME] = 3 * 10;
#ifdef ASYNC_LOW_LATENCY
if (ioctl(info->serial.fd, TIOCGSERIAL, &serial) == 0)
{
serial.flags |= ASYNC_LOW_LATENCY;
ioctl(info->serial.fd, TIOCSSERIAL, &serial);
}
#endif /* ASYNC_LOW_LATENCY */
/* configure baud rate for input and output */
cfsetispeed(&termIo, baud_const);
cfsetospeed(&termIo, baud_const);
/* set the attributes */
tcsetattr(info->serial.fd, TCSAFLUSH, &termIo);
#if defined(__APPLE__)
fcntl(info->serial.fd, F_SETFL, fcntl(info->serial.fd, F_GETFL) & ~O_NONBLOCK);
#endif /* __APPLE__ */
#endif
trace(log_portfw, "SUCCESS: serial port opened");
return 0;
}
static int serial_send_req_func(PortFwConfig * config, void * buffer, size_t length) {
int ret;
#ifdef WIN32
BOOL status;
DWORD write_count;
DWORD total_write_count = 0;
HANDLE WriteEvent;
WriteEvent = config->port_info.serial.write_overlapped.hEvent;
do
{
memset (&config->port_info.serial.write_overlapped, 0, sizeof(config->port_info.serial.write_overlapped));
config->port_info.serial.write_overlapped.hEvent = WriteEvent;
ResetEvent (WriteEvent);
status = WriteFile (config->port_info.serial.handle,
(char *)buffer + total_write_count,
length - total_write_count, &write_count,
&config->port_info.serial.write_overlapped);
/* ...and wait till last char is sent. */
if (status == FALSE) {
DWORD error = GetLastError ();
if (error == ERROR_IO_PENDING) {
status = GetOverlappedResult (config->port_info.serial.handle,
&config->port_info.serial.write_overlapped,
&write_count, TRUE);
if (!status) {
set_win32_errno(GetLastError());
ResetEvent (WriteEvent);
return -1;
}
}
else {
ResetEvent (WriteEvent);
set_win32_errno(error);
return -1;
}
}
total_write_count += write_count;
} while ((int) total_write_count < length);
ResetEvent (WriteEvent);
ret = length;
#else
do {
ret = write(config->port_info.serial.fd, buffer, length);
}
while (ret < 0 && (errno == EINTR));
#endif
return ret;
}
static int serial_recv_req_func(PortFwConfig * config, void * buffer, size_t length) {
#ifdef WIN32
BOOL fWaitingOnRead = FALSE;
HANDLE handle = config->port_info.serial.handle; /* serial device handle */
OVERLAPPED * read_overlapped = &config->port_info.serial.read_overlapped;
int total_read_count = 0;
ResetEvent(read_overlapped->hEvent);
while (1) {
if (!fWaitingOnRead) {
DWORD read_count;
DWORD max_bytes = 0;
DWORD com_errors;
COMSTAT com_status; /* To get the pending input bytes */
/* Get the number of pending chars. If there are more than 1 read all of them; otherwise wait
* only for the first character. Waiting for more than 1 character if they are not available
* would lead to block until the specified number of bytes has been received.
*/
if (ClearCommError (handle, &com_errors, &com_status)) {
max_bytes = com_status.cbInQue;
}
/* If there are enough received characters to transfer immediately to the client or if there are no
* more pending read characters and we have some characters to transfer; do the transfer right now.
* Note that READ_THRESHOLD is also used to avoid buffer overflow.
*/
if (max_bytes == 0 && total_read_count != 0) {
return total_read_count;
}
/* If there are no pending characters; try to read only one. Otherwise, we would block until
* we have received the number of characters specified.
*/
if (max_bytes == 0) max_bytes = 1;
else {
if ((total_read_count + max_bytes) > length) max_bytes = (length - total_read_count);
}
if (!ReadFile(handle, (char *)buffer + total_read_count, max_bytes, &read_count, read_overlapped)) {
if (GetLastError() != ERROR_IO_PENDING) {
set_win32_errno(GetLastError());
return -1;
}
else {
fWaitingOnRead = TRUE;
}
}
else {
/* read completed immediately
* Read the character but do not make it available to the client right now; check if
* we can read more data first.
*/
total_read_count += read_count;
fWaitingOnRead = FALSE;
}
}
else {
/* We are waiting for the result of an asynchronous read */
DWORD dwRes = WaitForSingleObject(read_overlapped->hEvent, INFINITE);
DWORD read_count;
switch(dwRes) {
case WAIT_OBJECT_0:
if (!GetOverlappedResult(handle, read_overlapped, &read_count, TRUE)) {
set_win32_errno(GetLastError());
return -1;
}
else {
/* Read completed successfully. */
total_read_count += read_count;
/* Reset flag so that another operation can be issued. */
fWaitingOnRead = FALSE;
}
break;
case WAIT_TIMEOUT:
/* Operation isn't complete yet. fWaitingOnRead flag isn't
* changed since I'll loop back around, and I don't want
* to issue another read until the first one finishes.
*
* This is a good time to do some background work.
*/
break;
default:
set_win32_errno(GetLastError());
return -1;
}
}
}
return total_read_count;
#else
int ret;
do
{
ret = read(config->port_info.serial.fd, buffer, length);
}
while (ret == 0 || (ret < 0 && (errno == EINTR)));
return ret;
#endif
}
static void serial_close(PortFwConfig * config) {
#ifdef WIN32
if (config->port_info.serial.handle == NULL) return;
SetCommTimeouts (config->port_info.serial.handle, &config->port_info.serial.saved_timeout);
SetCommState(config->port_info.serial.handle, &config->port_info.serial.saved_dcb);
CloseHandle (config->port_info.serial.handle);
config->port_info.serial.handle = NULL;
if (config->port_info.serial.read_overlapped.hEvent) CloseHandle(config->port_info.serial.read_overlapped.hEvent);
if (config->port_info.serial.write_overlapped.hEvent) CloseHandle(config->port_info.serial.write_overlapped.hEvent);
config->port_info.serial.read_overlapped.hEvent = NULL;
config->port_info.serial.write_overlapped.hEvent = NULL;
#else
if (config->port_info.serial.fd == -1) return;
close(config->port_info.serial.fd);
#endif
if (config->verbose) fprintf(stderr, "Connection with %s closed\n", config->port_config);
}
static void serial_connect(PortFwConfig * config, ConnectCallBack callback) {
int error = 0;
if (serial_parse_params(config) == -1) {
error = errno;
callback(config, error);
return;
}
else {
PortConnectInfo * info = &config->port_info;
info->send_req_func = serial_send_req_func;
info->recv_req_func = serial_recv_req_func;
info->close_func = serial_close;
info->cnct_callback = callback;
info->cnct_req.client_data = config;
info->cnct_req.done = serial_connect_done;
info->cnct_req.type = AsyncReqUser;
info->cnct_req.u.user.func = serial_connect_req_func;
info->cnct_req.u.user.data = config;
async_req_post(&info->cnct_req);
}
}
/* END SERIAL LINE SUPPORT */
#endif /* ENABLE_PortForward_Serial */
static void display_buffer(unsigned char * buffer, size_t size) {
if ((log_mode & log_portfw) && log_file) {
size_t i;
char tmp_buffer[256];
if (size > sizeof(tmp_buffer)) size = sizeof(tmp_buffer) - 1;
for (i = 0; i < size; i++) {
if (isprint(buffer[i])) tmp_buffer[i] = buffer[i];
else tmp_buffer[i] = '.';
}
tmp_buffer[i] = 0;
trace(log_portfw, tmp_buffer);
}
}
static void send_event_portfw_config_added(PortFwConfig * config) {
if (broadcast_group == NULL) return;
else {
OutputStream * out = &broadcast_group->out;
write_stringz(out, "E");
write_stringz(out, PortForward);
write_stringz(out, "configAdded");
write_portfw_config (out, config);
write_stream(out, 0);
write_stream(out, MARKER_EOM);
}
}
static void send_event_portfw_config_removed(PortFwConfig * config) {
if (broadcast_group == NULL) return;
else {
OutputStream * out = &broadcast_group->out;
write_stringz(out, "E");
write_stringz(out, PortForward);
write_stringz(out, "configRemoved");
write_stream(out, '[');
json_write_string(out, config->id);
write_stream(out, ']');
write_stream(out, 0);
write_stream(out, MARKER_EOM);
}
}
static void write_portfw_config(OutputStream * out, PortFwConfig * config) {
write_stream(out, '{');
json_write_string(out, "ID");
write_stream(out, ':');
json_write_string(out, config->id);
write_stream(out, ',');
json_write_string(out, "InputStream");
write_stream(out, ':');
json_write_string(out, config->in_stream_id);
write_stream(out, ',');
json_write_string(out, "OutputStream");
write_stream(out, ':');
json_write_string(out, config->out_stream_id);
if (config->client_data) {
write_stream(out, ',');
json_write_string(out, "ClientData");
write_stream(out, ':');
json_write_string(out, config->client_data);
}
write_stream(out, ',');
json_write_string(out, "Port");
write_stream(out, ':');
json_write_string(out, config->port_config);
if (config->port_params) {
write_stream(out, ',');
json_write_string(out, "Params");
write_stream(out, ':');
json_write_string(out, config->port_params);
}
write_stream(out, ',');
json_write_string(out, "Verbose");
write_stream(out, ':');
json_write_boolean(out, config->verbose);
write_stream(out, '}');
}
static void portfw_cmd_get_config(char * token, Channel * c) {
char id[256];
PortFwConfig * config;
int err = 0;
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
if ((config = find_portfw_config(id)) == NULL) err = set_errno(ERR_OTHER,
portfw_inv_config_err);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
if (err == 0) {
write_portfw_config(&c->out, config);
write_stream(&c->out, 0);
}
else {
write_stringz(&c->out, "null");
}
write_stream(&c->out, MARKER_EOM);
}
static void portfw_cmd_list(char * token, Channel * c) {
LINK * l = portfw_config_list.next;
int cnt = 0;
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, 0);
write_stream(&c->out, '[');
while (l != &portfw_config_list) {
PortFwConfig * config = link2pfwp(l);
if (cnt > 0) write_stream(&c->out, ',');
json_write_string(&c->out, config->id);
l = l->next;
cnt++;
}
write_stream(&c->out, ']');
write_stream(&c->out, 0);
write_stream(&c->out, MARKER_EOM);
}
static PortFwConfig * find_portfw_config(const char * id) {
LINK * l = portfw_config_list.next;
while (l != &portfw_config_list) {
PortFwConfig * config = link2pfwp(l);
if (strcmp(id, config->id) == 0 && !config->shutdown_in_progress) {
return config;
}
l = l->next;
}
return NULL ;
}
static int user_send_func(void * data) {
PortFwConfig * config = (PortFwConfig *) data;
return config->port_info.send_req_func(config, config->outbuf, config->outbuf_len);
}
static int user_recv_func(void * data) {
PortFwConfig * config = (PortFwConfig *) data;
return config->port_info.recv_req_func(config, config->inbuf, IN_BUF_SIZE);
}
static void write_stream_to_port(PortFwConfig * config) {
int eos = 0;
size_t read_size = 0;
assert (config->send_in_progress == 0);
if (virtual_stream_get_data(config->out_vstream,
config->outbuf + config->outbuf_len,
OUT_BUF_SIZE - config->outbuf_len,
&read_size, &eos) < 0) return;
config->outbuf_len += read_size;
if (read_size != 0) {
size_t write_size = 0;
write_size = config->outbuf_len;
if (write_size != 0) {
config->send_in_progress = 1;
if (config->send_req.type != AsyncReqUser) {
config->send_req.u.sio.bufp = config->outbuf;
config->send_req.u.sio.bufsz = write_size;
}
async_req_post(&config->send_req);
}
}
else
config->send_in_progress = 0;
}
static void release_config(PortFwConfig * config) {
loc_free(config->port_config);
loc_free(config);
}
static void delete_config(PortFwConfig * config) {
size_t wrote = 0;
config->shutdown_in_progress = 1;
if (config->connected) {
disconnect_port(config);
}
if (config->send_in_progress || config->recv_in_progress) {
return;
}
virtual_stream_add_data(config->in_vstream, NULL, 0, &wrote, 1);
virtual_stream_disconnect(config->channel, NULL, config->out_stream_id);
virtual_stream_disconnect(config->channel, NULL, config->in_stream_id);
virtual_stream_delete(config->in_vstream);
virtual_stream_delete(config->out_vstream);
list_remove (&config->link);
send_event_portfw_config_removed(config);
release_config(config);
}
static void done_recv_request(void * args) {
AsyncReqInfo * req = (AsyncReqInfo *)args;
PortFwConfig * config = (PortFwConfig *)(req)->client_data;
int rval;
if (req->type == AsyncReqUser) rval = req->u.user.rval;
else rval = req->u.sio.rval;
config->recv_in_progress = 0;
if (rval == 0
|| ((rval == -1 && req->error != EINTR)
&& (rval == -1 && (req->error != ECONNREFUSED || config->port_type != PORTFW_UDP_PORT)))
|| config->shutdown_in_progress) {
delete_config(config);
return;
}
if (rval == -1) {
/* Interrupted system call */
async_req_post(&config->recv_req);
return;
}
config->inbuf_len = rval;
if (rval > 0) display_buffer((unsigned char *)config->inbuf, rval);
write_port_to_stream(config);
}
static void done_send_request(void * args) {
AsyncReqInfo * req = (AsyncReqInfo *)args;
PortFwConfig * config = (PortFwConfig *)(req)->client_data;
int rval;
config->send_in_progress = 0;
if (req->type == AsyncReqUser) rval = req->u.user.rval;
else rval = req->u.sio.rval;
if (rval == 0
|| ((rval == -1 && req->error != EINTR)
&& (rval == -1 && (req->error != ECONNREFUSED || config->port_type != PORTFW_UDP_PORT)))
|| config->shutdown_in_progress) {
delete_config(config);
return;
}
if (rval == -1) {
/* Interrupted system call */
async_req_post(&config->send_req);
return;
}
if (rval > 0) display_buffer((unsigned char *)config->outbuf, rval);
if (config->outbuf_len != (size_t)rval) {
memmove(config->outbuf, config->outbuf + rval, config->outbuf_len - rval);
config->outbuf_len -= (size_t)rval;
async_req_post(&config->send_req);
return;
}
config->outbuf_len -= (size_t)rval;
write_stream_to_port(config);
}
static void write_stream_to_port_callback(VirtualStream *stream, int event,
void * arg) {
PortFwConfig * config = (PortFwConfig *) arg;
if (event == VS_EVENT_DATA_AVAILABLE) {
if (config->connected == 0 || config->shutdown_in_progress) return;
if (config->send_in_progress)
return;
write_stream_to_port(config);
}
}
static void write_port_to_stream(void * arg) {
PortFwConfig * config = (PortFwConfig *) arg;
assert (config->recv_in_progress == 0);
if (config->inbuf_len != 0) {
size_t wrote = 0;
virtual_stream_add_data(config->in_vstream,
config->inbuf + config->inbuf_pos, config->inbuf_len, &wrote,
0);
config->inbuf_pos += wrote;
config->inbuf_len -= wrote;
}
/* If all data has been consumed, start a new read request. Otherwise wait
* for the full buffer to be consumed.
*/
if (config->inbuf_len == 0) {
config->inbuf_pos = 0;
if (config->recv_req.type != AsyncReqUser) {
config->recv_req.u.sio.bufp = config->inbuf;
config->recv_req.u.sio.bufsz = IN_BUF_SIZE;
}
config->recv_in_progress = 1;
async_req_post(&config->recv_req);
}
}
static void write_port_to_stream_callback(VirtualStream *stream,
int event, void * arg) {
PortFwConfig * config = (PortFwConfig *) arg;
if (config->connected == 0 || config->shutdown_in_progress) return;
switch (event) {
case VS_EVENT_SPACE_AVAILABLE:
{
if (config->recv_in_progress) return;
write_port_to_stream(config);
}
break;
case VS_EVENT_DATA_AVAILABLE:
default:
/*
portfw_client_read(config);
*/
str_exception(ERR_OTHER, "Unhandled stream callback\n");
}
}
static void connect_callback(PortFwConfig * config, int error) {
if (error) {
if (config->verbose) fprintf(stderr, "Error opening %s: %d (%s)\n", config->port_config, error, errno_to_str(error));
}
else {
if (config->verbose) fprintf(stdout, "Successfully opened %s\n", config->port_config);
}
channel_unlock_with_msg(config->channel, PortForward);
write_stringz(&config->channel->out, "R");
write_stringz(&config->channel->out, config->token);
write_errno(&config->channel->out, error);
write_stream(&config->channel->out, MARKER_EOM);
if (error == 0) {
virtual_stream_create(PortForward, config->id, IN_BUF_SIZE,
VS_ENABLE_REMOTE_READ, write_port_to_stream_callback, config,
&config->in_vstream);
virtual_stream_create(PortForward, config->id, OUT_BUF_SIZE,
VS_ENABLE_REMOTE_WRITE, write_stream_to_port_callback, config,
&config->out_vstream);
virtual_stream_get_id(config->in_vstream, config->in_stream_id,
sizeof(config->in_stream_id));
virtual_stream_get_id(config->out_vstream, config->out_stream_id,
sizeof(config->out_stream_id));
virtual_stream_connect(config->channel, NULL, config->out_stream_id);
virtual_stream_connect(config->channel, NULL, config->in_stream_id);
list_add_first(&config->link, &portfw_config_list);
send_event_portfw_config_added(config);
config->send_req.client_data = config;
config->send_req.done = done_send_request;
config->recv_req.client_data = config;
config->recv_req.done = done_recv_request;
if (config->port_info.sock != -1) {
config->send_req.type = AsyncReqSend;
config->send_req.u.sio.sock = config->port_info.sock;
config->send_req.u.sio.flags = 0;
config->recv_req.type = AsyncReqRecv;
config->recv_req.u.sio.sock = config->port_info.sock;
config->recv_req.u.sio.flags = 0;
config->recv_req.u.sio.bufp = config->inbuf;
config->recv_req.u.sio.bufsz = IN_BUF_SIZE;
}
else {
config->send_req.type = AsyncReqUser;
config->send_req.u.user.func = user_send_func;
config->send_req.u.user.data = config;
config->recv_req.type = AsyncReqUser;
config->recv_req.u.user.func = user_recv_func;
config->recv_req.u.user.data = config;
}
config->recv_in_progress = 1;
async_req_post(&config->recv_req);
}
else {
disconnect_port(config);
release_config(config);
}
}
static void read_portfw_property(InputStream * inp, const char * name,
void * args) {
PortFwConfig * config = (PortFwConfig *) args;
if (strcmp(name, "ID") == 0) json_read_string(inp, config->id,
sizeof(config->id));
else if (strcmp(name, "Port") == 0) config->port_config =
json_read_alloc_string(inp);
else if (strcmp(name, "Params") == 0) config->port_params = json_read_alloc_string(inp);
else if (strcmp(name, "Verbose") == 0) config->verbose = json_read_boolean(inp);
else if (strcmp(name, "ClientData") == 0) config->client_data = json_read_alloc_string(inp);
else exception(ERR_JSON_SYNTAX);
}
static void portfw_cmd_create(char * token, Channel * c) {
int err = 0;
PortFwConfig * config = (PortFwConfig *) loc_alloc_zero(sizeof(PortFwConfig));
config->port_info.sock = -1;
json_read_struct(&c->inp, read_portfw_property, config);
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
if (config->id[0] == 0) str_exception(ERR_OTHER, "PortForward configuration must have an ID");
if (find_portfw_config(config->id) != NULL) err = set_errno(ERR_OTHER,
"PortForward Remote configuration already exists");
if (config->port_config == NULL) {
err = set_errno (ERR_OTHER, "Need to specify a port configuration");
}
if (err == 0) {
config->channel = c;
strlcpy(config->token, token, sizeof(config->token));
trace(log_portfw, "Received connection request for port %s", config->port_config);
if (strncasecmp(config->port_config, "tcp:", strlen("tcp:")) == 0) {
channel_lock_with_msg(c, PortForward);
config->port_type = PORTFW_TCP_PORT;
if (config->verbose) fprintf(stdout, "Received connection request for %s\n", config->port_config);
tcp_connect(config, connect_callback);
}
else if (strncasecmp(config->port_config, "udp:", strlen("udp:")) == 0) {
config->port_type = PORTFW_UDP_PORT;
#if ENABLE_PortForward_UDP
channel_lock_with_msg(c, PortForward);
if (config->verbose) fprintf(stdout, "Received connection request for %s\n", config->port_config);
udp_connect(config, connect_callback);
#else
if (config->verbose) fprintf(stderr, "Unsupported protocol \"udp\"\n");
err = EINVAL;
#endif
}
#if ENABLE_PortForward_Serial
else if (strncasecmp(config->port_config, "serial:", strlen("serial:")) == 0) {
config->port_type = PORTFW_SER_PORT;
if (config->verbose) fprintf(stdout, "Received connection request for %s\n", config->port_config);
channel_lock_with_msg(c, PortForward);
serial_connect(config, connect_callback);
}
#endif
else {
if (config->verbose) fprintf(stderr, "Unsupported connection request for %s\n", config->port_config);
err = EINVAL;
}
}
if (err == 0) {
return;
}
else {
release_config(config);
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void portfw_cmd_delete(char * token, Channel * c) {
char id[256];
int err = 0;
PortFwConfig * config;
json_read_string(&c->inp, id, sizeof(id));
if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX);
if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX);
if ((config = find_portfw_config(id)) == NULL) err = set_errno(ERR_OTHER,
portfw_inv_config_err);
else {
delete_config(config);
}
write_stringz(&c->out, "R");
write_stringz(&c->out, token);
write_errno(&c->out, err);
write_stream(&c->out, MARKER_EOM);
}
static void portfw_cmd_get_capabilities(char * token, Channel * c) {
char id[256];
PortFwConfig * config = NULL;
OutputStream * out = &c->out;
int err = 0;
json_read_string(&c->inp, id, sizeof(id));
json_test_char(&c->inp, MARKER_EOA);
json_test_char(&c->inp, MARKER_EOM);
if (strlen(id) > 0) {
if ((config = find_portfw_config(id)) == NULL) err = set_errno(ERR_OTHER,
portfw_inv_config_err);
}
write_stringz(out, "R");
write_stringz(out, token);
write_errno(out, err);
if (err) {
write_stringz(&c->out, "null");
}
else {
write_stream(out, '{');
json_write_string(out, "TCP");
write_stream(out, ':');
json_write_boolean(out, 1);
#if ENABLE_PortForward_Serial
write_stream(out,',');
json_write_string(out, "Serial");
write_stream(out, ':');
json_write_boolean(out, 1);
#endif /* ENABLE_PortForward_Serial */
#if ENABLE_PortForward_UDP
write_stream(out,',');
json_write_string(out, "UDP");
write_stream(out, ':');
json_write_boolean(out, 1);
#endif /* ENABLE_PortForward_UDP */
write_stream(out, '}');
write_stream(out, 0);
}
write_stream(out, MARKER_EOM);
}
static void channel_close_listener(Channel * c) {
LINK * l = portfw_config_list.next;
for (l = portfw_config_list.next; l != &portfw_config_list;) {
PortFwConfig * config = link2pfwp(l);
l = l->next;
if (config->channel == c) delete_config(config);
}
}
void ini_port_forward_service(Protocol *proto, TCFBroadcastGroup * bcg) {
static int ini_portfw = 0;
if (ini_portfw == 0) {
add_channel_close_listener(channel_close_listener);
broadcast_group = bcg;
ini_portfw = 1;
}
log_portfw = add_trace_mode(0, "portfw", "Port Forwarding");
add_command_handler(proto, PortForward, "getConfig", portfw_cmd_get_config);
add_command_handler(proto, PortForward, "list", portfw_cmd_list);
add_command_handler(proto, PortForward, "create", portfw_cmd_create);
add_command_handler(proto, PortForward, "delete", portfw_cmd_delete);
add_command_handler(proto, PortForward, "getCapabilities", portfw_cmd_get_capabilities);
}
#endif