Skip to content

Instantly share code, notes, and snippets.

@netravnen
Last active August 9, 2022 13:08
Show Gist options
  • Save netravnen/afef18a4bc644b837df85c1f8b2a24de to your computer and use it in GitHub Desktop.
Save netravnen/afef18a4bc644b837df85c1f8b2a24de to your computer and use it in GitHub Desktop.
The C program has been modified to be able to compile on Ubuntu 22.04 LTS with `clang -Wall` - Original copyright holder is user @tobez
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42)
* <[email protected]> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Anton Berezin
* ----------------------------------------------------------------------------
*/
/*
* $Id: pcaptail.c,v 1.1 2006/08/21 11:51:17 tobez Exp $
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <pcap.h>
int debug = 0;
/* XXX handle different endianness ! */
unsigned int
looks_like_packet(
struct pcap_pkthdr *hdr,
struct timeval *ts,
unsigned int snaplen,
int swap)
{
if (hdr->ts.tv_usec > 1000000) {
if (debug) fprintf(stderr, "Cannot have %d microseconds\n", hdr->ts.tv_usec);
return 0; /* cannot have that many microseconds */
}
if (hdr->ts.tv_sec < ts->tv_sec) {
if (debug) fprintf(stderr, "Decreasing seconds: %d, was %d\n", hdr->ts.tv_sec, ts->tv_sec);
return 0; /* seconds are non-decreasing */
}
if (hdr->ts.tv_sec == ts->tv_sec && hdr->ts.tv_usec < ts->tv_usec) {
if (debug) fprintf(stderr, "Decreasing time: %d.%d, was %d.%d\n",
hdr->ts.tv_sec, hdr->ts.tv_usec,
hdr->ts.tv_usec, ts->tv_usec);
return 0; /* time is non-decreasing */
}
if (hdr->caplen > snaplen) {
if (debug) fprintf(stderr, "caplen %u > snaplen %u\n", hdr->caplen, snaplen);
return 0; /* cannot have more than snaplen here */
}
if (hdr->len < hdr->caplen)
return 0; /* packet length cannot be smaller then recorder portion */
if (hdr->len > hdr->caplen && hdr->caplen != snaplen)
/* Packet length can only be larger than the recorded portion
* if we have the full snaplen recorded. */
return 0;
/* Ok, so far it looks like a packet to us,
* return the full length to skip, including the
* header size. */
return sizeof(*hdr) + hdr->caplen;
}
unsigned int
get_file_header(FILE *dump, struct pcap_file_header *fhdr)
{
size_t read;
read = fread(fhdr, sizeof *fhdr, 1, dump);
if (read != 1) {
perror("reading pcap header");
exit(1);
}
if (fhdr->magic != 0xa1b2c3d4) {
fprintf(stderr, "not a recognized pcap file\n");
exit(1);
}
return fhdr->snaplen;
}
off_t
seek_to_last_sequentially(
FILE *dump, off_t pos, off_t size,
struct timeval *ts, unsigned int snaplen, int *np)
{
struct pcap_pkthdr hdr;
unsigned int skip;
int n = 0;
while (pos + sizeof hdr < size) {
if (fseeko(dump, pos, SEEK_SET) != 0)
return -1;
if (fread(&hdr, sizeof hdr, 1, dump) != 1)
return -1;
skip = looks_like_packet(&hdr, ts, snaplen, 0);
if (skip <= sizeof hdr)
return -1;
n++;
if (pos + skip >= size) {
if (np) *np = n;
return pos;
}
pos += skip;
}
if (np) *np = n;
return pos;
}
off_t
try_to_get_packets(FILE *dump, off_t pos, off_t size,
struct timeval *ts0, unsigned int snaplen)
{
struct timeval ts;
off_t npos;
int cnt;
while (pos + sizeof(struct pcap_pkthdr) < size) {
ts = *ts0;
npos = seek_to_last_sequentially(dump, pos, size, &ts, snaplen, &cnt);
if (npos > 0 && cnt >= 10) {
// fprintf(stderr, "Hooray(%d:%lld)!\n", cnt, npos);
return npos;
}
pos++;
}
return -1;
}
off_t
get_size(FILE *dump)
{
if (fseeko(dump, 0, SEEK_END) != 0)
return -1;
return ftello(dump);
}
off_t
find_last_packet(FILE *dump, unsigned int snaplen, struct timeval *created)
{
off_t cur_size, r;
size_t chunk_size = 20000;
struct stat sb;
if (fstat(fileno(dump), &sb) != 0) {
perror("cannot stat pcap file");
exit(1);
}
created->tv_sec = sb.st_birthtime;
created->tv_usec = 0;
cur_size = get_size(dump);
if (cur_size < sizeof(struct pcap_file_header)) {
fprintf(stderr, "pcap file is too small\n");
exit(1);
}
while (chunk_size < snaplen * 10 * 2) {
if (cur_size < chunk_size)
return seek_to_last_sequentially(dump, sizeof(struct pcap_file_header), cur_size, created, snaplen, NULL);
r = try_to_get_packets(dump, cur_size - chunk_size, cur_size, created, snaplen);
if (r > 0)
return r;
chunk_size *= 2;
}
/* fall back to the worst case */
return seek_to_last_sequentially(dump, sizeof(struct pcap_file_header), cur_size, created, snaplen, NULL);
}
int
do_tail(FILE *dump, off_t pos, unsigned int snaplen, struct timeval *ts)
{
off_t size;
struct pcap_pkthdr hdr;
unsigned int skip;
unsigned char *buf;
for (;;) {
size = get_size(dump);
if (size < pos)
return -1;
if (size < pos + sizeof hdr) {
usleep(100000);
continue;
}
// fprintf(stderr, "new packet at %lld\n", pos);
if (fseeko(dump, pos, SEEK_SET) != 0) {
perror("seek error");
return -1;
}
if (fread(&hdr, sizeof hdr, 1, dump) != 1) {
perror("read error");
return -1;
}
skip = looks_like_packet(&hdr, ts, snaplen, 0);
if (skip <= sizeof hdr) {
perror("corrupted pcap stream");
return -1;
}
full_packet_here:
if (pos + skip >= size) {
usleep(100000);
size = get_size(dump);
if (size < pos) {
perror("seek error");
return -1;
}
goto full_packet_here;
}
/* print hdr + packet */
buf = malloc(skip);
if (!buf) {
perror("cannot allocate buffer");
return -1;
}
if (fseeko(dump, pos, SEEK_SET) != 0) {
perror("seek error");
return -1;
}
if (fread(buf, 1, skip, dump) != skip) {
perror("read error");
return -1;
}
fwrite(buf, 1, skip, stdout);
free(buf);
// fprintf(stderr, "full packet at %lld, skip: %u\n", pos, skip);
pos += skip;
}
return 0;
}
int
main(int argc, char **argv)
{
struct pcap_file_header fhdr;
unsigned int snaplen;
FILE *dump;
off_t pos;
struct timeval ts;
if (argc != 2 || !argv[1]) {
fprintf(stderr, "usage:\n\t%s pcapfile\n", argv[0]);
return 1;
}
dump = fopen(argv[1], "r");
snaplen = get_file_header(dump, &fhdr);
if (snaplen == 0)
return 1;
pos = find_last_packet(dump, snaplen, &ts);
if (pos <= 0) {
fprintf(stderr, "unable to find the last packet\n");
exit(1);
}
setvbuf(stdout, NULL, _IONBF, 0);
fwrite(&fhdr, sizeof fhdr, 1, stdout);
do_tail(dump, pos, snaplen, &ts);
exit(1);
return 0;
}
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42)
* <[email protected]> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return. Anton Berezin
* ----------------------------------------------------------------------------
*/
/*
* $Id: pcaptail.c,v 1.1 2006/08/21 11:51:17 tobez Exp $
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pcap.h>
int debug = 0;
/* XXX handle different endianness ! */
unsigned int
looks_like_packet(
struct pcap_pkthdr *hdr,
struct timeval *ts,
unsigned int snaplen,
int swap)
{
if (hdr->ts.tv_usec > 1000000) {
if (debug) fprintf(stderr, "Cannot have %ld microseconds\n", hdr->ts.tv_usec);
return 0; /* cannot have that many microseconds */
}
if (hdr->ts.tv_sec < ts->tv_sec) {
if (debug) fprintf(stderr, "Decreasing seconds: %ld, was %ld\n", hdr->ts.tv_sec, ts->tv_sec);
return 0; /* seconds are non-decreasing */
}
if (hdr->ts.tv_sec == ts->tv_sec && hdr->ts.tv_usec < ts->tv_usec) {
if (debug) fprintf(stderr, "Decreasing time: %ld.%ld, was %ld.%ld\n",
hdr->ts.tv_sec, hdr->ts.tv_usec,
hdr->ts.tv_usec, ts->tv_usec);
return 0; /* time is non-decreasing */
}
if (hdr->caplen > snaplen) {
if (debug) fprintf(stderr, "caplen %u > snaplen %u\n", hdr->caplen, snaplen);
return 0; /* cannot have more than snaplen here */
}
if (hdr->len < hdr->caplen)
return 0; /* packet length cannot be smaller then recorder portion */
if (hdr->len > hdr->caplen && hdr->caplen != snaplen)
/* Packet length can only be larger than the recorded portion
* if we have the full snaplen recorded. */
return 0;
/* Ok, so far it looks like a packet to us,
* return the full length to skip, including the
* header size. */
return sizeof(*hdr) + hdr->caplen;
}
unsigned int
get_file_header(FILE *dump, struct pcap_file_header *fhdr)
{
size_t read;
read = fread(fhdr, sizeof *fhdr, 1, dump);
if (read != 1) {
perror("reading pcap header");
exit(1);
}
if (fhdr->magic != 0xa1b2c3d4) {
fprintf(stderr, "not a recognized pcap file\n");
exit(1);
}
return fhdr->snaplen;
}
off_t
seek_to_last_sequentially(
FILE *dump, off_t pos, off_t size,
struct timeval *ts, unsigned int snaplen, int *np)
{
struct pcap_pkthdr hdr;
unsigned int skip;
int n = 0;
while (pos + sizeof hdr < size) {
if (fseeko(dump, pos, SEEK_SET) != 0)
return -1;
if (fread(&hdr, sizeof hdr, 1, dump) != 1)
return -1;
skip = looks_like_packet(&hdr, ts, snaplen, 0);
if (skip <= sizeof hdr)
return -1;
n++;
if (pos + skip >= size) {
if (np) *np = n;
return pos;
}
pos += skip;
}
if (np) *np = n;
return pos;
}
off_t
try_to_get_packets(FILE *dump, off_t pos, off_t size,
struct timeval *ts0, unsigned int snaplen)
{
struct timeval ts;
off_t npos;
int cnt;
while (pos + sizeof(struct pcap_pkthdr) < size) {
ts = *ts0;
npos = seek_to_last_sequentially(dump, pos, size, &ts, snaplen, &cnt);
if (npos > 0 && cnt >= 10) {
// fprintf(stderr, "Hooray(%d:%lld)!\n", cnt, npos);
return npos;
}
pos++;
}
return -1;
}
off_t
get_size(FILE *dump)
{
if (fseeko(dump, 0, SEEK_END) != 0)
return -1;
return ftello(dump);
}
off_t
find_last_packet(FILE *dump, unsigned int snaplen, struct timeval *created)
{
off_t cur_size, r;
size_t chunk_size = 20000;
struct stat sb;
if (fstat(fileno(dump), &sb) != 0) {
perror("cannot stat pcap file");
exit(1);
}
// error: no member named 'st_birthtime' in 'struct stat'
// st_birthtime exists on *BSD and OS X
// https://github.com/cython/cython/issues/3024
// https://stackoverflow.com/a/5929466
created->tv_sec = sb.st_mtime;
created->tv_usec = 0;
cur_size = get_size(dump);
if (cur_size < sizeof(struct pcap_file_header)) {
fprintf(stderr, "pcap file is too small\n");
exit(1);
}
while (chunk_size < snaplen * 10 * 2) {
if (cur_size < chunk_size)
return seek_to_last_sequentially(dump, sizeof(struct pcap_file_header), cur_size, created, snaplen, NULL);
r = try_to_get_packets(dump, cur_size - chunk_size, cur_size, created, snaplen);
if (r > 0)
return r;
chunk_size *= 2;
}
/* fall back to the worst case */
return seek_to_last_sequentially(dump, sizeof(struct pcap_file_header), cur_size, created, snaplen, NULL);
}
int
do_tail(FILE *dump, off_t pos, unsigned int snaplen, struct timeval *ts)
{
off_t size;
struct pcap_pkthdr hdr;
unsigned int skip;
unsigned char *buf;
for (;;) {
size = get_size(dump);
if (size < pos)
return -1;
if (size < pos + sizeof hdr) {
usleep(100000);
continue;
}
// fprintf(stderr, "new packet at %lld\n", pos);
if (fseeko(dump, pos, SEEK_SET) != 0) {
perror("seek error");
return -1;
}
if (fread(&hdr, sizeof hdr, 1, dump) != 1) {
perror("read error");
return -1;
}
skip = looks_like_packet(&hdr, ts, snaplen, 0);
if (skip <= sizeof hdr) {
perror("corrupted pcap stream");
return -1;
}
full_packet_here:
if (pos + skip >= size) {
usleep(100000);
size = get_size(dump);
if (size < pos) {
perror("seek error");
return -1;
}
goto full_packet_here;
}
/* print hdr + packet */
buf = malloc(skip);
if (!buf) {
perror("cannot allocate buffer");
return -1;
}
if (fseeko(dump, pos, SEEK_SET) != 0) {
perror("seek error");
return -1;
}
if (fread(buf, 1, skip, dump) != skip) {
perror("read error");
return -1;
}
fwrite(buf, 1, skip, stdout);
free(buf);
// fprintf(stderr, "full packet at %lld, skip: %u\n", pos, skip);
pos += skip;
}
return 0;
}
int
main(int argc, char **argv)
{
struct pcap_file_header fhdr;
unsigned int snaplen;
FILE *dump;
off_t pos;
struct timeval ts;
if (argc != 2 || !argv[1]) {
fprintf(stderr, "usage:\n\t%s pcapfile\n", argv[0]);
return 1;
}
dump = fopen(argv[1], "r");
snaplen = get_file_header(dump, &fhdr);
if (snaplen == 0)
return 1;
pos = find_last_packet(dump, snaplen, &ts);
if (pos <= 0) {
fprintf(stderr, "unable to find the last packet\n");
exit(1);
}
setvbuf(stdout, NULL, _IONBF, 0);
fwrite(&fhdr, sizeof fhdr, 1, stdout);
do_tail(dump, pos, snaplen, &ts);
exit(1);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment