]> the.earth.li Git - sersniff.git/blob - sersniff.c
bb6664fb95f928169e4461481ef2179b7f4413a6
[sersniff.git] / sersniff.c
1 /*
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
8
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.
13
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.
18
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
23
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
28 */
29
30 #define VERSION "0.0.4"
31
32 #include <stdio.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <termios.h>
36 #include <unistd.h>
37 #include <stdlib.h>
38 #include <time.h>
39 #include <sys/time.h>
40 #include <errno.h>
41
42 #include "disp_basic.h"
43 #include "tcp.h"
44
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 };
49
50 int openport(const char *device, speed_t baud, int setup)
51 {
52         int filedes;
53         struct termios serparams;
54
55         if ((filedes=open(device, O_RDWR | O_NONBLOCK))==-1) {
56                 /* Can't open device */
57                 fprintf(stderr,"%s: ",device);
58                 perror(device);
59                 exit(1);
60         }
61
62         if (setup) {
63                 bzero(&serparams, sizeof(serparams));
64
65                 cfsetspeed(&serparams, baud);
66                 serparams.c_cflag |= CLOCAL | CS8 | CREAD;
67
68                 if (tcflush(filedes, TCIFLUSH)) {
69                         fprintf(stderr,"%s: ",device);
70                         perror("tcflush");
71                         exit(1);
72                 }
73                 if (tcsetattr(filedes, TCSANOW, &serparams)) {
74                         fprintf(stderr,"%s: ",device);
75                         perror("tcsetattr");
76                         exit(1);
77                 }
78         }
79
80         return filedes;
81 }
82
83 int closeport(int filedes)
84 {
85         /* Should remove any locks at this point */
86         return close(filedes);
87 }
88
89 /*
90    this returns the string for the character passed to it
91    It could be expanded in the future to maybe to string recognitions?
92 */
93 char *chardecide(unsigned char c, int alpha, char *format) {
94         static char result[256];
95
96         /* everyone should take up 5 characters */
97         if (alpha) {
98            if ((c < 32) | (c > 126)) {
99                 switch (c) {
100                         case 10:
101                                 sprintf(result,"<LF>");
102                                 break;
103                         case 13:
104                                 sprintf(result,"<CR>");
105                                 break;
106                         case 27:
107                                 sprintf(result,"<ESC>");
108                                 break;
109                         default:
110                                 snprintf(result,256,"<%02hX>",c);
111                                 break;
112                 }
113            } else {
114                 snprintf(result,256,"%c",c);
115            }
116         } else {
117            snprintf(result,256,format,c);
118         }
119         return result;
120 }
121
122 void outputchar(unsigned char c, int port, int alpha,
123                 long usec_threshold, long usec_waited, char *name1, char *name2, char *format)
124 {
125         char *todisplay;
126
127         todisplay=chardecide(c,alpha,format);
128         disp_outputstr(port, todisplay, usec_threshold, usec_waited, name1, name2);
129 }
130
131 void mainloop(int port1, int port2, int silent, int alpha, int quit_on_eof, long usec_threshold, char *name1, char *name2, char *format)
132 {
133         unsigned char c1, c2;
134         int rc;
135         fd_set rfds;
136         fd_set efds;
137         int biggestfd=0;
138         int fdret;
139         struct timeval before;
140         struct timeval after;
141         long timediff;
142         int quit=0;
143
144         /* need the largest fd for the select call */
145         biggestfd=port1 > port2 ? port1 : port2;
146         biggestfd++;
147
148         while (!quit) {
149                 /* reset the select set */
150                 FD_ZERO(&rfds);
151                 FD_ZERO(&efds);
152                 FD_SET(port1,&rfds);
153                 FD_SET(port1,&efds);
154                 FD_SET(port2,&rfds);
155                 FD_SET(port2,&efds);
156
157                 if (gettimeofday(&before,NULL)) {
158                         perror("gettimeofday");
159                         exit(1);
160                 }
161                 if ((fdret=select(biggestfd, &rfds, NULL, &efds, NULL))<0) {
162                         perror("select");
163                         exit(1);
164                 }
165                 if (gettimeofday(&after,NULL)) {
166                         perror("gettimeofday");
167                         exit(1);
168                 }
169
170                 /* get seconds difference */
171                 timediff=after.tv_sec-before.tv_sec;
172                 /* convert to micro seconds */
173                 timediff*=USEC;
174                 /* add difference in usecs */
175                 timediff+=after.tv_usec-before.tv_usec;
176                 
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);
181                                 timediff=0;
182                                 if (!silent) write(port2,&c1,1);
183                         }
184                         if (rc==0 && quit_on_eof==1) {
185                                 /* EOF? */
186                                 quit=1;
187                         }
188                         if (rc<0 && errno!=EAGAIN) {
189                                 perror("read(port1)");
190                                 exit(1);
191                         }
192                 }
193                 
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);
198                                 timediff=0;
199                                 if (!silent) write(port1,&c2,1);
200                         }       
201                         if (rc==0 && quit_on_eof==1) {
202                                 /* EOF? */
203                                 quit=1;
204                         }
205                         if (rc<0 && errno!=EAGAIN) {
206                                 perror("read(port2)");
207                                 exit(1);
208                         }
209                 }
210
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");
216                         quit=1;
217                 }
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");
222                         quit=1;
223                 }
224         }
225
226         closeport(port2);
227         closeport(port1);
228 }
229
230 void usage()
231 {
232         fprintf(stderr,"sersniff v%s\n"
233
234 "Usage:\n"
235 "sersniff [-h] [-i DEV | -l PORT] [-o DEV | -c HOST:PORT] [-b BAUD] [-w USEC]\n"
236 "-h             This help\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"
241 "                TCP.\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"
248 "                       (default is %d)\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"
252 ,VERSION,USEC);
253         exit(1);
254 }
255
256 int main(int argc, char *argv[])
257 {
258         int optret, speed;
259         int port1, port2;
260         char *dev1=NULL;
261         char *dev2=NULL;
262         char *name1=NULL;
263         char *name2=NULL;
264         int listenport=0;
265         int connectport;
266         char *connecthost=NULL, *tmpchr=NULL;
267         int show_alpha=1;
268         speed_t baud=B0;
269         int silent=0;
270         long usec_threshold=USEC;
271         int setup_port=1;
272         int quit_on_eof=1;
273         char *format=NULL;
274
275         while ((optret=getopt(argc,argv,"hxsnzi:l:o:c:b:w:1:2:f:"))!=EOF) {
276                 switch (optret) {
277                 case '?': case 'h': case ':':
278                         usage();
279                 case 's':
280                         silent=1;
281                         break;
282                 case 'w':
283                         usec_threshold=atoi(optarg);
284                         break;
285                 case 'i':
286                         if ((tmpchr=strchr(optarg, ':'))!=NULL &&
287                             (strchr(optarg, '/')==NULL)) {
288                                 *tmpchr='\0';
289                                 listenport=atoi(++tmpchr);
290                         } else {
291                                 dev1=strdup(optarg);
292                         }
293                         break;
294                 case 'o':
295                         if ((tmpchr=strchr(optarg, ':'))!=NULL &&
296                             (strchr(optarg, '/')==NULL)) {
297                                 *tmpchr='\0';
298                                 connectport=atoi(++tmpchr);
299                                 connecthost=strdup(optarg);
300                         } else {
301                                 dev2=strdup(optarg);
302                         }
303                         break;
304                 case 'x':
305                         show_alpha=0;
306                         break;
307                 case 'b':
308                         for (speed=0;
309                              speed_str[speed];
310                              speed++) {
311                                 if (strstr(optarg,speed_str[speed])==optarg) {
312                                         baud=speed_num[speed];
313                                         break;
314                                 }
315                         }
316                         if (baud==B0) {
317                                 fprintf(stderr,"Unsupported Baud: '%s'\n",
318                                         optarg);
319                                 exit(1);
320                         }
321                         break;
322                 case 'n':
323                         setup_port=0;
324                         break;
325                 case '1':
326                         name1=strdup(optarg);
327                         break;
328                 case '2':
329                         name2=strdup(optarg);
330                         break;
331                 case 'f':
332                         format=strdup(optarg);
333                         break;
334                 case 'z':
335                         quit_on_eof=0;
336                         break;
337                 }
338         }
339
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");
347
348         disp_init();
349         if (dev1) {
350                 port1=openport(dev1, baud, setup_port);
351         } else {
352                 disp_outputstatus("Waiting for connection to TCP port.");
353                 port1=listensock(listenport);
354         }
355
356         if (dev2) {
357                 port2=openport(dev2, baud, setup_port);
358         } else {
359                 disp_outputstatus("Connecting to TCP port.");
360                 port2=opensock(connecthost, connectport);
361         }
362
363         if (port1 < 0 || port2 < 0) {
364                 fprintf(stderr,"Argh.  An open failed!\n");
365                 exit(1);
366         }
367
368         mainloop(port1, port2, silent, show_alpha, quit_on_eof, usec_threshold, name1, name2, format);
369
370         /* Clean up */
371         if (dev1) free(dev1);
372         if (dev2) free(dev2);
373         if (connecthost) free(connecthost);
374         disp_close();
375
376         return 0;
377 }