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