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 wsutil_static
pcap::pcap pcap::pcap
${CAP_LIBRARIES} ${CAP_LIBRARIES}
${ZLIB_LIBRARIES}
${ZLIBNG_LIBRARIES}
${NL_LIBRARIES} ${NL_LIBRARIES}
${APPLE_CORE_FOUNDATION_LIBRARY} ${APPLE_CORE_FOUNDATION_LIBRARY}
${APPLE_SYSTEM_CONFIGURATION_LIBRARY} ${APPLE_SYSTEM_CONFIGURATION_LIBRARY}
@ -3505,7 +3503,7 @@ if(BUILD_dumpcap AND PCAP_FOUND)
add_executable(dumpcap ${dumpcap_FILES}) add_executable(dumpcap ${dumpcap_FILES})
set_extra_executable_properties(dumpcap "Executables") set_extra_executable_properties(dumpcap "Executables")
target_link_libraries(dumpcap ${dumpcap_LIBS}) 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) target_compile_definitions(dumpcap PRIVATE ENABLE_STATIC)
executable_link_mingw_unicode(dumpcap) executable_link_mingw_unicode(dumpcap)
install(TARGETS 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. * 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, * 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 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 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. 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::
List time stamp types supported for the interface. If no time stamp type can be List time stamp types supported for the interface. If no time stamp type can be
set, no time stamp types are listed. 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 for writing can be displayed by the *--compress* method without an
argument. If the *--compress* option is not given, then the desired argument. If the *--compress* option is not given, then the desired
compression method, if any, is deduced from the extension of the filename compression method, if any, is deduced from the extension of the filename
given as argument to the *-w* option. Compression is not supported for given as argument to the *-w* option.
live capture.
When capturing packets, *TShark* writes to the standard error an When capturing packets, *TShark* writes to the standard error an
initial line listing the interfaces from which packets are being 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 *--compress* with no argument provides a list of the compression formats supported
for writing. The type given takes precedence over the extension of __outfile__. 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, The --compress-type option is not documented anywhere; it works with live
but only a limited set of capture options (multiple file mode (-b), but not captures only.
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.)
Should we document it? Deprecate it in favor of also using compress? Do nothing Should we document it? Deprecate it in favor of also using compress?
until it has closer feature parity to *--compress* but for captures?
//// ////
-- --

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. */ 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 */ GRWLock saved_shb_idb_lock; /**< Saved IDB RW mutex */
/* output file(s) */ /* output file(s) */
FILE *pdh; pcapio_writer* pdh;
int save_file_fd; 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. */ uint64_t bytes_written; /**< Bytes written for the current file. */
/* autostop conditions */ /* autostop conditions */
int packets_written; /**< Packets written for the current file. */ 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) { if (capture_opts->multi_files_on) {
ld->pdh = ringbuf_init_libpcap_fdopen(&err); ld->pdh = ringbuf_init_libpcap_fdopen(&err);
} else { } else {
ld->pdh = ws_fdopen(ld->save_file_fd, "wb"); ld->pdh = writecap_fdopen(ld->save_file_fd, wtap_name_to_compression_type(capture_opts->compress_type), &err);
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);
}
} }
if (ld->pdh) { if (ld->pdh) {
bool successful; 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); pcap_src->ts_nsec, &ld->bytes_written, &err);
} }
if (!successful) { if (!successful) {
fclose(ld->pdh); writecap_close(ld->pdh, NULL);
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) { success = writecap_close(ld->pdh, err_close);
if (err_close != NULL) {
*err_close = errno;
}
success = false;
} else {
success = true;
}
g_free(ld->io_buffer);
ld->io_buffer = NULL;
return success; return success;
} }
} }
@ -4005,8 +3975,14 @@ capture_loop_open_output(capture_options *capture_opts, int *save_file_fd,
} else { } else {
suffix = ".pcap"; 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); *save_file_fd = create_tempfile(capture_opts->temp_dir, &capfile_name, prefix, suffix, &err_tempfile);
g_free(prefix); g_free(prefix);
g_free(suffix);
is_tempfile = true; is_tempfile = true;
} }
@ -4086,11 +4062,9 @@ do_file_switch_or_stop(capture_options *capture_opts)
} }
if (!successful) { if (!successful) {
fclose(global_ld.pdh); writecap_close(global_ld.pdh, NULL);
global_ld.pdh = NULL; global_ld.pdh = NULL;
global_ld.go = false; global_ld.go = false;
g_free(global_ld.io_buffer);
global_ld.io_buffer = NULL;
return false; return false;
} }
if (global_ld.file_duration_timer) { 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) { if (global_ld.next_interval_time) {
global_ld.next_interval_time = get_next_time_interval(global_ld.interval_s); 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 (global_ld.inpkts_to_sync_pipe) {
if (!quiet) if (!quiet)
report_packet_count(global_ld.inpkts_to_sync_pipe); 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.err = 0; /* no error seen yet */
global_ld.pdh = NULL; global_ld.pdh = NULL;
global_ld.save_file_fd = -1; global_ld.save_file_fd = -1;
global_ld.io_buffer = NULL;
global_ld.file_count = 0; global_ld.file_count = 0;
global_ld.file_duration_timer = NULL; global_ld.file_duration_timer = NULL;
global_ld.next_interval_time = 0; 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 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 update its windows to indicate that we have a live capture in
progress. */ progress. */
fflush(global_ld.pdh); writecap_flush(global_ld.pdh, NULL);
report_new_capture_file(capture_opts->save_file); 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 (inpkts > 0) {
if (capture_opts->output_to_pipe) { if (capture_opts->output_to_pipe) {
fflush(global_ld.pdh); writecap_flush(global_ld.pdh, NULL);
} }
} /* inpkts */ } /* inpkts */
@ -4441,7 +4414,7 @@ capture_loop_start(capture_options *capture_opts, bool *stats_known, struct pcap
/* Let the parent process know. */ /* Let the parent process know. */
if (global_ld.inpkts_to_sync_pipe) { if (global_ld.inpkts_to_sync_pipe) {
/* do sync here */ /* do sync here */
fflush(global_ld.pdh); writecap_flush(global_ld.pdh, NULL);
/* Send our parent a message saying we've written out /* Send our parent a message saying we've written out
"global_ld.inpkts_to_sync_pipe" packets to the capture file. */ "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; break;
} }
if (capture_opts->output_to_pipe) { 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 */ /* check -c NUM */
if (global_capture_opts.has_autostop_packets && global_ld.packets_captured >= global_capture_opts.autostop_packets) { 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; global_ld.go = false;
return; return;
} }
/* check -a packets:NUM (treat like -c NUM) */ /* 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) { 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; global_ld.go = false;
return; return;
} }
@ -4881,7 +4854,7 @@ capture_loop_write_pcapng_cb(capture_src *pcap_src, const pcapng_block_header_t
bh->block_total_length, bh->block_total_length,
&global_ld.bytes_written, &err); &global_ld.bytes_written, &err);
fflush(global_ld.pdh); writecap_flush(global_ld.pdh, NULL);
if (!successful) { if (!successful) {
global_ld.go = false; global_ld.go = false;
global_ld.err = err; 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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 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)); ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break; break;
} }
fflush(fp); writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size); ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++; (*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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint8_t* packet; 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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 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)); ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break; break;
} }
fflush(fp); writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size); ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++; (*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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 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)); ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break; break;
} }
fflush(fp); writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size); ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++; (*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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint8_t* packet; 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 */ /* 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; uint8_t* packet;
uint32_t processed_packets = 0; 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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint32_t read_packets = 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)); ws_debug("Error in libpcap_write_packet(): %s", g_strerror(err));
break; break;
} }
fflush(fp); writecap_flush(fp, &err);
ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size); ws_debug("Dumped packet %u size: %u\n", *processed_packets, packet_size);
(*processed_packets)++; (*processed_packets)++;
packet_size = 0; 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 */ /* 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]; char line[SSH_READ_BLOCK_SIZE + 1];
uint8_t* packet; 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"); ws_debug("Starting reading loop");
switch (sw_type) { 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_session sshs;
ssh_channel channel; ssh_channel channel;
FILE* fp = stdout; pcapio_writer* fp;
uint64_t bytes_written = 0; uint64_t bytes_written = 0;
int err; int err;
int ret = EXIT_FAILURE; 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, "-")) { if (g_strcmp0(fifo, "-")) {
/* Open or create the output file */ /* Open or create the output file */
fp = fopen(fifo, "wb"); fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (!fp) { if (!fp) {
ws_warning("Error creating output file: %s", g_strerror(errno)); ws_warning("Error creating output file: %s", g_strerror(errno));
return EXIT_FAILURE; 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)) { 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; goto cleanup;
} }
fflush(fp); writecap_flush(fp, &err);
ws_debug("Create first ssh session"); ws_debug("Create first ssh session");
sshs = create_ssh_connection(ssh_params, &err_info); 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; ret = EXIT_SUCCESS;
cleanup: cleanup:
if (fp != stdout) writecap_close(fp, NULL);
fclose(fp);
return ret; return ret;
} }

View File

@ -42,7 +42,7 @@
#define DPAUXMON_VERSION_MINOR "1" #define DPAUXMON_VERSION_MINOR "1"
#define DPAUXMON_VERSION_RELEASE "0" #define DPAUXMON_VERSION_RELEASE "0"
FILE* pcap_fp; pcapio_writer* pcap_fp;
enum { enum {
EXTCAP_BASE_OPTIONS_ENUM, EXTCAP_BASE_OPTIONS_ENUM,
@ -95,17 +95,22 @@ static int list_config(char *interface)
return EXIT_SUCCESS; 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; uint64_t bytes_written = 0;
int err; int err;
if (!g_strcmp0(fifo, "-")) { 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; return EXIT_SUCCESS;
} }
*fp = fopen(fifo, "wb"); *fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (!(*fp)) { if (!(*fp)) {
ws_warning("Error creating output file: %s", g_strerror(errno)); ws_warning("Error creating output file: %s", g_strerror(errno));
return EXIT_FAILURE; return EXIT_FAILURE;
@ -116,12 +121,12 @@ static int setup_dumpfile(const char* fifo, FILE** fp)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
fflush(*fp); writecap_flush(*fp, &err);
return EXIT_SUCCESS; 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; uint64_t bytes_written = 0;
int err; int err;
@ -132,7 +137,7 @@ static int dump_packet(FILE* fp, const char* buf, const uint32_t buflen, uint64_
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} }
fflush(fp); writecap_flush(fp, &err);
return ret; return ret;
} }
@ -468,7 +473,7 @@ err_out:
free_out: free_out:
nl_socket_free(sock); nl_socket_free(sock);
close_out: close_out:
fclose(pcap_fp); writecap_close(pcap_fp, NULL);
} }
int main(int argc, char *argv[]) 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 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 #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; int ret = EXIT_SUCCESS;
uint8_t *entry_buff = g_new(uint8_t, ENTRY_BUF_LENGTH); 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; break;
} }
fflush(fp); writecap_flush(fp, &err);
} }
end: end:
@ -188,7 +188,7 @@ end:
static int sdj_start_export(const int start_from_entries, const bool start_from_end, const char* fifo) 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; uint64_t bytes_written = 0;
int err; int err;
sd_journal *jnl = NULL; sd_journal *jnl = NULL;
@ -202,12 +202,18 @@ static int sdj_start_export(const int start_from_entries, const bool start_from_
if (g_strcmp0(fifo, "-")) { if (g_strcmp0(fifo, "-")) {
/* Open or create the output file */ /* Open or create the output file */
fp = fopen(fifo, "wb"); fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (fp == NULL) { if (fp == NULL) {
ws_warning("Error creating output file: %s (%s)", fifo, g_strerror(errno)); ws_warning("Error creating output file: %s (%s)", fifo, g_strerror(errno));
return EXIT_FAILURE; 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;
}
}
appname = ws_strdup_printf(SDJOURNAL_EXTCAP_INTERFACE " (Wireshark) %s.%s.%s", appname = ws_strdup_printf(SDJOURNAL_EXTCAP_INTERFACE " (Wireshark) %s.%s.%s",
@ -299,9 +305,7 @@ cleanup:
g_free(err_info); g_free(err_info);
/* clean up and exit */ /* clean up and exit */
if (g_strcmp0(fifo, "-")) { writecap_close(fp, NULL);
fclose(fp);
}
return ret; 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; uint64_t bytes_written = 0;
int err; int err;
if (!g_strcmp0(fifo, "-")) { 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; return EXIT_SUCCESS;
} }
*fp = fopen(fifo, "wb"); *fp = writecap_fopen(fifo, WTAP_UNCOMPRESSED, &err);
if (!(*fp)) { if (!(*fp)) {
ws_warning("Error creating output file: %s", g_strerror(errno)); ws_warning("Error creating output file: %s", g_strerror(errno));
return EXIT_FAILURE; return EXIT_FAILURE;
@ -169,7 +174,7 @@ static int setup_dumpfile(const char* fifo, FILE** fp)
return EXIT_FAILURE; return EXIT_FAILURE;
} }
fflush(*fp); writecap_flush(*fp, &err);
return EXIT_SUCCESS; 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, 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; uint8_t* mbuf;
unsigned offset = 0; unsigned offset = 0;
@ -268,7 +274,7 @@ static int dump_packet(const char* proto_name, const uint16_t listenport, const
ret = EXIT_FAILURE; ret = EXIT_FAILURE;
} }
fflush(fp); writecap_flush(fp, &err);
g_free(mbuf); g_free(mbuf);
return ret; return ret;
@ -281,11 +287,11 @@ static void run_listener(const char* fifo, const uint16_t port, const char* prot
socket_handle_t sock; socket_handle_t sock;
char* buf; char* buf;
ssize_t buflen; ssize_t buflen;
FILE* fp = NULL; pcapio_writer* fp = NULL;
if (setup_dumpfile(fifo, &fp) == EXIT_FAILURE) { if (setup_dumpfile(fifo, &fp) == EXIT_FAILURE) {
if (fp) if (fp)
fclose(fp); writecap_close(fp, NULL);
return; 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); closesocket(sock);
g_free(buf); g_free(buf);
} }

View File

@ -55,18 +55,6 @@
#include <wsutil/array.h> #include <wsutil/array.h>
#include <wsutil/file_util.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 */ /* Ringbuffer file structure */
typedef struct _rb_file { typedef struct _rb_file {
char *name; char *name;
@ -85,137 +73,14 @@ typedef struct _ringbuf_data {
bool unlimited; /**< true if unlimited number of files */ bool unlimited; /**< true if unlimited number of files */
int fd; /**< Current ringbuffer file descriptor */ int fd; /**< Current ringbuffer file descriptor */
FILE *pdh; pcapio_writer* pdh;
char *io_buffer; /**< The IO buffer used to write to the file */
bool group_read_access; /**< true if files need to be opened with group read access */ 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 */ FILE *name_h; /**< write names of completed files to this handle */
char *compress_type; /**< compress type */ const char *compress_type; /**< compress type */
GMutex mutex; /**< mutex for oldnames */
char *oldnames[MAX_FILENAME_QUEUE]; /**< filename list of pending to be deleted */
} ringbuf_data; } ringbuf_data;
static ringbuf_data rb_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 * 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) */ /* remove old file (if any, so ignore error) */
ws_unlink(rfile->name); 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); g_free(rfile->name);
} }
@ -278,7 +138,7 @@ ringbuf_open_file(rb_file *rfile, int *err)
*/ */
int int
ringbuf_init(const char *capfile_name, unsigned num_files, bool group_read_access, 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; unsigned int i;
char *pfx; 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.unlimited = false;
rb_data.fd = -1; rb_data.fd = -1;
rb_data.pdh = NULL; rb_data.pdh = NULL;
rb_data.io_buffer = NULL;
rb_data.group_read_access = group_read_access; rb_data.group_read_access = group_read_access;
rb_data.name_h = NULL; rb_data.name_h = NULL;
rb_data.compress_type = compress_type; rb_data.compress_type = compress_type;
g_mutex_init(&rb_data.mutex);
/* just to be sure ... */ /* just to be sure ... */
if (num_files <= RINGBUFFER_MAX_NUM_FILES) { 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 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 the file name suffix, and arrange that the names given to the
ring buffer files have the specified suffix, i.e. put the ring buffer files have the specified suffix, i.e. put the
changing part of the name *before* the suffix. 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. */
pfx[0] = '\0'; 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); rb_data.fprefix = g_build_filename(dir_name, base_name, NULL);
pfx[0] = '.'; /* restore capfile_name */ pfx[0] = '.'; /* restore capfile_name */
rb_data.fsuffix = g_strdup(pfx); rb_data.fsuffix = g_strdup(pfx);
@ -413,29 +288,10 @@ ringbuf_current_filename(void)
/* /*
* Calls ws_fdopen() for the current ringbuffer file * Calls ws_fdopen() for the current ringbuffer file
*/ */
FILE * pcapio_writer*
ringbuf_init_libpcap_fdopen(int *err) ringbuf_init_libpcap_fdopen(int *err)
{ {
rb_data.pdh = ws_fdopen(rb_data.fd, "wb"); rb_data.pdh = writecap_fdopen(rb_data.fd, wtap_name_to_compression_type(rb_data.compress_type), err);
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);
}
return rb_data.pdh; return rb_data.pdh;
} }
@ -444,22 +300,17 @@ ringbuf_init_libpcap_fdopen(int *err)
* Switches to the next ringbuffer file * Switches to the next ringbuffer file
*/ */
bool 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; int next_file_index;
rb_file *next_rfile = NULL; rb_file *next_rfile = NULL;
/* close current file */ /* close current file */
if (fclose(rb_data.pdh) == EOF) { if (!writecap_close(rb_data.pdh, err)) {
if (err != NULL) {
*err = errno;
}
ws_close(rb_data.fd); /* XXX - the above should have closed this already */ 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.pdh = NULL; /* it's still closed, we just got an error while closing */
rb_data.fd = -1; rb_data.fd = -1;
g_free(rb_data.io_buffer);
rb_data.io_buffer = NULL;
return false; return false;
} }
@ -503,18 +354,12 @@ ringbuf_libpcap_dump_close(char **save_file, int *err)
/* close current file, if it's open */ /* close current file, if it's open */
if (rb_data.pdh != NULL) { if (rb_data.pdh != NULL) {
if (fclose(rb_data.pdh) == EOF) { if (!writecap_close(rb_data.pdh, err)) {
if (err != NULL) {
*err = errno;
}
ws_close(rb_data.fd); ws_close(rb_data.fd);
ret_val = false; ret_val = false;
} }
rb_data.pdh = NULL; rb_data.pdh = NULL;
rb_data.fd = -1; rb_data.fd = -1;
g_free(rb_data.io_buffer);
rb_data.io_buffer = NULL;
} }
if (rb_data.name_h != NULL) { if (rb_data.name_h != NULL) {
@ -557,8 +402,6 @@ ringbuf_free(void)
g_free(rb_data.fsuffix); g_free(rb_data.fsuffix);
rb_data.fsuffix = NULL; rb_data.fsuffix = NULL;
} }
CleanupOldCap(NULL);
} }
/* /*
@ -571,7 +414,7 @@ ringbuf_error_cleanup(void)
/* try to close via wtap */ /* try to close via wtap */
if (rb_data.pdh != NULL) { 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.fd = -1;
} }
rb_data.pdh = NULL; 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 (rb_data.name_h != NULL) {
if (EOF == fclose(rb_data.name_h)) { if (EOF == fclose(rb_data.name_h)) {

View File

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

View File

@ -353,12 +353,12 @@ static void
list_output_compression_types(void) { list_output_compression_types(void) {
GSList *output_compression_types; 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(); output_compression_types = wtap_get_all_output_compression_type_names_list();
for (GSList *compression_type = output_compression_types; for (GSList *compression_type = output_compression_types;
compression_type != NULL; compression_type != NULL;
compression_type = g_slist_next(compression_type)) { 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); g_slist_free(output_compression_types);
@ -1941,6 +1941,7 @@ main(int argc, char *argv[])
list_capture_types(); list_capture_types();
break; break;
case LONGOPT_COMPRESS: case LONGOPT_COMPRESS:
case LONGOPT_COMPRESS_TYPE:
list_output_compression_types(); list_output_compression_types();
break; break;
default: default:
@ -2090,10 +2091,21 @@ main(int argc, char *argv[])
goto clean_exit; 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) { if (compression_type != WTAP_UNCOMPRESSED && is_capturing) {
cmdarg_err("Writing to compressed output is not supported for live captures"); #ifdef HAVE_LIBPCAP
exit_status = WS_EXIT_INVALID_OPTION; exit_status = capture_opts_add_opt(&global_capture_opts, LONGOPT_COMPRESS_TYPE, wtap_compression_type_name(compression_type));
goto clean_exit; if (exit_status != 0) {
goto clean_exit;
}
#else
capture_option_specified = true;
arg_error = true;
#endif
} }
#ifndef HAVE_LIBPCAP #ifndef HAVE_LIBPCAP

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"); cmdarg_err("--compress-type can be set only once");
return 1; return 1;
} }
if (strcmp(optarg_str_p, "none") == 0) { 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);
} else if (strcmp(optarg_str_p, "gzip") == 0) { cmdarg_err("The available output compression type(s) are:");
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) GSList *output_compression_types;
; output_compression_types = wtap_get_all_output_compression_type_names_list();
#else for (GSList *compression_type = output_compression_types;
cmdarg_err("'gzip' compression is not supported"); compression_type != NULL;
return 1; compression_type = g_slist_next(compression_type)) {
#endif
} else { cmdarg_err_cont(" %s", (const char*)compression_type->data);
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) }
cmdarg_err("parameter of --compress-type can be 'none' or 'gzip'"); g_slist_free(output_compression_types);
#else
cmdarg_err("parameter of --compress-type can only be 'none'");
#endif
return 1; return 1;
} }
capture_opts->compress_type = g_strdup(optarg_str_p); 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->filenameLineEdit->setPlaceholderText(tr("Leave blank to use a temporary file"));
ui->rbCompressionNone->setChecked(true); 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->rbTimeNum->setChecked(true);
ui->tempDirLineEdit->setPlaceholderText(g_get_tmp_dir()); 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->stopMBCheckBox->setEnabled(checked?false:true);
ui->stopMBSpinBox->setEnabled(checked?false:true); ui->stopMBSpinBox->setEnabled(checked?false:true);
ui->stopMBComboBox->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) void CaptureOptionsDialog::on_cbUpdatePacketsRT_toggled(bool checked)
@ -1278,7 +1281,9 @@ bool CaptureOptionsDialog::saveOptionsToPreferences()
global_capture_opts.compress_type = NULL; global_capture_opts.compress_type = NULL;
} else if (ui->rbCompressionGzip->isChecked() ) { } else if (ui->rbCompressionGzip->isChecked() ) {
global_capture_opts.compress_type = qstring_strdup("gzip"); global_capture_opts.compress_type = qstring_strdup("gzip");
} else { } else if (ui->rbCompressionLZ4->isChecked() ) {
global_capture_opts.compress_type = qstring_strdup("lz4");
} else {
global_capture_opts.compress_type = NULL; global_capture_opts.compress_type = NULL;
} }

View File

@ -262,6 +262,60 @@
</item> </item>
</layout> </layout>
</item> </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> <item>
<widget class="QGroupBox" name="gbNewFileAuto"> <widget class="QGroupBox" name="gbNewFileAuto">
<property name="toolTip"> <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> </item>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="5" column="0" colspan="2">
<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">
<widget class="QGroupBox" name="nameTemplateGB"> <widget class="QGroupBox" name="nameTemplateGB">
<property name="title"> <property name="title">
<string>File infix pattern</string> <string>File infix pattern</string>

View File

@ -145,6 +145,17 @@ wtap_compression_type_extension(wtap_compression_type compression_type)
return NULL; 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 * GSList *
wtap_get_all_compression_type_extensions_list(void) wtap_get_all_compression_type_extensions_list(void)
{ {
@ -1199,7 +1210,7 @@ lz4_fill_out_buffer(FILE_T state)
state->out.avail += (unsigned)outBufSize; 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 /* 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 * seek point to the beginning of a block, before the header. We
* don't add a fast seek point after before the EndMark / footer, * 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 WS_DLL_PUBLIC
const char *wtap_compression_type_extension(wtap_compression_type compression_type); const char *wtap_compression_type_extension(wtap_compression_type compression_type);
WS_DLL_PUBLIC 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); GSList *wtap_get_all_compression_type_extensions_list(void);
WS_DLL_PUBLIC WS_DLL_PUBLIC
GSList *wtap_get_all_output_compression_type_names_list(void); GSList *wtap_get_all_output_compression_type_names_list(void);

View File

@ -8,9 +8,16 @@
# #
set(WRITECAP_SRC set(WRITECAP_SRC
${CMAKE_SOURCE_DIR}/wiretap/file_wrappers.c
pcapio.c pcapio.c
) )
if(WIN32)
list(APPEND WRITECAP_SRC
${CMAKE_SOURCE_DIR}/wsutil/file_util.c
)
endif()
set_source_files_properties( set_source_files_properties(
${WRITECAP_SRC} ${WRITECAP_SRC}
PROPERTIES PROPERTIES
@ -26,3 +33,27 @@ set_target_properties(writecap PROPERTIES
LINK_FLAGS "${WS_LINK_FLAGS}" LINK_FLAGS "${WS_LINK_FLAGS}"
FOLDER "Libs" 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 <glib.h>
#include <wsutil/epochs.h> #include <wsutil/epochs.h>
#include <wsutil/file_util.h>
#include "pcapio.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. /* Magic numbers in "libpcap" files.
@ -169,21 +179,272 @@ struct ws_option {
#define ISB_USRDELIV 8 #define ISB_USRDELIV 8
#define ADD_PADDING(x) ((((x) + 3) >> 2) << 2) #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 */ /* Write to capture file */
static bool 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) uint64_t *bytes_written, int *err)
{ {
size_t nwritten; size_t nwritten;
nwritten = fwrite(data, data_length, 1, pfile); switch (pfile->ctype) {
if (nwritten != 1) { #if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
if (ferror(pfile)) { case WTAP_GZIP_COMPRESSED:
*err = errno; nwritten = gzwfile_write(pfile->fh, data, (unsigned)data_length);
} else { /*
*err = 0; * gzwfile_write() returns 0 on error.
} */
return false; 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->fh)) {
*err = errno;
} else {
*err = WTAP_ERR_SHORT_WRITE;
}
return false;
}
break;
} }
(*bytes_written) += data_length; (*bytes_written) += data_length;
@ -196,7 +457,7 @@ write_to_file(FILE* pfile, const uint8_t* data, size_t data_length,
Returns true on success, false on failure. Returns true on success, false on failure.
Sets "*err" to an error code, or 0 for a short write, on failure*/ Sets "*err" to an error code, or 0 for a short write, on failure*/
bool 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; 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. /* Write a record for a packet to a dump file.
Returns true on success, false on failure. */ Returns true on success, false on failure. */
bool bool
libpcap_write_packet(FILE* pfile, libpcap_write_packet(pcapio_writer* pfile,
time_t sec, uint32_t usec, time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len, uint32_t caplen, uint32_t len,
const uint8_t *pd, const uint8_t *pd,
@ -247,7 +508,7 @@ pcapng_count_string_option(const char *option_value)
} }
static bool static bool
pcapng_write_string_option(FILE* pfile, pcapng_write_string_option(pcapio_writer* pfile,
uint16_t option_type, const char *option_value, uint16_t option_type, const char *option_value,
uint64_t *bytes_written, int *err) 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 */ /* Write a pre-formatted pcapng block directly to the output file */
bool bool
pcapng_write_block(FILE* pfile, pcapng_write_block(pcapio_writer* pfile,
const uint8_t *data, const uint8_t *data,
uint32_t length, uint32_t length,
uint64_t *bytes_written, uint64_t *bytes_written,
@ -308,7 +569,7 @@ pcapng_write_block(FILE* pfile,
} }
bool bool
pcapng_write_section_header_block(FILE* pfile, pcapng_write_section_header_block(pcapio_writer* pfile,
GPtrArray *comments, GPtrArray *comments,
const char *hw, const char *hw,
const char *os, const char *os,
@ -380,7 +641,7 @@ pcapng_write_section_header_block(FILE* pfile,
} }
bool bool
pcapng_write_interface_description_block(FILE* pfile, pcapng_write_interface_description_block(pcapio_writer* pfile,
const char *comment, /* OPT_COMMENT 1 */ const char *comment, /* OPT_COMMENT 1 */
const char *name, /* IDB_NAME 2 */ const char *name, /* IDB_NAME 2 */
const char *descr, /* IDB_DESCRIPTION 3 */ 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. /* Write a record for a packet to a dump file.
Returns true on success, false on failure. */ Returns true on success, false on failure. */
bool bool
pcapng_write_enhanced_packet_block(FILE* pfile, pcapng_write_enhanced_packet_block(pcapio_writer* pfile,
const char *comment, const char *comment,
time_t sec, uint32_t usec, time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len, uint32_t caplen, uint32_t len,
@ -632,7 +893,7 @@ pcapng_write_enhanced_packet_block(FILE* pfile,
} }
bool bool
pcapng_write_interface_statistics_block(FILE* pfile, pcapng_write_interface_statistics_block(pcapio_writer* pfile,
uint32_t interface_id, uint32_t interface_id,
uint64_t *bytes_written, uint64_t *bytes_written,
const char *comment, /* OPT_COMMENT 1 */ const char *comment, /* OPT_COMMENT 1 */

View File

@ -12,19 +12,45 @@
* SPDX-License-Identifier: GPL-2.0-or-later * 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 */ /* Writing pcap files */
/** Write the file header to a dump file. /** Write the file header to a dump file.
Returns true on success, false on failure. Returns true on success, false on failure.
Sets "*err" to an error code, or 0 for a short write, on failure*/ Sets "*err" to an error code, or 0 for a short write, on failure*/
extern bool 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); bool ts_nsecs, uint64_t *bytes_written, int *err);
/** Write a record for a packet to a dump file. /** Write a record for a packet to a dump file.
Returns true on success, false on failure. */ Returns true on success, false on failure. */
extern bool extern bool
libpcap_write_packet(FILE* pfile, libpcap_write_packet(pcapio_writer* pfile,
time_t sec, uint32_t usec, time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len, uint32_t caplen, uint32_t len,
const uint8_t *pd, const uint8_t *pd,
@ -34,7 +60,7 @@ libpcap_write_packet(FILE* pfile,
/* Write a pre-formatted pcapng block */ /* Write a pre-formatted pcapng block */
extern bool extern bool
pcapng_write_block(FILE* pfile, pcapng_write_block(pcapio_writer* pfile,
const uint8_t *data, const uint8_t *data,
uint32_t block_total_length, uint32_t block_total_length,
uint64_t *bytes_written, uint64_t *bytes_written,
@ -44,7 +70,7 @@ pcapng_write_block(FILE* pfile,
* *
*/ */
extern bool 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 GPtrArray *comments, /**< Comments on the section, Optinon 1 opt_comment
* UTF-8 strings containing comments that areassociated to the current block. * 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 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 *comment, /* OPT_COMMENT 1 */
const char *name, /* IDB_NAME 2 */ const char *name, /* IDB_NAME 2 */
const char *descr, /* IDB_DESCRIPTION 3 */ const char *descr, /* IDB_DESCRIPTION 3 */
@ -78,7 +104,7 @@ pcapng_write_interface_description_block(FILE* pfile,
int *err); int *err);
extern bool extern bool
pcapng_write_interface_statistics_block(FILE* pfile, pcapng_write_interface_statistics_block(pcapio_writer* pfile,
uint32_t interface_id, uint32_t interface_id,
uint64_t *bytes_written, uint64_t *bytes_written,
const char *comment, /* OPT_COMMENT 1 */ const char *comment, /* OPT_COMMENT 1 */
@ -89,7 +115,7 @@ pcapng_write_interface_statistics_block(FILE* pfile,
int *err); int *err);
extern bool extern bool
pcapng_write_enhanced_packet_block(FILE* pfile, pcapng_write_enhanced_packet_block(pcapio_writer* pfile,
const char *comment, const char *comment,
time_t sec, uint32_t usec, time_t sec, uint32_t usec,
uint32_t caplen, uint32_t len, uint32_t caplen, uint32_t len,