1 /* netscreen.c 2 * 3 * Juniper NetScreen snoop output parser 4 * Created by re-using a lot of code from cosine.c 5 * Copyright (c) 2007 by Sake Blok <sake@euronet.nl> 6 * 7 * Wiretap Library 8 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu> 9 * 10 * SPDX-License-Identifier: GPL-2.0-or-later 11 */ 12 13 #include "config.h" 14 #include "wtap-int.h" 15 #include "netscreen.h" 16 #include "file_wrappers.h" 17 18 #include <stdlib.h> 19 #include <string.h> 20 21 /* XXX TODO: 22 * 23 * o Construct a list of interfaces, with interface names, give 24 * them link-layer types based on the interface name and packet 25 * data, and supply interface IDs with each packet (i.e., make 26 * this supply a pcapng-style set of interfaces and associate 27 * packets with interfaces). This is probably the right way 28 * to "Pass the interface names and the traffic direction to either 29 * the frame-structure, a pseudo-header or use PPI." See the 30 * message at 31 * 32 * https://www.wireshark.org/lists/wireshark-dev/200708/msg00029.html 33 * 34 * to see whether any further discussion is still needed. I suspect 35 * it doesn't; pcapng existed at the time, as per the final 36 * message in that thread: 37 * 38 * https://www.wireshark.org/lists/wireshark-dev/200708/msg00039.html 39 * 40 * but I don't think we fully *supported* it at that point. Now 41 * that we do, we have the infrastructure to support this, except 42 * that we currently have no way to translate interface IDs to 43 * interface names in the "frame" dissector or to supply interface 44 * information as part of the packet metadata from Wiretap modules. 45 * That should be fixed so that we can show interface information, 46 * such as the interface name, in packet dissections from, for example, 47 * pcapng captures. 48 */ 49 50 static gboolean info_line(const gchar *line); 51 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info, 52 char *hdr); 53 static gboolean netscreen_check_file_type(wtap *wth, int *err, 54 gchar **err_info); 55 static gboolean netscreen_read(wtap *wth, wtap_rec *rec, Buffer *buf, 56 int *err, gchar **err_info, gint64 *data_offset); 57 static gboolean netscreen_seek_read(wtap *wth, gint64 seek_off, 58 wtap_rec *rec, Buffer *buf, int *err, gchar **err_info); 59 static gboolean parse_netscreen_packet(FILE_T fh, wtap_rec *rec, 60 Buffer* buf, char *line, int *err, gchar **err_info); 61 static int parse_single_hex_dump_line(char* rec, guint8 *buf, 62 guint byte_offset); 63 64 static int netscreen_file_type_subtype = -1; 65 66 void register_netscreen(void); 67 68 /* Returns TRUE if the line appears to be a line with protocol info. 69 Otherwise it returns FALSE. */ 70 static gboolean info_line(const gchar *line) 71 { 72 int i=NETSCREEN_SPACES_ON_INFO_LINE; 73 74 while (i-- > 0) { 75 if (g_ascii_isspace(*line)) { 76 line++; 77 continue; 78 } else { 79 return FALSE; 80 } 81 } 82 return TRUE; 83 } 84 85 /* Seeks to the beginning of the next packet, and returns the 86 byte offset. Copy the header line to hdr. Returns -1 on failure, 87 and sets "*err" to the error and sets "*err_info" to null or an 88 additional error string. */ 89 static gint64 netscreen_seek_next_packet(wtap *wth, int *err, gchar **err_info, 90 char *hdr) 91 { 92 gint64 cur_off; 93 char buf[NETSCREEN_LINE_LENGTH]; 94 95 while (1) { 96 cur_off = file_tell(wth->fh); 97 if (cur_off == -1) { 98 /* Error */ 99 *err = file_error(wth->fh, err_info); 100 return -1; 101 } 102 if (file_gets(buf, sizeof(buf), wth->fh) == NULL) { 103 /* EOF or error. */ 104 *err = file_error(wth->fh, err_info); 105 break; 106 } 107 if (strstr(buf, NETSCREEN_REC_MAGIC_STR1) || 108 strstr(buf, NETSCREEN_REC_MAGIC_STR2)) { 109 (void) g_strlcpy(hdr, buf, NETSCREEN_LINE_LENGTH); 110 return cur_off; 111 } 112 } 113 return -1; 114 } 115 116 /* Look through the first part of a file to see if this is 117 * NetScreen snoop output. 118 * 119 * Returns TRUE if it is, FALSE if it isn't or if we get an I/O error; 120 * if we get an I/O error, "*err" will be set to a non-zero value and 121 * "*err_info" is set to null or an additional error string. 122 */ 123 static gboolean netscreen_check_file_type(wtap *wth, int *err, gchar **err_info) 124 { 125 char buf[NETSCREEN_LINE_LENGTH]; 126 guint reclen, line; 127 128 buf[NETSCREEN_LINE_LENGTH-1] = '\0'; 129 130 for (line = 0; line < NETSCREEN_HEADER_LINES_TO_CHECK; line++) { 131 if (file_gets(buf, NETSCREEN_LINE_LENGTH, wth->fh) == NULL) { 132 /* EOF or error. */ 133 *err = file_error(wth->fh, err_info); 134 return FALSE; 135 } 136 137 reclen = (guint) strlen(buf); 138 if (reclen < MIN(strlen(NETSCREEN_HDR_MAGIC_STR1), strlen(NETSCREEN_HDR_MAGIC_STR2))) { 139 continue; 140 } 141 142 if (strstr(buf, NETSCREEN_HDR_MAGIC_STR1) || 143 strstr(buf, NETSCREEN_HDR_MAGIC_STR2)) { 144 return TRUE; 145 } 146 } 147 *err = 0; 148 return FALSE; 149 } 150 151 152 wtap_open_return_val netscreen_open(wtap *wth, int *err, gchar **err_info) 153 { 154 155 /* Look for a NetScreen snoop header line */ 156 if (!netscreen_check_file_type(wth, err, err_info)) { 157 if (*err != 0 && *err != WTAP_ERR_SHORT_READ) 158 return WTAP_OPEN_ERROR; 159 return WTAP_OPEN_NOT_MINE; 160 } 161 162 if (file_seek(wth->fh, 0L, SEEK_SET, err) == -1) /* rewind */ 163 return WTAP_OPEN_ERROR; 164 165 wth->file_encap = WTAP_ENCAP_UNKNOWN; 166 wth->file_type_subtype = netscreen_file_type_subtype; 167 wth->snapshot_length = 0; /* not known */ 168 wth->subtype_read = netscreen_read; 169 wth->subtype_seek_read = netscreen_seek_read; 170 wth->file_tsprec = WTAP_TSPREC_DSEC; 171 172 return WTAP_OPEN_MINE; 173 } 174 175 /* Find the next packet and parse it; called from wtap_read(). */ 176 static gboolean netscreen_read(wtap *wth, wtap_rec *rec, Buffer *buf, 177 int *err, gchar **err_info, gint64 *data_offset) 178 { 179 gint64 offset; 180 char line[NETSCREEN_LINE_LENGTH]; 181 182 /* Find the next packet */ 183 offset = netscreen_seek_next_packet(wth, err, err_info, line); 184 if (offset < 0) 185 return FALSE; 186 187 /* Parse the header and convert the ASCII hex dump to binary data */ 188 if (!parse_netscreen_packet(wth->fh, rec, buf, line, err, err_info)) 189 return FALSE; 190 191 /* 192 * If the per-file encapsulation isn't known, set it to this 193 * packet's encapsulation. 194 * 195 * If it *is* known, and it isn't this packet's encapsulation, 196 * set it to WTAP_ENCAP_PER_PACKET, as this file doesn't 197 * have a single encapsulation for all packets in the file. 198 */ 199 if (wth->file_encap == WTAP_ENCAP_UNKNOWN) 200 wth->file_encap = rec->rec_header.packet_header.pkt_encap; 201 else { 202 if (wth->file_encap != rec->rec_header.packet_header.pkt_encap) 203 wth->file_encap = WTAP_ENCAP_PER_PACKET; 204 } 205 206 *data_offset = offset; 207 return TRUE; 208 } 209 210 /* Used to read packets in random-access fashion */ 211 static gboolean 212 netscreen_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec, Buffer *buf, 213 int *err, gchar **err_info) 214 { 215 char line[NETSCREEN_LINE_LENGTH]; 216 217 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1) { 218 return FALSE; 219 } 220 221 if (file_gets(line, NETSCREEN_LINE_LENGTH, wth->random_fh) == NULL) { 222 *err = file_error(wth->random_fh, err_info); 223 if (*err == 0) { 224 *err = WTAP_ERR_SHORT_READ; 225 } 226 return FALSE; 227 } 228 229 return parse_netscreen_packet(wth->random_fh, rec, buf, line, 230 err, err_info); 231 } 232 233 /* Parses a packet record header. There are a few possible formats: 234 * 235 * XXX list extra formats here! 236 6843828.0: trust(o) len=98:00121ebbd132->00600868d659/0800 237 192.168.1.1 -> 192.168.1.10/6 238 vhl=45, tos=00, id=37739, frag=0000, ttl=64 tlen=84 239 tcp:ports 2222->2333, seq=3452113890, ack=1540618280, flag=5018/ACK 240 00 60 08 68 d6 59 00 12 1e bb d1 32 08 00 45 00 .`.h.Y.....2..E. 241 00 54 93 6b 00 00 40 06 63 dd c0 a8 01 01 c0 a8 .T.k..@.c....... 242 01 0a 08 ae 09 1d cd c3 13 e2 5b d3 f8 28 50 18 ..........[..(P. 243 1f d4 79 21 00 00 e7 76 89 64 16 e2 19 0a 80 09 ..y!...v.d...... 244 31 e7 04 28 04 58 f3 d9 b1 9f 3d 65 1a db d8 61 1..(.X....=e...a 245 2c 21 b6 d3 20 60 0c 8c 35 98 88 cf 20 91 0e a9 ,!...`..5....... 246 1d 0b .. 247 248 249 */ 250 static gboolean 251 parse_netscreen_packet(FILE_T fh, wtap_rec *rec, Buffer* buf, 252 char *line, int *err, gchar **err_info) 253 { 254 int pkt_len; 255 int sec; 256 int dsec; 257 char cap_int[NETSCREEN_MAX_INT_NAME_LENGTH]; 258 char direction[2]; 259 char cap_src[13]; 260 char cap_dst[13]; 261 guint8 *pd; 262 gchar *p; 263 int n, i = 0; 264 int offset = 0; 265 gchar dststr[13]; 266 267 rec->rec_type = REC_TYPE_PACKET; 268 rec->block = wtap_block_create(WTAP_BLOCK_PACKET); 269 rec->presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN; 270 /* Suppress compiler warnings */ 271 memset(cap_int, 0, sizeof(cap_int)); 272 memset(cap_dst, 0, sizeof(cap_dst)); 273 274 if (sscanf(line, "%9d.%9d: %15[a-z0-9/:.-](%1[io]) len=%9d:%12s->%12s/", 275 &sec, &dsec, cap_int, direction, &pkt_len, cap_src, cap_dst) < 5) { 276 *err = WTAP_ERR_BAD_FILE; 277 *err_info = g_strdup("netscreen: Can't parse packet-header"); 278 return -1; 279 } 280 if (pkt_len < 0) { 281 *err = WTAP_ERR_BAD_FILE; 282 *err_info = g_strdup("netscreen: packet header has a negative packet length"); 283 return FALSE; 284 } 285 if ((guint)pkt_len > WTAP_MAX_PACKET_SIZE_STANDARD) { 286 /* 287 * Probably a corrupt capture file; don't blow up trying 288 * to allocate space for an immensely-large packet. 289 */ 290 *err = WTAP_ERR_BAD_FILE; 291 *err_info = g_strdup_printf("netscreen: File has %u-byte packet, bigger than maximum of %u", 292 (guint)pkt_len, WTAP_MAX_PACKET_SIZE_STANDARD); 293 return FALSE; 294 } 295 296 /* 297 * If direction[0] is 'o', the direction is NETSCREEN_EGRESS, 298 * otherwise it's NETSCREEN_INGRESS. 299 */ 300 301 rec->ts.secs = sec; 302 rec->ts.nsecs = dsec * 100000000; 303 rec->rec_header.packet_header.len = pkt_len; 304 305 /* Make sure we have enough room for the packet */ 306 ws_buffer_assure_space(buf, pkt_len); 307 pd = ws_buffer_start_ptr(buf); 308 309 while(1) { 310 311 /* The last packet is not delimited by an empty line, but by EOF 312 * So accept EOF as a valid delimiter too 313 */ 314 if (file_gets(line, NETSCREEN_LINE_LENGTH, fh) == NULL) { 315 break; 316 } 317 318 /* 319 * Skip blanks. 320 * The number of blanks is not fixed - for wireless 321 * interfaces, there may be 14 extra spaces before 322 * the hex data. 323 */ 324 for (p = &line[0]; g_ascii_isspace(*p); p++) 325 ; 326 /* packets are delimited with empty lines */ 327 if (*p == '\0') { 328 break; 329 } 330 331 n = parse_single_hex_dump_line(p, pd, offset); 332 333 /* the smallest packet has a length of 6 bytes, if 334 * the first hex-data is less then check whether 335 * it is a info-line and act accordingly 336 */ 337 if (offset == 0 && n < 6) { 338 if (info_line(line)) { 339 if (++i <= NETSCREEN_MAX_INFOLINES) { 340 continue; 341 } 342 } else { 343 *err = WTAP_ERR_BAD_FILE; 344 *err_info = g_strdup("netscreen: cannot parse hex-data"); 345 return FALSE; 346 } 347 } 348 349 /* If there is no more data and the line was not empty, 350 * then there must be an error in the file 351 */ 352 if (n == -1) { 353 *err = WTAP_ERR_BAD_FILE; 354 *err_info = g_strdup("netscreen: cannot parse hex-data"); 355 return FALSE; 356 } 357 358 /* Adjust the offset to the data that was just added to the buffer */ 359 offset += n; 360 361 /* If there was more hex-data than was announced in the len=x 362 * header, then then there must be an error in the file 363 */ 364 if (offset > pkt_len) { 365 *err = WTAP_ERR_BAD_FILE; 366 *err_info = g_strdup("netscreen: too much hex-data"); 367 return FALSE; 368 } 369 } 370 371 /* 372 * Determine the encapsulation type, based on the 373 * first 4 characters of the interface name 374 * 375 * XXX convert this to a 'case' structure when adding more 376 * (non-ethernet) interfacetypes 377 */ 378 if (strncmp(cap_int, "adsl", 4) == 0) { 379 /* The ADSL interface can be bridged with or without 380 * PPP encapsulation. Check whether the first six bytes 381 * of the hex data are the same as the destination mac 382 * address in the header. If they are, assume ethernet 383 * LinkLayer or else PPP 384 */ 385 g_snprintf(dststr, 13, "%02x%02x%02x%02x%02x%02x", 386 pd[0], pd[1], pd[2], pd[3], pd[4], pd[5]); 387 if (strncmp(dststr, cap_dst, 12) == 0) 388 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET; 389 else 390 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP; 391 } 392 else if (strncmp(cap_int, "seri", 4) == 0) 393 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_PPP; 394 else 395 rec->rec_header.packet_header.pkt_encap = WTAP_ENCAP_ETHERNET; 396 397 rec->rec_header.packet_header.caplen = offset; 398 399 return TRUE; 400 } 401 402 /* Take a string representing one line from a hex dump, with leading white 403 * space removed, and converts the text to binary data. We place the bytes 404 * in the buffer at the specified offset. 405 * 406 * Returns number of bytes successfully read, -1 if bad. */ 407 static int 408 parse_single_hex_dump_line(char* rec, guint8 *buf, guint byte_offset) 409 { 410 int num_items_scanned; 411 guint8 character; 412 guint8 byte; 413 414 415 for (num_items_scanned = 0; num_items_scanned < 16; num_items_scanned++) { 416 character = *rec++; 417 if (character >= '0' && character <= '9') 418 byte = character - '0' + 0; 419 else if (character >= 'A' && character <= 'F') 420 byte = character - 'A' + 0xA; 421 else if (character >= 'a' && character <= 'f') 422 byte = character - 'a' + 0xa; 423 else if (character == ' ' || character == '\r' || character == '\n' || character == '\0') { 424 /* Nothing more to parse */ 425 break; 426 } else 427 return -1; /* not a hex digit, space before ASCII dump, or EOL */ 428 byte <<= 4; 429 character = *rec++ & 0xFF; 430 if (character >= '0' && character <= '9') 431 byte += character - '0' + 0; 432 else if (character >= 'A' && character <= 'F') 433 byte += character - 'A' + 0xA; 434 else if (character >= 'a' && character <= 'f') 435 byte += character - 'a' + 0xa; 436 else 437 return -1; /* not a hex digit */ 438 buf[byte_offset + num_items_scanned] = byte; 439 character = *rec++ & 0xFF; 440 if (character == '\0' || character == '\r' || character == '\n') { 441 /* Nothing more to parse */ 442 break; 443 } else if (character != ' ') { 444 /* not space before ASCII dump */ 445 return -1; 446 } 447 } 448 if (num_items_scanned == 0) 449 return -1; 450 451 return num_items_scanned; 452 } 453 454 static const struct supported_block_type netscreen_blocks_supported[] = { 455 /* 456 * We support packet blocks, with no comments or other options. 457 */ 458 { WTAP_BLOCK_PACKET, MULTIPLE_BLOCKS_SUPPORTED, NO_OPTIONS_SUPPORTED } 459 }; 460 461 static const struct file_type_subtype_info netscreen_info = { 462 "NetScreen snoop text file", "netscreen", "txt", NULL, 463 FALSE, BLOCKS_SUPPORTED(netscreen_blocks_supported), 464 NULL, NULL, NULL 465 }; 466 467 void register_netscreen(void) 468 { 469 netscreen_file_type_subtype = wtap_register_file_type_subtype(&netscreen_info); 470 471 /* 472 * Register name for backwards compatibility with the 473 * wtap_filetypes table in Lua. 474 */ 475 wtap_register_backwards_compatibility_lua_name("NETSCREEN", 476 netscreen_file_type_subtype); 477 } 478 479 /* 480 * Editor modelines - https://www.wireshark.org/tools/modelines.html 481 * 482 * Local variables: 483 * c-basic-offset: 8 484 * tab-width: 8 485 * indent-tabs-mode: t 486 * End: 487 * 488 * vi: set shiftwidth=8 tabstop=8 noexpandtab: 489 * :indentSize=8:tabSize=8:noTabs=false: 490 */ 491