/* * 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 #include #include #include #ifdef _WIN32 #include typedef unsigned int socklen_t; #define CLOSESOCKET(fd) closesocket(fd) #else /* ! _WIN32 */ #include #include #include #include #include #include #include #define CLOSESOCKET(fd) close(fd) #include #include #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> <[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 ] [-b ]\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) { 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(); } } } if (pargs < 3) 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; }