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