/src/wireshark/wiretap/log3gpp.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* log3gpp.c |
2 | | * Routines encapsulating/dumping 3gpp protocol logs. |
3 | | * The purpose of this format is to be able to log the 3GPP protocol stack on a mobile phone. |
4 | | * Copyright 2008, Vincent Helfre |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * SPDX-License-Identifier: GPL-2.0-or-later |
11 | | */ |
12 | | |
13 | | #include "config.h" |
14 | | #include "log3gpp.h" |
15 | | |
16 | | #define WS_LOG_DOMAIN LOG_DOMAIN_WIRETAP |
17 | | |
18 | | #include <errno.h> |
19 | | #include <string.h> |
20 | | #include <stdlib.h> |
21 | | |
22 | | #include "wtap-int.h" |
23 | | #include "file_wrappers.h" |
24 | | |
25 | 0 | #define MAX_FIRST_LINE_LENGTH 200 |
26 | 0 | #define MAX_TIMESTAMP_LINE_LENGTH 100 |
27 | | #define MAX_LINE_LENGTH 65536 |
28 | 0 | #define MAX_TIMESTAMP_LEN 32 |
29 | 0 | #define MAX_SECONDS_CHARS 16 |
30 | 0 | #define MAX_SUBSECOND_DECIMALS 4 |
31 | 0 | #define MAX_PROTOCOL_NAME 64 |
32 | 0 | #define MAX_PROTOCOL_PAR_STRING 64 |
33 | | |
34 | | /* 'u' or 'd' of a packet as read from file */ |
35 | | typedef enum packet_direction_t |
36 | | { |
37 | | uplink, |
38 | | downlink |
39 | | } packet_direction_t; |
40 | | |
41 | | typedef struct { |
42 | | time_t start_secs; |
43 | | uint32_t start_usecs; |
44 | | } log3gpp_t; |
45 | | |
46 | | int first_packet_offset; |
47 | | char firstline[MAX_FIRST_LINE_LENGTH]; |
48 | | char secondline[MAX_TIMESTAMP_LINE_LENGTH]; |
49 | | int secondline_length; |
50 | | |
51 | | /***********************************************************/ |
52 | | /* Transient data used for parsing */ |
53 | | |
54 | | /* 'Magic number' at start of 3gpp log files. */ |
55 | | static const char log3gpp_magic[] = "3GPP protocols transcript"; |
56 | | |
57 | | /* Protocol name of the packet that the packet was captured at */ |
58 | | static char protocol_name[MAX_PROTOCOL_NAME+1]; |
59 | | |
60 | | /* Optional string parameter giving info required for the protocol dissector */ |
61 | | static char protocol_parameters[MAX_PROTOCOL_PAR_STRING+1]; |
62 | | /************************************************************/ |
63 | | /* Functions called from wiretap core */ |
64 | | static bool log3gpp_read( wtap* wth, wtap_rec* rec, |
65 | | int* err, char** err_info, int64_t* data_offset); |
66 | | static bool log3gpp_seek_read(struct wtap *wth, int64_t seek_off, |
67 | | wtap_rec *rec, |
68 | | int *err, char **err_info); |
69 | | |
70 | | /************************************************************/ |
71 | | /* Private helper functions */ |
72 | | static bool read_new_line(FILE_T fh, int* length, |
73 | | char* buf, size_t bufsize, int* err, |
74 | | char** err_info); |
75 | | |
76 | | static bool parse_line(char* linebuff, int line_length, int *seconds, int *useconds, |
77 | | long *data_offset, |
78 | | int *data_chars, |
79 | | packet_direction_t *direction, |
80 | | bool *is_text_data); |
81 | | static int write_stub_header(unsigned char *frame_buffer, char *timestamp_string, |
82 | | packet_direction_t direction); |
83 | | static unsigned char hex_from_char(char c); |
84 | | /*not used static char char_from_hex(unsigned char hex);*/ |
85 | | |
86 | | static bool get_file_time_stamp(const char* linebuff, time_t *secs, uint32_t *usecs); |
87 | | |
88 | | |
89 | | static int log3gpp_file_type_subtype = -1; |
90 | | |
91 | | void register_log3gpp(void); |
92 | | |
93 | | /***************************************************************************/ |
94 | | /* Free log3gpp-specific capture info from file that was open for reading */ |
95 | | /***************************************************************************/ |
96 | | static void log3gpp_close(wtap* wth) |
97 | 0 | { |
98 | 0 | log3gpp_t* log3gpp = (log3gpp_t*)wth->priv; |
99 | | /* Also free this capture info */ |
100 | 0 | g_free(log3gpp); |
101 | 0 | wth->priv = NULL; |
102 | 0 | } |
103 | | |
104 | | /********************************************/ |
105 | | /* Open file (for reading) */ |
106 | | /********************************************/ |
107 | | wtap_open_return_val |
108 | | log3gpp_open(wtap *wth, int *err, char **err_info _U_) |
109 | 0 | { |
110 | 0 | time_t timestamp; |
111 | 0 | uint32_t usecs; |
112 | 0 | log3gpp_t *log3gpp; |
113 | 0 | wtap_open_return_val retval; |
114 | | /* Buffer to hold a single text line read from the file */ |
115 | 0 | static char linebuff[MAX_LINE_LENGTH]; |
116 | 0 | int firstline_length = 0; |
117 | | |
118 | | /* Clear errno before reading from the file */ |
119 | 0 | errno = 0; |
120 | | |
121 | | /********************************************************************/ |
122 | | /* First line needs to contain at least as many characters as magic */ |
123 | | |
124 | | /*ws_warning("Open file"); */ |
125 | |
|
126 | 0 | if (!read_new_line(wth->fh, &firstline_length, linebuff, |
127 | 0 | sizeof linebuff, err, err_info)) { |
128 | 0 | if (*err != 0 && *err != WTAP_ERR_SHORT_READ) { |
129 | 0 | return WTAP_OPEN_ERROR; |
130 | 0 | } |
131 | 0 | else { |
132 | 0 | return WTAP_OPEN_NOT_MINE; |
133 | 0 | } |
134 | 0 | } |
135 | | |
136 | 0 | if (((size_t)firstline_length < strlen(log3gpp_magic)) || |
137 | 0 | firstline_length >= MAX_FIRST_LINE_LENGTH) |
138 | 0 | { |
139 | 0 | retval = WTAP_OPEN_NOT_MINE; |
140 | 0 | return retval; |
141 | 0 | } |
142 | | |
143 | | /* This file is not for us if it doesn't match our signature */ |
144 | 0 | if (memcmp(log3gpp_magic, linebuff, strlen(log3gpp_magic)) != 0) |
145 | 0 | { |
146 | 0 | retval = WTAP_OPEN_NOT_MINE; |
147 | 0 | return retval; |
148 | 0 | } |
149 | | |
150 | | /***********************************************************/ |
151 | | /* Second line contains file timestamp */ |
152 | 0 | if (!read_new_line(wth->fh, &secondline_length, |
153 | 0 | linebuff, sizeof linebuff, err, err_info)) { |
154 | 0 | if (*err != 0 && *err != WTAP_ERR_SHORT_READ) { |
155 | 0 | return WTAP_OPEN_ERROR; |
156 | 0 | } |
157 | 0 | else { |
158 | 0 | return WTAP_OPEN_NOT_MINE; |
159 | 0 | } |
160 | 0 | } |
161 | | |
162 | 0 | first_packet_offset = firstline_length + secondline_length; |
163 | |
|
164 | 0 | if ((secondline_length >= MAX_TIMESTAMP_LINE_LENGTH) || |
165 | 0 | (!get_file_time_stamp(linebuff, ×tamp, &usecs))) |
166 | 0 | { |
167 | | /* Give up if file time line wasn't valid */ |
168 | 0 | retval = WTAP_OPEN_NOT_MINE; |
169 | 0 | return retval; |
170 | 0 | } |
171 | | |
172 | | /* Allocate struct and fill in timestamp (netmon re used)*/ |
173 | 0 | log3gpp = g_new(log3gpp_t, 1); |
174 | 0 | log3gpp->start_secs = timestamp; |
175 | 0 | log3gpp->start_usecs = usecs; |
176 | 0 | wth->priv = (void *)log3gpp; |
177 | | |
178 | | /************************************************************/ |
179 | | /* File is for us. Fill in details so packets can be read */ |
180 | | |
181 | | /* Set our file type */ |
182 | 0 | wth->file_type_subtype = log3gpp_file_type_subtype; |
183 | | |
184 | | /* Use our own encapsulation to send all packets to our stub dissector */ |
185 | 0 | wth->file_encap = WTAP_ENCAP_LOG_3GPP; |
186 | | |
187 | | /* Callbacks for reading operations */ |
188 | 0 | wth->subtype_read = log3gpp_read; |
189 | 0 | wth->subtype_seek_read = log3gpp_seek_read; |
190 | 0 | wth->subtype_close = log3gpp_close; |
191 | | |
192 | | /* Choose microseconds (have 4 decimal places...) */ |
193 | 0 | wth->file_tsprec = WTAP_TSPREC_USEC; |
194 | |
|
195 | 0 | *err = errno; |
196 | | |
197 | | /* |
198 | | * Add an IDB; we don't know how many interfaces were |
199 | | * involved, so we just say one interface, about which |
200 | | * we only know the link-layer type, snapshot length, |
201 | | * and time stamp resolution. |
202 | | */ |
203 | 0 | wtap_add_generated_idb(wth); |
204 | |
|
205 | 0 | retval = WTAP_OPEN_MINE; |
206 | 0 | return retval; |
207 | 0 | } |
208 | | |
209 | | |
210 | | /**************************************************/ |
211 | | /* Read packet function. */ |
212 | | /* Look for and read the next usable packet */ |
213 | | /* - return true and details if found */ |
214 | | /**************************************************/ |
215 | | bool log3gpp_read(wtap* wth, wtap_rec* rec, |
216 | | int* err, char** err_info, int64_t* data_offset) |
217 | 0 | { |
218 | 0 | int64_t offset = file_tell(wth->fh); |
219 | 0 | static char linebuff[MAX_LINE_LENGTH + 1]; |
220 | 0 | long dollar_offset; |
221 | 0 | packet_direction_t direction; |
222 | 0 | bool is_text_data; |
223 | 0 | log3gpp_t *log3gpp = (log3gpp_t *)wth->priv; |
224 | | |
225 | | /* Search for a line containing a usable packet */ |
226 | 0 | while (1) |
227 | 0 | { |
228 | 0 | int line_length, seconds, useconds, data_chars; |
229 | 0 | int64_t this_offset = offset; |
230 | | |
231 | | /* Are looking for first packet after 2nd line */ |
232 | 0 | if (file_tell(wth->fh) == 0) |
233 | 0 | { |
234 | 0 | this_offset += (int64_t)first_packet_offset +1+1; |
235 | 0 | } |
236 | | |
237 | | /* Clear errno before reading from the file */ |
238 | 0 | errno = 0; |
239 | | |
240 | | /* Read a new line from file into linebuff */ |
241 | 0 | if (!read_new_line(wth->fh, &line_length, linebuff, |
242 | 0 | sizeof linebuff, err, err_info)) { |
243 | 0 | if (*err != 0) { |
244 | 0 | return false; /* error */ |
245 | 0 | } |
246 | | /* No more lines can be read, so quit. */ |
247 | 0 | break; |
248 | 0 | } |
249 | | |
250 | | /* Try to parse the line as a frame record */ |
251 | 0 | if (parse_line(linebuff, line_length, &seconds, &useconds, |
252 | 0 | &dollar_offset, |
253 | 0 | &data_chars, |
254 | 0 | &direction, |
255 | 0 | &is_text_data)) |
256 | 0 | { |
257 | 0 | unsigned char *frame_buffer; |
258 | 0 | int n; |
259 | 0 | int stub_offset = 0; |
260 | 0 | char timestamp_string[MAX_TIMESTAMP_LEN+1]; |
261 | | /*not used int64_t *pkey = NULL;*/ |
262 | |
|
263 | 0 | snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100); |
264 | | |
265 | | /* All packets go to 3GPP protocol stub dissector */ |
266 | 0 | rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP; |
267 | 0 | rec->rec_type = REC_TYPE_PACKET; |
268 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
269 | 0 | rec->presence_flags = WTAP_HAS_TS; |
270 | | |
271 | | /* Set data_offset to the beginning of the line we're returning. |
272 | | This will be the seek_off parameter when this frame is re-read. |
273 | | */ |
274 | 0 | *data_offset = this_offset; |
275 | | |
276 | | /* Fill in timestamp (capture base + packet offset) */ |
277 | 0 | rec->ts.secs = log3gpp->start_secs + seconds; |
278 | 0 | if ((log3gpp->start_usecs + useconds) >= 1000000) |
279 | 0 | { |
280 | 0 | rec->ts.secs++; |
281 | 0 | } |
282 | 0 | rec->ts.nsecs = |
283 | 0 | ((log3gpp->start_usecs + useconds) % 1000000) *1000; |
284 | |
|
285 | 0 | if (!is_text_data) |
286 | 0 | { |
287 | | /* Get buffer pointer ready */ |
288 | 0 | ws_buffer_assure_space(&rec->data, |
289 | 0 | strlen(timestamp_string)+1 + /* timestamp */ |
290 | 0 | strlen(protocol_name)+1 + /* Protocol name */ |
291 | 0 | 1 + /* direction */ |
292 | 0 | (size_t)(data_chars/2)); |
293 | |
|
294 | 0 | frame_buffer = ws_buffer_start_ptr(&rec->data); |
295 | | /*********************/ |
296 | | /* Write stub header */ |
297 | 0 | stub_offset = write_stub_header(frame_buffer, timestamp_string, |
298 | 0 | direction); |
299 | | |
300 | | /* Binary data length is half bytestring length + stub header */ |
301 | 0 | rec->rec_header.packet_header.len = data_chars/2 + stub_offset; |
302 | 0 | rec->rec_header.packet_header.caplen = data_chars/2 + stub_offset; |
303 | | /********************************/ |
304 | | /* Copy packet data into buffer */ |
305 | 0 | for (n=0; n <= data_chars; n+=2) |
306 | 0 | { |
307 | 0 | frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) | |
308 | 0 | hex_from_char(linebuff[dollar_offset+n+1]); |
309 | 0 | } |
310 | 0 | *err = errno = 0; |
311 | 0 | return true; |
312 | 0 | } |
313 | 0 | else |
314 | 0 | { |
315 | | /* Get buffer pointer ready */ |
316 | 0 | ws_buffer_assure_space(&rec->data, |
317 | 0 | strlen(timestamp_string)+1 + /* timestamp */ |
318 | 0 | strlen(protocol_name)+1 + /* Protocol name */ |
319 | 0 | 1 + /* direction */ |
320 | 0 | data_chars); |
321 | 0 | frame_buffer = ws_buffer_start_ptr(&rec->data); |
322 | | |
323 | | /*********************/ |
324 | | /* Write stub header */ |
325 | 0 | stub_offset = write_stub_header(frame_buffer, timestamp_string, |
326 | 0 | direction); |
327 | | |
328 | | /* Binary data length is bytestring length + stub header */ |
329 | 0 | rec->rec_header.packet_header.len = data_chars + stub_offset; |
330 | 0 | rec->rec_header.packet_header.caplen = data_chars + stub_offset; |
331 | | |
332 | | /* do not convert the ascii char */ |
333 | 0 | memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars); |
334 | 0 | frame_buffer[stub_offset+data_chars-1]= '\0'; |
335 | 0 | *err = errno = 0; |
336 | 0 | return true; |
337 | 0 | } |
338 | 0 | } |
339 | 0 | } |
340 | | |
341 | | /* No packet details to return... */ |
342 | 0 | *err = errno; |
343 | 0 | return false; |
344 | 0 | } |
345 | | |
346 | | |
347 | | /**************************************************/ |
348 | | /* Read & seek function. */ |
349 | | /**************************************************/ |
350 | | static bool |
351 | | log3gpp_seek_read(wtap *wth, int64_t seek_off, wtap_rec *rec, |
352 | | int *err, char **err_info) |
353 | 0 | { |
354 | 0 | long dollar_offset; |
355 | 0 | static char linebuff[MAX_LINE_LENGTH + 1]; |
356 | 0 | packet_direction_t direction; |
357 | 0 | int seconds, useconds, data_chars; |
358 | 0 | bool is_text_data; |
359 | 0 | log3gpp_t* log3gpp = (log3gpp_t*)wth->priv; |
360 | 0 | int length = 0; |
361 | 0 | unsigned char *frame_buffer; |
362 | | |
363 | | /* Reset errno */ |
364 | 0 | *err = errno = 0; |
365 | | |
366 | | /* Seek to beginning of packet */ |
367 | 0 | if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) |
368 | 0 | { |
369 | 0 | return false; |
370 | 0 | } |
371 | | |
372 | | /* Re-read whole line (this really should succeed) */ |
373 | 0 | if (!read_new_line(wth->random_fh, &length, linebuff, |
374 | 0 | sizeof linebuff, err, err_info)) { |
375 | 0 | return false; |
376 | 0 | } |
377 | | |
378 | | /* Try to parse this line again (should succeed as re-reading...) */ |
379 | 0 | if (parse_line(linebuff, length, &seconds, &useconds, |
380 | 0 | &dollar_offset, |
381 | 0 | &data_chars, |
382 | 0 | &direction, |
383 | 0 | &is_text_data)) |
384 | 0 | { |
385 | 0 | int n; |
386 | 0 | int stub_offset = 0; |
387 | 0 | char timestamp_string[32]; |
388 | 0 | snprintf(timestamp_string, 32, "%d.%04d", seconds, useconds/100); |
389 | | |
390 | | /* Make sure all packets go to log3gpp dissector */ |
391 | 0 | rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_LOG_3GPP; |
392 | 0 | rec->rec_type = REC_TYPE_PACKET; |
393 | 0 | rec->block = wtap_block_create(WTAP_BLOCK_PACKET); |
394 | 0 | rec->presence_flags = WTAP_HAS_TS; |
395 | | |
396 | | /* Fill in timestamp (capture base + packet offset) */ |
397 | 0 | rec->ts.secs = log3gpp->start_secs + seconds; |
398 | 0 | if ((log3gpp->start_usecs + useconds) >= 1000000) |
399 | 0 | { |
400 | 0 | rec->ts.secs++; |
401 | 0 | } |
402 | 0 | rec->ts.nsecs = |
403 | 0 | ((log3gpp->start_usecs + useconds) % 1000000) * 1000; |
404 | | |
405 | | /*********************/ |
406 | | /* Write stub header */ |
407 | 0 | ws_buffer_assure_space(&rec->data, |
408 | 0 | strlen(timestamp_string)+1 + /* timestamp */ |
409 | 0 | strlen(protocol_name)+1 + /* Protocol name */ |
410 | 0 | 1 + /* direction */ |
411 | 0 | data_chars); |
412 | 0 | frame_buffer = ws_buffer_start_ptr(&rec->data); |
413 | 0 | stub_offset = write_stub_header(frame_buffer, timestamp_string, |
414 | 0 | direction); |
415 | |
|
416 | 0 | if (!is_text_data) |
417 | 0 | { |
418 | | /********************************/ |
419 | | /* Copy packet data into buffer */ |
420 | 0 | for (n=0; n <= data_chars; n+=2) |
421 | 0 | { |
422 | 0 | frame_buffer[stub_offset + n/2] = (hex_from_char(linebuff[dollar_offset+n]) << 4) | |
423 | 0 | hex_from_char(linebuff[dollar_offset+n+1]); |
424 | 0 | } |
425 | 0 | *err = errno = 0; |
426 | 0 | return true; |
427 | 0 | } |
428 | 0 | else |
429 | 0 | { |
430 | | /* do not convert the ascii char */ |
431 | 0 | memcpy(&frame_buffer[stub_offset],&linebuff[dollar_offset],data_chars); |
432 | 0 | frame_buffer[stub_offset+data_chars-1] = '\0'; |
433 | 0 | *err = errno = 0; |
434 | 0 | return true; |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | /* If get here, must have failed */ |
439 | 0 | *err = errno; |
440 | 0 | *err_info = ws_strdup_printf("prot 3gpp: seek_read failed to read/parse " |
441 | 0 | "line at position %" PRId64, |
442 | 0 | seek_off); |
443 | 0 | return false; |
444 | 0 | } |
445 | | |
446 | | /****************************/ |
447 | | /* Private helper functions */ |
448 | | /****************************/ |
449 | | |
450 | | /**********************************************************************/ |
451 | | /* Read a new line from the file, starting at offset. */ |
452 | | /* - writes data to static var linebuff */ |
453 | | /* - on return 'offset' will point to the next position to read from */ |
454 | | /* - return true if this read is successful */ |
455 | | /**********************************************************************/ |
456 | | static bool |
457 | | read_new_line(FILE_T fh, int* length, |
458 | | char* linebuff, size_t linebuffsize, int* err, char** err_info) |
459 | 0 | { |
460 | | /* Read in a line */ |
461 | 0 | int64_t pos_before = file_tell(fh); |
462 | |
|
463 | 0 | if (file_gets(linebuff, (int)linebuffsize - 1, fh) == NULL) { |
464 | | /* No characters found, or error */ |
465 | 0 | *err = file_error(fh, err_info); |
466 | 0 | return false; |
467 | 0 | } |
468 | | |
469 | | /* Set length (avoiding strlen()) and offset.. */ |
470 | 0 | *length = (int)(file_tell(fh) - pos_before); |
471 | | |
472 | | /* ...but don't want to include newline in line length */ |
473 | 0 | if (*length > 0 && linebuff[*length - 1] == '\n') { |
474 | 0 | linebuff[*length - 1] = '\0'; |
475 | 0 | *length = *length - 1; |
476 | 0 | } |
477 | | /* Nor do we want '\r' (as will be written when log is created on windows) */ |
478 | 0 | if (*length > 0 && linebuff[*length - 1] == '\r') { |
479 | 0 | linebuff[*length - 1] = '\0'; |
480 | 0 | *length = *length - 1; |
481 | 0 | } |
482 | |
|
483 | 0 | return true; |
484 | 0 | } |
485 | | |
486 | | |
487 | | /**********************************************************************/ |
488 | | /* Parse a line from buffer, by identifying: */ |
489 | | /* - timestamp */ |
490 | | /* - data position and length */ |
491 | | /* Return true if this packet looks valid and can be displayed */ |
492 | | /**********************************************************************/ |
493 | | bool parse_line(char* linebuff, int line_length, int *seconds, int *useconds, |
494 | | long *data_offset, int *data_chars, |
495 | | packet_direction_t *direction, |
496 | | bool *is_text_data) |
497 | 0 | { |
498 | 0 | int n = 0; |
499 | 0 | int protocol_chars = 0; |
500 | 0 | int prot_option_chars = 0; |
501 | 0 | char seconds_buff[MAX_SECONDS_CHARS+1]; |
502 | 0 | int seconds_chars; |
503 | 0 | char subsecond_decimals_buff[MAX_SUBSECOND_DECIMALS]; |
504 | 0 | int subsecond_decimals_chars; |
505 | | |
506 | | /*********************************************************************/ |
507 | | /* Find and read the timestamp */ |
508 | | /*********************************************************************/ |
509 | | /* Now scan to the next digit, which should be the start of the timestamp */ |
510 | 0 | for (; !g_ascii_isdigit((unsigned char)linebuff[n]) && (n < line_length); n++); |
511 | 0 | if (n >= line_length) |
512 | 0 | { |
513 | 0 | return false; |
514 | 0 | } |
515 | | |
516 | | /* Seconds */ |
517 | 0 | for (seconds_chars = 0; |
518 | 0 | (linebuff[n] != '.') && |
519 | 0 | (seconds_chars <= MAX_SECONDS_CHARS) && |
520 | 0 | (n < line_length); |
521 | 0 | n++, seconds_chars++) |
522 | 0 | { |
523 | 0 | if (!g_ascii_isdigit((unsigned char)linebuff[n])) |
524 | 0 | { |
525 | | /* Found a non-digit before decimal point. Fail */ |
526 | 0 | return false; |
527 | 0 | } |
528 | 0 | seconds_buff[seconds_chars] = linebuff[n]; |
529 | 0 | } |
530 | 0 | if (seconds_chars > MAX_SECONDS_CHARS || n >= line_length) |
531 | 0 | { |
532 | | /* Didn't fit in buffer. Fail rather than use truncated */ |
533 | 0 | return false; |
534 | 0 | } |
535 | | |
536 | | /* Convert found value into number */ |
537 | 0 | seconds_buff[seconds_chars] = '\0'; |
538 | | |
539 | | /* Already know they are digits, so avoid expense of ws_strtoi32() */ |
540 | 0 | int multiplier = 1; |
541 | 0 | *seconds = 0; |
542 | 0 | for (int d = seconds_chars - 1; d >= 0; d--) { |
543 | 0 | *seconds += ((seconds_buff[d] - '0') * multiplier); |
544 | 0 | multiplier *= 10; |
545 | 0 | } |
546 | | |
547 | | /* The decimal point must follow the last of the seconds digits */ |
548 | 0 | if (linebuff[n] != '.') |
549 | 0 | { |
550 | 0 | return false; |
551 | 0 | } |
552 | | /* Skip it */ |
553 | 0 | n++; |
554 | | |
555 | | /* Subsecond decimal digits (expect 4-digit accuracy) */ |
556 | 0 | for (subsecond_decimals_chars = 0; |
557 | 0 | (linebuff[n] != ' ') && (subsecond_decimals_chars < MAX_SUBSECOND_DECIMALS) && (n < line_length); |
558 | 0 | n++, subsecond_decimals_chars++) |
559 | 0 | { |
560 | 0 | if (!g_ascii_isdigit((unsigned char)linebuff[n])) |
561 | 0 | { |
562 | 0 | return false; |
563 | 0 | } |
564 | 0 | subsecond_decimals_buff[subsecond_decimals_chars] = linebuff[n]; |
565 | 0 | } |
566 | | |
567 | 0 | if (subsecond_decimals_chars > MAX_SUBSECOND_DECIMALS || n >= line_length) |
568 | 0 | { |
569 | | /* More numbers than expected - give up */ |
570 | 0 | return false; |
571 | 0 | } |
572 | | |
573 | | /* Convert found value into microseconds */ |
574 | 0 | while (subsecond_decimals_chars < MAX_SUBSECOND_DECIMALS) { |
575 | 0 | subsecond_decimals_buff[subsecond_decimals_chars++] = '0'; |
576 | 0 | } |
577 | | /* Already know they are digits, so avoid expense of ws_strtoi32() */ |
578 | 0 | *useconds = ((subsecond_decimals_buff[0] - '0') * 100000) + |
579 | 0 | ((subsecond_decimals_buff[1] - '0') * 10000) + |
580 | 0 | ((subsecond_decimals_buff[2] - '0') * 1000) + |
581 | 0 | ((subsecond_decimals_buff[3] - '0') * 100); |
582 | | |
583 | | /* Space character must follow end of timestamp */ |
584 | 0 | if (linebuff[n] != ' ') |
585 | 0 | { |
586 | 0 | return false; |
587 | 0 | } |
588 | 0 | n++; |
589 | | |
590 | | /*********************************************************************/ |
591 | | /* Find and read protocol name */ |
592 | | /*********************************************************************/ |
593 | 0 | for (protocol_chars = 0; |
594 | 0 | (linebuff[n] != ' ') && (protocol_chars < MAX_PROTOCOL_NAME) && (n < line_length); |
595 | 0 | n++, protocol_chars++) |
596 | 0 | { |
597 | 0 | if (!g_ascii_isalnum((unsigned char)linebuff[n]) && linebuff[n] != '_' && linebuff[n] != '.' && linebuff[n] != '-') |
598 | 0 | { |
599 | 0 | return false; |
600 | 0 | } |
601 | 0 | protocol_name[protocol_chars] = linebuff[n]; |
602 | 0 | } |
603 | 0 | if (protocol_chars == MAX_PROTOCOL_NAME || n >= line_length) |
604 | 0 | { |
605 | | /* If doesn't fit, fail rather than truncate */ |
606 | 0 | return false; |
607 | 0 | } |
608 | 0 | protocol_name[protocol_chars] = '\0'; |
609 | | |
610 | | /* Space char must follow protocol name */ |
611 | 0 | if (linebuff[n] != ' ') |
612 | 0 | { |
613 | 0 | return false; |
614 | 0 | } |
615 | | /* Skip it */ |
616 | 0 | n++; |
617 | | |
618 | | /* Scan ahead to the next space */ |
619 | 0 | for (; (!g_ascii_isalnum((unsigned char)linebuff[n])) && (n < line_length); n++); |
620 | 0 | if (n >= line_length) |
621 | 0 | { |
622 | 0 | return false; |
623 | 0 | } |
624 | | |
625 | | |
626 | 0 | if (strcmp(protocol_name,"TXT") == 0) |
627 | 0 | { |
628 | 0 | *direction = uplink; |
629 | 0 | *data_offset = n; |
630 | 0 | *data_chars = line_length - n; |
631 | 0 | *is_text_data = true; |
632 | 0 | } |
633 | 0 | else |
634 | 0 | { |
635 | | /* Next character gives direction of message (must be 'u' or 'd') */ |
636 | 0 | if (linebuff[n] == 'u') |
637 | 0 | { |
638 | 0 | *direction = uplink; |
639 | 0 | } |
640 | 0 | else if (linebuff[n] == 'd') |
641 | 0 | { |
642 | 0 | *direction = downlink; |
643 | 0 | } |
644 | 0 | else |
645 | 0 | { |
646 | 0 | return false; |
647 | 0 | } |
648 | 0 | n++; |
649 | | |
650 | | /* Now skip ahead to find start of data (marked by '$') */ |
651 | 0 | for (; (n < line_length) && (linebuff[n] != '$') && (prot_option_chars < MAX_PROTOCOL_PAR_STRING); |
652 | 0 | n++,prot_option_chars++) |
653 | 0 | { |
654 | 0 | protocol_parameters[prot_option_chars] = linebuff[n]; |
655 | 0 | } |
656 | 0 | protocol_parameters[prot_option_chars] = '\0'; |
657 | 0 | if (prot_option_chars == MAX_PROTOCOL_PAR_STRING || n >= line_length) |
658 | 0 | { |
659 | | /* If doesn't fit, fail rather than truncate */ |
660 | 0 | return false; |
661 | 0 | } |
662 | | |
663 | | /* Skip it */ |
664 | 0 | n++; |
665 | | |
666 | | /* Set offset to data start within line */ |
667 | 0 | *data_offset = n; |
668 | | |
669 | | /* Set number of chars that comprise the hex string protocol data */ |
670 | 0 | *data_chars = line_length - n; |
671 | |
|
672 | 0 | *is_text_data = false; |
673 | 0 | } |
674 | 0 | return true; |
675 | 0 | } |
676 | | |
677 | | /*****************************************************************/ |
678 | | /* Write the stub info to the data buffer while reading a packet */ |
679 | | /*****************************************************************/ |
680 | | int write_stub_header(unsigned char *frame_buffer, char *timestamp_string, |
681 | | packet_direction_t direction) |
682 | 0 | { |
683 | 0 | int stub_offset = 0; |
684 | | |
685 | | /* Timestamp within file */ |
686 | 0 | (void) g_strlcpy((char*)&frame_buffer[stub_offset], timestamp_string, MAX_TIMESTAMP_LEN+1); |
687 | 0 | stub_offset += (int)(strlen(timestamp_string) + 1); |
688 | | |
689 | | /* Protocol name */ |
690 | 0 | (void) g_strlcpy((char*)&frame_buffer[stub_offset], protocol_name, MAX_PROTOCOL_NAME+1); |
691 | 0 | stub_offset += (int)(strlen(protocol_name) + 1); |
692 | | |
693 | | /* Direction */ |
694 | 0 | frame_buffer[stub_offset] = direction; |
695 | 0 | stub_offset++; |
696 | | |
697 | | /* Option string (might be string of length 0) */ |
698 | 0 | (void) g_strlcpy((char*)&frame_buffer[stub_offset], protocol_parameters,MAX_PROTOCOL_PAR_STRING+1); |
699 | 0 | stub_offset += (int)(strlen(protocol_parameters) + 1); |
700 | 0 | return stub_offset; |
701 | 0 | } |
702 | | |
703 | | |
704 | | /********************************************************/ |
705 | | /* Return hex nibble equivalent of hex string character */ |
706 | | /********************************************************/ |
707 | | unsigned char hex_from_char(char c) |
708 | 0 | { |
709 | 0 | if ((c >= '0') && (c <= '9')) |
710 | 0 | { |
711 | 0 | return c - '0'; |
712 | 0 | } |
713 | | |
714 | 0 | if ((c >= 'a') && (c <= 'f')) |
715 | 0 | { |
716 | 0 | return 0x0a + (c - 'a'); |
717 | 0 | } |
718 | | |
719 | 0 | if ((c >= 'A') && (c <= 'F')) |
720 | 0 | { |
721 | 0 | return 0x0a + (c - 'A'); |
722 | 0 | } |
723 | | /* Not a valid hex string character */ |
724 | 0 | return 0xff; |
725 | 0 | } |
726 | | |
727 | | |
728 | | /********************************************************/ |
729 | | /* Return character corresponding to hex nibble value */ |
730 | | /********************************************************/ |
731 | | /*char char_from_hex(unsigned char hex) |
732 | | { |
733 | | static char hex_lookup[16] = |
734 | | { '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; |
735 | | |
736 | | if (hex > 15) |
737 | | { |
738 | | return '?'; |
739 | | } |
740 | | |
741 | | return hex_lookup[hex]; |
742 | | }*/ |
743 | | |
744 | | /************************************************************************/ |
745 | | /* Parse year, month, day, hour, minute, seconds out of formatted line. */ |
746 | | /* Set secs and usecs as output */ |
747 | | /* Return false if no valid time can be read */ |
748 | | /************************************************************************/ |
749 | | bool get_file_time_stamp(const char* linebuff, time_t *secs, uint32_t *usecs) |
750 | 0 | { |
751 | 0 | int n; |
752 | 0 | struct tm tm; |
753 | 0 | #define MAX_MONTH_LETTERS 9 |
754 | 0 | char month[MAX_MONTH_LETTERS+1]; |
755 | |
|
756 | 0 | int day, year, hour, minute, second; |
757 | 0 | int scan_found; |
758 | | |
759 | | /* If line longer than expected, file is probably not correctly formatted */ |
760 | 0 | if (strlen(linebuff) > MAX_TIMESTAMP_LINE_LENGTH) |
761 | 0 | { |
762 | 0 | return false; |
763 | 0 | } |
764 | | |
765 | | /**************************************************************/ |
766 | | /* First is month. Read until get a space following the month */ |
767 | 0 | for (n=0; (n < MAX_MONTH_LETTERS) && (linebuff[n] != ' '); n++) |
768 | 0 | { |
769 | 0 | month[n] = linebuff[n]; |
770 | 0 | } |
771 | 0 | month[n] = '\0'; |
772 | |
|
773 | 0 | if (strcmp(month, "January" ) == 0) tm.tm_mon = 0; |
774 | 0 | else if (strcmp(month, "February" ) == 0) tm.tm_mon = 1; |
775 | 0 | else if (strcmp(month, "March" ) == 0) tm.tm_mon = 2; |
776 | 0 | else if (strcmp(month, "April" ) == 0) tm.tm_mon = 3; |
777 | 0 | else if (strcmp(month, "May" ) == 0) tm.tm_mon = 4; |
778 | 0 | else if (strcmp(month, "June" ) == 0) tm.tm_mon = 5; |
779 | 0 | else if (strcmp(month, "July" ) == 0) tm.tm_mon = 6; |
780 | 0 | else if (strcmp(month, "August" ) == 0) tm.tm_mon = 7; |
781 | 0 | else if (strcmp(month, "September") == 0) tm.tm_mon = 8; |
782 | 0 | else if (strcmp(month, "October" ) == 0) tm.tm_mon = 9; |
783 | 0 | else if (strcmp(month, "November" ) == 0) tm.tm_mon = 10; |
784 | 0 | else if (strcmp(month, "December" ) == 0) tm.tm_mon = 11; |
785 | 0 | else |
786 | 0 | { |
787 | | /* Give up if not found a properly-formatted date */ |
788 | 0 | return false; |
789 | 0 | } |
790 | | /* Skip space char */ |
791 | 0 | n++; |
792 | | |
793 | | /********************************************************/ |
794 | | /* Scan for remaining numerical fields */ |
795 | 0 | scan_found = sscanf(linebuff+n, "%d, %d %d:%d:%d.%u", |
796 | 0 | &day, &year, &hour, &minute, &second, usecs); |
797 | 0 | if (scan_found != 6) |
798 | 0 | { |
799 | | /* Give up if not all found */ |
800 | 0 | return false; |
801 | 0 | } |
802 | | |
803 | | /******************************************************/ |
804 | | /* Fill in remaining fields and return it in a time_t */ |
805 | 0 | tm.tm_year = year - 1900; |
806 | 0 | tm.tm_mday = day; |
807 | 0 | tm.tm_hour = hour; |
808 | 0 | tm.tm_min = minute; |
809 | 0 | tm.tm_sec = second; |
810 | 0 | tm.tm_isdst = -1; /* daylight saving time info not known */ |
811 | | |
812 | | /* Get seconds from this time */ |
813 | 0 | *secs = mktime(&tm); |
814 | | |
815 | | /* Multiply 4 digits given to get micro-seconds */ |
816 | 0 | *usecs = *usecs * 100; |
817 | |
|
818 | 0 | return true; |
819 | 0 | } |
820 | | |
821 | | static const struct supported_block_type log3gpp_blocks_supported[] = { |
822 | | /* |
823 | | * We support packet blocks, with no comments or other options. |
824 | | */ |
825 | | { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } |
826 | | }; |
827 | | |
828 | | static const struct file_type_subtype_info log3gpp_info = { |
829 | | "3GPP Log", "3gpp_log", "*.log", NULL, |
830 | | true, BLOCKS_SUPPORTED(log3gpp_blocks_supported), |
831 | | NULL, NULL, NULL |
832 | | }; |
833 | | |
834 | | void register_log3gpp(void) |
835 | 8 | { |
836 | 8 | log3gpp_file_type_subtype = wtap_register_file_type_subtype(&log3gpp_info); |
837 | | |
838 | | /* |
839 | | * Register name for backwards compatibility with the |
840 | | * wtap_filetypes table in Lua. |
841 | | */ |
842 | 8 | wtap_register_backwards_compatibility_lua_name("LOG_3GPP", |
843 | 8 | log3gpp_file_type_subtype); |
844 | 8 | } |
845 | | |
846 | | /* |
847 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
848 | | * |
849 | | * Local variables: |
850 | | * c-basic-offset: 4 |
851 | | * tab-width: 8 |
852 | | * indent-tabs-mode: nil |
853 | | * End: |
854 | | * |
855 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
856 | | * :indentSize=4:tabSize=8:noTabs=true: |
857 | | */ |