Add error checking for fwrites; replacement for one of patches provided in bug #1864
[obnox/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            256
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
574 static gboolean do_fwrite(const void *data, size_t size, size_t count, FILE *stream, int *err_p) {
575     size_t nwritten;
576
577     nwritten = fwrite(data, size, count, stream);
578     if (nwritten != count) {
579         if (nwritten == 0 && ferror(stream))
580             *err_p = errno;
581         else
582             *err_p = WTAP_ERR_SHORT_WRITE;
583         return FALSE;
584     }
585     return TRUE;
586 }
587
588 gboolean catapult_dct2000_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
589                                const union wtap_pseudo_header *pseudo_header,
590                                const guchar *pd, int *err)
591 {
592     guint32 n;
593     line_prefix_info_t *prefix = NULL;
594     gchar time_string[16];
595
596     /******************************************************/
597     /* Look up the file_externals structure for this file */
598     /* Find wtap external structure for this wtap */
599     dct2000_file_externals_t *file_externals =
600         (dct2000_file_externals_t*)g_hash_table_lookup(file_externals_table,
601                                                        pseudo_header->dct2000.wth);
602
603     if (wdh->dump.dct2000 == NULL)
604     {
605         /* Allocate the dct2000-specific dump structure */
606         wdh->dump.dct2000 = g_malloc(sizeof(catapult_dct2000_t));
607
608         /* Write out saved first line */
609         if (! do_fwrite(file_externals->firstline, 1, file_externals->firstline_length, wdh->fh, err))
610             return FALSE;
611         if (! do_fwrite("\n", 1, 1, wdh->fh, err))
612             return FALSE;
613
614         /* Also write out saved second line with timestamp corresponding to the
615            opening time of the log.
616         */
617         if (! do_fwrite(file_externals->secondline, 1, file_externals->secondline_length, wdh->fh, err))
618             return FALSE;
619         if (! do_fwrite("\n", 1, 1, wdh->fh, err))
620             return FALSE;
621
622         /* Allocate the dct2000-specific dump structure */
623         wdh->dump.dct2000 = g_malloc(sizeof(catapult_dct2000_t));
624
625         /* Copy time of beginning of file */
626         wdh->dump.dct2000->start_time.secs =
627             pseudo_header->dct2000.wth->capture.catapult_dct2000->start_secs;
628         wdh->dump.dct2000->start_time.nsecs =
629             (pseudo_header->dct2000.wth->capture.catapult_dct2000->start_usecs * 1000);
630
631         /* Set flag do don't write header out again */
632         wdh->dump.dct2000->first_packet_written = TRUE;
633     }
634
635
636     /******************************************************************/
637     /* Write out this packet's prefix, including calculated timestamp */
638
639     /* Look up line data prefix using stored offset */
640     prefix = (line_prefix_info_t*)g_hash_table_lookup(file_externals->packet_prefix_table,
641                                                       (const void*)&(pseudo_header->dct2000.seek_off));
642
643     /* Write out text before timestamp */
644     if (! do_fwrite(prefix->before_time, 1, strlen(prefix->before_time), wdh->fh, err))
645         return FALSE;
646
647     /* Calculate time of this packet to write, relative to start of dump */
648     if (phdr->ts.nsecs >= wdh->dump.dct2000->start_time.nsecs)
649     {
650         g_snprintf(time_string, 16, "%ld.%04d",
651                  (long)(phdr->ts.secs - wdh->dump.dct2000->start_time.secs),
652                  (phdr->ts.nsecs - wdh->dump.dct2000->start_time.nsecs) / 100000);
653     }
654     else
655     {
656         g_snprintf(time_string, 16, "%ld.%04u",
657                  (long)(phdr->ts.secs - wdh->dump.dct2000->start_time.secs-1),
658                  ((1000000000 + (phdr->ts.nsecs / 100000)) - (wdh->dump.dct2000->start_time.nsecs / 100000)) % 10000);
659     }
660
661     /* Write out the calculated timestamp */
662     if (! do_fwrite(time_string, 1, strlen(time_string), wdh->fh, err))
663         return FALSE;
664
665     /* Write out text between timestamp and start of hex data */
666     if (prefix->after_time == NULL)
667     {
668         if (! do_fwrite(" l ", 1, strlen(" l "), wdh->fh, err))
669             return FALSE;
670     }
671     else
672     {
673         if (! do_fwrite(prefix->after_time, 1, strlen(prefix->after_time), wdh->fh, err))
674             return FALSE;
675     }
676
677
678     /****************************************************************/
679     /* Need to skip stub header at start of pd before we reach data */
680
681     /* Context name */
682     for (n=0; pd[n] != '\0'; n++);
683     n++;
684
685     /* Context port number */
686     n++;
687
688     /* Timestamp */
689     for (; pd[n] != '\0'; n++);
690     n++;
691
692     /* Protocol name */
693     for (; pd[n] != '\0'; n++);
694     n++;
695
696     /* Variant number (as string) */
697     for (; pd[n] != '\0'; n++);
698     n++;
699
700     /* Outhdr (as string) */
701     for (; pd[n] != '\0'; n++);
702     n++;
703
704     /* Direction & encap */
705     n += 2;
706
707
708     /**************************************/
709     /* Remainder is encapsulated protocol */
710     if (! do_fwrite("$", 1, 1, wdh->fh, err))
711         return FALSE;
712
713     /* Each binary byte is written out as 2 hex string chars */ 
714     for (; n < phdr->len; n++)
715     {
716         gchar c[2];
717         c[0] = char_from_hex((guchar)(pd[n] >> 4));
718         c[1] = char_from_hex((guchar)(pd[n] & 0x0f));
719
720         /* Write both hex chars of byte together */
721         if (! do_fwrite(c, 1, 2, wdh->fh, err))
722             return FALSE;
723     }
724
725     /* End the line */
726     if (! do_fwrite("\n", 1, 1, wdh->fh, err))
727         return FALSE;
728
729     return TRUE;
730 }
731
732
733 /******************************************************/
734 /* Close a file we've been writing to.                */
735 /******************************************************/
736 static gboolean catapult_dct2000_dump_close(wtap_dumper *wdh _U_, int *err _U_)
737 {
738     return TRUE;
739 }
740
741
742
743
744 /****************************/
745 /* Private helper functions */
746 /****************************/
747
748 /**********************************************************************/
749 /* Read a new line from the file, starting at offset.                 */
750 /* - writes data to static var linebuff                               */
751 /* - on return 'offset' will point to the next position to read from  */
752 /* - return TRUE if this read is successful                           */
753 /**********************************************************************/
754 gboolean read_new_line(FILE_T fh, gint64 *offset, gint *length)
755 {
756     char *result;
757
758     /* Read in a line */
759     result = file_gets(linebuff, MAX_LINE_LENGTH, fh);
760     if (result == NULL)
761     {
762         /* No characters found */
763         return FALSE;
764     }
765
766     /* Set length and offset.. */
767     *length = strlen(linebuff);
768     *offset = *offset + *length;
769
770     /* ...but don't want to include newline in line length */
771     if (linebuff[*length-1] == '\n')
772     {
773         linebuff[*length-1] = '\0';
774         *length = *length - 1;
775     }
776
777     return TRUE;
778 }
779
780
781 /**********************************************************************/
782 /* Parse a line from buffer, by identifying:                          */
783 /* - context, port and direction of packet                            */
784 /* - timestamp                                                        */
785 /* - data position and length                                         */
786 /* Return TRUE if this packet looks valid and can be displayed        */
787 /**********************************************************************/
788 gboolean parse_line(gint line_length, gint *seconds, gint *useconds,
789                     long *before_time_offset, long *after_time_offset,
790                     long *data_offset, gint *data_chars,
791                     packet_direction_t *direction,
792                     int *encap)
793 {
794     int  n = 0;
795     int  port_digits = 0;
796     char port_number_string[MAX_PORT_DIGITS+1];
797     int  variant_digits = 0;
798     int  variant = 1;
799     int  protocol_chars = 0;
800     int  outhdr_chars = 0;
801
802     char seconds_buff[MAX_SECONDS_CHARS+1];
803     int  seconds_chars;
804     char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS+1];
805     int  subsecond_decimals_chars;
806     int  skip_first_byte = FALSE;
807
808     gboolean atm_header_present = FALSE;
809
810     /* Read context name until find '.' */
811     for (n=0; linebuff[n] != '.' && (n < MAX_CONTEXT_NAME) && (n+1 < line_length); n++)
812     {
813         if (!isalnum((guchar)linebuff[n]) && (linebuff[n] != '_'))
814         {
815             return FALSE;
816         }
817         context_name[n] = linebuff[n];
818     }
819     if (n == MAX_CONTEXT_NAME || (n+1 >= line_length))
820     {
821         return FALSE;
822     }
823
824     /* '.' must follow context name */
825     if (linebuff[n] != '.')
826     {
827         return FALSE;
828     }
829     context_name[n] = '\0';
830     /* Skip it */
831     n++;
832
833
834     /* Now read port number */
835     for (port_digits = 0;
836          (linebuff[n] != '/') && (port_digits <= MAX_PORT_DIGITS) && (n+1 < line_length);
837          n++, port_digits++)
838     {
839         if (!isdigit((guchar)linebuff[n]))
840         {
841             return FALSE;
842         }
843         port_number_string[port_digits] = linebuff[n];
844     }
845     if (port_digits > MAX_PORT_DIGITS || (n+1 >= line_length))
846     {
847         return FALSE;
848     }
849
850     /* Slash char must follow port number */
851     if (linebuff[n] != '/')
852     {
853         return FALSE;
854     }
855     port_number_string[port_digits] = '\0';
856     context_port = atoi(port_number_string);
857     /* Skip it */
858     n++;
859
860
861     /* Now for the protocol name */
862     for (protocol_chars = 0;
863          (linebuff[n] != '/') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length);
864          n++, protocol_chars++)
865     {
866         if (!isalnum((guchar)linebuff[n]) && linebuff[n] != '_')
867         {
868             return FALSE;
869         }
870         protocol_name[protocol_chars] = linebuff[n];
871     }
872     if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length)
873     {
874         /* If doesn't fit, fail rather than truncate */
875         return FALSE;
876     }
877     protocol_name[protocol_chars] = '\0';
878
879     /* Slash char must follow protocol name */
880     if (linebuff[n] != '/')
881     {
882         return FALSE;
883     }
884     /* Skip it */
885     n++;
886
887
888     /* Following the / is the variant number.  No digits indicate 1 */
889     for (variant_digits = 0;
890          (isdigit((guchar)linebuff[n])) && (variant_digits <= MAX_VARIANT_DIGITS) && (n+1 < line_length);
891          n++, variant_digits++)
892     {
893         if (!isdigit((guchar)linebuff[n]))
894         {
895             return FALSE;
896         }
897         variant_name[variant_digits] = linebuff[n];
898     }
899     if (variant_digits > MAX_VARIANT_DIGITS || (n+1 >= line_length))
900     {
901         return FALSE;
902     }
903     if (variant_digits > 0)
904     {
905         variant_name[variant_digits] = '\0';
906         variant = atoi(variant_name);
907     }
908     else
909     {
910         strcpy(variant_name, "1");
911     }
912
913
914     /* Outheader values may follow */
915     outhdr_name[0] = '\0';
916     if (linebuff[n] == ',')
917     {
918         /* Skip , */
919         n++;
920
921         for (outhdr_chars = 0;
922              (isdigit((guchar)linebuff[n]) || linebuff[n] == ',') &&
923              (outhdr_chars <= MAX_OUTHDR_NAME) && (n+1 < line_length);
924              n++, outhdr_chars++)
925         {
926             if (!isdigit((guchar)linebuff[n]) && (linebuff[n] != ','))
927             {
928                 return FALSE;
929             }
930             outhdr_name[outhdr_chars] = linebuff[n];
931         }
932         if (outhdr_chars > MAX_OUTHDR_NAME || (n+1 >= line_length))
933         {
934             return FALSE;
935         }
936         /* Terminate (possibly empty) string */
937         outhdr_name[outhdr_chars] = '\0';
938     }
939
940
941
942
943     /******************************************************************/
944     /* Now check whether we know how to use a packet of this protocol */
945
946     if ((strcmp(protocol_name, "ip") == 0) ||
947         (strcmp(protocol_name, "sctp") == 0) ||
948         (strcmp(protocol_name, "gre") == 0) ||
949         (strcmp(protocol_name, "mipv6") == 0) ||
950         (strcmp(protocol_name, "igmp") == 0))
951     {
952         *encap = WTAP_ENCAP_RAW_IP;
953     }
954     else
955
956     /* FP may be carried over ATM, which has separate atm header to parse */
957     if ((strcmp(protocol_name, "fp") == 0) ||
958         (strcmp(protocol_name, "fp_r4") == 0) ||
959         (strcmp(protocol_name, "fp_r5") == 0) ||
960         (strcmp(protocol_name, "fp_r6") == 0))
961     {
962         if ((variant > 256) && (variant % 256 == 3))
963         {
964             /* FP over udp is contained in IPPrim... */
965             *encap = 0;
966         }
967         else
968         {
969             /* FP over AAL0 or AAL2 */
970             *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
971             atm_header_present = TRUE;
972         }
973     }
974     else if (strcmp(protocol_name, "fpiur_r5") == 0)
975     {
976         /* FP (IuR) over AAL2 */
977         *encap = WTAP_ENCAP_ATM_PDUS_UNTRUNCATED;
978         atm_header_present = TRUE;
979     }
980
981
982     else
983     if (strcmp(protocol_name, "ppp") == 0)
984     {
985         *encap = WTAP_ENCAP_PPP;
986     }
987     else
988     if (strcmp(protocol_name, "isdn_l3") == 0)
989     {
990        /* TODO: find out what this byte means... */
991         skip_first_byte = TRUE;
992         *encap = WTAP_ENCAP_ISDN;
993     }
994     else
995     if (strcmp(protocol_name, "isdn_l2") == 0)
996     {
997         *encap = WTAP_ENCAP_ISDN;
998     }
999     else
1000     if (strcmp(protocol_name, "ethernet") == 0)
1001     {
1002         *encap = WTAP_ENCAP_ETHERNET;
1003     }
1004     else
1005     if ((strcmp(protocol_name, "saalnni_sscop") == 0) ||
1006         (strcmp(protocol_name, "saaluni_sscop") == 0))
1007     {
1008         *encap = DCT2000_ENCAP_SSCOP;
1009     }
1010     else
1011     if (strcmp(protocol_name, "frelay_l2") == 0)
1012     {
1013         *encap = WTAP_ENCAP_FRELAY;
1014     }
1015     else
1016     if (strcmp(protocol_name, "ss7_mtp2") == 0)
1017     {
1018         *encap = DCT2000_ENCAP_MTP2;
1019     }
1020     else
1021     if ((strcmp(protocol_name, "nbap") == 0) ||
1022         (strcmp(protocol_name, "nbap_r4") == 0) ||
1023         (strncmp(protocol_name, "nbap_sscfuni", strlen("nbap_sscfuni")) == 0))
1024     {
1025         /* The entire message in these cases is nbap, so use an encap value. */
1026         *encap = DCT2000_ENCAP_NBAP;
1027     }
1028     else
1029     {
1030         /* Not a supported board port protocol/encap, but can show as raw data or
1031            in some cases find protocol embedded inside primitive */
1032         *encap = DCT2000_ENCAP_UNHANDLED;
1033     }
1034
1035
1036     /* Find separate ATM header if necessary */
1037     if (atm_header_present)
1038     {
1039         int header_chars_seen = 0;
1040
1041         /* Scan ahead to the next $ */
1042         for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1043         /* Skip it */
1044         n++;
1045         if (n+1 >= line_length)
1046         {
1047             return FALSE;
1048         }
1049
1050         /* Read consecutive hex chars into atm header buffer */
1051         for (;
1052              ((linebuff[n] >= '0') && (linebuff[n] <= '?') &&
1053               (n < line_length) &&
1054               (header_chars_seen < AAL_HEADER_CHARS));
1055              n++, header_chars_seen++)
1056         {
1057             aal_header_chars[header_chars_seen] = linebuff[n];
1058             /* Next 6 characters after '9' are mapped to a->f */
1059             if (!isdigit((guchar)linebuff[n]))
1060             {
1061                 aal_header_chars[header_chars_seen] = 'a' + (linebuff[n] - '9') -1;
1062             }
1063         }
1064
1065         if (header_chars_seen != AAL_HEADER_CHARS || n >= line_length)
1066         {
1067             return FALSE;
1068         }
1069     }
1070
1071
1072     /* Scan ahead to the next space */
1073     for (; (linebuff[n] != ' ') && (n+1 < line_length); n++);
1074     if (n+1 >= line_length)
1075     {
1076         return FALSE;
1077     }
1078     /* Skip it */
1079     n++;
1080
1081     /* Next character gives direction of message (must be 's' or 'r') */
1082     if (linebuff[n] == 's')
1083     {
1084         *direction = sent;
1085     }
1086     else
1087     if (linebuff[n] == 'r')
1088     {
1089         *direction = received;
1090     }
1091     else
1092     {
1093         return FALSE;
1094     }
1095
1096
1097     /*********************************************************************/
1098     /* Find and read the timestamp                                       */
1099
1100     /* Now scan to the next digit, which should be the start of the timestamp */
1101     for (; !isdigit((guchar)linebuff[n]) && (n < line_length); n++);
1102     if (n >= line_length)
1103     {
1104         return FALSE;
1105     }
1106
1107     *before_time_offset = n;
1108
1109     /* Seconds */
1110     for (seconds_chars = 0;
1111          (linebuff[n] != '.') &&
1112          (seconds_chars <= MAX_SECONDS_CHARS) &&
1113          (n < line_length);
1114          n++, seconds_chars++)
1115     {
1116         if (!isdigit((guchar)linebuff[n]))
1117         {
1118             /* Found a non-digit before decimal point. Fail */
1119             return FALSE;
1120         }
1121         seconds_buff[seconds_chars] = linebuff[n];
1122     }
1123     if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length)
1124     {
1125         /* Didn't fit in buffer.  Fail rather than use truncated */
1126         return FALSE;
1127     }
1128
1129     /* Convert found value into number */
1130     seconds_buff[seconds_chars] = '\0';
1131     *seconds = atoi(seconds_buff);
1132
1133     /* The decimal point must follow the last of the seconds digits */
1134     if (linebuff[n] != '.')
1135     {
1136         return FALSE;
1137     }
1138     /* Skip it */
1139     n++;
1140
1141     /* Subsecond decimal digits (expect 4-digit accuracy) */
1142     for (subsecond_decimals_chars = 0;
1143          (linebuff[n] != ' ') &&
1144          (subsecond_decimals_chars <= MAX_SUBSECOND_DECIMALS) &&
1145          (n < line_length);
1146          n++, subsecond_decimals_chars++)
1147     {
1148         if (!isdigit((guchar)linebuff[n]))
1149         {
1150             return FALSE;
1151         }
1152         subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n];
1153     }
1154     if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length)
1155     {
1156         /* More numbers than expected - give up */
1157         return FALSE;
1158     }
1159     /* Convert found value into microseconds */
1160     subsecond_decimals_buff[subsecond_decimals_chars] = '\0';
1161     *useconds = atoi(subsecond_decimals_buff) * 100;
1162
1163     /* Space character must follow end of timestamp */
1164     if (linebuff[n] != ' ')
1165     {
1166         return FALSE;
1167     }
1168
1169     *after_time_offset = n;
1170
1171     /* Now skip ahead to find start of data (marked by '$') */
1172     for (; (linebuff[n] != '$') && (n+1 < line_length); n++);
1173     if (n+1 >= line_length)
1174     {
1175         return FALSE;
1176     }
1177     /* Skip it */
1178     n++;
1179
1180     /* Set offset to data start within line */
1181     *data_offset = n;
1182
1183     /* Set number of chars that comprise the hex string protocol data */
1184     *data_chars = line_length - n;
1185
1186     /* May need to skip first byte (2 hex string chars) */
1187     if (skip_first_byte)
1188     {
1189         *data_offset += 2;
1190         *data_chars -= 2;
1191     }
1192
1193
1194     return TRUE;
1195 }
1196
1197 /*****************************************************************/
1198 /* Write the stub info to the data buffer while reading a packet */
1199 /*****************************************************************/
1200 int write_stub_header(guchar *frame_buffer, char *timestamp_string,
1201                       packet_direction_t direction, int encap)
1202 {
1203     int stub_offset = 0;
1204     
1205     strcpy((char*)frame_buffer, context_name);
1206     stub_offset += (strlen(context_name) + 1);
1207
1208     /* Context port number */
1209     frame_buffer[stub_offset] = context_port;
1210     stub_offset++;
1211
1212     /* Timestamp within file */
1213     strcpy((char*)&frame_buffer[stub_offset], timestamp_string);
1214     stub_offset += (strlen(timestamp_string) + 1);
1215
1216     /* Protocol name */
1217     strcpy((char*)&frame_buffer[stub_offset], protocol_name);
1218     stub_offset += (strlen(protocol_name) + 1);
1219
1220     /* Protocol variant number (as string) */
1221     strcpy((void*)&frame_buffer[stub_offset], variant_name);
1222     stub_offset += (strlen(variant_name) + 1);
1223
1224     /* Outhdr */
1225     strcpy((char*)&frame_buffer[stub_offset], outhdr_name);
1226     stub_offset += (strlen(outhdr_name) + 1);
1227
1228     /* Direction */
1229     frame_buffer[stub_offset] = direction;
1230     stub_offset++;
1231
1232     /* Encap */
1233     frame_buffer[stub_offset] = (guint8)encap;
1234     stub_offset++;
1235
1236     return stub_offset;
1237 }
1238
1239
1240 /**************************************************************/
1241 /* Set pseudo-header info depending upon packet encapsulation */
1242 /**************************************************************/
1243 void set_pseudo_header_info(wtap *wth,
1244                             int pkt_encap,
1245                             gint64 file_offset,
1246                             union wtap_pseudo_header *pseudo_header,
1247                             packet_direction_t direction)
1248 {
1249     pseudo_header->dct2000.seek_off = file_offset;
1250     pseudo_header->dct2000.wth = wth;
1251
1252     switch (pkt_encap)
1253     {
1254         case WTAP_ENCAP_ATM_PDUS_UNTRUNCATED:
1255             set_aal_info(pseudo_header, direction);
1256             break;
1257         case WTAP_ENCAP_ISDN:
1258             set_isdn_info(pseudo_header, direction);
1259             break;
1260         case WTAP_ENCAP_PPP:
1261             set_ppp_info(pseudo_header, direction);
1262             break;
1263
1264         default:
1265             /* Other supported types don't need to set anything here... */
1266             break;
1267     }
1268 }
1269
1270
1271 /*********************************************/
1272 /* Fill in atm pseudo-header with known info */
1273 /*********************************************/
1274 void set_aal_info(union wtap_pseudo_header *pseudo_header, packet_direction_t direction)
1275 {
1276     /* 'aal_head_chars' has this format (for AAL2 at least):
1277        Global Flow Control (4 bits) | VPI (8 bits) | VCI (16 bits) |
1278        Payload Type (4 bits) | Padding (3 bits?) | Link? (1 bit) |
1279        Channel Identifier (8 bits) | ...
1280     */
1281
1282     /* Indicate that this is a reassembled PDU */
1283     pseudo_header->dct2000.inner_pseudo_header.atm.flags = 0x00;
1284
1285     /* Channel 0 is DTE->DCE, 1 is DCE->DTE. Always set 0 for now.
1286        TODO: Can we infer the correct value here?
1287        Meanwhile, just use the direction to make them distinguishable...
1288     */
1289     pseudo_header->dct2000.inner_pseudo_header.atm.channel = (direction == received);
1290
1291     /* Assume always AAL2 for FP */
1292     pseudo_header->dct2000.inner_pseudo_header.atm.aal = AAL_2;
1293
1294     pseudo_header->dct2000.inner_pseudo_header.atm.type = TRAF_UMTS_FP;
1295     pseudo_header->dct2000.inner_pseudo_header.atm.subtype = TRAF_ST_UNKNOWN;
1296
1297     /* vpi is 8 bits (2nd & 3rd nibble) */
1298     pseudo_header->dct2000.inner_pseudo_header.atm.vpi =
1299         ((hex_from_char(aal_header_chars[1]) << 4) |
1300           hex_from_char(aal_header_chars[2]));
1301
1302     /* vci is next 16 bits */
1303     pseudo_header->dct2000.inner_pseudo_header.atm.vci =
1304         ((hex_from_char(aal_header_chars[3]) << 12) |
1305          (hex_from_char(aal_header_chars[4]) << 8) |
1306          (hex_from_char(aal_header_chars[5]) << 4) |
1307          hex_from_char(aal_header_chars[6]));
1308
1309     /* 0 means we don't know how many cells the frame comprises. */
1310     pseudo_header->dct2000.inner_pseudo_header.atm.cells = 0;
1311
1312     /* cid is usually last byte.  Unless last char is not hex digit, in which
1313        case cid is derived from last char in ascii */
1314     if (isalnum((guchar)aal_header_chars[11]))
1315     {
1316         pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1317             ((hex_from_char(aal_header_chars[10]) << 4) |
1318               hex_from_char(aal_header_chars[11]));
1319     }
1320     else
1321     {
1322         pseudo_header->dct2000.inner_pseudo_header.atm.aal2_cid =
1323             (int)aal_header_chars[11] - 48;
1324     }
1325
1326 }
1327
1328
1329 /**********************************************/
1330 /* Fill in isdn pseudo-header with known info */
1331 /**********************************************/
1332 void set_isdn_info(union wtap_pseudo_header *pseudo_header,
1333                    packet_direction_t direction)
1334 {
1335     /* This field is used to set the 'Source' and 'Destination' columns to
1336        'User' or 'Network'. If we assume that we're simulating the network,
1337        treat Received messages as being destined for the network.
1338     */
1339     pseudo_header->dct2000.inner_pseudo_header.isdn.uton = (direction == received);
1340
1341     /* This corresponds to the circuit ID.  0 is treated as LAPD,
1342        everything else would be treated as a B-channel
1343     */
1344     pseudo_header->dct2000.inner_pseudo_header.isdn.channel = 0;
1345 }
1346
1347
1348 /*********************************************/
1349 /* Fill in ppp pseudo-header with known info */
1350 /*********************************************/
1351 static void set_ppp_info(union wtap_pseudo_header *pseudo_header,
1352                          packet_direction_t direction)
1353 {
1354     /* Set direction. */
1355     pseudo_header->dct2000.inner_pseudo_header.p2p.sent = (direction == sent);
1356 }
1357
1358
1359 /********************************************************/
1360 /* Return hex nibble equivalent of hex string character */
1361 /********************************************************/
1362 guchar hex_from_char(gchar c)
1363 {
1364     if ((c >= '0') && (c <= '9'))
1365     {
1366         return c - '0';
1367     }
1368
1369     if ((c >= 'a') && (c <= 'f'))
1370     {
1371         return 0x0a + (c - 'a');
1372     }
1373
1374     /* Not a valid hex string character */
1375     return 0xff;
1376 }
1377
1378
1379 /********************************************************/
1380 /* Return character corresponding to hex nibble value   */
1381 /********************************************************/
1382 gchar char_from_hex(guchar hex)
1383 {
1384     static char hex_lookup[16] =
1385     { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
1386
1387     if (hex > 15)
1388     {
1389         return '?';
1390     }
1391
1392     return hex_lookup[hex];
1393 }
1394
1395 /***************************************************/
1396 /* Equality function for file_externals hash table */
1397 /***************************************************/
1398 gint wth_equal(gconstpointer v, gconstpointer v2)
1399 {
1400     return (v == v2);
1401 }
1402
1403 /***********************************************/
1404 /* Hash function for file_externals hash table */
1405 /***********************************************/
1406 guint wth_hash_func(gconstpointer v)
1407 {
1408     return (guint)(unsigned long)v;
1409 }
1410
1411
1412 /***********************************************/
1413 /* Equality test for packet prefix hash tables */
1414 /***********************************************/
1415 gint packet_offset_equal(gconstpointer v, gconstpointer v2)
1416 {
1417     /* Dereferenced pointers must have same gint64 offset value */
1418     return (*(const gint64*)v == *(const gint64*)v2);
1419 }
1420
1421
1422 /********************************************/
1423 /* Hash function for packet-prefix hash table */
1424 /********************************************/
1425 guint packet_offset_hash_func(gconstpointer v)
1426 {
1427     /* Use low-order bits of git64 offset value */
1428     return (guint)(*(const gint64*)v);
1429 }
1430
1431
1432 /************************************************************************/
1433 /* Parse year, month, day, hour, minute, seconds out of formatted line. */
1434 /* Set secs and usecs as output                                         */
1435 /* Return FALSE if no valid time can be read                            */
1436 /************************************************************************/
1437 gboolean get_file_time_stamp(time_t *secs, guint32 *usecs)
1438 {
1439     int n;
1440     struct tm tm;
1441     #define MAX_MONTH_LETTERS 9
1442     char month[MAX_MONTH_LETTERS+1];
1443
1444     int day, year, hour, minute, second;
1445     int scan_found;
1446
1447     /* If line longer than expected, file is probably not correctly formatted */
1448     if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH)
1449     {
1450         return FALSE;
1451     }
1452
1453     /**************************************************************/
1454     /* First is month. Read until get a space following the month */
1455     for (n=0; (linebuff[n] != ' ') && (n < MAX_MONTH_LETTERS); n++)
1456     {
1457         month[n] = linebuff[n];
1458     }
1459     month[n] = '\0';
1460
1461     if      (strcmp(month, "January"  ) == 0)  tm.tm_mon = 0;
1462     else if (strcmp(month, "February" ) == 0)  tm.tm_mon = 1;
1463     else if (strcmp(month, "March"    ) == 0)  tm.tm_mon = 2;
1464     else if (strcmp(month, "April"    ) == 0)  tm.tm_mon = 3;
1465     else if (strcmp(month, "May"      ) == 0)  tm.tm_mon = 4;
1466     else if (strcmp(month, "June"     ) == 0)  tm.tm_mon = 5;
1467     else if (strcmp(month, "July"     ) == 0)  tm.tm_mon = 6;
1468     else if (strcmp(month, "August"   ) == 0)  tm.tm_mon = 7;
1469     else if (strcmp(month, "September") == 0)  tm.tm_mon = 8;
1470     else if (strcmp(month, "October"  ) == 0)  tm.tm_mon = 9;
1471     else if (strcmp(month, "November" ) == 0)  tm.tm_mon = 10;
1472     else if (strcmp(month, "December" ) == 0)  tm.tm_mon = 11;
1473     else
1474     {
1475         /* Give up if not found a properly-formatted date */
1476         return FALSE;
1477     }
1478     /* Skip space char */
1479     n++;
1480
1481     /********************************************************/
1482     /* Scan for remaining numerical fields                  */
1483     scan_found = sscanf(linebuff+n, "%d, %d     %d:%d:%d.%u",
1484                         &day, &year, &hour, &minute, &second, usecs);
1485     if (scan_found != 6)
1486     {
1487         /* Give up if not all found */
1488         return FALSE;
1489     }
1490
1491     /******************************************************/
1492     /* Fill in remaining fields and return it in a time_t */
1493     tm.tm_year = year - 1900;
1494     tm.tm_mday = day;
1495     tm.tm_hour = hour;
1496     tm.tm_min = minute;
1497     tm.tm_sec = second;
1498     tm.tm_isdst = -1;    /* daylight saving time info not known */
1499
1500     /* Get seconds from this time */
1501     *secs = mktime(&tm);
1502
1503     /* Multiply 4 digits given to get micro-seconds */
1504     *usecs = *usecs * 100;
1505
1506     return TRUE;
1507 }
1508
1509 /* Free the data allocated inside a line_prefix_info_t */
1510 gboolean free_line_prefix_info(gpointer key, gpointer value,
1511                                gpointer user_data _U_)
1512 {
1513     line_prefix_info_t *info = (line_prefix_info_t*)value;
1514
1515     /* Free the 64-bit key value */
1516     g_free(key);
1517
1518     /* Free the strings inside */
1519     g_free(info->before_time);
1520     if (info->after_time)
1521     {
1522         g_free(info->after_time);
1523     }
1524
1525     /* And the structure itself */
1526     g_free(info);
1527
1528     /* Item will always be removed from table */
1529     return TRUE;
1530 }
1531