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