/* Copyright (C) 2006 AbsoluteValue Systems, Inc. All Rights Reserved. Originally written and maintained by: Solomon Peachy Local redirection & Debugging code by: Mark Mathews This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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, see . */ #include "common.h" #include #include #include #include #include #include #include #include #include #include #include #include struct sob_config { uint32_t baud; uint32_t stopbits; uint32_t databits; uint8_t parity; uint8_t flow; }; /* ================================================================ */ static void dump_config(char *prefix, struct sob_config *cfg) { syslog(LOG_INFO, "%s Serial %d-%d%c%d Flow %c", prefix, cfg->baud, cfg->databits, cfg->parity, cfg->stopbits, cfg->flow); } void printhelp() { fprintf(stderr, " Serial to Serial forwarder and sniffer\n\n"); fprintf(stderr, "\t-d -s [ -x | -r ]\n"); fprintf(stderr, "\t-D -S [ -X | -R ]\n"); fprintf(stderr, "commsparams: -\n"); fprintf(stderr, "\n"); } static int run = 1; int main (int argc, char **argv) { struct sob_config sob_cfg1; struct sob_config sob_cfg2; /* Basic communications parameters */ int modem_lines = 0; int hw_flow = 0; int sw_flow = 0; int hw_flow2 = 0; int sw_flow2 = 0; char *ser_dev1 = NULL; char *ser_dev2 = NULL; int ser_fd1; int ser_fd2; char *comm_params1 = NULL; char *comm_params2 = NULL; struct termios ser_cfg1; struct termios ser_cfg2; int daemonize = 0; int i; /* Parse command line */ while(1) { int c; c = getopt(argc, argv, "hd:s:D:S:xrXR"); if (c == -1) break; switch (c) { case 'h': printhelp(); exit(0); case 'd': ser_dev1 = optarg; break; case 'D': ser_dev2 = optarg; break; case 's': comm_params1 = optarg; break; case 'S': comm_params2 = optarg; break; case 'x': sw_flow = 1; break; case 'r': hw_flow = 1; break; case 'X': sw_flow2 = 1; break; case 'R': hw_flow2 = 1; break; default: fprintf(stderr, "getopt returned bogus option '%c'\n", c); return 1; } } if (!ser_dev1 || !ser_dev2 || !comm_params1 || !comm_params2) { fprintf(stderr, "ERROR: missing command-line arguments.\n\n"); printhelp(); return 2; } if (hw_flow && sw_flow) { fprintf(stderr, "ERROR: can't use both hardware and software flow control.\n\n"); printhelp(); return 10; } if (hw_flow2 && sw_flow2) { fprintf(stderr, "ERROR: can't use both hardware and software flow control.\n\n"); printhelp(); return 10; } if (hw_flow) sob_cfg1.flow = 'H'; else if (sw_flow) sob_cfg1.flow = 'S'; else sob_cfg1.flow = 'N'; if (hw_flow2) sob_cfg2.flow = 'H'; else if (sw_flow2) sob_cfg2.flow = 'S'; else sob_cfg2.flow = 'N'; /* Parse serial parameters #1 */ i = sscanf(comm_params1, "%d-%d%c%d", &sob_cfg1.baud, &sob_cfg1.databits, &sob_cfg1.parity, &sob_cfg1.stopbits); if (i < 4) { fprintf(stderr, "Invalid commparameters (%s)\n", comm_params1); printhelp(); return 6; } sob_cfg1.parity &= ~0x20; /* Case insensitive */ /* Parse serial parameters #2 */ i = sscanf(comm_params2, "%d-%d%c%d", &sob_cfg2.baud, &sob_cfg2.databits, &sob_cfg2.parity, &sob_cfg2.stopbits); if (i < 4) { fprintf(stderr, "Invalid commparameters (%s)\n", comm_params2); printhelp(); return 6; } sob_cfg2.parity &= ~0x20; /* Case insensitive */ /* Open serial port #1 */ ser_fd1 = open(ser_dev1, O_RDWR|O_NONBLOCK|O_NOCTTY|O_ASYNC); if (ser_fd1 < 0) { perror("Could not open serial device #1"); return 5; } if (!isatty(ser_fd1)) { perror("Not a TTY\n"); return 999; } /* Set serial Parameters #1*/ i = setserial(&ser_cfg1, ser_fd1, sob_cfg1.baud, sob_cfg1.databits, sob_cfg1.parity, sob_cfg1.stopbits, hw_flow, sw_flow, modem_lines); if (i > 0) { printhelp(); return 7; } if (i < 0) { perror("Could not initialize serial device"); return 8; } /* Turn off Break #1*/ i = ioctl(ser_fd1, TIOCCBRK); if (i < 0) { perror("Could not turn off BREAK"); return 18; } /* Open serial port #2 */ ser_fd2 = open(ser_dev2, O_RDWR|O_NONBLOCK|O_NOCTTY|O_ASYNC); if (ser_fd2 < 0) { perror("Could not open serial device #2"); return 5; } if (!isatty(ser_fd2)) { perror("Not a TTY\n"); return 999; } /* Set serial Parameters #2*/ i = setserial(&ser_cfg2, ser_fd2, sob_cfg2.baud, sob_cfg2.databits, sob_cfg2.parity, sob_cfg2.stopbits, hw_flow2, sw_flow2, modem_lines); if (i > 0) { printhelp(); return 7; } if (i < 0) { perror("Could not initialize serial device"); return 8; } /* Turn off Break #2*/ i = ioctl(ser_fd2, TIOCCBRK); if (i < 0) { perror("Could not turn off BREAK"); return 18; } /* Ignore sigpipes */ signal(SIGPIPE, SIG_IGN); if (daemonize) daemon(0, 0); /* And at this point, start! */ openlog("sob", LOG_PID, LOG_DAEMON); struct pollfd pfds[2]; /* Set up the poll */ pfds[0].fd = ser_fd1; pfds[0].revents = 0; pfds[0].events = POLLIN|POLLPRI; pfds[1].fd = ser_fd2; pfds[1].revents = 0; pfds[1].events = POLLIN|POLLPRI; syslog(LOG_INFO, "serialsnif bound and active"); dump_config("Local config#1: ", &sob_cfg1); dump_config("Local config#2: ", &sob_cfg2); #define TV_FLATTEN(__tv__) ((__tv__.tv_sec * 1000000) + __tv__.tv_usec) /* Now we can run */ while (run) { pfds[0].revents = 0; pfds[1].revents = 0; i = poll(pfds, 2, 1000); /* If the poll timed out, just do it again */ if (i == 0) { continue; } /* Handle serial port reset */ if (pfds[0].revents & (POLLERR|POLLHUP|POLLNVAL)) { syslog(LOG_ERR, "Serial port #1 reset, purging buffers (%d)\n", pfds[0].revents); tcflush(ser_fd1, TCIOFLUSH); close(ser_fd1); ser_fd1 = open(ser_dev1,O_RDWR|O_NONBLOCK|O_NOCTTY|O_ASYNC); if (ser_fd1 < 0) { syslog(LOG_CRIT, "Could not open serial device, terminating: %m"); return 5; } i = tcsetattr(ser_fd1, TCSANOW, &ser_cfg1); if (i < 0) { syslog(LOG_CRIT, "Could not initialize serial device: %m"); } pfds[1].fd = ser_fd1; continue; } if (pfds[1].revents & (POLLERR|POLLHUP|POLLNVAL)) { syslog(LOG_ERR, "Serial port #2 reset, purging buffers (%d)\n", pfds[1].revents); tcflush(ser_fd2, TCIOFLUSH); close(ser_fd2); ser_fd2 = open(ser_dev1,O_RDWR|O_NONBLOCK|O_NOCTTY|O_ASYNC); if (ser_fd2 < 0) { syslog(LOG_CRIT, "Could not open serial device, terminating: %m"); return 5; } i = tcsetattr(ser_fd2, TCSANOW, &ser_cfg2); if (i < 0) { syslog(LOG_CRIT, "Could not initialize serial device: %m"); } pfds[1].fd = ser_fd2; continue; } /* Handle the serial port IN */ if (pfds[0].revents & (POLLIN|POLLPRI)) { uint8_t c; while ((i = read(ser_fd1, &c, sizeof(c))) > 0) { char buf[20]; write(ser_fd2, &c, sizeof(c)); sprintf(buf, "1> 0x%02x '%c'\n", c, isprint(c)? c : '.' ); write(fileno(stdout), buf, strlen(buf)); } } if (pfds[1].revents & (POLLIN|POLLPRI)) { uint8_t c; while ((i = read(ser_fd2, &c, sizeof(c))) > 0) { char buf[20]; write(ser_fd1, &c, sizeof(c)); sprintf(buf, "2> 0x%02x '%c'\n", c, isprint(c)? c : '.' ); write(fileno(stdout), buf, strlen(buf)); } } } /* Clean up */ syslog(LOG_INFO, "sobredird shutting down"); tcflush(ser_fd1, TCIOFLUSH); tcflush(ser_fd2, TCIOFLUSH); close(ser_fd1); close(ser_fd2); closelog(); return 0; }