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