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