1 /* $OpenBSD: savefile.c,v 1.18 2023/08/10 15:47:05 sashan Exp $ */ 2 3 /* 4 * Copyright (c) 1993, 1994, 1995, 1996, 1997 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that: (1) source code distributions 9 * retain the above copyright notice and this paragraph in its entirety, (2) 10 * distributions including binary code include the above copyright notice and 11 * this paragraph in its entirety in the documentation or other materials 12 * provided with the distribution, and (3) all advertising materials mentioning 13 * features or use of this software display the following acknowledgement: 14 * ``This product includes software developed by the University of California, 15 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of 16 * the University nor the names of its contributors may be used to endorse 17 * or promote products derived from this software without specific prior 18 * written permission. 19 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED 20 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 21 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 22 * 23 * savefile.c - supports offline use of tcpdump 24 * Extraction/creation by Jeffrey Mogul, DECWRL 25 * Modified by Steve McCanne, LBL. 26 * 27 * Used to save the received packet headers, after filtering, to 28 * a file, and then read them later. 29 * The first record in the file contains saved values for the machine 30 * dependent values so we can print the dump file on any architecture. 31 */ 32 33 #include <sys/types.h> 34 #include <sys/time.h> 35 36 #include <errno.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <unistd.h> 41 42 #ifdef HAVE_OS_PROTO_H 43 #include "os-proto.h" 44 #endif 45 46 #include "pcap-int.h" 47 48 #define TCPDUMP_MAGIC 0xa1b2c3d4 49 50 /* 51 * We use the "receiver-makes-right" approach to byte order, 52 * because time is at a premium when we are writing the file. 53 * In other words, the pcap_file_header and pcap_pkthdr, 54 * records are written in host byte order. 55 * Note that the packets are always written in network byte order. 56 * 57 * ntoh[ls] aren't sufficient because we might need to swap on a big-endian 58 * machine (if the file was written in little-end order). 59 */ 60 #define SWAPLONG(y) \ 61 ((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff)) 62 #define SWAPSHORT(y) \ 63 ( (((y)&0xff)<<8) | ((u_short)((y)&0xff00)>>8) ) 64 65 #define SFERR_TRUNC 1 66 #define SFERR_BADVERSION 2 67 #define SFERR_BADF 3 68 #define SFERR_EOF 4 /* not really an error, just a status */ 69 70 static int 71 sf_write_header(FILE *fp, int linktype, int thiszone, int snaplen) 72 { 73 struct pcap_file_header hdr; 74 75 hdr.magic = TCPDUMP_MAGIC; 76 hdr.version_major = PCAP_VERSION_MAJOR; 77 hdr.version_minor = PCAP_VERSION_MINOR; 78 79 hdr.thiszone = thiszone; 80 hdr.snaplen = snaplen; 81 hdr.sigfigs = 0; 82 hdr.linktype = linktype; 83 84 if (fwrite((char *)&hdr, sizeof(hdr), 1, fp) != 1) 85 return (-1); 86 87 return (0); 88 } 89 90 static void 91 swap_hdr(struct pcap_file_header *hp) 92 { 93 hp->version_major = SWAPSHORT(hp->version_major); 94 hp->version_minor = SWAPSHORT(hp->version_minor); 95 hp->thiszone = SWAPLONG(hp->thiszone); 96 hp->sigfigs = SWAPLONG(hp->sigfigs); 97 hp->snaplen = SWAPLONG(hp->snaplen); 98 hp->linktype = SWAPLONG(hp->linktype); 99 } 100 101 pcap_t * 102 pcap_open_offline(const char *fname, char *errbuf) 103 { 104 pcap_t *p; 105 FILE *fp; 106 107 if (fname[0] == '-' && fname[1] == '\0') 108 fp = stdin; 109 else { 110 fp = fopen(fname, "r"); 111 if (fp == NULL) { 112 snprintf(errbuf, PCAP_ERRBUF_SIZE, "%s: %s", fname, 113 pcap_strerror(errno)); 114 return (NULL); 115 } 116 } 117 p = pcap_fopen_offline(fp, errbuf); 118 if (p == NULL) { 119 if (fp != stdin) 120 fclose(fp); 121 } 122 return (p); 123 } 124 125 pcap_t * 126 pcap_fopen_offline(FILE *fp, char *errbuf) 127 { 128 pcap_t *p; 129 struct pcap_file_header hdr; 130 int linklen; 131 132 p = calloc(1, sizeof(*p)); 133 if (p == NULL) { 134 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE); 135 return (NULL); 136 } 137 138 /* 139 * Set this field so we don't double-close in pcap_close! 140 */ 141 p->fd = -1; 142 143 if (fread((char *)&hdr, sizeof(hdr), 1, fp) != 1) { 144 snprintf(errbuf, PCAP_ERRBUF_SIZE, "fread: %s", 145 pcap_strerror(errno)); 146 goto bad; 147 } 148 if (hdr.magic != TCPDUMP_MAGIC) { 149 if (SWAPLONG(hdr.magic) != TCPDUMP_MAGIC) { 150 snprintf(errbuf, PCAP_ERRBUF_SIZE, 151 "bad dump file format"); 152 goto bad; 153 } 154 p->sf.swapped = 1; 155 swap_hdr(&hdr); 156 } 157 if (hdr.version_major < PCAP_VERSION_MAJOR) { 158 snprintf(errbuf, PCAP_ERRBUF_SIZE, "archaic file format"); 159 goto bad; 160 } 161 p->tzoff = hdr.thiszone; 162 p->snapshot = hdr.snaplen; 163 /* 164 * Handle some LINKTYPE_ values in pcap headers that aren't 165 * the same as the corresponding OpenBSD DLT_ values. 166 * 167 * Those LINKTYPE_ values were assigned for DLT_s whose 168 * numerical values differ between platforms, so that 169 * the link-layer type value in pcap file headers can 170 * be platform-independent. This means that code reading 171 * a pcap file doesn't have to know on which platform a 172 * file was written in order to read it correctly. 173 * 174 * See 175 * 176 * https://www.tcpdump.org/linktypes.html 177 * 178 * for the current list of LINKTYPE_ values and the corresponding 179 * DLT_ values. 180 */ 181 switch (hdr.linktype) { 182 183 case 100: 184 /* LINKTYPE_ATM_RFC1483 */ 185 p->linktype = DLT_ATM_RFC1483; 186 break; 187 188 case 101: 189 /* LINKTYPE_RAW */ 190 p->linktype = DLT_RAW; 191 break; 192 193 case 102: 194 /* LINKTYPE_SLIP_BSDOS */ 195 p->linktype = DLT_SLIP_BSDOS; 196 break; 197 198 case 103: 199 /* LINKTYPE_PPP_BSDOS */ 200 p->linktype = DLT_PPP_BSDOS; 201 break; 202 203 case 108: 204 /* LINKTYPE_LOOP */ 205 p->linktype = DLT_LOOP; 206 break; 207 208 case 109: 209 /* LINKTYPE_ENC */ 210 p->linktype = DLT_ENC; 211 break; 212 213 case 256: 214 /* LINKTYPE_PFSYNC */ 215 p->linktype = DLT_PFSYNC; 216 break; 217 218 default: 219 p->linktype = hdr.linktype; 220 break; 221 } 222 p->sf.rfile = fp; 223 p->bufsize = hdr.snaplen; 224 225 /* Align link header as required for proper data alignment */ 226 /* XXX should handle all types */ 227 switch (p->linktype) { 228 229 case DLT_EN10MB: 230 linklen = 14; 231 break; 232 233 case DLT_FDDI: 234 linklen = 13 + 8; /* fddi_header + llc */ 235 break; 236 237 case DLT_NULL: 238 default: 239 linklen = 0; 240 break; 241 } 242 243 if (p->bufsize < 0) 244 p->bufsize = BPF_MAXBUFSIZE; 245 p->sf.base = malloc(p->bufsize + BPF_ALIGNMENT); 246 if (p->sf.base == NULL) { 247 strlcpy(errbuf, "out of swap", PCAP_ERRBUF_SIZE); 248 goto bad; 249 } 250 p->buffer = p->sf.base + BPF_ALIGNMENT - (linklen % BPF_ALIGNMENT); 251 p->sf.version_major = hdr.version_major; 252 p->sf.version_minor = hdr.version_minor; 253 #ifdef PCAP_FDDIPAD 254 /* XXX padding only needed for kernel fcode */ 255 pcap_fddipad = 0; 256 #endif 257 258 return (p); 259 bad: 260 free(p); 261 return (NULL); 262 } 263 264 /* 265 * Read sf_readfile and return the next packet. Return the header in hdr 266 * and the contents in buf. Return 0 on success, SFERR_EOF if there were 267 * no more packets, and SFERR_TRUNC if a partial packet was encountered. 268 */ 269 static int 270 sf_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char *buf, int buflen) 271 { 272 FILE *fp = p->sf.rfile; 273 274 /* read the stamp */ 275 if (fread((char *)hdr, sizeof(struct pcap_pkthdr), 1, fp) != 1) { 276 /* probably an EOF, though could be a truncated packet */ 277 return (1); 278 } 279 280 if (p->sf.swapped) { 281 /* these were written in opposite byte order */ 282 hdr->caplen = SWAPLONG(hdr->caplen); 283 hdr->len = SWAPLONG(hdr->len); 284 hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec); 285 hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec); 286 } 287 /* 288 * We interchanged the caplen and len fields at version 2.3, 289 * in order to match the bpf header layout. But unfortunately 290 * some files were written with version 2.3 in their headers 291 * but without the interchanged fields. 292 */ 293 if (p->sf.version_minor < 3 || 294 (p->sf.version_minor == 3 && hdr->caplen > hdr->len)) { 295 int t = hdr->caplen; 296 hdr->caplen = hdr->len; 297 hdr->len = t; 298 } 299 300 if (hdr->caplen > buflen) { 301 /* 302 * This can happen due to Solaris 2.3 systems tripping 303 * over the BUFMOD problem and not setting the snapshot 304 * correctly in the savefile header. If the caplen isn't 305 * grossly wrong, try to salvage. 306 */ 307 static u_char *tp = NULL; 308 static int tsize = 0; 309 310 if (hdr->caplen > 65535) { 311 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 312 "bogus savefile header"); 313 return (-1); 314 } 315 316 if (tsize < hdr->caplen) { 317 tsize = ((hdr->caplen + 1023) / 1024) * 1024; 318 free(tp); 319 tp = malloc(tsize); 320 if (tp == NULL) { 321 tsize = 0; 322 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 323 "BUFMOD hack malloc"); 324 return (-1); 325 } 326 } 327 if (fread((char *)tp, hdr->caplen, 1, fp) != 1) { 328 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 329 "truncated dump file"); 330 return (-1); 331 } 332 /* 333 * We can only keep up to buflen bytes. Since caplen > buflen 334 * is exactly how we got here, we know we can only keep the 335 * first buflen bytes and must drop the remainder. Adjust 336 * caplen accordingly, so we don't get confused later as 337 * to how many bytes we have to play with. 338 */ 339 hdr->caplen = buflen; 340 memcpy((char *)buf, (char *)tp, buflen); 341 342 } else { 343 /* read the packet itself */ 344 345 if (fread((char *)buf, hdr->caplen, 1, fp) != 1) { 346 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, 347 "truncated dump file"); 348 return (-1); 349 } 350 } 351 return (0); 352 } 353 354 /* 355 * Print out packets stored in the file initialized by sf_read_init(). 356 * If cnt > 0, return after 'cnt' packets, otherwise continue until eof. 357 */ 358 int 359 pcap_offline_read(pcap_t *p, int cnt, pcap_handler callback, u_char *user) 360 { 361 struct bpf_insn *fcode = p->fcode.bf_insns; 362 int status = 0; 363 int n = 0; 364 365 while (status == 0) { 366 struct pcap_pkthdr h; 367 368 /* 369 * Has "pcap_breakloop()" been called? 370 * If so, return immediately - if we haven't read any 371 * packets, clear the flag and return -2 to indicate 372 * that we were told to break out of the loop, otherwise 373 * leave the flag set, so that the *next* call will break 374 * out of the loop without having read any packets, and 375 * return the number of packets we've processed so far. 376 */ 377 if (p->break_loop) { 378 if (n == 0) { 379 p->break_loop = 0; 380 return (PCAP_ERROR_BREAK); 381 } else 382 return (n); 383 } 384 385 status = sf_next_packet(p, &h, p->buffer, p->bufsize); 386 if (status) { 387 if (status == 1) 388 return (0); 389 return (status); 390 } 391 392 if (fcode == NULL || 393 bpf_filter(fcode, p->buffer, h.len, h.caplen)) { 394 (*callback)(user, &h, p->buffer); 395 if (++n >= cnt && cnt > 0) 396 break; 397 } 398 } 399 /*XXX this breaks semantics tcpslice expects */ 400 return (n); 401 } 402 403 /* 404 * Output a packet to the initialized dump file. 405 */ 406 void 407 pcap_dump(u_char *user, const struct pcap_pkthdr *h, const u_char *sp) 408 { 409 FILE *f; 410 411 f = (FILE *)user; 412 /* XXX we should check the return status */ 413 (void)fwrite((char *)h, sizeof(*h), 1, f); 414 (void)fwrite((char *)sp, h->caplen, 1, f); 415 } 416 417 static pcap_dumper_t * 418 pcap_setup_dump(pcap_t *p, FILE *f, const char *fname) 419 { 420 if (sf_write_header(f, p->linktype, p->tzoff, p->snapshot) == -1) { 421 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "Can't write to %s: %s", 422 fname, pcap_strerror(errno)); 423 if (f != stdout) 424 (void)fclose(f); 425 return (NULL); 426 } 427 return ((pcap_dumper_t *)f); 428 } 429 430 /* 431 * Initialize so that sf_write() will output to the file named 'fname'. 432 */ 433 pcap_dumper_t * 434 pcap_dump_open(pcap_t *p, const char *fname) 435 { 436 FILE *f; 437 if (fname[0] == '-' && fname[1] == '\0') 438 f = stdout; 439 else { 440 f = fopen(fname, "w"); 441 if (f == NULL) { 442 snprintf(p->errbuf, PCAP_ERRBUF_SIZE, "%s: %s", 443 fname, pcap_strerror(errno)); 444 return (NULL); 445 } 446 } 447 return (pcap_setup_dump(p, f, fname)); 448 } 449 450 /* 451 * Initialize so that sf_write() will output to the given stream. 452 */ 453 pcap_dumper_t * 454 pcap_dump_fopen(pcap_t *p, FILE *f) 455 { 456 return (pcap_setup_dump(p, f, "stream")); 457 } 458 459 FILE * 460 pcap_dump_file(pcap_dumper_t *p) 461 { 462 return ((FILE *)p); 463 } 464 465 long 466 pcap_dump_ftell(pcap_dumper_t *p) 467 { 468 return (ftell((FILE *)p)); 469 } 470 471 int 472 pcap_dump_flush(pcap_dumper_t *p) 473 { 474 475 if (fflush((FILE *)p) == EOF) 476 return (-1); 477 else 478 return (0); 479 } 480 481 void 482 pcap_dump_close(pcap_dumper_t *p) 483 { 484 485 #ifdef notyet 486 if (ferror((FILE *)p)) 487 return-an-error; 488 /* XXX should check return from fclose() too */ 489 #endif 490 (void)fclose((FILE *)p); 491 } 492