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