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