2 sersniff.c - A program to tunnel between 2 serial ports and
3 sniff the data that passes between them. Designed to aid
4 working out the protocol between a Nokia 9000i and NServer.
5 Written by Jonathan McDowell for Project Purple, 1999
6 Extra stuff by Cornelius Cook (cook@cpoint.net), 1999
7 OSX support and EOF support by Pete Baker (peteb4ker@gmail.com), 2011
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software Foundation
21 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 http://www.gnu.org/copyleft/gpl.html
24 07/09/1999 - Started writing.
25 21Nov1999 - Cook: added command line support and extra error checking
26 27Nov1999 - Cook: added select, timer & changed output look
27 24Jun2011 - Baker: Added OSX support. Added EOF character support
30 #define VERSION "0.0.4"
42 #include "disp_basic.h"
45 char * speed_str[] = { "300", "1200", "2400", "4800", "9600", "19200", "38400",
46 "57600", "115200", "230400", NULL };
47 speed_t speed_num[] = { B300, B1200, B2400, B4800, B9600, B19200, B38400,
48 B57600, B115200, B230400, B0 };
50 int openport(const char *device, speed_t baud, int setup)
53 struct termios serparams;
55 if ((filedes=open(device, O_RDWR | O_NONBLOCK))==-1) {
56 /* Can't open device */
57 fprintf(stderr,"%s: ",device);
63 bzero(&serparams, sizeof(serparams));
65 cfsetspeed(&serparams, baud);
66 serparams.c_cflag |= CLOCAL | CS8 | CREAD;
68 if (tcflush(filedes, TCIFLUSH)) {
69 fprintf(stderr,"%s: ",device);
73 if (tcsetattr(filedes, TCSANOW, &serparams)) {
74 fprintf(stderr,"%s: ",device);
83 int closeport(int filedes)
85 /* Should remove any locks at this point */
86 return close(filedes);
90 this returns the string for the character passed to it
91 It could be expanded in the future to maybe to string recognitions?
93 char *chardecide(unsigned char c, int alpha, char *format) {
94 static char result[256];
96 /* everyone should take up 5 characters */
98 if ((c < 32) | (c > 126)) {
101 sprintf(result,"<LF>");
104 sprintf(result,"<CR>");
107 sprintf(result,"<ESC>");
110 snprintf(result,256,"<%02hX>",c);
114 snprintf(result,256,"%c",c);
117 snprintf(result,256,format,c);
122 void outputchar(unsigned char c, int port, int alpha,
123 long usec_threshold, long usec_waited, char *name1, char *name2, char *format)
127 todisplay=chardecide(c,alpha,format);
128 disp_outputstr(port, todisplay, usec_threshold, usec_waited, name1, name2);
131 void mainloop(int port1, int port2, int silent, int alpha, int quit_on_eof, long usec_threshold, char *name1, char *name2, char *format)
133 unsigned char c1, c2;
139 struct timeval before;
140 struct timeval after;
144 /* need the largest fd for the select call */
145 biggestfd=port1 > port2 ? port1 : port2;
149 /* reset the select set */
157 if (gettimeofday(&before,NULL)) {
158 perror("gettimeofday");
161 if ((fdret=select(biggestfd, &rfds, NULL, &efds, NULL))<0) {
165 if (gettimeofday(&after,NULL)) {
166 perror("gettimeofday");
170 /* get seconds difference */
171 timediff=after.tv_sec-before.tv_sec;
172 /* convert to micro seconds */
174 /* add difference in usecs */
175 timediff+=after.tv_usec-before.tv_usec;
177 if (FD_ISSET(port1, &rfds)) {
178 for (rc=read(port1, &c1, 1);
179 rc>0; rc=read(port1, &c1, 1) ) {
180 outputchar(c1,1,alpha,usec_threshold,timediff,name1,name2,format);
182 if (!silent) write(port2,&c1,1);
184 if (rc==0 && quit_on_eof==1) {
188 if (rc<0 && errno!=EAGAIN) {
189 perror("read(port1)");
194 if (FD_ISSET(port2, &rfds)) {
195 for (rc=read(port2, &c2, 1);
196 rc>0; rc=read(port2, &c2, 1) ) {
197 outputchar(c2,2,alpha,usec_threshold,timediff,name1,name2,format);
199 if (!silent) write(port1,&c2,1);
201 if (rc==0 && quit_on_eof==1) {
205 if (rc<0 && errno!=EAGAIN) {
206 perror("read(port2)");
211 /* check for exceptions (sockets closed, broken, etc) */
212 if (FD_ISSET(port1, &efds)) {
213 /* I can't remember right now what to actually
214 check for on a fd exception, so we'll just quit */
215 fprintf(stderr,"\nException on port1\n");
218 if (FD_ISSET(port2, &efds)) {
219 /* I can't remember right now what to actually
220 check for on a fd exception, so we'll just quit */
221 fprintf(stderr,"\nException on port2\n");
232 fprintf(stderr,"sersniff v%s\n"
235 "sersniff [-h] [-i DEV | -l PORT] [-o DEV | -c HOST:PORT] [-b BAUD] [-w USEC]\n"
237 "-x Show hex characters instead of alpha\n"
238 "-f PRINTF_OPTS printf style options for printing hex characters\n"
239 " when '-x' switch is given (default \"<%%02hX>\")\n"
240 "-i DEVICE Port 1 device (defaults to /dev/ttyS0). Use host:port for\n"
242 "-1 PORT1_NAME Port 1 name to be printed (defaults to 'Port1')\n"
243 "-o DEVICE Port 2 device (defaults to /dev/ttyS1). Use :port for TCP.\n"
244 "-2 PORT2_NAME Port 2 name to be printed (defaults to 'Port2')\n"
245 "-b BAUD Baud rate (Defaults to 19200)\n"
246 "-n No port configuration (do not set BAUD or change settings)\n"
247 "-w USECS How many microsecs to wait before reporting a delay\n"
249 "-s Silent - don't pass data from port1 <=> port2,\n"
250 " just display what we see from them.\n"
251 "-z Don't quit when an EOF character is received.\n"
256 int main(int argc, char *argv[])
266 char *connecthost=NULL, *tmpchr=NULL;
270 long usec_threshold=USEC;
275 while ((optret=getopt(argc,argv,"hxsnzi:l:o:c:b:w:1:2:f:"))!=EOF) {
277 case '?': case 'h': case ':':
283 usec_threshold=atoi(optarg);
286 if ((tmpchr=strchr(optarg, ':'))!=NULL &&
287 (strchr(optarg, '/')==NULL)) {
289 listenport=atoi(++tmpchr);
295 if ((tmpchr=strchr(optarg, ':'))!=NULL &&
296 (strchr(optarg, '/')==NULL)) {
298 connectport=atoi(++tmpchr);
299 connecthost=strdup(optarg);
311 if (strstr(optarg,speed_str[speed])==optarg) {
312 baud=speed_num[speed];
317 fprintf(stderr,"Unsupported Baud: '%s'\n",
326 name1=strdup(optarg);
329 name2=strdup(optarg);
332 format=strdup(optarg);
340 /* Default settings */
341 if (!dev1 && !listenport) dev1=strdup("/dev/ttyS0");
342 if (!name1 && !listenport) name1=strdup("Port1");
343 if (!dev2 && !connecthost) dev2=strdup("/dev/ttyS1");
344 if (!name2 && !connecthost) name2=strdup("Port2");
345 if (baud==B0) baud=B19200;
346 if (!format) format=strdup("0x%02hX");
350 port1=openport(dev1, baud, setup_port);
352 disp_outputstatus("Waiting for connection to TCP port.");
353 port1=listensock(listenport);
357 port2=openport(dev2, baud, setup_port);
359 disp_outputstatus("Connecting to TCP port.");
360 port2=opensock(connecthost, connectport);
363 if (port1 < 0 || port2 < 0) {
364 fprintf(stderr,"Argh. An open failed!\n");
368 mainloop(port1, port2, silent, show_alpha, quit_on_eof, usec_threshold, name1, name2, format);
371 if (dev1) free(dev1);
372 if (dev2) free(dev2);
373 if (connecthost) free(connecthost);