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