/*
 *  sniff232 - Simple RS232 sniffer
 *  
 *  Trivial program to sniff communications made between two RS232 devices
 *
 *  Computer <------> [port1] Computer with sniff232 [port2] <------> Device
 *
 *  Written in about half hour based on my own needs.
 *  Use at your own risk.
 *
 *  Cicuttin Matteo (C) 2007 - matteo.cicuttin@gmail.com
 *  Released under BSD license.
 *
 *  To compile:
 *  gcc -O2 -Wall -pedantic -o sniff232 sniff232.c
 *
 *  History:
 *  v0.0 (11/07/2007) -		Created sniff232
 *
 */
 
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/select.h>
#include <signal.h>

#define BUFSZ	512
#define STEP	16
#define MAX(a,b) ( (a > b) ? a : b )

int ctrlc;

void
die(char *reason)
{
	perror(reason);
	exit(-1);
}

void
hexdump(unsigned char *buf, int buflen)
{
	int i, c, s;
	for (i = 0; i < buflen; i+= STEP)
	{
		c = 0;
		//printf("%8.8x: ", i);
		while ( (c < STEP) && (i+c < buflen) )
			printf("%2.2x ", buf[i+c++]);
		
		s = 1 + (STEP - c);
		while (s--)
			printf("   ");
		
		c = 0;
		while ( (c < STEP) && (i+c < buflen) )
		{
			printf("%c", isprint(buf[i+c]) ? buf[i+c] : '.' );
			c++;
		}
			
		printf("\n");
	}
}

int
open_line(char *devname)
{
	int fd;
	
	fd = open(devname, O_RDWR | O_NOCTTY | O_NDELAY );
	if (fd == -1)
		die("open");
	
	fcntl(fd, F_SETFL, 0);
	
	return fd;
}

void
configure_port(int fd)
{
	struct termios tios;
	
	tcgetattr(fd, &tios);
	
	cfsetispeed(&tios, B19200);
	cfsetospeed(&tios, B19200);
	tios.c_cflag |= (CLOCAL | CREAD);
	tios.c_oflag = 0;
	tios.c_lflag = 0;
	tios.c_iflag = IGNBRK;	
	tcsetattr(fd, TCSANOW, &tios);
}

void
ctrlc_handler(void)
{
    printf("\rDetected CTRL-C, exiting.\n");
    ctrlc = 1;
}

int
main(void)
{
	int		port1, port2;
	int		err;
	int		rchs;
	fd_set	readset;
	unsigned char	buffer[BUFSZ];
	
	struct sigaction	sa;
	
	ctrlc = 0;
	sa.sa_handler = ctrlc_handler;
	sa.sa_flags = 0;

//	rchs = read(0, buffer, BUFSZ);
//	hexdump(buffer, rchs);
	
//	exit(-1);
	
	if (sigaction(SIGINT, &sa, NULL) < 0)
        die("sigaction");
	
	port1 = open_line("/dev/cuad0");
	port2 = open_line("/dev/cuad1");
	
	printf("Ports opened with fds %d and %d\n", port1, port2);
	
	configure_port(port1);
	configure_port(port2);
	
	printf("Sniffer ready. . .\n");

	FD_ZERO(&readset);	
	while(!ctrlc)
	{
		FD_SET(port1, &readset);
		FD_SET(port2, &readset);
		
		//select descriptors
		err = select(MAX(port1, port2) + 1, &readset, NULL, NULL, NULL);
		
		//check for errors
		if ( err < 0 )
			if ( EINTR == errno ) continue;
			else die("select");
		
		//port1 ready
		if (FD_ISSET(port1, &readset))
		{
			FD_CLR(port1, &readset);
			rchs = read(port1, buffer, BUFSZ);
			if (rchs == -1)
				die("read");
			
			if ( write(port2, buffer, rchs) == -1 )
				die("write");
			
			printf("port1 -> port2\n");
			hexdump(buffer, rchs);
			continue;
		}
		
		//port2 ready
		if (FD_ISSET(port2, &readset))
		{
			FD_CLR(port2, &readset);
			rchs = read(port2, buffer, BUFSZ);
			if (rchs == -1)
				die("read");
			
			if ( write(port1, buffer, rchs) == -1 )
				die("write");
			
			printf("port2 -> port1\n");
			hexdump(buffer, rchs);
			continue;
		}
	}
}

