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