blob: d0a43bd9dc63802dc1509d43c1fa3e42723e8597 [file] [log] [blame]
/*
* agent-proxy.c udp/tcp proxy for the gdbserver, wdbagent and kgdb
*
* Copyright (c) 2005-2010 Wind River Systems, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#ifdef _WIN32
#include <winsock.h>
typedef unsigned int socklen_t;
#define CLOSESOCKET(fd) closesocket(fd)
#else /* ! _WIN32 */
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <netdb.h>
#define CLOSESOCKET(fd) close(fd)
#include <errno.h>
#include <sys/stat.h>
#endif /* ! _WIN32 */
#include "agent-proxy.h"
#ifdef DEBUGLOG
int debug = 1;
#else
int debug = 0;
#endif
int logchar = 0;
/* Class of port */
#define CLS_LOCAL_PORT 0x1
#define CLS_REMOTE_PORT 0x2
#define CLS_SCRIPT_PORT 0x4
#define CLS_SCRIPT_CLIENT 0x8
/* Operation mode of script port */
#define SCRIPT_READ 0x4
#define SCRIPT_WRITE 0x2
#define NO_TELNET_OPTION_NEGOTIATION 0x10
static struct port_st *rports = NULL;
static int nsockhandle;
static int listen_fd = -1;
static int fifo_con_fd = -1;
static struct port_st *l_ports;
static struct port_st *r_ports;
fd_set master_rds;
static fd_set master_wds;
static char *progname;
static int localPortReadMessage(struct port_st *l_port);
static int scriptPortReadMessage(struct port_st *l_port);
static int scriptClientPortReadMessage(struct port_st *l_port);
static int remotePortReadMessage(struct port_st *l_port);
static int remotePortAccept(struct port_st *l_port);
static int remotePortFifoConRead(struct port_st *l_port);
static int breakOnConnect = 1;
static int gdbSplit = 1;
#define MAX_GDB_BUF 1024 * 8
static char gdbArr[MAX_GDB_BUF];
static int gdbPtr = 0;
static int gdbGotDollar = 0;
static int telnetNegotiation = 0;
static char *fifo_con_file;
/*
* Program Usage.
*/
void usage()
{
printf("agent-proxy version %1.2f\n", AGENT_VER);
printf("Usage:\n");
printf
(" agent-proxy <[udp:][virt IP:]local port | stdin> <remote host> <[udp:]remote port>\n");
printf
(" When using a debug splitter: -B to turn off break on connect\n");
printf
(" When using a debug splitter: -G to turn off gdb protocol split\n");
printf
(" When using a debug splitter: -s ### to set alternate break char\n");
#ifdef USE_LATENCY
printf(" Optional Delay Args: [-l <latency ms>] [-b <baudrate>]\n");
#endif /* USE_LATENCY */
printf(" For udp wdb agent proxy example for default ports\n");
printf(" agent-proxy udp:0x1001 10.0.0.2 udp:0x4321\n");
printf(" agent-proxy udp:0x1002 remotehost2 udp:0x4321\n");
printf(" For KGDB udp target to udp host with default g\n");
printf(" agent-proxy udp:3331 10.0.0.2 udp:6443\n");
printf(" For KGDB udp target to tcp host with default g\n");
printf(" agent-proxy 3332 10.0.0.3 udp:6443\n");
printf(" Also you can bind to a local vitual interface IE:\n");
printf(" agent-proxy udp:47.1.1.2:0x4321 10.0.0.2 udp:0x4321\n");
printf(" agent-proxy udp:47.1.1.3:0x4321 10.0.0.3 udp:0x4321\n");
printf(" agent-proxy udp:47.1.1.3:6443 10.0.0.3 udp:6443\n");
printf(" agent-proxy 47.1.1.3:44444 10.0.0.3 44444\n");
printf
(" Use stdin instead of a local port for kgdb, perhaps for inetd\n");
printf(" agent-proxy stdin 10.0.0.3 udp:6443\n");
printf("\n");
printf(" Script proxy services to a terminal server on port 2011\n");
printf(" agent-proxy 4440+4441 10.0.0.10 2011\n");
printf(" Debug spliter to terminal server\n");
printf(" agent-proxy 4440^4441 10.0.0.10 2011\n");
printf(" Debug spliter to serial port at 115200 baud\n");
printf(" agent-proxy 4440^4441 0 /dev/ttyS0,115200\n");
printf("\n");
exit(1);
}
#ifdef USE_LATENCY
/*
* Add microsecond value to timeval.
*/
static void timevaladd_usec(struct timeval *tv, unsigned long usec)
{
tv->tv_sec += usec / 1000000;
tv->tv_usec += usec % 1000000;
if (tv->tv_usec >= 1000000) {
tv->tv_sec++;
tv->tv_usec -= 1000000;
}
}
/*
* Subtract two timeval values
*/
static void timevalsub(struct timeval *tv1, struct timeval *tv2)
{
tv1->tv_sec -= tv2->tv_sec;
if (tv1->tv_usec < tv2->tv_usec) {
tv1->tv_sec--;
tv1->tv_usec += 1000000;
}
tv1->tv_usec -= tv2->tv_usec;
}
/*
* Compare two timeval values and return in the typical fasion as strcmp().
*/
static int timevalcmp(struct timeval *tv1, struct timeval *tv2)
{
if (tv1->tv_sec < tv2->tv_sec)
return -1;
if (tv1->tv_sec > tv2->tv_sec)
return 1;
if (tv1->tv_usec < tv2->tv_usec)
return -1;
if (tv1->tv_usec > tv2->tv_usec)
return 1;
return 0;
}
#ifdef WIN32
/*
* Windows implementation of POSIX function gettimeofday().
*/
static int gettimeofday(struct timeval *tv, struct timezone *tz)
{
DWORD mstime = GetTickCount();
tv->tv_sec = mstime / 1000;
tv->tv_usec = (mstime % 1000) * 1000;
return 0;
}
#endif /* WIN32 */
#endif /* USE_LATEENCY */
static void setRemoteSockOpts(int nsock)
{
int on;
/*
* set SO_LINGER socket option on socket so that it
* closes the connections gracefully, when required to
* close.
*/
on = 0;
setsockopt(nsock, SOL_SOCKET, SO_LINGER, (void *)&on, sizeof on);
on = 1;
/* Set TCP_NODELAY socket option to optimize communication */
setsockopt(nsock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof on);
}
/*
* Compute the value that should be used for the first parameter to
* select from the global set of file handles that are in use by the
* proxy.
*/
static void refresh_nsockhandle()
{
int temp = -1;
struct port_st *iport = rports;
while (iport != NULL) {
if (temp < iport->sock) {
temp = iport->sock;
}
iport = iport->next;
}
nsockhandle = temp + 1;
}
/*
* TCP specific routine for shutting down comunications
*/
static void tcp_portclose(struct port_st *port)
{
shutdown(port->sock, 2);
CLOSESOCKET(port->sock);
FD_CLR(port->sock, &master_rds);
port->sock = -1;
}
/*
* TCP specific routine for reading
*/
static int tcp_portread(struct port_st *port, char *buf, int size, int opts)
{
int ret = recv(port->sock, buf, size, opts);
return ret;
}
/*
* TCP specific routine for reading
*/
static int tcp_portwrite(struct port_st *port, char *buf, int size, int opts)
{
int ret = send(port->sock, buf, size, opts);
#if 0
{
int i;
printf("WRITE%i: ", port->sock);
for (i = 0; i < size; i++) {
printf("%0x ", (unsigned char)buf[i]);
}
printf("\n");
}
#endif
return ret;
}
/*
* UDP specific routine for reading
*/
static int udp_portread(struct port_st *port, char *buf, int size, int opts)
{
unsigned int len = sizeof(port->serv_addr);
return recvfrom(port->sock, buf, size, opts,
(struct sockaddr *)&port->serv_addr, &len);
}
/*
* UDP specific routine for writing
*/
static int udp_portwrite(struct port_st *port, char *buf, int size, int opts)
{
return sendto(port->sock, buf, size, opts,
(struct sockaddr *)&port->serv_addr,
sizeof(port->serv_addr));
}
/*
* STDIN specific routine for reading
*/
static int stdin_portread(struct port_st *port, char *buf, int size, int opts)
{
return read(port->sock, buf, size);
}
/*
* STDOUT specific routine for writing
*/
static int stdin_portwrite(struct port_st *port, char *buf, int size, int opts)
{
return write(fileno(stdout), buf, size);
}
/*
* Remove a communications port from the managed list.
*/
static void killport(struct port_st *port)
{
struct port_st *iport = rports;
struct port_st *prev_iport = rports;
if (debug)
printf("Killing cls: %i port: %i peer %i\n", port->cls,
port->sock, (port->peer ? port->peer->sock : -1));
if (port->cls == CLS_LOCAL_PORT ||
port->cls == CLS_REMOTE_PORT || port->cls == CLS_SCRIPT_PORT) {
/* Don't close the listener or the master receiver! */
if (port->cls == CLS_REMOTE_PORT &&
(port->type == PORT_UDP || port->type == PORT_LISTEN ||
port->type == STDINOUT || port->type == PORT_FIFO_CON)) {
port->peer = NULL;
}
if (port->type == PORT_LISTEN && listen_fd >= 0) {
/* Kill off the existing port and swap back to the
* original listen handle
*/
FD_CLR(port->sock, &master_rds);
CLOSESOCKET(port->sock);
FD_SET(listen_fd, &master_rds);
refresh_nsockhandle();
port->readMessage = remotePortAccept;
port->sock = listen_fd;
listen_fd = -1;
}
if (port->type == PORT_FIFO_CON && fifo_con_fd >= 0) {
/* Kill off the existing port and swap back to the
* original handle
*/
FD_CLR(port->sock, &master_rds);
CLOSESOCKET(port->sock);
FD_SET(fifo_con_fd, &master_rds);
refresh_nsockhandle();
port->readMessage = remotePortFifoConRead;
port->sock = fifo_con_fd;
fifo_con_fd = -1;
}
return;
}
if (port->scriptRef && port == port->scriptRef->lscript) {
if (!(port->scriptRef->rscript->type == PORT_UDP ||
port->scriptRef->rscript->type == PORT_LISTEN ||
port->scriptRef->rscript->type == STDINOUT ||
port->scriptRef->rscript->type == PORT_FIFO_CON))
port->scriptRef->scriptInUse = 0;
}
if (rports == port) {
rports = rports->next;
if (port->portclose)
port->portclose(port);
if (port->peer && iport->peer->sock != -1) {
killport(port->peer);
}
free(port);
port = NULL;
return;
}
while (iport != NULL) {
if (iport == port) {
prev_iport->next = iport->next;
if (port->portclose)
port->portclose(port);
if (port->peer && iport->peer->sock != -1) {
killport(port->peer);
}
free(port);
port = NULL;
refresh_nsockhandle();
break;
}
prev_iport = iport;
iport = iport->next;
}
}
/*
* Setup a local communication channel which will what an external
* client will connect to.
*
*/
static int setup_local_port(struct port_st *lport, char *port,
struct port_st *attachPort)
{
char *endstr;
char *local_bind_addr = 0;
strcpy(lport->name, "localhost");
lport->portclose = NULL;
lport->isLocal = 1;
if (attachPort == NULL) {
lport->cls = CLS_LOCAL_PORT;
lport->readMessage = localPortReadMessage;
} else {
lport->cls = CLS_SCRIPT_PORT;
lport->readMessage = scriptPortReadMessage;
}
/* Determine port type */
#ifndef _WIN32
if (strncmp("stdin", port, 5) == 0) {
lport->portread = stdin_portread;
lport->portwrite = stdin_portwrite;
lport->sock = fileno(stdin);
lport->type = STDINOUT;
/* Switch to non-blocking mode */
fcntl(lport->sock, F_SETFL,
fcntl(lport->sock, F_GETFL) | O_NONBLOCK);
} else
#endif
if (strncmp("udp:", port, 4) == 0)
/* Now try UDP/TCP */
{
lport->portread = udp_portread;
lport->portwrite = udp_portwrite;
lport->sock = socket(AF_INET, SOCK_DGRAM, 0);
lport->type = PORT_UDP;
port += 4;
} else {
/* Default to TCP type port */
lport->portread = tcp_portread;
lport->portwrite = tcp_portwrite;
lport->sock = socket(AF_INET, SOCK_STREAM, 0);
setRemoteSockOpts(lport->sock);
lport->type = PORT_TCP;
}
/* Determine if we are receving a bind address else select the
* localhost
*/
if ((endstr = strchr(port, ':'))) {
*endstr = '\0';
local_bind_addr = port;
port = endstr + 1;
}
if (lport->type == PORT_TCP || lport->type == PORT_UDP) {
int tmp;
#ifdef _WIN32
int val;
#else
socklen_t val;
#endif
struct sockaddr_in serv_addr;
if (strncmp(port, "0x", 2) == 0)
lport->port = strtol(port, &endstr, 16);
else
lport->port = atoi(port);
if (lport->sock < 0) {
printf("Error opening socket");
return 1;
}
/* Setup the socket to bind to the local port and allow
* address reuse unless it is UDP */
if (lport->type != PORT_UDP) {
tmp = 1;
setsockopt(lport->sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&tmp, sizeof(tmp));
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if (local_bind_addr != 0) {
serv_addr.sin_addr.s_addr = inet_addr(local_bind_addr);
} else {
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
serv_addr.sin_port = htons((short)lport->port);
val = sizeof(serv_addr);
if (bind(lport->sock, (struct sockaddr *)&serv_addr, val) < 0) {
CLOSESOCKET(lport->sock);
printf("Error: on socket bind, address in use\n");
return 1;
}
if (lport->type == PORT_TCP) {
if (listen(lport->sock, 1) < 0) {
printf("Error: on listen()\n");
CLOSESOCKET(lport->sock);
return 1;
}
}
}
/* add it to listen queue */
lport->next = rports;
rports = lport;
if (debug)
printf("Added local port: %s %i\n", lport->name, lport->sock);
return 0;
}
/*
* Establish communications to the remote system which the external
* client desires to connect to.
*/
static struct port_st *open_remote_port(struct port_st *peer)
{
struct port_st *iport;
int tmp;
if (peer->remote->type == PORT_TCP) {
iport = (struct port_st *)malloc(sizeof(struct port_st));
memset(iport, 0, sizeof(struct port_st));
iport->readMessage = remotePortReadMessage;
iport->portclose = tcp_portclose;
iport->portread = tcp_portread;
iport->portwrite = tcp_portwrite;
iport->port = peer->remote->port;
iport->sock = socket(AF_INET, SOCK_STREAM, 0);
setRemoteSockOpts(iport->sock);
if (iport->sock < 0) {
free(iport);
iport = NULL;
return NULL;
}
tmp =
connect(iport->sock,
(struct sockaddr *)&peer->remote->serv_addr,
sizeof(peer->remote->serv_addr));
if (tmp < 0) {
CLOSESOCKET(iport->sock);
free(iport);
iport = NULL;
return NULL;
}
iport->peer = peer;
/* Add the new socket to the list */
iport->next = rports;
rports = iport;
refresh_nsockhandle();
FD_SET(iport->sock, &master_rds);
return iport;
} else if (peer->remote->type == PORT_UDP ||
peer->remote->type == PORT_RS232) {
if (peer) {
peer->remote->peer = peer;
return peer->remote;
}
}
return NULL;
}
/*
* Setup the initial parameters for the remote system that external
* clients want to connect to.
*/
static int setup_remote_port(struct port_st *rport, char *host, char *port)
{
char *endstr;
char *ptr;
rport->cls = CLS_REMOTE_PORT;
strncpy(rport->name, host, NAMESIZE - 1);
rport->sock = -1;
rport->readMessage = remotePortReadMessage;
/* Determine port type */
if (strncmp("udp:", port, 4) == 0) {
rport->sock = socket(AF_INET, SOCK_DGRAM, 0);
if (rport->sock < 0) {
printf("Could not allocate socket\n");
return 1;
}
rport->type = PORT_UDP;
port += 4;
/* Check if we should be sending/receiving from a particular
* source port for udp
*/
if ((ptr = strchr(port, ':'))) {
#ifdef _WIN32
int val;
#else
socklen_t val;
#endif
struct sockaddr_in serv_addr;
int srcport;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
*ptr = '\0';
ptr++;
if (strncmp(port, "0x", 2) == 0)
srcport = strtol(port, &endstr, 16);
else
srcport = atoi(port);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons((short)srcport);
val = sizeof(serv_addr);
if (debug)
printf("Binding local port %i\n", srcport);
if (bind
(rport->sock, (struct sockaddr *)&serv_addr,
val) < 0) {
printf("Could not bind remote udp socket\n");
return 1;
}
port = ptr;
}
} else if (strncmp(port, "tcplisten:", 10) == 0) {
int tmp;
port += 10;
rport->type = PORT_LISTEN;
rport->sock = socket(AF_INET, SOCK_STREAM, 0);
if (rport->sock < 0) {
printf("Could not allocate socket\n");
return 1;
}
tmp = 1;
setsockopt(rport->sock, SOL_SOCKET, SO_REUSEADDR,
(char *)&tmp, sizeof(tmp));
rport->readMessage = remotePortAccept;
}
#ifndef _WIN32
else if (strncmp(port, "fifocon:", 8) == 0) {
port += 8;
rport->type = PORT_FIFO_CON;
fifo_con_file = strdup(port);
if (mkfifo(port, 0700)) {
if (errno != EEXIST) {
fprintf(stderr, "Error creating %s fifo\n", port);
return 1;
}
}
rport->sock = open(port, O_RDONLY|O_NONBLOCK);
if (rport->sock < 0) {
fprintf(stderr, "Error opening fifo\n");
return 1;
}
rport->readMessage = remotePortFifoConRead;
/* Setup call backs */
rport->portwrite = tcp_portwrite;
rport->portread = tcp_portread;
rport->portclose = tcp_portclose;
/* Add this port to the remote queue */
rport->next = rports;
rports = rport;
FD_SET(rport->sock, &master_rds);
} else if (port[0] == '/' || port[0] == 'C' || port[0] == 'c') {
char *baudinfo;
if ((baudinfo = strchr(port, ','))) {
*baudinfo = '\0';
baudinfo++;
}
if (port[0] == '/') {
rport->sock = open(port, O_RDWR);
if (rport->sock < 0) {
printf("ERROR: opening %s\n", port);
return 1;
}
if (baudinfo) {
if (setbaudrate(rport->sock, atoi(baudinfo)))
return 1;
}
setstopbits(rport->sock, "1");
setcondefaults(rport->sock);
rport->type = PORT_RS232;
fcntl(rport->sock, F_SETFL,
fcntl(rport->sock, F_GETFL) | O_NONBLOCK);
rport->portwrite = rs232_portwrite;
rport->portread = rs232_portread;
rport->portclose = rs232_portclose;
/* Add this port to the remote queue */
rport->next = rports;
rports = rport;
FD_SET(rport->sock, &master_rds);
}
}
#endif /* ! _WIN32 */
else {
/* Default to TCP type port */
rport->type = PORT_TCP;
}
if (rport->type == PORT_TCP || rport->type == PORT_UDP ||
rport->type == PORT_LISTEN) {
#ifdef _WIN32
int val;
#else
socklen_t val;
#endif
struct hostent *hostent;
hostent = gethostbyname(host);
if (!hostent) {
printf("Could not lookup hostname: %s\n", host);
return 1;
}
if (strncmp(port, "0x", 2) == 0)
rport->port = strtol(port, &endstr, 16);
else
rport->port = atoi(port);
/* Setup remote address */
memset(&rport->serv_addr, 0, sizeof(rport->serv_addr));
rport->serv_addr.sin_family = AF_INET;
memcpy(&rport->serv_addr.sin_addr.s_addr, hostent->h_addr,
sizeof(struct in_addr));
rport->serv_addr.sin_port = htons((short)rport->port);
val = sizeof(rport->serv_addr);
if (rport->type == PORT_UDP) {
rport->portwrite = udp_portwrite;
rport->portread = udp_portread;
rport->portclose = tcp_portclose;
/* type is PORT_UDP */
if (rport->sock < 0) {
printf("Error opening remote socket");
return 1;
}
/* Issue a connect to the remote address. On UDP this
* just means this is the only address we'll send and
* receive from
*/
connect(rport->sock,
(struct sockaddr *)&rport->serv_addr,
sizeof(rport->serv_addr));
/* Add this port to the remote queue */
rport->next = rports;
rports = rport;
FD_SET(rport->sock, &master_rds);
}
if (rport->type == PORT_LISTEN) {
rport->portwrite = udp_portwrite;
rport->portread = udp_portread;
rport->portclose = tcp_portclose;
/* type is PORT_UDP */
if (rport->sock < 0) {
printf("Error opening remote socket");
return 1;
}
if (bind
(rport->sock, (struct sockaddr *)&rport->serv_addr,
val) < 0) {
printf("Could not bind remote udp socket\n");
return 1;
}
listen(rport->sock, 1);
/* Add this port to the remote queue */
rport->next = rports;
rports = rport;
FD_SET(rport->sock, &master_rds);
}
}
if (debug)
printf("Rport socket: %i\n", rport->sock);
return 0;
}
static void killScriptClient(struct port_st *s_port, struct port_st **iport,
int incrementIport)
{
struct port_st *prev = s_port->clients;
struct port_st *find = s_port->clients;
/* Find the previous port */
while (find != *iport) {
prev = find;
find = find->clientNext;
}
if (incrementIport) {
if ((*iport)->clientNext == find)
*iport = find->clientNext;
else
*iport = (*iport)->clientNext;
}
if (find == s_port->clients) {
s_port->clients = find->clientNext;
} else {
prev->clientNext = find->clientNext;
}
killport(find);
}
static int writeScriptClients(struct port_st *s_port, char *buf, int bytes,
int opts)
{
struct port_st *iport = s_port->clients;
int got;
int i;
if (s_port->breakPort && gdbSplit) {
int xmit = 0;
for (i = 0; i < bytes; i++) {
if (gdbPtr >= MAX_GDB_BUF) {
gdbPtr = 0;
gdbGotDollar = 0;
} else if (buf[i] == '+' || buf[i] == '-') {
gdbArr[gdbPtr++] = buf[i];
if (!gdbGotDollar)
xmit = 1;
} else if (buf[i] == '$') {
gdbGotDollar = 1;
gdbArr[gdbPtr++] = buf[i];
} else if (gdbGotDollar) {
gdbArr[gdbPtr++] = buf[i];
if (gdbGotDollar > 1)
gdbGotDollar++;
if (buf[i] == '#' && gdbGotDollar <= 1) {
gdbGotDollar++;
}
if (gdbGotDollar >= 4) {
gdbGotDollar = 0;
xmit = 1;
}
}
}
if (!xmit)
return 1;
buf = gdbArr;
bytes = gdbPtr;
}
while (iport != NULL) {
got = iport->portwrite(iport, buf, bytes, opts);
if (logchar)
printf(">=%i#%i= ", iport->sock, got);
if (got <= 0) {
if (debug)
printf
("ERROR on write of client port %i got %i\n",
iport->sock, got);
killScriptClient(s_port, &iport, 1);
continue;
}
iport = iport->clientNext;
}
if (s_port->breakPort) {
gdbPtr = 0;
}
return 1;
}
static int serialBreak(struct port_st *port)
{
#ifdef HAVE_TERMIOS
tcsendbreak(port->sock, 0);
#endif /* linux */
#ifdef HAVE_TERMIO
return ioctl(port->sock, TCSBRK, 0);
#endif /* solaris */
return 0;
}
char defaultBrkStr[2] = { 0xff, 0xf3 };
char defaultBrkStrLen = 2;
char staticBrkStr[3] = { 0xff, 0xf3, 'g' };
char staticBrkStrLen = 3;
char *breakStr;
char breakStrLen = 0;
static int sendSpecialBreak(struct port_st *port, char *breakString, int len)
{
char *ptr = breakString;
int i;
int rec;
for (i = 0; i < len; i++) {
if ((unsigned char)ptr[i] == 0xff &&
(i + 1 < len) &&
(unsigned char)ptr[i + 1] == 0xf3 &&
port->type == PORT_RS232) {
serialBreak(port);
i++;
} else {
rec = port->portwrite(port, &ptr[i], 1, 0);
if (rec != 1)
return 1;
}
}
return 0;
}
static int processIACoptions(struct port_st *iport, int got)
{
int i;
int j = 0;
for (i = 0; i < got; i++) {
if (iport->inIAC > 0) {
if ((unsigned char)iport->buf[i] == IAC
&& iport->inIAC == 1) {
/* Eat all the IAC commands except break */
/* IAC_client_send_break()... */
} else {
if ((unsigned char)iport->buf[i] == 0xf3) {
if (iport->readMessage ==
scriptClientPortReadMessage) {
if (iport->scriptRef->breakPort)
sendSpecialBreak(iport->
scriptRef->
rscript,
breakStr,
breakStrLen);
else
sendSpecialBreak(iport->
scriptRef->
rscript,
defaultBrkStr,
defaultBrkStrLen);
} else
sendSpecialBreak(iport->peer,
defaultBrkStr,
defaultBrkStrLen);
iport->inIAC = 0;
} else {
iport->inIAC++;
}
}
if (iport->inIAC >= 3) {
iport->inIAC = 0;
}
} else {
if ((unsigned char)iport->buf[i] == IAC) {
iport->inIAC = 1;
} else {
if (iport->readMessage ==
scriptClientPortReadMessage
&& (unsigned char)iport->buf[j] == 3
&& iport->scriptRef->breakPort
&& iport->scriptRef->rscript) {
sendSpecialBreak(iport->scriptRef->
rscript, breakStr,
breakStrLen);
} else {
if (j != i)
iport->buf[j] = iport->buf[i];
j++;
}
}
}
}
return j;
}
/* Take care of a read case from a script client port
* 0 == success
* 1 == failure
*/
static int scriptClientPortReadMessage(struct port_st *iport)
{
int got;
got = iport->portread(iport, iport->buf, sizeof(iport->buf), 0);
if (got <= 0) {
killScriptClient(iport->scriptRef, &iport, 0);
/* No further processing */
goto bad_status;
}
if (debug) {
printf("Read from script: %i got: %i\n", iport->sock, got);
}
if (logchar) {
int j;
printf("<%i=", iport->sock);
for (j = 0; j < got; j++)
printf("%c", iport->buf[j]);
printf("= ");
}
if (!(iport->mode & NO_TELNET_OPTION_NEGOTIATION)) {
got = processIACoptions(iport, got);
if (got <= 0)
goto good_status;
}
if (!iport->scriptRef->scriptInUse)
goto good_status;
/* Send to remote port based on mode bits */
if (iport->scriptRef->rscript
&& iport->scriptRef->rscript->mode & SCRIPT_WRITE) {
got =
iport->scriptRef->rscript->portwrite(iport->scriptRef->
rscript, iport->buf,
got, 0);
if (logchar)
printf(">=%i#%i= ", iport->scriptRef->rscript->sock,
got);
if (got <= 0) {
killport(iport->scriptRef->rscript);
/* No further processing */
goto bad_status;
}
}
/* Send to local and port based on mode bits */
if (iport->scriptRef->lscript
&& iport->scriptRef->lscript->mode & SCRIPT_WRITE) {
got =
iport->scriptRef->lscript->portwrite(iport->scriptRef->
lscript, iport->buf,
got, 0);
if (logchar)
printf(">=%i#%i= ", iport->scriptRef->lscript->sock,
got);
if (got <= 0) {
killport(iport->scriptRef->lscript);
/* No further processing */
goto bad_status;
}
}
good_status:
if (logchar)
printf("\n");
return 0;
bad_status:
if (logchar)
printf("\n");
return 1;
}
static void iacStartup(struct port_st *iport)
{
char resp_buf[4];
/* Send the telnet negotion to put telnet in binary, no echo, single char mode */
sprintf(resp_buf, "%c%c%c", 0xff, 0xfb, 0x01); /* IAC WILL ECHO */
iport->portwrite(iport, (char *)resp_buf, 3, 0);
sprintf(resp_buf, "%c%c%c", 0xff, 0xfb, 0x03); /* IAC WILL Suppress go ahead */
iport->portwrite(iport, (char *)resp_buf, 3, 0);
sprintf(resp_buf, "%c%c%c", 0xff, 0xfd, 0x03); /* IAC DO Suppress go ahead */
sprintf(resp_buf, "%c%c%c", 0xff, 0xfb, 0x00); /* IAC WILL Binary */
iport->portwrite(iport, (char *)resp_buf, 3, 0);
sprintf(resp_buf, "%c%c%c", 0xff, 0xfd, 0x00); /* IAC DO Binary */
iport->portwrite(iport, (char *)resp_buf, 3, 0);
}
/* Take care of a read case from a script connection port.
*
* This will create a script client connection
* 0 == success
* 1 == failure
*/
static int scriptPortReadMessage(struct port_st *s_port)
{
struct port_st *iport;
struct sockaddr addr;
socklen_t addr_len = sizeof(addr);
int nsock;
if (s_port->type != PORT_TCP) {
printf("Error: Only TCP ports are supported for scripting\n");
return 1;
}
if ((nsock = accept(s_port->sock, &addr, &addr_len)) < 0) {
printf("error on socket accept() %i\n", nsock);
}
if (debug)
printf("Opened from remote %i \n", nsock);
setRemoteSockOpts(nsock);
/* Add the newly attached script client to the clients list of the script refrence */
iport = (struct port_st *)malloc(sizeof(struct port_st));
memset(iport, 0, sizeof(struct port_st));
iport->readMessage = scriptClientPortReadMessage;
iport->portclose = tcp_portclose;
iport->portread = tcp_portread;
iport->portwrite = tcp_portwrite;
iport->sock = nsock;
iport->type = PORT_TCP;
iport->cls = CLS_SCRIPT_CLIENT;
iport->scriptRef = s_port;
iport->clientNext = s_port->clients;
s_port->clients = iport;
if (debug)
printf("Added script client: %i\n", iport->sock);
/* Connect up to the external port and put that on
* the queue as well as setting up the peer.
*/
/* Add the new socket to the master list */
iport->next = rports;
rports = iport;
refresh_nsockhandle();
FD_SET(nsock, &master_rds);
if (!(s_port->mode & NO_TELNET_OPTION_NEGOTIATION) &&
!iport->scriptRef->breakPort)
iacStartup(iport);
if (breakOnConnect && iport->scriptRef->rscript &&
iport->scriptRef->breakPort)
sendSpecialBreak(iport->scriptRef->rscript, breakStr,
breakStrLen);
return 0;
}
/* Take care of a read case from a local port
* 0 == success
* 1 == failure
*/
static int localPortReadMessage(struct port_st *l_port)
{
struct port_st *peer;
struct port_st *iport;
int got;
if (l_port->type == PORT_TCP) {
struct sockaddr addr;
socklen_t addr_len = sizeof(addr);
int nsock;
if ((nsock = accept(l_port->sock, &addr, &addr_len)) < 0) {
printf("error on socket accept() %i\n", nsock);
}
if (debug)
printf("Opened from remote %i \n", nsock);
setRemoteSockOpts(nsock);
/* Connect the peer else close the remote socket */
iport = (struct port_st *)malloc(sizeof(struct port_st));
if (iport <= 0) {
printf("ERROR allocating memory\n");
exit(-1);
}
memset(iport, 0, sizeof(struct port_st));
iport->readMessage = remotePortReadMessage;
iport->portclose = tcp_portclose;
iport->portread = tcp_portread;
iport->portwrite = tcp_portwrite;
iport->sock = nsock;
iport->type = PORT_TCP;
iport->remote = l_port->remote;
iport->isLocal = 1;
if ((peer = open_remote_port(iport)) == NULL) {
if (debug)
printf("Error opening remote socket\n");
free(iport);
iport = NULL;
shutdown(nsock, 2);
CLOSESOCKET(nsock);
return 0;
} else {
iport->peer = peer;
/* Connect up to the external port and put that on
* the queue as well as setting up the peer.
*/
/* Add the new socket to the list */
iport->next = rports;
rports = iport;
refresh_nsockhandle();
FD_SET(nsock, &master_rds);
}
if (l_port->remote->type == PORT_RS232) {
telnetNegotiation = 1;
iacStartup(iport);
}
/* The local connection is bound to the remote. Now connect
* the script port if a scriptRef exists
*/
if (l_port->scriptRef != NULL
&& !l_port->scriptRef->scriptInUse) {
/* local setup */
iport->scriptRef = l_port->scriptRef;
iport->mode = l_port->scriptRef->lmode;
/* Remote setup */
peer->scriptRef = l_port->scriptRef;
peer->mode = l_port->scriptRef->rmode;
/* general script setup */
l_port->scriptRef->lscript = iport;
l_port->scriptRef->rscript = peer;
l_port->scriptRef->scriptInUse = 1;
}
} else if (l_port->type == PORT_UDP || l_port->type == STDINOUT) {
if ((peer = open_remote_port(l_port)) == NULL) {
/* Throw away any read because the remote side is not there */
printf("Warning remote socket could not be opened\n");
l_port->portread(l_port, l_port->buf,
sizeof(l_port->buf), 0);
} else {
l_port->peer = peer;
got =
l_port->portread(l_port, l_port->buf,
sizeof(l_port->buf), 0);
if (debug)
printf
("Read from child1: %i got: %i write to %i\n",
l_port->sock, got, l_port->peer->sock);
/* If stdin is closed, then we need to exit */
if (got <= 0 && l_port->type == STDINOUT) {
printf
("Terminating because STDIN read return <= 0\n");
exit(0);
}
if (logchar) {
int j;
printf("<%i=", l_port->sock);
for (j = 0; j < got; j++)
printf("%c", l_port->buf[j]);
printf("=\n");
}
got =
l_port->peer->portwrite(l_port->peer, l_port->buf,
got, 0);
if (got <= 0) {
printf("Error writing to remote: %s on %i\n",
l_port->peer->name, l_port->peer->sock);
}
}
}
return 0;
}
/* Take care of a read case from a remote port
* 0 == success
* 1 == failure
*/
static int remotePortAccept(struct port_st *iport)
{
int fd;
fd = accept(iport->sock, 0, 0);
if (fd < 0)
/* We return zero so no one closes the socket */
return 0;
if (listen_fd < 0) {
/* Swap the new port for the listen port */
listen_fd = iport->sock;
iport->sock = fd;
FD_CLR(listen_fd, &master_rds);
FD_SET(iport->sock, &master_rds);
refresh_nsockhandle();
iport->readMessage = remotePortReadMessage;
}
return 0;
}
#define MAX_FIFO_BUF 50
char fifo_buf[MAX_FIFO_BUF];
int fifo_idx = 0;
/* Take care of a read case from the console fifo
* 0 == success
* 1 == failure
*/
static int remotePortFifoConRead(struct port_st *iport)
{
int cc;
char ibuf[2];
if ((cc = read(iport->sock, ibuf, 1)) > 0) {
if (ibuf[0] == '\n') {
int port = atoi(fifo_buf);
int sock;
struct sockaddr_in serv_addr;
fifo_idx = 0;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
goto fifo_out;
setRemoteSockOpts(sock);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons((short)port);
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(sock, (struct sockaddr *) &serv_addr,
sizeof (serv_addr)) < 0) {
close(sock);
fprintf(stderr,"Error connecting to local port %i\r\n", port);
goto fifo_out;
}
if (fifo_con_fd < 0) {
/* Swap the new port for the fifo_con port */
fifo_con_fd = iport->sock;
iport->sock = sock;
FD_CLR(fifo_con_fd, &master_rds);
FD_SET(iport->sock, &master_rds);
refresh_nsockhandle();
iport->readMessage = remotePortReadMessage;
}
}
fifo_out:
if (ibuf[0] != '\r' && ibuf[0] != '\n') {
fifo_buf[fifo_idx] = ibuf[0];
fifo_idx++;
}
if (fifo_idx >= MAX_FIFO_BUF)
fifo_idx = 0;
} else {
close(iport->sock);
FD_CLR(iport->sock, &master_rds);
iport->sock = open(fifo_con_file, O_RDONLY|O_NONBLOCK);
if (iport->sock < 0) {
fprintf(stderr, "Error opening fifo\r\n");
exit(1);
}
FD_SET(iport->sock, &master_rds);
refresh_nsockhandle();
}
return 0;
}
/* Take care of a read case from a remote port
* 0 == success
* 1 == failure
*/
static int remotePortReadMessage(struct port_st *iport)
{
int rgot;
int wgot;
rgot = iport->portread(iport, iport->buf, sizeof(iport->buf), 0);
if (logchar) {
int j;
printf("<%i=", iport->sock);
for (j = 0; j < rgot; j++)
printf("%c", iport->buf[j]);
printf("= ");
}
if (rgot <= 0) {
killport(iport);
/* No further processing */
goto bad_status;
} else {
if (telnetNegotiation) {
rgot = processIACoptions(iport, rgot);
if (rgot <= 0)
goto good_status;
}
if (iport->peer && iport->peer->sock >= 0) {
wgot =
iport->peer->portwrite(iport->peer, iport->buf,
rgot, 0);
if (logchar)
printf(">=%i#%i= ", iport->sock, wgot);
if (debug)
printf
("Read from child2: %i got: %i write to %i\n",
iport->sock, rgot, iport->peer->sock);
if (wgot <= 0) {
killport(iport);
/* No further processing */
goto bad_status;
}
} else {
if (debug)
printf
("Read from child3: %i got: %i to /dev/null\n",
iport->sock, rgot);
}
if (iport->scriptRef) {
if (iport->mode & SCRIPT_READ)
return writeScriptClients(iport->scriptRef,
iport->buf, rgot, 0);
}
}
good_status:
if (logchar)
printf("\n");
return 0;
bad_status:
if (logchar)
printf("\n");
return 1;
}
static int parse_local_port(struct port_st *lport, char *portStr)
{
char *scriptStr = NULL;
int breakPort = 0;
/* Check to see if there scriptPort parameters */
if ((scriptStr = strchr(portStr, '+'))) {
*scriptStr = '\0';
scriptStr++;
} else if ((scriptStr = strchr(portStr, '^'))) {
*scriptStr = '\0';
scriptStr++;
breakPort = 1;
}
if (setup_local_port(lport, portStr, NULL))
return 1;
/* setup the script port if one was passed in */
if (scriptStr) {
struct port_st *scriptPort;
scriptPort = (struct port_st *)malloc(sizeof(struct port_st));
memset(scriptPort, 0, (sizeof(struct port_st)));
if (setup_local_port(scriptPort, scriptStr, lport)) {
printf("Error: connecting to %s\n", scriptStr);
free(scriptPort);
return 1;
}
/* If the local port is udp we must immediately set the lscript handle */
if (lport->type == PORT_UDP || lport->type == STDINOUT) {
scriptPort->scriptInUse = 1;
scriptPort->lscript = lport;
}
FD_SET(scriptPort->sock, &master_rds);
lport->scriptRef = scriptPort;
/* setup additional attributes */
scriptPort->lmode = 0; /* No access to back to the local port by default */
scriptPort->rmode = (SCRIPT_READ | SCRIPT_WRITE);
scriptPort->breakPort = breakPort;
}
FD_SET(lport->sock, &master_rds);
return 0;
}
/*
* Main entry
*/
int main(int argc, char *argv[])
{
struct port_st *iport; /* Iterator over ports */
fd_set rds;
fd_set wds;
fd_set eds;
FILE *pidf;
int pid;
int ind;
#ifdef USE_LATENCY
int baud;
int latency;
#endif /* USE_LATENCY */
int got;
int select_ret;
char *s;
char *pidfile = 0;
int c;
int do_fork = 0;
int pargs = 0;
char *proxy_args[3]; /* Each of the main three aguments */
#ifdef _WIN32
WSADATA wsaData; /* Windows Socket init data */
#endif /* _WIN32 */
progname = argv[0];
#ifdef _WIN32
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
fprintf(stderr, "%s: Error starting WSA\n", progname);
exit(1);
}
#else /* ! _WIN32 */
signal(SIGPIPE, SIG_IGN);
#endif /* ! _WIN32 */
if (argc < 2) {
usage();
}
printf("Agent Proxy %01.2f Started with:", AGENT_VER);
for (ind = 1; ind < argc; ind++) {
printf(" %s", argv[ind]);
}
printf("\n");
/* Initalize the default break handler */
breakStr = staticBrkStr;
breakStrLen = staticBrkStrLen;
for (ind = 1; ind < argc; ind++) {
s = argv[ind];
if (*s != '-') {
/* Add a proxy agurment */
if (pargs > 2) {
fprintf(stderr, "Too many aguments at: %s\n",
s);
exit(-1);
}
proxy_args[pargs] = s;
pargs++;
continue;
}
s++;
if (*s == '-' && s[1] == '\0') {
/* Terminate option scanning on -- */
ind++;
break;
}
while ((c = *s++) != '\0') {
switch (c) {
case 'd':
logchar = 1;
break;
case 'v':
debug = 1;
break;
case 'D':
do_fork = 1;
break;
case 'f':
if (ind + 1 >= argc) {
fprintf(stderr,
"%s: no argument specified for option -%c\n",
progname, c);
usage();
}
pidfile = argv[ind + 1];
ind++;
break;
case 'G':
gdbSplit = 0;
break;
case 'B':
breakOnConnect = 0;
break;
case 'b':
case 'l':
case 'p':
case 's':
if (*s == '\0') {
if (ind + 1 >= argc) {
fprintf(stderr,
"%s: no argument specified for option -%c\n",
progname, c);
usage();
}
s = argv[++ind];
}
switch (c) {
case 's':
breakStr = (char *)malloc(sizeof(char));
breakStrLen = 1;
breakStr[0] = (unsigned char)atoi(s);
break;
#ifdef USE_LATENCY
case 'b':
baud = atoi(s);
break;
case 'l':
latency = atoi(s);
break;
#endif /* USE_LATENCY */
default:
fprintf(stderr,
"%s: option -%c not recognized\n",
progname, c);
usage();
}
s = "";
break;
default:
fprintf(stderr,
"%s: option -%c not recognized\n",
progname, c);
usage();
}
}
}
/* Initialize the master read and write handles */
FD_ZERO(&master_rds);
FD_ZERO(&master_wds);
l_ports = (struct port_st *)malloc(sizeof(struct port_st));
memset(l_ports, 0, sizeof(struct port_st));
if (parse_local_port(l_ports, proxy_args[0])) {
printf("Open of local port failed\n");
exit(1);
}
r_ports = (struct port_st *)malloc(sizeof(struct port_st));
memset(r_ports, 0, sizeof(struct port_st));
if (setup_remote_port(r_ports, proxy_args[1], proxy_args[2])) {
printf("Open of local port failed\n");
exit(1);
}
/* Connect the l_port to the now setup r_port */
l_ports->remote = r_ports;
/* When the remote side is udp and we are using script ports,
* activate communications right away
*/
if ((r_ports->type == PORT_UDP || r_ports->type == PORT_LISTEN ||
r_ports->type == PORT_FIFO_CON)
&& l_ports->scriptRef) {
l_ports->scriptRef->scriptInUse = 1;
l_ports->scriptRef->rscript = r_ports;
l_ports->scriptRef->rscript->mode =
(SCRIPT_READ | SCRIPT_WRITE);
r_ports->scriptRef = l_ports->scriptRef;
}
/* Open the pid file handle */
if (pidfile) {
pidf = fopen(pidfile, "w");
if (!pidf) {
printf("ERROR: Could not open pid file: %s\n", pidfile);
exit(1);
}
}
#ifndef _WIN32
if (do_fork) {
if ((pid = fork())) {
if (pidfile) {
fprintf(pidf, "%i", pid);
fclose(pidf);
}
exit(0);
}
if (pidfile)
fclose(pidf);
} else
#endif
if (pidfile) {
fprintf(pidf, "%i", getpid());
fclose(pidf);
}
printf("Agent Proxy running. pid: %i\n", getpid());
refresh_nsockhandle();
while (1) {
memcpy(&rds, &master_rds, sizeof(master_rds));
memcpy(&eds, &master_rds, sizeof(master_rds));
memcpy(&wds, &master_wds, sizeof(master_wds));
select_ret = select(nsockhandle, &rds, &wds, &eds, NULL);
if (select_ret <= 0) {
if (debug)
printf("Select return: %i\n", select_ret);
}
/* Iterate over all the listening ports to see if any have
* data that should be transmitted.
*/
iport = rports;
while (iport != NULL && select_ret > 0) {
if (FD_ISSET(iport->sock, &rds)) {
FD_CLR(iport->sock, &rds);
select_ret--;
if (iport->readMessage(iport)) {
/* reset the list pointer to the top because one
* or more list handles a was destroyed with
* killport.
*/
iport = rports;
continue;
}
}
iport = iport->next;
}
/* Check for any Out Of Band OOB data */
iport = rports;
while (iport != NULL && select_ret > 0) {
if (FD_ISSET(iport->sock, &eds)) {
FD_CLR(iport->sock, &eds);
select_ret--;
got =
iport->portread(iport, iport->buf, 1,
MSG_OOB);
if (got <= 0) {
killport(iport);
/* reset the list pointer to the top because one
* or more list handles a was destroyed with
* killport.
*/
iport = rports;
continue;
} else {
if (debug)
printf
("OOB child: %i got: %i\n",
iport->sock, got);
got =
iport->peer->portwrite(iport->peer,
iport->buf,
got,
MSG_OOB);
if (got <= 0) {
killport(iport);
/* reset the list pointer to the top because one
* or more list handles a was destroyed with
* killport.
*/
iport = rports;
continue;
}
}
}
iport = iport->next;
}
}
return 0;
}