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