From Martin Mathieson:
[obnox/wireshark/wip.git] / wiretap / catapult_dct2000.c
1 /* catapult_dct2000.c
2  *
3  * $Id: netmon.c 15534 2005-08-25 21:29:54Z ulfl $
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <errno.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 #include "wtap-int.h"
32 #include "file_wrappers.h"
33 #include "buffer.h"
34
35 #include "catapult_dct2000.h"
36
37 #define MAX_FIRST_LINE_LENGTH      1024
38 #define MAX_TIMESTAMP_LINE_LENGTH  100
39 #define MAX_LINE_LENGTH            32000
40 #define MAX_SUBSECOND_DECIMALS     4
41 #define MAX_CONTEXT_NAME           64
42 #define MAX_PROTOCOL_NAME          64
43 #define MAX_SECONDS_CHARS          16
44 #define MAX_PORT_DIGITS            2
45 #define AAL_HEADER_CHARS           12
46
47 /* TODO:
48    - support for x.25?
49 */
50
51 /* 's' or 'r' of a packet as read from .out file */
52 typedef enum packet_direction_t
53 {
54     sent,
55     received
56 } packet_direction_t;
57
58
59 typedef struct
60 {
61     gchar *before_time;
62     gchar *after_time;
63 } line_prefix_info_t;
64
65 /*******************************************************************/
66 /* Information stored external to a file (wtap) needed for dumping */
67 typedef struct dct2000_file_externals
68 {
69     /* Buffer to hold first line, including magic and format number */
70     gchar firstline[MAX_FIRST_LINE_LENGTH];
71     gint  firstline_length;
72
73     /* Buffer to hold second line with formatted file creation data/time */
74     gchar secondline[MAX_TIMESTAMP_LINE_LENGTH];
75     gint  secondline_length;
76
77     /* Hash table to store text prefix data part of displayed packets.
78        Records (file offset -> pre-data-prefix-string)
79        N.B. This is only needed for dumping
80     */
81     GHashTable *line_header_prefixes_table;
82 } dct2000_file_externals_t;
83
84 /* This global table maps wtap -> file_external structs */
85 static GHashTable *file_externals_table = NULL;
86
87
88 /***********************************************************/
89 /* Transient data used for parsing                         */
90
91 /* Buffer to hold a single text line read from the file */
92 static gchar linebuff[MAX_LINE_LENGTH];
93
94 /* Buffer for separate AAL header */
95 static gchar aal_header_chars[AAL_HEADER_CHARS];
96
97 /* 'Magic number' at start of Catapult DCT2000 .out files. */
98 static const gchar catapult_dct2000_magic[] = "Session Transcript";
99
100 /* Context name + port that the packet was captured at */
101 static gchar context_name[MAX_CONTEXT_NAME];
102 static guint8 context_port;
103
104 /* The DCT2000 protocol name of the packet */
105 static gchar protocol_name[MAX_PROTOCOL_NAME];
106
107 /*************************************************/
108 /* Preference state (shared with stub protocol). */
109 /* Set to FALSE to get better use out of other   */
110 /* wiretap applications (mergecap, editcap)      */
111 gboolean catapult_dct2000_board_ports_only = FALSE;
112
113 /************************************************************/
114 /* Functions called from wiretap                            */
115 static gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info,
116                                       long *data_offset);
117 static gboolean catapult_dct2000_seek_read(wtap *wth, long seek_off,
118                                            union wtap_pseudo_header *pseudo_header,
119                                            guchar *pd, int length,
120                                            int *err, gchar **err_info);
121 static void catapult_dct2000_close(wtap *wth);
122
123 static gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
124                                       const union wtap_pseudo_header *pseudo_header,
125                                       const guchar *pd, int *err);
126 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh, int *err);
127
128
129 /************************************************************/
130 /* Private helper functions                                 */
131 static gboolean read_new_line(FILE_T fh, long *offset, gint *length);
132 static gboolean parse_line(gint length, gint *seconds, gint *useconds,
133                            long *before_time_offset, long *after_time_offset,
134                            long *data_offset,
135                            gint *data_chars,
136                            packet_direction_t *direction,
137                            int *encap,
138                            gboolean seek_read);
139 static guchar hex_from_char(gchar c);
140 static gchar char_from_hex(guchar hex);
141
142 static void set_pseudo_header_info(wtap *wth,
143                                    int pkt_encap,
144                                    long file_offset,
145                                    union wtap_pseudo_header *pseudo_header,
146                                    gint length,
147                                    packet_direction_t direction);
148 static void set_aal_info(union wtap_pseudo_header *pseudo_header, gint length,
149                          packet_direction_t direction);
150 static void set_isdn_info(union wtap_pseudo_header *pseudo_header,
151                           packet_direction_t direction);
152 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
153                          packet_direction_t direction);
154
155
156 static gint prefix_equal(gconstpointer v, gconstpointer v2);
157 static guint prefix_hash_func(gconstpointer v);
158 static gboolean get_file_time_stamp(time_t *secs, guint32 *usecs);
159 static void free_header_prefix(void *data);
160
161
162
163 /********************************************/
164 /* Open file                                */
165 /********************************************/
166 int catapult_dct2000_open(wtap *wth, int *err, gchar **err_info _U_)
167 {
168     long    offset = 0;
169     time_t  timestamp;
170     guint32 usecs;
171     dct2000_file_externals_t *file_externals;
172
173     /* Clear errno before reading from the file */
174     errno = 0;
175
176
177     /*********************************************************************/
178     /* Need entry in file_externals table                                */
179
180     /* Create file externals table if it doesn't yet exist */
181     if (file_externals_table == NULL)
182     {
183         file_externals_table = g_hash_table_new_full(prefix_hash_func, prefix_equal, NULL, g_free);
184     }
185
186     /* Allocate a new file_externals structure */
187     file_externals = g_malloc(sizeof(dct2000_file_externals_t));
188     memset((void*)file_externals, '\0', sizeof(dct2000_file_externals_t));
189
190     /* Add an entry into the table for this wtap structure */
191     g_hash_table_insert(file_externals_table,
192                         (void*)wth, (void*)file_externals);
193
194
195     /********************************************************************/
196     /* First line needs to contain at least as many characters as magic */
197
198     read_new_line(wth->fh, &offset, &(file_externals->firstline_length));
199     if ((size_t)file_externals->firstline_length < strlen(catapult_dct2000_magic))
200     {
201         return 0;
202     }
203
204     /* This file is not for us if it doesn't match our signature */
205     if (memcmp(catapult_dct2000_magic, linebuff, strlen(catapult_dct2000_magic)) != 0)
206     {
207         return 0;
208     }
209
210     /* Copy this first line into buffer so could write out later */
211     strncpy(file_externals->firstline, linebuff, file_externals->firstline_length);
212
213
214     /***********************************************************/
215     /* Second line contains file timestamp                     */
216     /* Store this offset in in wth->capture->catapult_dct2000  */
217
218     read_new_line(wth->fh, &offset, &(file_externals->secondline_length));
219     if (!get_file_time_stamp(&timestamp, &usecs))
220     {
221         /* Give up if time wasn't valid */
222         return 0;
223     }
224
225     wth->capture.catapult_dct2000 = g_malloc(sizeof(catapult_dct2000_t));
226     wth->capture.catapult_dct2000->start_secs = timestamp;
227     wth->capture.catapult_dct2000->start_usecs = usecs;
228
229     /* Copy this second line into buffer so could write out later */
230     strncpy(file_externals->secondline, linebuff, file_externals->secondline_length);
231
232
233     /************************************************************/
234     /* File is for us. Fill in details so packets can be read   */
235
236     /* Set our file type */
237     wth->file_type = WTAP_FILE_CATAPULT_DCT2000;
238
239     /* Use our own encapsulation to send all packets to our stub dissector */
240     wth->file_encap = WTAP_ENCAP_CATAPULT_DCT2000;
241
242     /* Callbacks for reading operations */
243     wth->subtype_read = catapult_dct2000_read;
244     wth->subtype_seek_read = catapult_dct2000_seek_read;
245     wth->subtype_close = catapult_dct2000_close;
246
247     /* Choose microseconds (have 4 decimal places...) */
248     wth->tsprecision = WTAP_FILE_TSPREC_USEC;
249
250
251     /**********************************************/
252     /* (Re)-initialise line_header_prefixes_table */
253         if (file_externals->line_header_prefixes_table)
254     {
255                 g_hash_table_destroy(file_externals->line_header_prefixes_table);
256     }
257     /* Values (line prefix strings) will be automatically freed... */
258     file_externals->line_header_prefixes_table =
259         g_hash_table_new_full(prefix_hash_func, prefix_equal, NULL, free_header_prefix);
260
261     *err = errno;
262     return 1;
263 }
264
265
266 /**************************************************/
267 /* Read function.                                 */
268 /* Look for and read the next usable packet       */
269 /* - return TRUE and details if found             */
270 /**************************************************/
271 gboolean catapult_dct2000_read(wtap *wth, int *err, gchar **err_info _U_,
272                                long *data_offset)
273 {
274     long offset = wth->data_offset;
275     long dollar_offset, before_time_offset, after_time_offset;
276     packet_direction_t direction;
277     int encap;
278
279     /* Find wtap external structure for this wtap */
280     dct2000_file_externals_t *file_externals =
281         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table, wth);
282
283     /* Search for a line containing a usable board-port frame */
284     while (1)
285     {
286         int length, seconds, useconds, data_chars;
287         long this_offset = offset;
288
289         /* Are looking for first packet after 2nd line */
290         if (wth->data_offset == 0)
291         {
292             this_offset += (file_externals->firstline_length+1+
293                             file_externals->secondline_length+1);
294         }
295
296         /* Clear errno before reading from the file */
297         errno = 0;
298
299         /* Read a new line from file into linebuff */
300         if (read_new_line(wth->fh, &offset, &length) == FALSE)
301         {
302             /* Get out when no more lines to be read */
303             break;
304         }
305
306         /* Try to parse the line as a message */
307         if (parse_line(length, &seconds, &useconds,
308                        &before_time_offset, &after_time_offset,
309                        &dollar_offset,
310                        &data_chars, &direction, &encap, FALSE))
311         {
312             guchar *frame_buffer;
313             int n;
314             int stub_offset = 0;
315             line_prefix_info_t *line_prefix_info;
316             char timestamp_string[32];
317             sprintf(timestamp_string, "%d.%04d", seconds, useconds/100);
318
319             /* All packets go to Catapult DCT2000 stub dissector */
320             wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
321
322             /* Set data_offset to the beginning of the line we're returning.
323                This will be the seek_off parameter when this frame is re-read.
324             */
325             *data_offset = this_offset;
326
327             /* This is the position in the file where the next _read() will be called from */
328             wth->data_offset = this_offset + length + 1;
329
330             /* Fill in timestamp (capture base + packet offset) */
331             wth->phdr.ts.secs = wth->capture.catapult_dct2000->start_secs + seconds;
332             if ((wth->capture.catapult_dct2000->start_usecs + useconds) >= 1000000)
333             {
334                 wth->phdr.ts.secs++;
335             }
336             wth->phdr.ts.nsecs =
337                 ((wth->capture.catapult_dct2000->start_usecs + useconds) % 1000000) *1000;
338
339             /* Get buffer pointer ready */
340             buffer_assure_space(wth->frame_buffer,
341                                 strlen(context_name)+1 +  /* Context name */
342                                 1 +                       /* port */
343                                 strlen(protocol_name)+1 + /* Protocol name */
344                                 1 +                       /* direction */
345                                 1 +                       /* encap */
346                                 (data_chars/2));
347             frame_buffer = buffer_start_ptr(wth->frame_buffer);
348
349
350             /*********************/
351             /* Write stub header */
352
353             /* Context name */
354             strcpy(frame_buffer, context_name);
355             stub_offset += (strlen(context_name) + 1);
356
357             /* Context port number */
358             frame_buffer[stub_offset] = context_port;
359             stub_offset++;
360
361             /* Timestamp within file */
362             strcpy(&frame_buffer[stub_offset], timestamp_string);
363             stub_offset += (strlen(timestamp_string) + 1);
364
365             /* Protocol name */
366             strcpy(&frame_buffer[stub_offset], protocol_name);
367             stub_offset += (strlen(protocol_name) + 1);
368
369             /* Direction */
370             frame_buffer[stub_offset] = direction;
371             stub_offset++;
372
373             /* Encap */
374             frame_buffer[stub_offset] = (guint8)encap;
375             stub_offset++;
376
377             /* Binary data length is half bytestring length + stub header */
378             wth->phdr.len = data_chars/2 + stub_offset;
379             wth->phdr.caplen = data_chars/2 + stub_offset;
380
381
382             /*************************/
383             /* Copy data into buffer */
384             for (n=0; n <= data_chars; n+=2)
385             {
386                 frame_buffer[stub_offset + n/2] =
387                     (hex_from_char(linebuff[dollar_offset+n]) << 4) |
388                      hex_from_char(linebuff[dollar_offset+n+1]);
389             }
390             
391             /* Store the packet prefix in the hash table */
392             line_prefix_info = g_malloc(sizeof(line_prefix_info_t));
393
394             line_prefix_info->before_time = g_malloc(before_time_offset+1);
395             strncpy(line_prefix_info->before_time, linebuff, before_time_offset);
396             line_prefix_info->before_time[before_time_offset] = '\0';
397
398             line_prefix_info->after_time = g_malloc(dollar_offset - after_time_offset);
399             strncpy(line_prefix_info->after_time, linebuff+after_time_offset,
400                    dollar_offset - after_time_offset);
401             line_prefix_info->after_time[dollar_offset - after_time_offset-1] = '\0';
402
403             /* Add entry into table */
404             g_hash_table_insert(file_externals->line_header_prefixes_table,
405                                 (void*)this_offset, line_prefix_info);
406
407
408             /* Set pseudo-header if necessary */
409             set_pseudo_header_info(wth, encap, this_offset, &wth->pseudo_header,
410                                    data_chars/2, direction);
411
412             /* OK, we have packet details to return */
413             *err = errno;
414             return TRUE;
415         }
416     }
417
418     /* No packet details to return... */
419     *err = errno;
420     return FALSE;
421 }
422
423
424 /**************************************************/
425 /* Read & seek function.                          */
426 /**************************************************/
427 static gboolean
428 catapult_dct2000_seek_read(wtap *wth, long seek_off,
429                            union wtap_pseudo_header *pseudo_header, guchar *pd,
430                            int length, int *err, gchar **err_info)
431 {
432     long offset = wth->data_offset;
433     long dollar_offset, before_time_offset, after_time_offset;
434     packet_direction_t direction;
435     int encap;
436     int seconds, useconds, data_chars;
437
438     /* Reset errno */
439     *err = errno = 0;
440
441     /* Seek to beginning of packet */
442     if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
443     {
444         return FALSE;
445     }
446
447     /* Re-read whole line (this should succeed) */
448     if (read_new_line(wth->random_fh, &offset, &length) == FALSE)
449     {
450         return FALSE;
451     }
452
453     /* Try to parse this line again (should succeed as re-reading...) */
454     if (parse_line(length, &seconds, &useconds,
455                    &before_time_offset, &after_time_offset,
456                    &dollar_offset,
457                    &data_chars, &direction, &encap, TRUE))
458     {
459         int n;
460         int stub_offset = 0;
461         char timestamp_string[32];
462         sprintf(timestamp_string, "%d.%04d", seconds, useconds/100);
463
464         /* Make sure all packets go to catapult dct2000 dissector */
465         wth->phdr.pkt_encap = WTAP_ENCAP_CATAPULT_DCT2000;
466
467
468         /*********************/
469         /* Write stub header */
470
471         strcpy(pd, context_name);
472         stub_offset += (strlen(context_name) + 1);
473
474         /* Context port number */
475         pd[stub_offset] = context_port;
476         stub_offset++;
477
478         /* Timestamp within file */
479         strcpy(&pd[stub_offset], timestamp_string);
480         stub_offset += (strlen(timestamp_string) + 1);
481
482         /* Protocol name */
483         strcpy(&pd[stub_offset], protocol_name);
484         stub_offset += (strlen(protocol_name) + 1);
485
486         /* Direction */
487         pd[stub_offset] = direction;
488         stub_offset++;
489
490         /* Encap */
491         pd[stub_offset] = encap;
492         stub_offset++;
493
494         /********************************/
495         /* Copy packet data into buffer */
496         for (n=0; n <= data_chars; n+=2)
497         {
498             pd[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) |
499                                      hex_from_char(linebuff[dollar_offset+n+1]);
500         }
501
502         /* Set packet pseudo-header if necessary */
503         set_pseudo_header_info(wth, encap, seek_off, pseudo_header, data_chars/2, direction);
504
505         *err = errno = 0;
506         return TRUE;
507     }
508
509     /* If get here, must have failed */
510     *err = errno;
511     *err_info = g_strdup_printf("catapult dct2000: seek_read failed to read/parse "
512                                 "line at position %ld", seek_off);
513     return FALSE;
514 }
515
516
517 /******************************************/
518 /* Free dct2000-specific capture info     */
519 /******************************************/
520 void catapult_dct2000_close(wtap *wth)
521 {
522     /* Destroy line prefix table for this entry */
523     dct2000_file_externals_t *file_externals =
524         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table, wth);
525     g_hash_table_destroy(file_externals->line_header_prefixes_table);
526
527     /* And remove the entry itself */
528     g_hash_table_remove(file_externals_table, (void*)wth);
529
530     /* Also free this capture info */
531     g_free(wth->capture.catapult_dct2000);
532 }
533
534
535
536
537 /***************************/
538 /* Dump functions          */
539 /***************************/
540
541 /*****************************************************/
542 /* The file that we are writing to has been opened.  */
543 /* Set other dump callbacks.                         */
544 /*****************************************************/
545 gboolean catapult_dct2000_dump_open(wtap_dumper *wdh, gboolean cant_seek _U_, int *err _U_)
546 {
547     /* Fill in other dump callbacks */
548         wdh->subtype_write = catapult_dct2000_dump;
549         wdh->subtype_close = catapult_dct2000_dump_close;
550
551     return TRUE;
552 }
553
554 /*********************************************************/
555 /* Respond to queries about which encap types we support */
556 /* writing to.                                           */
557 /*********************************************************/
558 int catapult_dct2000_dump_can_write_encap(int encap)
559 {
560     switch (encap)
561     {
562         case WTAP_ENCAP_CATAPULT_DCT2000:
563             /* We support this */
564             return 0;
565         default:
566             return WTAP_ERR_UNSUPPORTED_ENCAP;
567     }
568 }
569
570
571 /*****************************************/
572 /* Write a single packet out to the file */
573 /*****************************************/
574 gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
575                                const union wtap_pseudo_header *pseudo_header,
576                                const guchar *pd, int *err _U_)
577 {
578     guint32 n;
579     line_prefix_info_t *prefix = NULL;
580     gchar time_string[16];
581
582     /******************************************************/
583     /* Look up the file_externals structure for this file */
584     /* Find wtap external structure for this wtap */
585     dct2000_file_externals_t *file_externals =
586         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table,
587                                                        pseudo_header->dct2000.wth);
588
589     if (wdh->dump.dct2000 == NULL)
590     {
591         /* Allocate the dct2000-specific dump structure */
592         wdh->dump.dct2000 = g_malloc(sizeof(catapult_dct2000_t));
593
594         /* Write out saved first line */
595         fwrite(file_externals->firstline, 1, file_externals->firstline_length, wdh->fh);
596         fwrite("\n", 1, 1, wdh->fh);
597
598         /* Also write out saved second line with timestamp corresponding to the
599            opening time of the log.
600         */
601         fwrite(file_externals->secondline, 1, file_externals->secondline_length, wdh->fh);
602         fwrite("\n", 1, 1, wdh->fh);
603
604         /* Allocate the dct2000-specific dump structure */
605         wdh->dump.dct2000 = g_malloc(sizeof(catapult_dct2000_t));
606
607         /* Copy time of beginning of file */
608         wdh->dump.dct2000->start_time.secs =
609             pseudo_header->dct2000.wth->capture.catapult_dct2000->start_secs;
610         wdh->dump.dct2000->start_time.nsecs =
611             (pseudo_header->dct2000.wth->capture.catapult_dct2000->start_usecs * 1000);
612
613         /* Set flag do don't write header out again */
614         wdh->dump.dct2000->first_packet_written = TRUE;
615     }
616
617
618     /******************************************************************/
619     /* Write out this packet's prefix, including calculated timestamp */
620
621     /* Look up line data prefix using stored offset */
622     prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->line_header_prefixes_table,
623                                                       (void*)pseudo_header->dct2000.seek_off);
624
625     /* Write out text before timestamp */
626     fwrite(prefix->before_time, 1, strlen(prefix->before_time), wdh->fh);
627
628     /* Calculate time of this packet to write, relative to start of dump */
629     if (phdr->ts.nsecs >= wdh->dump.dct2000->start_time.nsecs)
630     {
631         g_snprintf(time_string, 16, "%ld.%04d",
632                  phdr->ts.secs - wdh->dump.dct2000->start_time.secs,
633                  (phdr->ts.nsecs - wdh->dump.dct2000->start_time.nsecs) / 100000);
634     }
635     else
636     {
637         g_snprintf(time_string, 16, "%ld.%04u",
638                  phdr->ts.secs - wdh->dump.dct2000->start_time.secs-1,
639                  ((1000000000 + (phdr->ts.nsecs / 100000)) - (wdh->dump.dct2000->start_time.nsecs / 100000)) % 10000);
640     }
641
642     /* Write out the calculated timestamp */
643     fwrite(time_string, 1, strlen(time_string), wdh->fh);
644
645     /* Write out text between timestamp and start of hex data */
646     fwrite(prefix->after_time, 1, strlen(prefix->after_time), wdh->fh);
647
648
649     /****************************************************************/
650     /* Need to skip stub header at start of pd before we reach data */
651
652     /* Context name */
653     for (n=0; pd[n] != '\0'; n++);
654     n++;
655
656     /* Context port number */
657     n++;
658
659     /* Timestamp */
660     for (; pd[n] != '\0'; n++);
661     n++;
662
663     /* Protocol name */
664     for (; pd[n] != '\0'; n++);
665     n++;
666
667     /* Direction & encap */
668     n += 2;
669
670
671     /**************************************/
672     /* Remainder is encapsulated protocol */
673     fwrite("$", 1, 1, wdh->fh);
674
675     /* Each binary byte is written out as 2 hex string chars */ 
676     for (; n < phdr->len; n++)
677     {
678         gchar c[2];
679         c[0] = char_from_hex((guchar)(pd[n] >> 4));
680         c[1] = char_from_hex((guchar)(pd[n] & 0x0f));
681
682         /* Write both hex chars of byte together */
683         fwrite(c, 1, 2, wdh->fh);
684     }
685
686     /* End the line */
687     fwrite("\n", 1, 1, wdh->fh);
688
689     return TRUE;
690 }
691
692
693 /******************************************************/
694 /* Close a file we've been writing to.                */
695 /******************************************************/
696 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh _U_, int *err _U_)
697 {
698     return TRUE;
699 }
700
701
702
703
704 /****************************/
705 /* Private helper functions */
706 /****************************/
707
708 /**********************************************************************/
709 /* Read a new line from the file, starting at offset.                 */
710 /* - writes data to static var linebuff                               */
711 /* - on return 'offset' will point to the next position to read from  */
712 /* - return TRUE if this read is successful                           */
713 /**********************************************************************/
714 gboolean read_new_line(FILE_T fh, long *offset, gint *length)
715 {
716     char *result;
717
718     /* Read in a line */
719     result = file_gets(linebuff, MAX_LINE_LENGTH, fh);
720     if (result == NULL)
721     {
722         /* No characters found */
723         return FALSE;
724     }
725
726     /* Set length and offset.. */
727     *length = strlen(linebuff);
728     *offset = *offset + *length;
729
730     /* ...but don't want to include newline in line length */
731     if (linebuff[*length-1] == '\n')
732     {
733         linebuff[*length-1] = '\0';
734         *length = *length - 1;
735     }
736
737     return TRUE;
738 }
739
740
741 /**********************************************************************/
742 /* Parse a line from buffer, by identifying:                          */
743 /* - context, port and direction of packet                            */
744 /* - timestamp                                                        */
745 /* - data position and length                                         */
746 /* Return TRUE if this packet looks valid and can be displayed        */
747 /**********************************************************************/
748 gboolean parse_line(gint length, gint *seconds, gint *useconds,
749                     long *before_time_offset, long *after_time_offset,
750                     long *data_offset, gint *data_chars,
751                     packet_direction_t *direction,
752                     int *encap,
753                     gboolean seek_read)
754 {
755     int  n = 0;
756     int  port_digits = 0;
757     char port_number_string[MAX_PORT_DIGITS+1];
758     int  protocol_chars = 0;
759     char seconds_buff[MAX_SECONDS_CHARS];
760     int  seconds_chars;
761     char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS];
762     int  subsecond_decimals_chars;
763     gboolean atm_header_present = FALSE;
764
765     /* Read context name until find '.' */
766     for (n=0; linebuff[n] != '.' && (n < MAX_CONTEXT_NAME); n++)
767     {
768         if (!isalnum(linebuff[n]) && (linebuff[n] != '_'))
769         {
770             return FALSE;
771         }
772         context_name[n] = linebuff[n];
773     }
774
775     /* '.' must follow context name */
776     if (linebuff[n] != '.')
777     {
778         return FALSE;
779     }
780     context_name[n] = '\0';
781     /* Skip it */
782     n++;
783
784
785     /* Now read port number */
786     for (port_digits = 0;
787          (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS);
788          n++, port_digits++)
789     {
790         if (!isdigit(linebuff[n]))
791         {
792             return FALSE;
793         }
794         port_number_string[port_digits] = linebuff[n];
795     }
796
797     /* Slash char must follow port number */
798     if (linebuff[n] != '/')
799     {
800         return FALSE;
801     }
802     port_number_string[port_digits] = '\0';
803     context_port = atoi(port_number_string);
804     /* Skip it */
805     n++;
806
807
808     /* Now for the protocol name */
809     for (protocol_chars = 0;
810          (linebuff[n] != '/') && (protocol_chars <= MAX_PROTOCOL_NAME) &&
811          (n < MAX_LINE_LENGTH);
812          n++, protocol_chars++)
813     {
814         if (!isalnum(linebuff[n]) && linebuff[n] != '_')
815         {
816             return FALSE;
817         }
818         protocol_name[protocol_chars] = linebuff[n];
819     }
820     protocol_name[protocol_chars] = '\0';
821
822     /* Slash char must follow protocol name */
823     if (linebuff[n] != '/')
824     {
825         return FALSE;
826     }
827
828
829     /******************************************************************/
830     /* Now check whether we know how to use a packet of this protocol */
831
832     if ((strcmp(protocol_name, "ip") == 0)  || (strcmp(protocol_name, "sctp") == 0))
833     {
834         *encap = WTAP_ENCAP_RAW_IP;
835     }
836     else
837
838     /* For ATM protocols, we need to read the separate atm headerparse */
839     if ((strcmp(protocol_name, "fp") == 0) ||
840         (strcmp(protocol_name, "fp_r4") == 0) ||
841         (strcmp(protocol_name, "fp_r5") == 0) ||
842         (strcmp(protocol_name, "fp_r6") == 0))
843     {
844         *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
845         atm_header_present = TRUE;
846     }
847
848     else
849     if (strcmp(protocol_name, "ppp") == 0)
850     {
851         *encap = WTAP_ENCAP_PPP;
852     }
853     else
854     if (strcmp(protocol_name, "isdn_l3") == 0)
855     {
856         /* Despite the name, this does seem to correspond to L2... */
857         *encap = WTAP_ENCAP_ISDN;
858     }
859     else
860     if (strcmp(protocol_name, "ethernet") == 0)
861     {
862         *encap = WTAP_ENCAP_ETHERNET;
863     }
864     else
865     if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
866         (strcmp(protocol_name, "saaluni_sscop") == 0))
867     {
868         *encap = DCT2000_ENCAP_SSCOP;
869     }
870     else
871     if (strcmp(protocol_name, "frelay_l2") == 0)
872     {
873         *encap = WTAP_ENCAP_FRELAY;
874     }
875     else
876     if (strcmp(protocol_name, "ss7_mtp2") == 0)
877     {
878         *encap = DCT2000_ENCAP_MTP2;
879     }
880     else
881     {
882         /* Only reject protocol if reading for the first time and preference
883            setting says board ports only.  This should not fail to read a
884            non board-port protocol on re-reading because the preference setting
885            has since changed...
886         */
887         if (catapult_dct2000_board_ports_only && !seek_read)
888         {
889             return FALSE;
890         }
891         else
892         {
893             /* Not a supported protocol/encap, but should show as raw data anyway */
894             *encap = DCT2000_ENCAP_UNHANDLED;
895         }
896     }
897
898
899     /* Find separate ATM header if necessary */
900     if (atm_header_present)
901     {
902         int header_chars_seen = 0;
903
904         /* Scan ahead to the next $ */
905         for (; (linebuff[n] != '$') && (n < MAX_LINE_LENGTH); n++);
906         /* Skip it */
907         n++;
908
909         /* Read consecutive hex chars into atm header buffer */
910         for (;
911              (isalnum(linebuff[n]) &&
912               (n < MAX_LINE_LENGTH) &&
913               (header_chars_seen < AAL_HEADER_CHARS));
914              n++, header_chars_seen++)
915         {
916             aal_header_chars[header_chars_seen] = linebuff[n];
917         }
918
919         if (header_chars_seen != AAL_HEADER_CHARS)
920         {
921             return FALSE;
922         }
923     }
924
925
926     /* Scan ahead to the next space */
927     for (; (linebuff[n] != ' ') && (n < MAX_LINE_LENGTH); n++);
928     /* Skip it */
929     n++;
930
931     /* Next character gives direction of message (must be 's' or 'r') */
932     if (linebuff[n] == 's')
933     {
934         *direction = sent;
935     }
936     else
937     if (linebuff[n] == 'r')
938     {
939         *direction = received;
940     }
941     else
942     {
943         return FALSE;
944     }
945
946
947     /*********************************************************************/
948     /* Find and read the timestamp                                       */
949
950     /* Now scan to the next digit, which should be the start of the timestamp */
951     for (; !isdigit(linebuff[n]) && (n < MAX_LINE_LENGTH); n++);
952
953     *before_time_offset = n;
954
955     /* Seconds */
956     for (seconds_chars = 0;
957          (linebuff[n] != '.') &&
958          (seconds_chars <= MAX_SECONDS_CHARS) &&
959          (n < MAX_LINE_LENGTH);
960          n++, seconds_chars++)
961     {
962         if (!isdigit(linebuff[n]))
963         {
964             return FALSE;
965         }
966         seconds_buff[seconds_chars] = linebuff[n];
967     }
968     /* Convert found value into number */
969     seconds_buff[seconds_chars] = '\0';
970     *seconds = atoi(seconds_buff);
971
972     /* The decimal point must follow the last of the seconds digits */
973     if (linebuff[n] != '.')
974     {
975         return FALSE;
976     }
977     /* Skip it */
978     n++;
979
980     /* Subsecond decimal digits (expect 4-digit accuracy) */
981     for (subsecond_decimals_chars = 0;
982          (linebuff[n] != ' ') &&
983          (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
984          (n < MAX_LINE_LENGTH);
985          n++, subsecond_decimals_chars++)
986     {
987         if (!isdigit(linebuff[n]))
988         {
989             return FALSE;
990         }
991         subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
992     }
993     /* Convert found value into microseconds */
994     subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
995     *useconds = atoi(subsecond_decimals_buff) * 100;
996
997     /* Space character must follow end of timestamp */
998     if (linebuff[n] != ' ')
999     {
1000         return FALSE;
1001     }
1002
1003     *after_time_offset = n;
1004
1005     /* Now skip ahead to find start of data (marked by '$') */
1006     for (; (linebuff[n] != '$') && (n < MAX_LINE_LENGTH); n++);
1007     /* Skip it */
1008     n++;
1009
1010     /* Set offset to data start within line */
1011     *data_offset = n;
1012
1013     /* Set number of chars that comprise the hex string protocol data */
1014     *data_chars = length - n;
1015
1016     /* Need to skip first byte (2 hex string chars) from ISDN messages.
1017        TODO: find out what this byte means...
1018     */
1019     if (*encap == WTAP_ENCAP_ISDN)
1020     {
1021         *data_offset += 2;
1022         *data_chars -= 2;
1023     }
1024
1025     return TRUE;
1026 }
1027
1028
1029 /**************************************************************/
1030 /* Set pseudo-header info depending upon packet encapsulation */
1031 /**************************************************************/
1032 void set_pseudo_header_info(wtap *wth,
1033                             int pkt_encap,
1034                             long file_offset,
1035                             union wtap_pseudo_header *pseudo_header,
1036                             gint length,
1037                             packet_direction_t direction)
1038 {
1039     pseudo_header->dct2000.seek_off = file_offset;
1040     pseudo_header->dct2000.wth = wth;
1041
1042     switch (pkt_encap)
1043     {
1044         case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1045             set_aal_info(pseudo_header, length, direction);
1046             break;
1047         case WTAP_ENCAP_ISDN:
1048             set_isdn_info(pseudo_header, direction);
1049             break;
1050         case WTAP_ENCAP_PPP:
1051             set_ppp_info(pseudo_header, direction);
1052             break;
1053
1054         default:
1055             /* Other supported types don't need to set anything here... */
1056             break;
1057     }
1058 }
1059
1060
1061 /*********************************************/
1062 /* Fill in atm pseudo-header with known info */
1063 /*********************************************/
1064 void set_aal_info(union wtap_pseudo_header *pseudo_header, gint length,
1065                   packet_direction_t direction)
1066 {
1067     /* 'aal_head_chars' has this format (for AAL2 at least):
1068        Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1069        Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1070        Channel Identifier (8 bits) | ...
1071     */
1072
1073     /* Indicate that this is a reassembled PDU */
1074     pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1075
1076     /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1077        TODO: Can we infer the correct value here?
1078        Meanwhile, just use the direction to make them distinguishable...
1079     */
1080     pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1081
1082     /* Assume always AAL2 for FP */
1083     pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1084
1085     pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UNKNOWN;
1086     pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1087
1088     /* vpi is 8 bits (2nd & 3rd nibble) */
1089     pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1090         ((hex_from_char(aal_header_chars[1]) << 4) |
1091           hex_from_char(aal_header_chars[2]));
1092
1093     /* vci is next 16 bits */
1094     pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1095         ((hex_from_char(aal_header_chars[3]) << 12) |
1096          (hex_from_char(aal_header_chars[4]) << 8) |
1097          (hex_from_char(aal_header_chars[5]) << 4) |
1098          hex_from_char(aal_header_chars[6]));
1099
1100     /* 0 means we don't know how many cells the frame comprises. */
1101     pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1102
1103     pseudo_header->dct2000.inner_pseudo_header.atm.aal5t_u2u = 0;
1104     pseudo_header->dct2000.inner_pseudo_header.atm.aal5t_len = length;
1105     pseudo_header->dct2000.inner_pseudo_header.atm.aal5t_chksum = 0;
1106 }
1107
1108
1109 /**********************************************/
1110 /* Fill in isdn pseudo-header with known info */
1111 /**********************************************/
1112 void set_isdn_info(union wtap_pseudo_header *pseudo_header,
1113                    packet_direction_t direction)
1114 {
1115     /* This field is used to set the 'Source' and 'Destination' columns to
1116        'User' or 'Network'. If we assume that we're simulating the network,
1117        treat Received messages as being destined for the network.
1118     */
1119     pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1120
1121     /* This corresponds to the circuit ID.  0 is treated as LAPD,
1122        everything else would be treated as a B-channel
1123     */
1124     pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1125 }
1126
1127
1128 /*********************************************/
1129 /* Fill in ppp pseudo-header with known info */
1130 /*********************************************/
1131 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
1132                          packet_direction_t direction)
1133 {
1134     /* Set direction. */
1135     pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1136 }
1137
1138
1139 /********************************************************/
1140 /* Return hex nibble equivalent of hex string character */
1141 /********************************************************/
1142 guchar hex_from_char(gchar c)
1143 {
1144     if ((c >= '0') && (c <= '9'))
1145     {
1146         return c - '0';
1147     }
1148
1149     if ((c >= 'a') && (c <= 'f'))
1150     {
1151         return 0x0a + (c - 'a');
1152     }
1153
1154     /* Not a valid hex string character */
1155     return 0xff;
1156 }
1157
1158
1159 /********************************************************/
1160 /* Return character corresponding to hex nibble value   */
1161 /********************************************************/
1162 gchar char_from_hex(guchar hex)
1163 {
1164     static char hex_lookup[16] =
1165     { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1166
1167     if (hex > 15)
1168     {
1169         return '?';
1170     }
1171
1172     return hex_lookup[hex];
1173 }
1174
1175
1176 /********************************************/
1177 /* Equality test for line-prefix hash table */
1178 /********************************************/
1179 gint prefix_equal(gconstpointer v, gconstpointer v2)
1180 {
1181     return (v == v2);
1182 }
1183
1184
1185 /********************************************/
1186 /* Hash function for line-prefix hash table */
1187 /********************************************/
1188 guint prefix_hash_func(gconstpointer v)
1189 {
1190     /* Just use pointer itself (is actually byte offset of line in file) */ 
1191     return (guint)v;
1192 }
1193
1194
1195 /************************************************************************/
1196 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1197 /* Set secs and usecs as output                                         */
1198 /* Return FALSE if no valid time can be read                            */
1199 /************************************************************************/
1200 gboolean get_file_time_stamp(time_t *secs, guint32 *usecs)
1201 {
1202     int n;
1203     struct tm tm;
1204     #define MAX_MONTH_LETTERS 9
1205     char month[MAX_MONTH_LETTERS+1];
1206
1207     int day, year, hour, minute, second;
1208     int scan_found;
1209
1210     /* If line longer than expected, file is probably not correctly formatted */
1211     if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
1212     {
1213         return FALSE;
1214     }
1215
1216     /**************************************************************/
1217     /* First is month. Read until get a space following the month */
1218     for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++)
1219     {
1220         month[n] = linebuff[n];
1221     }
1222     month[n] = '\0';
1223
1224     if      (strcmp(month, "January"  ) == 0)  tm.tm_mon = 0;
1225     else if (strcmp(month, "February" ) == 0)  tm.tm_mon = 1;
1226     else if (strcmp(month, "March"    ) == 0)  tm.tm_mon = 2;
1227     else if (strcmp(month, "April"    ) == 0)  tm.tm_mon = 3;
1228     else if (strcmp(month, "May"      ) == 0)  tm.tm_mon = 4;
1229     else if (strcmp(month, "June"     ) == 0)  tm.tm_mon = 5;
1230     else if (strcmp(month, "July"     ) == 0)  tm.tm_mon = 6;
1231     else if (strcmp(month, "August"   ) == 0)  tm.tm_mon = 7;
1232     else if (strcmp(month, "September") == 0)  tm.tm_mon = 8;
1233     else if (strcmp(month, "October"  ) == 0)  tm.tm_mon = 9;
1234     else if (strcmp(month, "November" ) == 0)  tm.tm_mon = 10;
1235     else if (strcmp(month, "December" ) == 0)  tm.tm_mon = 11;
1236     else
1237     {
1238         /* Give up if not found a properly-formatted date */
1239         return FALSE;
1240     }
1241     /* Skip space char */
1242     n++;
1243
1244     /********************************************************/
1245     /* Scan for remaining numerical fields                  */
1246     scan_found = sscanf(linebuff+n, "%d, %d     %d:%d:%d.%u",
1247                         &day, &year, &hour, &minute, &second, usecs);
1248     if (scan_found != 6)
1249     {
1250         /* Give up if not all found */
1251         return FALSE;
1252     }
1253
1254     /******************************************************/
1255     /* Fill in remaining fields and return it in a time_t */
1256     tm.tm_year = year - 1900;
1257     tm.tm_mday = day;
1258     tm.tm_hour = hour;
1259     tm.tm_min = minute;
1260     tm.tm_sec = second;
1261     tm.tm_isdst = -1;    /* daylight saving time info not known */
1262
1263     /* Get seconds from this time */
1264     *secs = mktime(&tm);
1265
1266     /* Multiply 4 digits given to get micro-seconds */
1267     *usecs = *usecs * 100;
1268
1269     return TRUE;
1270 }
1271
1272 /* Free the data allocated inside a line_prefix_info_t */
1273 void free_header_prefix(void *data)
1274 {
1275     line_prefix_info_t *info = (line_prefix_info_t*)data;
1276
1277     /* Free the strings inside */
1278     g_free(info->before_time);
1279     g_free(info->after_time);
1280
1281     /* And the structure itself */
1282     g_free(info);
1283 }
1284