Support for writing compressed captures on the fly

Add support for writing compressed captures on the fly (gzip and LZ4,
as supported for compressing while writing elsewhere) through writecap,
by linking in file_wrappers statically.

Remove the "compress a multiple file (but not ringbuffer) capture
at file rotation time" option in preference to this.

Fix #9311. Fix #19159
This commit is contained in:
John Thacker 2025-01-21 09:47:46 -05:00 committed by AndersBroman
parent 20f8cef7ef
commit d9a42ee5eb
20 changed files with 588 additions and 381 deletions

View File

@ -3458,8 +3458,6 @@ if(BUILD_dumpcap AND PCAP_FOUND)
wsutil_static
pcap::pcap
${CAP_LIBRARIES}
${ZLIB_LIBRARIES}
${ZLIBNG_LIBRARIES}
${NL_LIBRARIES}
${APPLE_CORE_FOUNDATION_LIBRARY}
${APPLE_SYSTEM_CONFIGURATION_LIBRARY}
@ -3505,7 +3503,7 @@ if(BUILD_dumpcap AND PCAP_FOUND)
add_executable(dumpcap ${dumpcap_FILES})
set_extra_executable_properties(dumpcap "Executables")
target_link_libraries(dumpcap ${dumpcap_LIBS})
target_include_directories(dumpcap SYSTEM PRIVATE ${ZLIB_INCLUDE_DIRS} ${ZLIBNG_INCLUDE_DIRS} ${NL_INCLUDE_DIRS})
target_include_directories(dumpcap SYSTEM PRIVATE ${NL_INCLUDE_DIRS})
target_compile_definitions(dumpcap PRIVATE ENABLE_STATIC)
executable_link_mingw_unicode(dumpcap)
install(TARGETS dumpcap

View File

@ -51,6 +51,11 @@ The following features are either new or have been significantly updated since v
* Source packages are now compressed using zstd.
* Live captures can be compressed while writing. (Previously there was
support for compressing when performing multiple file capture, at file
rotation time.) The `--compress` option in TShark works on live captures
as well. wsbuglink:9311[]
* Absolute time fields, regardless of field display in the Packet Details,
are always written in ISO 8601 format in UTC with -T json. This was already
the case for -T ek since version 4.2.0. JSON is primarily a data interchange

View File

@ -470,6 +470,13 @@ This option may be specified multiple times. Note that Wireshark
currently only displays the first comment of a capture file.
--
////
The --compress-type option is not documented anywhere.
Should we document it? Deprecate it in favor of using compress as
used in other tools?
////
--list-time-stamp-types::
List time stamp types supported for the interface. If no time stamp type can be
set, no time stamp types are listed.

View File

@ -139,8 +139,7 @@ a compression method as well; the list of supported compression methods
for writing can be displayed by the *--compress* method without an
argument. If the *--compress* option is not given, then the desired
compression method, if any, is deduced from the extension of the filename
given as argument to the *-w* option. Compression is not supported for
live capture.
given as argument to the *-w* option.
When capturing packets, *TShark* writes to the standard error an
initial line listing the interfaces from which packets are being
@ -2348,17 +2347,11 @@ Compress the output file using the type compression format.
*--compress* with no argument provides a list of the compression formats supported
for writing. The type given takes precedence over the extension of __outfile__.
NOTE: This option only works with the *-r* option, i.e., when reading a
capture file, not for live captures.
////
The --compress-type option is not documented anywhere; it works with live captures,
but only a limited set of capture options (multiple file mode (-b), but not
ringbuffer mode (no -b files), and only compressed upon file rotation.)
It works with TShark and dumpcap, but not from the command line in Wireshark
(though the Wireshark GUI can pass the option to dumpcap.)
The --compress-type option is not documented anywhere; it works with live
captures only.
Should we document it? Deprecate it in favor of also using compress? Do nothing
until it has closer feature parity to *--compress* but for captures?
Should we document it? Deprecate it in favor of also using compress?
////
--

View File

@ -358,9 +358,8 @@ typedef struct _loop_data {
GArray *saved_idbs; /**< Array of saved_idb_t, written when we have a new section or output file. */
GRWLock saved_shb_idb_lock; /**< Saved IDB RW mutex */
/* output file(s) */
FILE *pdh;
pcapio_writer* pdh;
int save_file_fd;
char *io_buffer; /**< Our IO buffer if we increase the size from the standard size */
uint64_t bytes_written; /**< Bytes written for the current file. */
/* autostop conditions */
int packets_written; /**< Packets written for the current file. */
@ -3525,25 +3524,7 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err
if (capture_opts->multi_files_on) {
ld->pdh = ringbuf_init_libpcap_fdopen(&err);
} else {
ld->pdh = ws_fdopen(ld->save_file_fd, "wb");
if (ld->pdh == NULL) {
err = errno;
} else {
size_t buffsize = IO_BUF_SIZE;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
ws_statb64 statb;
if (ws_fstat64(ld->save_file_fd, &statb) == 0) {
if (statb.st_blksize > IO_BUF_SIZE) {
buffsize = statb.st_blksize;
}
}
#endif
/* Increase the size of the IO buffer */
ld->io_buffer = (char *)g_malloc(buffsize);
setvbuf(ld->pdh, ld->io_buffer, _IOFBF, buffsize);
ws_debug("capture_loop_init_output: buffsize %zu", buffsize);
}
ld->pdh = writecap_fdopen(ld->save_file_fd, wtap_name_to_compression_type(capture_opts->compress_type), &err);
}
if (ld->pdh) {
bool successful;
@ -3561,10 +3542,8 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err
pcap_src->ts_nsec, &ld->bytes_written, &err);
}
if (!successful) {
fclose(ld->pdh);
writecap_close(ld->pdh, NULL);
ld->pdh = NULL;
g_free(ld->io_buffer);
ld->io_buffer = NULL;
}
}
@ -3628,16 +3607,7 @@ capture_loop_close_output(capture_options *capture_opts, loop_data *ld, int *err
}
}
}
if (fclose(ld->pdh) == EOF) {
if (err_close != NULL) {
*err_close = errno;
}
success = false;
} else {
success = true;
}
g_free(ld->io_buffer);
ld->io_buffer = NULL;
success = writecap_close(ld->pdh, err_close);
return success;
}
}
@ -4005,8 +3975,14 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
} else {
suffix = ".pcap";
}
const char* compression_suffix = wtap_compression_type_extension(wtap_name_to_compression_type(capture_opts->compress_type));
/* If not compressed, compression_suffix is NULL and g_strjoin
* handles the string list terminating early correctly.
*/
suffix = g_strjoin(".", suffix, compression_suffix, NULL);
*save_file_fd = create_tempfile(capture_opts->temp_dir, &capfile_name, prefix, suffix, &err_tempfile);
g_free(prefix);
g_free(suffix);
is_tempfile = true;
}
@ -4086,11 +4062,9 @@ do_file_switch_or_stop(capture_options *capture_opts)
}
if (!successful) {
fclose(global_ld.pdh);
writecap_close(global_ld.pdh, NULL);
global_ld.pdh = NULL;
global_ld.go = false;
g_free(global_ld.io_buffer);
global_ld.io_buffer = NULL;
return false;
}
if (global_ld.file_duration_timer) {
@ -4099,7 +4073,7 @@ do_file_switch_or_stop(capture_options *capture_opts)
if (global_ld.next_interval_time) {
global_ld.next_interval_time = get_next_time_interval(global_ld.interval_s);
}
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
if (global_ld.inpkts_to_sync_pipe) {
if (!quiet)
report_packet_count(global_ld.inpkts_to_sync_pipe);
@ -4244,7 +4218,6 @@ capture_loop_start(capture_options *capture_opts, bool *stats_known, struct pcap
global_ld.err = 0; /* no error seen yet */
global_ld.pdh = NULL;
global_ld.save_file_fd = -1;
global_ld.io_buffer = NULL;
global_ld.file_count = 0;
global_ld.file_duration_timer = NULL;
global_ld.next_interval_time = 0;
@ -4318,7 +4291,7 @@ capture_loop_start(capture_options *capture_opts, bool *stats_known, struct pcap
message to our parent so that they'll open the capture file and
update its windows to indicate that we have a live capture in
progress. */
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
report_new_capture_file(capture_opts->save_file);
}
@ -4411,7 +4384,7 @@ capture_loop_start(capture_options *capture_opts, bool *stats_known, struct pcap
if (inpkts > 0) {
if (capture_opts->output_to_pipe) {
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
}
} /* inpkts */
@ -4441,7 +4414,7 @@ capture_loop_start(capture_options *capture_opts, bool *stats_known, struct pcap
/* Let the parent process know. */
if (global_ld.inpkts_to_sync_pipe) {
/* do sync here */
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
/* Send our parent a message saying we've written out
"global_ld.inpkts_to_sync_pipe" packets to the capture file. */
@ -4489,7 +4462,7 @@ capture_loop_start(capture_options *capture_opts, bool *stats_known, struct pcap
break;
}
if (capture_opts->output_to_pipe) {
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
}
}
}
@ -4811,13 +4784,13 @@ capture_loop_wrote_one_packet(capture_src *pcap_src) {
/* check -c NUM */
if (global_capture_opts.has_autostop_packets && global_ld.packets_captured >= global_capture_opts.autostop_packets) {
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
global_ld.go = false;
return;
}
/* check -a packets:NUM (treat like -c NUM) */
if (global_capture_opts.has_autostop_written_packets && global_ld.packets_captured >= global_capture_opts.autostop_written_packets) {
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
global_ld.go = false;
return;
}
@ -4881,7 +4854,7 @@ capture_loop_write_pcapng_cb(capture_src *pcap_src, const pcapng_block_header_t
bh->block_total_length,
&global_ld.bytes_written, &err);
fflush(global_ld.pdh);
writecap_flush(global_ld.pdh, NULL);
if (!successful) {
global_ld.go = false;
global_ld.err = err;

View File

@ -894,7 +894,7 @@ static int parse_line_asa(uint8_t* packet, unsigned* offset, char* line, uint32_
}
/* IOS: Reads response and parses buffer till prompt received */
static int process_buffer_response_ios(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
static int process_buffer_response_ios(ssh_channel channel, uint8_t* packet, pcapio_writer* fp, const uint32_t count, uint32_t *processed_packets)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 1;
@ -928,7 +928,7 @@ static int process_buffer_response_ios(ssh_channel channel, uint8_t* packet, FIL
ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break;
}
fflush(fp);
writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++;
}
@ -958,7 +958,7 @@ static int process_buffer_response_ios(ssh_channel channel, uint8_t* packet, FIL
}
/* IOS: Queries buffer content and reads it */
static void ssh_loop_read_ios(ssh_channel channel, FILE* fp, const uint32_t count)
static void ssh_loop_read_ios(ssh_channel channel, pcapio_writer* fp, const uint32_t count)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint8_t* packet;
@ -1016,7 +1016,7 @@ static void ssh_loop_read_ios(ssh_channel channel, FILE* fp, const uint32_t coun
}
/* IOS-XE 16: Reads response and parses buffer till prompt received */
static int process_buffer_response_ios_xe_16(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
static int process_buffer_response_ios_xe_16(ssh_channel channel, uint8_t* packet, pcapio_writer* fp, const uint32_t count, uint32_t *processed_packets)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 1;
@ -1048,7 +1048,7 @@ static int process_buffer_response_ios_xe_16(ssh_channel channel, uint8_t* packe
ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break;
}
fflush(fp);
writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++;
}
@ -1078,7 +1078,7 @@ static int process_buffer_response_ios_xe_16(ssh_channel channel, uint8_t* packe
}
/* IOS-XE 17: Reads response and parses buffer till prompt received */
static int process_buffer_response_ios_xe_17(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets)
static int process_buffer_response_ios_xe_17(ssh_channel channel, uint8_t* packet, pcapio_writer* fp, const uint32_t count, uint32_t *processed_packets)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 1;
@ -1122,7 +1122,7 @@ static int process_buffer_response_ios_xe_17(ssh_channel channel, uint8_t* packe
ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break;
}
fflush(fp);
writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++;
}
@ -1146,7 +1146,7 @@ static int process_buffer_response_ios_xe_17(ssh_channel channel, uint8_t* packe
}
/* IOS-XE 16: Queries buffer content and reads it */
static void ssh_loop_read_ios_xe_16(ssh_channel channel, FILE* fp, const uint32_t count)
static void ssh_loop_read_ios_xe_16(ssh_channel channel, pcapio_writer* fp, const uint32_t count)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint8_t* packet;
@ -1204,7 +1204,7 @@ static void ssh_loop_read_ios_xe_16(ssh_channel channel, FILE* fp, const uint32_
}
/* IOS-XE 17: Queries buffer content and reads it */
static void ssh_loop_read_ios_xe_17(ssh_channel channel, FILE* fp, const uint32_t count)
static void ssh_loop_read_ios_xe_17(ssh_channel channel, pcapio_writer* fp, const uint32_t count)
{
uint8_t* packet;
uint32_t processed_packets = 0;
@ -1234,7 +1234,7 @@ static void ssh_loop_read_ios_xe_17(ssh_channel channel, FILE* fp, const uint32_
}
/* ASA: Reads response and parses buffer till prompt end of packet received */
static int process_buffer_response_asa(ssh_channel channel, uint8_t* packet, FILE* fp, const uint32_t count, uint32_t *processed_packets, uint32_t *current_max)
static int process_buffer_response_asa(ssh_channel channel, uint8_t* packet, pcapio_writer* fp, const uint32_t count, uint32_t *processed_packets, uint32_t *current_max)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 1;
@ -1273,7 +1273,7 @@ static int process_buffer_response_asa(ssh_channel channel, uint8_t* packet, FIL
ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break;
}
fflush(fp);
writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++;
packet_size = 0;
@ -1305,7 +1305,7 @@ static int process_buffer_response_asa(ssh_channel channel, uint8_t* packet, FIL
}
/* ASA: Queries buffer content and reads it */
static void ssh_loop_read_asa(ssh_channel channel, FILE* fp, const uint32_t count)
static void ssh_loop_read_asa(ssh_channel channel, pcapio_writer* fp, const uint32_t count)
{
char line[SSH_READ_BLOCK_SIZE + 1];
uint8_t* packet;
@ -1357,7 +1357,7 @@ static void ssh_loop_read_asa(ssh_channel channel, FILE* fp, const uint32_t coun
}
static void ssh_loop_read(ssh_channel channel, FILE* fp, const uint32_t count _U_, CISCO_SW_TYPE sw_type)
static void ssh_loop_read(ssh_channel channel, pcapio_writer* fp, const uint32_t count _U_, CISCO_SW_TYPE sw_type)
{
ws_debug("Starting reading loop");
switch (sw_type) {
@ -2132,7 +2132,7 @@ static int ssh_open_remote_connection(const ssh_params_t* ssh_params, const char
{
ssh_session sshs;
ssh_channel channel;
FILE* fp = stdout;
pcapio_writer* fp;
uint64_t bytes_written = 0;
int err;
int ret = EXIT_FAILURE;
@ -2140,11 +2140,17 @@ static int ssh_open_remote_connection(const ssh_params_t* ssh_params, const char
if (g_strcmp0(fifo, "-")) {
/* Open or create the output file */
fp = fopen(fifo, "wb");
fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (!fp) {
ws_warning("Error creating output file: %s", g_strerror(errno));
return EXIT_FAILURE;
}
} else {
fp = writecap_open_stdout(WTAP_UNCOMPRESSED, &err);
if (!fp) {
ws_warning("Error opening standard out: %s", g_strerror(errno));
return EXIT_FAILURE;
}
}
if (!libpcap_write_file_header(fp, 1, PCAP_SNAPLEN, false, &bytes_written, &err)) {
@ -2152,7 +2158,7 @@ static int ssh_open_remote_connection(const ssh_params_t* ssh_params, const char
goto cleanup;
}
fflush(fp);
writecap_flush(fp, &err);
ws_debug("Create first ssh session");
sshs = create_ssh_connection(ssh_params, &err_info);
@ -2216,8 +2222,7 @@ static int ssh_open_remote_connection(const ssh_params_t* ssh_params, const char
ret = EXIT_SUCCESS;
cleanup:
if (fp != stdout)
fclose(fp);
writecap_close(fp, NULL);
return ret;
}

View File

@ -42,7 +42,7 @@
#define DPAUXMON_VERSION_MINOR "1"
#define DPAUXMON_VERSION_RELEASE "0"
FILE* pcap_fp;
pcapio_writer* pcap_fp;
enum {
EXTCAP_BASE_OPTIONS_ENUM,
@ -95,17 +95,22 @@ static int list_config(char *interface)
return EXIT_SUCCESS;
}
static int setup_dumpfile(const char* fifo, FILE** fp)
static int setup_dumpfile(const char* fifo, pcapio_writer** fp)
{
uint64_t bytes_written = 0;
int err;
if (!g_strcmp0(fifo, "-")) {
*fp = stdout;
*fp = writecap_open_stdout(WTAP_UNCOMPRESSED, &err);
if (!(*fp)) {
ws_warning("Error opening standard out: %s", g_strerror(errno));
return EXIT_FAILURE;
}
/* XXX - Why does this not write the pcap file header to stdout? */
return EXIT_SUCCESS;
}
*fp = fopen(fifo, "wb");
*fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (!(*fp)) {
ws_warning("Error creating output file: %s", g_strerror(errno));
return EXIT_FAILURE;
@ -116,12 +121,12 @@ static int setup_dumpfile(const char* fifo, FILE** fp)
return EXIT_FAILURE;
}
fflush(*fp);
writecap_flush(*fp, &err);
return EXIT_SUCCESS;
}
static int dump_packet(FILE* fp, const char* buf, const uint32_t buflen, uint64_t ts_usecs)
static int dump_packet(pcapio_writer* fp, const char* buf, const uint32_t buflen, uint64_t ts_usecs)
{
uint64_t bytes_written = 0;
int err;
@ -132,7 +137,7 @@ static int dump_packet(FILE* fp, const char* buf, const uint32_t buflen, uint64_
ret = EXIT_FAILURE;
}
fflush(fp);
writecap_flush(fp, &err);
return ret;
}
@ -468,7 +473,7 @@ err_out:
free_out:
nl_socket_free(sock);
close_out:
fclose(pcap_fp);
writecap_close(pcap_fp, NULL);
}
int main(int argc, char *argv[])

View File

@ -66,7 +66,7 @@ static const struct ws_option longopts[] = {
#define ENTRY_BUF_LENGTH WTAP_MAX_PACKET_SIZE_STANDARD
#define MAX_EXPORT_ENTRY_LENGTH (ENTRY_BUF_LENGTH - 4 - 4 - 4) // Block type - total length - total length
static int sdj_dump_entries(sd_journal *jnl, FILE* fp)
static int sdj_dump_entries(sd_journal *jnl, pcapio_writer* fp)
{
int ret = EXIT_SUCCESS;
uint8_t *entry_buff = g_new(uint8_t, ENTRY_BUF_LENGTH);
@ -178,7 +178,7 @@ static int sdj_dump_entries(sd_journal *jnl, FILE* fp)
break;
}
fflush(fp);
writecap_flush(fp, &err);
}
end:
@ -188,7 +188,7 @@ end:
static int sdj_start_export(const int start_from_entries, const bool start_from_end, const char* fifo)
{
FILE* fp = stdout;
pcapio_writer* fp = NULL;
uint64_t bytes_written = 0;
int err;
sd_journal *jnl = NULL;
@ -202,11 +202,17 @@ static int sdj_start_export(const int start_from_entries, const bool start_from_
if (g_strcmp0(fifo, "-")) {
/* Open or create the output file */
fp = fopen(fifo, "wb");
fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (fp == NULL) {
ws_warning("Error creating output file: %s (%s)", fifo, g_strerror(errno));
return EXIT_FAILURE;
}
} else {
fp = writecap_open_stdout(WTAP_UNCOMPRESSED, &err);
if (fp == NULL) {
ws_warning("Error opening standard out: %s", g_strerror(errno));
return EXIT_FAILURE;
}
}
@ -299,9 +305,7 @@ cleanup:
g_free(err_info);
/* clean up and exit */
if (g_strcmp0(fifo, "-")) {
fclose(fp);
}
writecap_close(fp, NULL);
return ret;
}

View File

@ -148,17 +148,22 @@ cleanup_setup_listener:
}
static int setup_dumpfile(const char* fifo, FILE** fp)
static int setup_dumpfile(const char* fifo, pcapio_writer** fp)
{
uint64_t bytes_written = 0;
int err;
if (!g_strcmp0(fifo, "-")) {
*fp = stdout;
*fp = writecap_open_stdout(WTAP_UNCOMPRESSED, &err);
if (!(*fp)) {
ws_warning("Error opening standard out: %s", g_strerror(errno));
return EXIT_FAILURE;
}
/* XXX - Why does this not write the pcap file header to stdout? */
return EXIT_SUCCESS;
}
*fp = fopen(fifo, "wb");
*fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (!(*fp)) {
ws_warning("Error creating output file: %s", g_strerror(errno));
return EXIT_FAILURE;
@ -169,7 +174,7 @@ static int setup_dumpfile(const char* fifo, FILE** fp)
return EXIT_FAILURE;
}
fflush(*fp);
writecap_flush(*fp, &err);
return EXIT_SUCCESS;
}
@ -239,7 +244,8 @@ static void add_end_options(uint8_t* mbuf, unsigned* offset)
}
static int dump_packet(const char* proto_name, const uint16_t listenport, const char* buf,
const ssize_t buflen, const struct sockaddr_in clientaddr, FILE* fp)
const ssize_t buflen, const struct sockaddr_in clientaddr,
pcapio_writer* fp)
{
uint8_t* mbuf;
unsigned offset = 0;
@ -268,7 +274,7 @@ static int dump_packet(const char* proto_name, const uint16_t listenport, const
ret = EXIT_FAILURE;
}
fflush(fp);
writecap_flush(fp, &err);
g_free(mbuf);
return ret;
@ -281,11 +287,11 @@ static void run_listener(const char* fifo, const uint16_t port, const char* prot
socket_handle_t sock;
char* buf;
ssize_t buflen;
FILE* fp = NULL;
pcapio_writer* fp = NULL;
if (setup_dumpfile(fifo, &fp) == EXIT_FAILURE) {
if (fp)
fclose(fp);
writecap_close(fp, NULL);
return;
}
@ -328,7 +334,7 @@ static void run_listener(const char* fifo, const uint16_t port, const char* prot
}
}
fclose(fp);
writecap_close(fp, NULL);
closesocket(sock);
g_free(buf);
}

View File

@ -55,18 +55,6 @@
#include <wsutil/array.h>
#include <wsutil/file_util.h>
#ifdef HAVE_ZLIBNG
#define ZLIB_PREFIX(x) zng_ ## x
#include <zlib-ng.h>
typedef zng_stream zlib_stream;
#else
#ifdef HAVE_ZLIB
#define ZLIB_PREFIX(x) x
#include <zlib.h>
typedef z_stream zlib_stream;
#endif /* HAVE_ZLIB */
#endif
/* Ringbuffer file structure */
typedef struct _rb_file {
char *name;
@ -85,137 +73,14 @@ typedef struct _ringbuf_data {
bool unlimited; /**< true if unlimited number of files */
int fd; /**< Current ringbuffer file descriptor */
FILE *pdh;
char *io_buffer; /**< The IO buffer used to write to the file */
pcapio_writer* pdh;
bool group_read_access; /**< true if files need to be opened with group read access */
FILE *name_h; /**< write names of completed files to this handle */
char *compress_type; /**< compress type */
GMutex mutex; /**< mutex for oldnames */
char *oldnames[MAX_FILENAME_QUEUE]; /**< filename list of pending to be deleted */
const char *compress_type; /**< compress type */
} ringbuf_data;
static ringbuf_data rb_data;
/*
* delete pending uncompressed pcap files.
*/
static void
CleanupOldCap(char* name)
{
ws_statb64 statb;
size_t i;
g_mutex_lock(&rb_data.mutex);
/* Delete pending delete file */
for (i = 0; i < array_length(rb_data.oldnames); i++) {
if (rb_data.oldnames[i] != NULL) {
ws_unlink(rb_data.oldnames[i]);
if (ws_stat64(rb_data.oldnames[i], &statb) != 0) {
g_free(rb_data.oldnames[i]);
rb_data.oldnames[i] = NULL;
}
}
}
if (name) {
/* push the current file to pending list if it failed to delete */
if (ws_stat64(name, &statb) == 0) {
for (i = 0; i < array_length(rb_data.oldnames); i++) {
if (rb_data.oldnames[i] == NULL) {
rb_data.oldnames[i] = g_strdup(name);
break;
}
}
}
}
g_mutex_unlock(&rb_data.mutex);
}
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
/*
* compress capture file
*/
static int
ringbuf_exec_compress(char* name)
{
uint8_t *buffer = NULL;
char* outgz = NULL;
int fd = -1;
ssize_t nread;
bool delete_org_file = true;
gzFile fi = NULL;
fd = ws_open(name, O_RDONLY | O_BINARY, 0000);
if (fd < 0) {
return -1;
}
outgz = ws_strdup_printf("%s.gz", name);
fi = ZLIB_PREFIX(gzopen)(outgz, "wb");
g_free(outgz);
if (fi == NULL) {
ws_close(fd);
return -1;
}
#define FS_READ_SIZE 65536
buffer = (uint8_t*)g_malloc(FS_READ_SIZE);
if (buffer == NULL) {
ws_close(fd);
ZLIB_PREFIX(gzclose)(fi);
return -1;
}
while ((nread = ws_read(fd, buffer, FS_READ_SIZE)) > 0) {
int n = ZLIB_PREFIX(gzwrite)(fi, buffer, (unsigned int)nread);
if (n <= 0) {
/* mark compression as failed */
delete_org_file = false;
break;
}
}
if (nread < 0) {
/* mark compression as failed */
delete_org_file = false;
}
ws_close(fd);
ZLIB_PREFIX(gzclose)(fi);
g_free(buffer);
/* delete the original file only if compression succeeds */
if (delete_org_file) {
ws_unlink(name);
CleanupOldCap(name);
}
g_free(name);
return 0;
}
/*
* thread to compress capture file
*/
static void*
exec_compress_thread(void* arg)
{
ringbuf_exec_compress((char*)arg);
return NULL;
}
/*
* start a thread to compress capture file
*/
static int
ringbuf_start_compress_file(rb_file* rfile)
{
char* name = g_strdup(rfile->name);
g_thread_new("exec_compress", &exec_compress_thread, name);
return 0;
}
#endif
/*
* create the next filename and open a new binary file with that name
*/
@ -232,11 +97,6 @@ ringbuf_open_file(rb_file *rfile, int *err)
/* remove old file (if any, so ignore error) */
ws_unlink(rfile->name);
}
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
else if (rb_data.compress_type != NULL && strcmp(rb_data.compress_type, "gzip") == 0) {
ringbuf_start_compress_file(rfile);
}
#endif
g_free(rfile->name);
}
@ -278,7 +138,7 @@ ringbuf_open_file(rb_file *rfile, int *err)
*/
int
ringbuf_init(const char *capfile_name, unsigned num_files, bool group_read_access,
char *compress_type, bool has_nametimenum)
const char *compress_type, bool has_nametimenum)
{
unsigned int i;
char *pfx;
@ -292,11 +152,9 @@ ringbuf_init(const char *capfile_name, unsigned num_files, bool group_read_acces
rb_data.unlimited = false;
rb_data.fd = -1;
rb_data.pdh = NULL;
rb_data.io_buffer = NULL;
rb_data.group_read_access = group_read_access;
rb_data.name_h = NULL;
rb_data.compress_type = compress_type;
g_mutex_init(&rb_data.mutex);
/* just to be sure ... */
if (num_files <= RINGBUFFER_MAX_NUM_FILES) {
@ -322,12 +180,29 @@ ringbuf_init(const char *capfile_name, unsigned num_files, bool group_read_acces
Treat it as a separator between the rest of the file name and
the file name suffix, and arrange that the names given to the
ring buffer files have the specified suffix, i.e. put the
changing part of the name *before* the suffix.
XXX - If we ever handle writing compressed files directly
(#19159) make sure we deal with any compression suffix
appropriately. */
changing part of the name *before* the suffix. */
pfx[0] = '\0';
/* Is the suffix a compression type extension? (XXX - Should
* we only check this if compressing, and only for the compression
* type used?) */
GSList *compression_type_extensions = wtap_get_all_compression_type_extensions_list();
for (GSList *compression_type_extension = compression_type_extensions;
compression_type_extension != NULL;
compression_type_extension = g_slist_next(compression_type_extension)) {
if (g_ascii_strcasecmp(pfx + 1, (const char*)compression_type_extension->data) == 0) {
/* It's a compression type extension. Is there a previous extension? */
char *sfx = strrchr(base_name, '.');
if (sfx != NULL) {
/* Yes. Use both extensions as the suffix. */
pfx[0] = '.'; /* restore last suffix */
sfx[0] = '\0';
pfx = sfx;
}
break;
}
}
g_slist_free(compression_type_extensions);
rb_data.fprefix = g_build_filename(dir_name, base_name, NULL);
pfx[0] = '.'; /* restore capfile_name */
rb_data.fsuffix = g_strdup(pfx);
@ -413,29 +288,10 @@ ringbuf_current_filename(void)
/*
* Calls ws_fdopen() for the current ringbuffer file
*/
FILE *
pcapio_writer*
ringbuf_init_libpcap_fdopen(int *err)
{
rb_data.pdh = ws_fdopen(rb_data.fd, "wb");
if (rb_data.pdh == NULL) {
if (err != NULL) {
*err = errno;
}
} else {
size_t buffsize = IO_BUF_SIZE;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
ws_statb64 statb;
if (ws_fstat64(rb_data.fd, &statb) == 0) {
if (statb.st_blksize > IO_BUF_SIZE) {
buffsize = statb.st_blksize;
}
}
#endif
/* Increase the size of the IO buffer */
rb_data.io_buffer = (char *)g_realloc(rb_data.io_buffer, buffsize);
setvbuf(rb_data.pdh, rb_data.io_buffer, _IOFBF, buffsize);
}
rb_data.pdh = writecap_fdopen(rb_data.fd, wtap_name_to_compression_type(rb_data.compress_type), err);
return rb_data.pdh;
}
@ -444,22 +300,17 @@ ringbuf_init_libpcap_fdopen(int *err)
* Switches to the next ringbuffer file
*/
bool
ringbuf_switch_file(FILE **pdh, char **save_file, int *save_file_fd, int *err)
ringbuf_switch_file(pcapio_writer* *pdh, char **save_file, int *save_file_fd, int *err)
{
int next_file_index;
rb_file *next_rfile = NULL;
/* close current file */
if (fclose(rb_data.pdh) == EOF) {
if (err != NULL) {
*err = errno;
}
if (!writecap_close(rb_data.pdh, err)) {
ws_close(rb_data.fd); /* XXX - the above should have closed this already */
rb_data.pdh = NULL; /* it's still closed, we just got an error while closing */
rb_data.fd = -1;
g_free(rb_data.io_buffer);
rb_data.io_buffer = NULL;
return false;
}
@ -503,18 +354,12 @@ ringbuf_libpcap_dump_close(char **save_file, int *err)
/* close current file, if it's open */
if (rb_data.pdh != NULL) {
if (fclose(rb_data.pdh) == EOF) {
if (err != NULL) {
*err = errno;
}
if (!writecap_close(rb_data.pdh, err)) {
ws_close(rb_data.fd);
ret_val = false;
}
rb_data.pdh = NULL;
rb_data.fd = -1;
g_free(rb_data.io_buffer);
rb_data.io_buffer = NULL;
}
if (rb_data.name_h != NULL) {
@ -557,8 +402,6 @@ ringbuf_free(void)
g_free(rb_data.fsuffix);
rb_data.fsuffix = NULL;
}
CleanupOldCap(NULL);
}
/*
@ -571,7 +414,7 @@ ringbuf_error_cleanup(void)
/* try to close via wtap */
if (rb_data.pdh != NULL) {
if (fclose(rb_data.pdh) == 0) {
if (writecap_close(rb_data.pdh, NULL) == 0) {
rb_data.fd = -1;
}
rb_data.pdh = NULL;
@ -590,8 +433,6 @@ ringbuf_error_cleanup(void)
}
}
}
g_free(rb_data.io_buffer);
rb_data.io_buffer = NULL;
if (rb_data.name_h != NULL) {
if (EOF == fclose(rb_data.name_h)) {

View File

@ -13,7 +13,7 @@
#define __RINGBUFFER_H__
#include <wireshark.h>
#include <stdio.h>
#include <writecap/pcapio.h>
#define RINGBUFFER_UNLIMITED_FILES 0
/* Minimum number of ringbuffer files */
@ -24,12 +24,12 @@
/* Maximum number for FAT filesystems */
#define RINGBUFFER_WARN_NUM_FILES 65535
int ringbuf_init(const char *capture_name, unsigned num_files, bool group_read_access, char* compress_type,
bool nametimenum);
int ringbuf_init(const char *capture_name, unsigned num_files, bool group_read_access,
const char *compress_type, bool nametimenum);
bool ringbuf_is_initialized(void);
const char *ringbuf_current_filename(void);
FILE *ringbuf_init_libpcap_fdopen(int *err);
bool ringbuf_switch_file(FILE **pdh, char **save_file, int *save_file_fd,
pcapio_writer* ringbuf_init_libpcap_fdopen(int *err);
bool ringbuf_switch_file(pcapio_writer* *pdh, char **save_file, int *save_file_fd,
int *err);
bool ringbuf_libpcap_dump_close(char **save_file, int *err);
void ringbuf_free(void);

View File

@ -353,12 +353,12 @@ static void
list_output_compression_types(void) {
GSList *output_compression_types;
fprintf(stderr, "tshark: The available output compression type(s) for the \"--compress\" flag are:\n");
cmdarg_err("The available output compression type(s) are:");
output_compression_types = wtap_get_all_output_compression_type_names_list();
for (GSList *compression_type = output_compression_types;
compression_type != NULL;
compression_type = g_slist_next(compression_type)) {
fprintf(stderr, " %s\n", (const char *)compression_type->data);
cmdarg_err_cont(" %s", (const char *)compression_type->data);
}
g_slist_free(output_compression_types);
@ -1941,6 +1941,7 @@ main(int argc, char *argv[])
list_capture_types();
break;
case LONGOPT_COMPRESS:
case LONGOPT_COMPRESS_TYPE:
list_output_compression_types();
break;
default:
@ -2090,11 +2091,22 @@ main(int argc, char *argv[])
goto clean_exit;
}
/* XXX - We have two different long options for compression type;
* one (undocumented) for live capturing and one for not. That is confusing.
* LONGOPT_COMPRESS doesn't set "capture_option_specified" because it can be
* used when capturing or when not capturing.
*/
if (compression_type != WTAP_UNCOMPRESSED && is_capturing) {
cmdarg_err("Writing to compressed output is not supported for live captures");
exit_status = WS_EXIT_INVALID_OPTION;
#ifdef HAVE_LIBPCAP
exit_status = capture_opts_add_opt(&global_capture_opts, LONGOPT_COMPRESS_TYPE, wtap_compression_type_name(compression_type));
if (exit_status != 0) {
goto clean_exit;
}
#else
capture_option_specified = true;
arg_error = true;
#endif
}
#ifndef HAVE_LIBPCAP
if (capture_option_specified)

View File

@ -1146,21 +1146,18 @@ capture_opts_add_opt(capture_options *capture_opts, int opt, const char *optarg_
cmdarg_err("--compress-type can be set only once");
return 1;
}
if (strcmp(optarg_str_p, "none") == 0) {
;
} else if (strcmp(optarg_str_p, "gzip") == 0) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
;
#else
cmdarg_err("'gzip' compression is not supported");
return 1;
#endif
} else {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
cmdarg_err("parameter of --compress-type can be 'none' or 'gzip'");
#else
cmdarg_err("parameter of --compress-type can only be 'none'");
#endif
if (!wtap_can_write_compression_type(wtap_name_to_compression_type(optarg_str_p))) {
cmdarg_err("\"%s\" isn't a valid output compression mode", optarg_str_p);
cmdarg_err("The available output compression type(s) are:");
GSList *output_compression_types;
output_compression_types = wtap_get_all_output_compression_type_names_list();
for (GSList *compression_type = output_compression_types;
compression_type != NULL;
compression_type = g_slist_next(compression_type)) {
cmdarg_err_cont(" %s", (const char*)compression_type->data);
}
g_slist_free(output_compression_types);
return 1;
}
capture_opts->compress_type = g_strdup(optarg_str_p);

View File

@ -200,6 +200,16 @@ CaptureOptionsDialog::CaptureOptionsDialog(QWidget *parent) :
ui->filenameLineEdit->setPlaceholderText(tr("Leave blank to use a temporary file"));
ui->rbCompressionNone->setChecked(true);
#if defined(HAVE_ZLIB) || defined(HAVE_ZLIBNG)
ui->rbCompressionGzip->setEnabled(true);
#else
ui->rbCompressionGzip->setEnabled(false);
#endif
#if defined(HAVE_LZ4FRAME_H)
ui->rbCompressionLZ4->setEnabled(true);
#else
ui->rbCompressionLZ4->setEnabled(false);
#endif
ui->rbTimeNum->setChecked(true);
ui->tempDirLineEdit->setPlaceholderText(g_get_tmp_dir());
@ -610,13 +620,6 @@ void CaptureOptionsDialog::on_gbNewFileAuto_toggled(bool checked)
ui->stopMBCheckBox->setEnabled(checked?false:true);
ui->stopMBSpinBox->setEnabled(checked?false:true);
ui->stopMBComboBox->setEnabled(checked?false:true);
ui->gbCompression->setEnabled(checked);
ui->rbCompressionNone->setEnabled(checked);
#if defined(HAVE_ZLIB) || defined(HAVE_ZLIBNG)
ui->rbCompressionGzip->setEnabled(checked);
#else
ui->rbCompressionGzip->setEnabled(false);
#endif
}
void CaptureOptionsDialog::on_cbUpdatePacketsRT_toggled(bool checked)
@ -1278,6 +1281,8 @@ bool CaptureOptionsDialog::saveOptionsToPreferences()
global_capture_opts.compress_type = NULL;
} else if (ui->rbCompressionGzip->isChecked() ) {
global_capture_opts.compress_type = qstring_strdup("gzip");
} else if (ui->rbCompressionLZ4->isChecked() ) {
global_capture_opts.compress_type = qstring_strdup("lz4");
} else {
global_capture_opts.compress_type = NULL;
}

View File

@ -262,6 +262,60 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_comp">
<item>
<widget class="QLabel" name="labelCompression">
<property name="text">
<string>Compression:</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbCompressionNone">
<property name="text">
<string>None</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbCompressionGzip">
<property name="text">
<string>gzip</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbCompressionLZ4">
<property name="text">
<string>LZ4</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_comp">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="gbNewFileAuto">
<property name="toolTip">
@ -466,36 +520,7 @@ For example, use 1 hour to have a new file created every hour on the hour.</stri
</item>
</widget>
</item>
<item row="5" column="0">
<widget class="QGroupBox" name="gbCompression">
<property name="title">
<string>compression</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QRadioButton" name="rbCompressionNone">
<property name="text">
<string>None</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rbCompressionGzip">
<property name="text">
<string>gzip</string>
</property>
<attribute name="buttonGroup">
<string notr="true">buttonGroup</string>
</attribute>
</widget>
</item>
</layout>
</widget>
</item>
<item row="5" column="1" colspan="2">
<item row="5" column="0" colspan="2">
<widget class="QGroupBox" name="nameTemplateGB">
<property name="title">
<string>File infix pattern</string>

View File

@ -145,6 +145,17 @@ wtap_compression_type_extension(wtap_compression_type compression_type)
return NULL;
}
const char *
wtap_compression_type_name(wtap_compression_type compression_type)
{
for (struct compression_type *p = compression_types;
p->type != WTAP_UNCOMPRESSED; p++) {
if (p->type == compression_type)
return p->name;
}
return NULL;
}
GSList *
wtap_get_all_compression_type_extensions_list(void)
{
@ -1199,7 +1210,7 @@ lz4_fill_out_buffer(FILE_T state)
state->out.avail += (unsigned)outBufSize;
if (compressedSize == 0 && ret > 4) {
if (compressedSize == 0 && ret > LZ4F_BLOCK_HEADER_SIZE) {
/* End of block plus the next block header. We want to add a fast
* seek point to the beginning of a block, before the header. We
* don't add a fast seek point after before the EndMark / footer,

View File

@ -1990,6 +1990,8 @@ const char *wtap_compression_type_description(wtap_compression_type compression_
WS_DLL_PUBLIC
const char *wtap_compression_type_extension(wtap_compression_type compression_type);
WS_DLL_PUBLIC
const char *wtap_compression_type_name(wtap_compression_type compression_type);
WS_DLL_PUBLIC
GSList *wtap_get_all_compression_type_extensions_list(void);
WS_DLL_PUBLIC
GSList *wtap_get_all_output_compression_type_names_list(void);

View File

@ -8,9 +8,16 @@
#
set(WRITECAP_SRC
${CMAKE_SOURCE_DIR}/wiretap/file_wrappers.c
pcapio.c
)
if(WIN32)
list(APPEND WRITECAP_SRC
${CMAKE_SOURCE_DIR}/wsutil/file_util.c
)
endif()
set_source_files_properties(
${WRITECAP_SRC}
PROPERTIES
@ -26,3 +33,27 @@ set_target_properties(writecap PROPERTIES
LINK_FLAGS "${WS_LINK_FLAGS}"
FOLDER "Libs"
)
target_compile_definitions(writecap
PRIVATE
# Necessary to avoid C4273 inconsistent DLL linkage on MSVC
ENABLE_STATIC
)
target_link_libraries(writecap
PRIVATE
${GMODULE2_LIBRARIES}
${LZ4_LIBRARIES}
${ZLIB_LIBRARIES}
${ZLIBNG_LIBRARIES}
${ZSTD_LIBRARIES}
${WIN_WS2_32_LIBRARY}
)
target_include_directories(writecap SYSTEM
PRIVATE
${LZ4_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
${ZLIBNG_INCLUDE_DIRS}
${ZSTD_INCLUDE_DIRS}
)

View File

@ -44,8 +44,18 @@
#include <glib.h>
#include <wsutil/epochs.h>
#include <wsutil/file_util.h>
#include "pcapio.h"
#include <wiretap/file_wrappers.h>
typedef void* WFILE_T;
struct pcapio_writer {
WFILE_T fh;
char* io_buffer;
wtap_compression_type ctype;
};
/* Magic numbers in "libpcap" files.
@ -169,22 +179,273 @@ struct ws_option {
#define ISB_USRDELIV 8
#define ADD_PADDING(x) ((((x) + 3) >> 2) << 2)
static WFILE_T
writecap_file_open(pcapio_writer* pfile, const char *filename)
{
WFILE_T fh;
switch (pfile->ctype) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
return gzwfile_open(filename);
#endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
return lz4wfile_open(filename);
#endif /* HAVE_LZ4FRAME_H */
default:
fh = ws_fopen(filename, "wb");
/* Increase the size of the IO buffer if uncompressed.
* Compression has its own buffer that reduces writes.
*/
if (fh != NULL) {
size_t buffsize = IO_BUF_SIZE;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
ws_statb64 statb;
if (ws_stat64(filename, &statb) == 0) {
if (statb.st_blksize > IO_BUF_SIZE) {
buffsize = statb.st_blksize;
}
}
#endif
pfile->io_buffer = (char *)g_malloc(buffsize);
setvbuf(fh, pfile->io_buffer, _IOFBF, buffsize);
//ws_debug("buffsize %zu", buffsize);
}
return fh;
}
}
static WFILE_T
writecap_file_fdopen(pcapio_writer* pfile, int fd)
{
WFILE_T fh;
switch (pfile->ctype) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
return gzwfile_fdopen(fd);
#endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
return lz4wfile_fdopen(fd);
#endif /* HAVE_LZ4FRAME_H */
default:
fh = ws_fdopen(fd, "wb");
/* Increase the size of the IO buffer if uncompressed.
* Compression has its own buffer that reduces writes.
*/
if (fh != NULL) {
size_t buffsize = IO_BUF_SIZE;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
ws_statb64 statb;
if (ws_fstat64(fd, &statb) == 0) {
if (statb.st_blksize > IO_BUF_SIZE) {
buffsize = statb.st_blksize;
}
}
#endif
pfile->io_buffer = (char *)g_malloc(buffsize);
setvbuf(fh, pfile->io_buffer, _IOFBF, buffsize);
//ws_debug("buffsize %zu", buffsize);
}
return fh;
}
}
pcapio_writer*
writecap_fopen(const char *filename, wtap_compression_type ctype, int *err)
{
pcapio_writer* pfile;
*err = 0;
pfile = g_new0(struct pcapio_writer, 1);
if (pfile == NULL) {
*err = errno;
return NULL;
}
pfile->ctype = ctype;
errno = WTAP_ERR_CANT_OPEN;
void* fh = writecap_file_open(pfile, filename);
if (fh == NULL) {
*err = errno;
g_free(pfile);
return NULL;
}
pfile->fh = fh;
return pfile;
}
pcapio_writer*
writecap_fdopen(int fd, wtap_compression_type ctype, int *err)
{
pcapio_writer* pfile;
*err = 0;
pfile = g_new0(struct pcapio_writer, 1);
if (pfile == NULL) {
*err = errno;
return NULL;
}
pfile->ctype = ctype;
errno = WTAP_ERR_CANT_OPEN;
WFILE_T fh = writecap_file_fdopen(pfile, fd);
if (fh == NULL) {
*err = errno;
g_free(pfile);
return NULL;
}
pfile->fh = fh;
return pfile;
}
pcapio_writer*
writecap_open_stdout(wtap_compression_type ctype, int *err)
{
int new_fd;
pcapio_writer* pfile;
new_fd = ws_dup(1);
if (new_fd == -1) {
*err = errno;
return NULL;
}
#ifdef _WIN32
/*
* Put the new descriptor into binary mode.
*
* XXX - even if the file format we're writing is a text
* format?
*/
if (_setmode(new_fd, O_BINARY) == -1) {
/* "Should not happen" */
*err = errno;
ws_close(new_fd);
return NULL;
}
#endif
pfile = writecap_fdopen(new_fd, ctype, err);
if (pfile == NULL) {
/* Failed; close the new fd */
ws_close(new_fd);
return NULL;
}
return pfile;
}
bool
writecap_flush(pcapio_writer* pfile, int *err)
{
switch (pfile->ctype) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
if (gzwfile_flush((GZWFILE_T)pfile->fh) == -1) {
if (err) {
*err = gzwfile_geterr((GZWFILE_T)pfile->fh);
}
return false;
}
break;
#endif
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
if (lz4wfile_flush((LZ4WFILE_T)pfile->fh) == -1) {
if (err) {
*err = lz4wfile_geterr((LZ4WFILE_T)pfile->fh);
}
return false;
}
break;
#endif /* HAVE_LZ4FRAME_H */
default:
if (fflush((FILE*)pfile->fh) == EOF) {
if (err) {
*err = errno;
}
return false;
}
}
return true;
}
bool
writecap_close(pcapio_writer* pfile, int *errp)
{
int err = 0;
errno = WTAP_ERR_CANT_CLOSE;
switch (pfile->ctype) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
err = gzwfile_close(pfile->fh);
break;
#endif
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
err = lz4wfile_close(pfile->fh);
break;
#endif /* HAVE_LZ4FRAME_H */
default:
if (fclose(pfile->fh) == EOF) {
err = errno;
}
}
g_free(pfile->io_buffer);
g_free(pfile);
if (errp) {
*errp = err;
}
return err == 0;
}
/* Write to capture file */
static bool
write_to_file(FILE* pfile, const uint8_t* data, size_t data_length,
write_to_file(pcapio_writer* pfile, const uint8_t* data, size_t data_length,
uint64_t *bytes_written, int *err)
{
size_t nwritten;
nwritten = fwrite(data, data_length, 1, pfile);
switch (pfile->ctype) {
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
case WTAP_GZIP_COMPRESSED:
nwritten = gzwfile_write(pfile->fh, data, (unsigned)data_length);
/*
* gzwfile_write() returns 0 on error.
*/
if (nwritten == 0) {
*err = gzwfile_geterr(pfile->fh);
return false;
}
break;
#endif
#ifdef HAVE_LZ4FRAME_H
case WTAP_LZ4_COMPRESSED:
nwritten = lz4wfile_write(pfile->fh, data, data_length);
/*
* lz4wfile_write() returns 0 on error.
*/
if (nwritten == 0) {
*err = lz4wfile_geterr(pfile->fh);
return false;
}
break;
#endif /* HAVE_LZ4FRAME_H */
default:
nwritten = fwrite(data, data_length, 1, pfile->fh);
if (nwritten != 1) {
if (ferror(pfile)) {
if (ferror(pfile->fh)) {
*err = errno;
} else {
*err = 0;
*err = WTAP_ERR_SHORT_WRITE;
}
return false;
}
break;
}
(*bytes_written) += data_length;
return true;
@ -196,7 +457,7 @@ write_to_file(FILE* pfile, const uint8_t* data, size_t data_length,
Returns true on success, false on failure.
Sets "*err" to an error code, or 0 for a short write, on failure*/
bool
libpcap_write_file_header(FILE* pfile, int linktype, int snaplen, bool ts_nsecs, uint64_t *bytes_written, int *err)
libpcap_write_file_header(pcapio_writer* pfile, int linktype, int snaplen, bool ts_nsecs, uint64_t *bytes_written, int *err)
{
struct pcap_hdr file_hdr;
@ -215,7 +476,7 @@ libpcap_write_file_header(FILE* pfile, int linktype, int snaplen, bool ts_nsecs,
/* Write a record for a packet to a dump file.
Returns true on success, false on failure. */
bool
libpcap_write_packet(FILE* pfile,
libpcap_write_packet(pcapio_writer* pfile,
time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len,
const uint8_t *pd,
@ -247,7 +508,7 @@ pcapng_count_string_option(const char *option_value)
}
static bool
pcapng_write_string_option(FILE* pfile,
pcapng_write_string_option(pcapio_writer* pfile,
uint16_t option_type, const char *option_value,
uint64_t *bytes_written, int *err)
{
@ -279,7 +540,7 @@ pcapng_write_string_option(FILE* pfile,
/* Write a pre-formatted pcapng block directly to the output file */
bool
pcapng_write_block(FILE* pfile,
pcapng_write_block(pcapio_writer* pfile,
const uint8_t *data,
uint32_t length,
uint64_t *bytes_written,
@ -308,7 +569,7 @@ pcapng_write_block(FILE* pfile,
}
bool
pcapng_write_section_header_block(FILE* pfile,
pcapng_write_section_header_block(pcapio_writer* pfile,
GPtrArray *comments,
const char *hw,
const char *os,
@ -380,7 +641,7 @@ pcapng_write_section_header_block(FILE* pfile,
}
bool
pcapng_write_interface_description_block(FILE* pfile,
pcapng_write_interface_description_block(pcapio_writer* pfile,
const char *comment, /* OPT_COMMENT 1 */
const char *name, /* IDB_NAME 2 */
const char *descr, /* IDB_DESCRIPTION 3 */
@ -540,7 +801,7 @@ pcapng_write_interface_description_block(FILE* pfile,
/* Write a record for a packet to a dump file.
Returns true on success, false on failure. */
bool
pcapng_write_enhanced_packet_block(FILE* pfile,
pcapng_write_enhanced_packet_block(pcapio_writer* pfile,
const char *comment,
time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len,
@ -632,7 +893,7 @@ pcapng_write_enhanced_packet_block(FILE* pfile,
}
bool
pcapng_write_interface_statistics_block(FILE* pfile,
pcapng_write_interface_statistics_block(pcapio_writer* pfile,
uint32_t interface_id,
uint64_t *bytes_written,
const char *comment, /* OPT_COMMENT 1 */

View File

@ -12,19 +12,45 @@
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <wiretap/wtap.h>
typedef struct pcapio_writer pcapio_writer;
extern pcapio_writer*
writecap_fopen(const char *filename, wtap_compression_type ctype, int *err);
extern pcapio_writer*
writecap_fdopen(int fd, wtap_compression_type ctype, int *err);
extern pcapio_writer*
writecap_open_stdout(wtap_compression_type ctype, int *err);
extern bool
writecap_flush(pcapio_writer* pfile, int *err);
/* Close open file handles and frees memory associated with pfile.
*
* Return true on success, returns false and sets err (optional) on failure.
* err can be NULL, e.g. if closing after some other failure that is more
* relevant to report, or when exiting a program. */
extern bool
writecap_close(pcapio_writer* pfile, int *err);
/* Writing pcap files */
/** Write the file header to a dump file.
Returns true on success, false on failure.
Sets "*err" to an error code, or 0 for a short write, on failure*/
extern bool
libpcap_write_file_header(FILE* pfile, int linktype, int snaplen,
libpcap_write_file_header(pcapio_writer* pfile, int linktype, int snaplen,
bool ts_nsecs, uint64_t *bytes_written, int *err);
/** Write a record for a packet to a dump file.
Returns true on success, false on failure. */
extern bool
libpcap_write_packet(FILE* pfile,
libpcap_write_packet(pcapio_writer* pfile,
time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len,
const uint8_t *pd,
@ -34,7 +60,7 @@ libpcap_write_packet(FILE* pfile,
/* Write a pre-formatted pcapng block */
extern bool
pcapng_write_block(FILE* pfile,
pcapng_write_block(pcapio_writer* pfile,
const uint8_t *data,
uint32_t block_total_length,
uint64_t *bytes_written,
@ -44,7 +70,7 @@ pcapng_write_block(FILE* pfile,
*
*/
extern bool
pcapng_write_section_header_block(FILE* pfile, /**< Write information */
pcapng_write_section_header_block(pcapio_writer* pfile, /**< Write information */
GPtrArray *comments, /**< Comments on the section, Optinon 1 opt_comment
* UTF-8 strings containing comments that areassociated to the current block.
*/
@ -63,7 +89,7 @@ pcapng_write_section_header_block(FILE* pfile, /**< Write information */
);
extern bool
pcapng_write_interface_description_block(FILE* pfile,
pcapng_write_interface_description_block(pcapio_writer* pfile,
const char *comment, /* OPT_COMMENT 1 */
const char *name, /* IDB_NAME 2 */
const char *descr, /* IDB_DESCRIPTION 3 */
@ -78,7 +104,7 @@ pcapng_write_interface_description_block(FILE* pfile,
int *err);
extern bool
pcapng_write_interface_statistics_block(FILE* pfile,
pcapng_write_interface_statistics_block(pcapio_writer* pfile,
uint32_t interface_id,
uint64_t *bytes_written,
const char *comment, /* OPT_COMMENT 1 */
@ -89,7 +115,7 @@ pcapng_write_interface_statistics_block(FILE* pfile,
int *err);
extern bool
pcapng_write_enhanced_packet_block(FILE* pfile,
pcapng_write_enhanced_packet_block(pcapio_writer* pfile,
const char *comment,
time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len,