2013-08-25 09:59:50 -04:00
|
|
|
/*
|
|
|
|
Copyright (C) 2006 AbsoluteValue Systems, Inc. All Rights Reserved.
|
|
|
|
|
|
|
|
Originally written and maintained by: Solomon Peachy <pizza@shaftnet.org>
|
|
|
|
Local redirection & Debugging code by: Mark Mathews <mark@linux-wlan.com>
|
|
|
|
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/poll.h>
|
|
|
|
|
|
|
|
#include <sys/time.h>
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <syslog.h>
|
2014-10-27 13:05:29 -04:00
|
|
|
#include <ctype.h>
|
2013-08-25 09:59:50 -04:00
|
|
|
|
|
|
|
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");
|
2013-08-25 10:10:55 -04:00
|
|
|
fprintf(stderr, "\t-d <serialdev> -s <commsparams> [ -x | -r ]\n");
|
|
|
|
fprintf(stderr, "\t-D <serialdev> -S <commsparams> [ -X | -R ]\n");
|
2013-08-25 09:59:50 -04:00
|
|
|
fprintf(stderr, "commsparams: <baud>-<databits><parity><stopbits>\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;
|
2013-08-25 10:10:55 -04:00
|
|
|
int hw_flow2 = 0;
|
|
|
|
int sw_flow2 = 0;
|
2013-08-25 09:59:50 -04:00
|
|
|
|
|
|
|
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;
|
2013-08-25 10:10:55 -04:00
|
|
|
c = getopt(argc, argv, "hd:s:D:S:xrXR");
|
2013-08-25 09:59:50 -04:00
|
|
|
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;
|
2013-08-25 10:10:55 -04:00
|
|
|
case 'x':
|
|
|
|
sw_flow = 1;
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
hw_flow = 1;
|
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
sw_flow2 = 1;
|
|
|
|
break;
|
|
|
|
case 'R':
|
|
|
|
hw_flow2 = 1;
|
|
|
|
break;
|
|
|
|
|
2013-08-25 09:59:50 -04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2013-08-25 10:10:55 -04:00
|
|
|
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';
|
|
|
|
|
2013-08-25 09:59:50 -04:00
|
|
|
/* 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,
|
2013-08-25 10:10:55 -04:00
|
|
|
hw_flow2, sw_flow2, modem_lines);
|
2013-08-25 09:59:50 -04:00
|
|
|
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));
|
2014-10-27 13:05:29 -04:00
|
|
|
sprintf(buf, "1> 0x%02x '%c'\n", c, isprint(c)? c : '.' );
|
2013-08-25 09:59:50 -04:00
|
|
|
write(fileno(stdout), buf, strlen(buf));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pfds[1].revents & (POLLIN|POLLPRI)) {
|
|
|
|
uint8_t c;
|
|
|
|
|
|
|
|
while ((i = read(ser_fd2, &c, sizeof(c))) > 0) {
|
2014-09-23 11:14:35 -04:00
|
|
|
char buf[20];
|
2013-08-25 09:59:50 -04:00
|
|
|
write(ser_fd1, &c, sizeof(c));
|
2014-10-27 13:05:29 -04:00
|
|
|
sprintf(buf, "2> 0x%02x '%c'\n", c, isprint(c)? c : '.' );
|
2014-09-23 11:14:35 -04:00
|
|
|
write(fileno(stdout), buf, strlen(buf));
|
2013-08-25 09:59:50 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|