From 4563aa127e67f18cd0f2bddb31fb584ea1abf3c9 Mon Sep 17 00:00:00 2001 From: David Liedle Date: Sat, 9 Aug 2025 17:20:43 -0600 Subject: [PATCH 1/3] fix: resolve multiple security vulnerabilities and improve memory safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Security fixes: - Fix format string vulnerability in python_interface.c (sprintf → snprintf) - Fix buffer overflow risks in intercept.cc and intercept_server.cc (strcpy → strncpy) - Fix potential integer overflows in af_packet_v3.c malloc operations - Add bounds checking to sprintf operations in pcap.h - Add overflow validation before memory allocations Platform fixes: - Add macOS/Darwin compatibility for gettid() using pthread_self() - Fix signal handling for non-Linux platforms - Fix bind_and_dispatch() function signature for non-Linux builds Also adds CLAUDE.md documentation for future AI-assisted development. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- CLAUDE.md | 108 ++++++++++++++++++++++++++++++++++++++++ src/af_packet_v3.c | 23 +++++++-- src/capture.c | 3 +- src/intercept.cc | 3 +- src/intercept_server.cc | 3 +- src/output.c | 6 +++ src/pcap.h | 4 +- src/python_interface.c | 2 +- src/signal_handling.c | 2 + 9 files changed, 144 insertions(+), 10 deletions(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..6edc1045 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,108 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Mercury is an open-source network metadata capture and analysis package for network security monitoring, fingerprinting, and protocol analysis. It consists of: +- **mercury**: High-performance Linux packet capture application using AF_PACKET +- **libmerc**: Core packet processing library (C++17) +- **pmercury**: Python interface via Cython bindings +- **Utilities**: cert_analyze, tls_scanner, batch_gcd for specialized analysis + +## Essential Commands + +### Build Commands +```bash +# Standard build +./configure && make + +# Debug build +make debug-mercury + +# Build libraries only +make libs + +# Install with systemd service +sudo make install MERCURY_CFG=mercury.cfg + +# Non-root installation +sudo make install-nonroot + +# Build Cython wheel +cd src/cython && make wheel +``` + +### Testing Commands +```bash +# Run all tests +make test + +# Run unit tests +make unit_tests +cd unit_tests && make + +# Generate coverage report +make coverage_report + +# Run specific test +cd unit_tests && ./mercury_test "[test_name]" +``` + +### Development Commands +```bash +# Format code +make format + +# Generate documentation +make doc # Doxygen +make sphinx # Sphinx docs + +# Version management +make increment-patchlevel # Bump patch version +make increment-minor-version # Bump minor version +``` + +## Architecture Overview + +### Core Components + +**libmerc Library** (`src/libmerc/`) +- Main entry point: `libmerc.cc`, `pkt_proc.cc` - packet processing engine +- Protocol parsers in individual files: `tls.cc`, `http.cc`, `dns.cc`, `ssh.cc`, etc. +- Analysis engine: `analysis.cc` - process identification and malware detection +- Memory-safe parsing: `datum.h` - zero-copy packet parsing without heap allocation + +**Protocol Fingerprinting** +- TLS fingerprint generation using JA3/JA3S algorithms +- HTTP header fingerprinting +- DNS query pattern analysis +- All fingerprinting code outputs JSON for easy integration + +**Performance Critical Paths** +- Packet processing uses lockless ring buffers and memory pools +- JSON output bypasses std::ostream for speed (`json_object.h`) +- Protocol parsing uses fixed-size stack buffers to avoid heap allocation + +### Python Integration + +The Python interface (`python/pmercury/`) uses Cython bindings (`src/cython/`) to expose: +- `mercury_python` module for packet analysis +- `perform_analysis()` and `perform_analysis_with_weights()` functions +- Direct access to fingerprinting and analysis capabilities + +### Key Design Patterns + +1. **Safe Parsing**: All protocol parsers use the `datum` class for bounds-checked parsing +2. **JSON Output**: Custom JSON generator optimized for network metadata +3. **Resource Files**: Encrypted archives containing protocol databases and ML models +4. **Platform Abstraction**: Conditional compilation for Linux (AF_PACKET) vs macOS (PCAP only) + +## Important Notes + +- Primary development platform is Linux; macOS support is limited to PCAP file processing +- C++17 required for compilation +- Performance testing should use AF_PACKET on Linux for accurate results +- When modifying protocol parsers, ensure datum bounds checking is maintained +- New features should include unit tests in `unit_tests/` +- JSON output must follow guidelines in `doc/guidelines.md` \ No newline at end of file diff --git a/src/af_packet_v3.c b/src/af_packet_v3.c index bd2ce138..38a5ac16 100644 --- a/src/af_packet_v3.c +++ b/src/af_packet_v3.c @@ -10,6 +10,7 @@ #include #include #include +#include #ifndef _GNU_SOURCE #define _GNU_SOURCE /* Needed for gettid() definition from unistd.h */ @@ -505,7 +506,7 @@ int create_dedicated_socket(struct thread_storage *thread_stor, int fanout_arg) * The start of each block is a struct tpacket_block_desc so make * array of pointers to the start of each block struct */ - struct tpacket_block_desc **block_header = (struct tpacket_block_desc**)malloc(thread_stor->ring_params.tp_block_nr * sizeof(struct tpacket_hdr_v1 *)); + struct tpacket_block_desc **block_header = (struct tpacket_block_desc**)malloc(thread_stor->ring_params.tp_block_nr * sizeof(struct tpacket_block_desc *)); if (block_header == NULL) { fprintf(stderr, "error: could not allocate block_header pointer array for thread %d\n", thread_stor->tnum); munmap(mapped_buffer, map_buf_len); @@ -885,15 +886,29 @@ enum status bind_and_dispatch(struct mercury_config *cfg, statst.verbosity = cfg->verbosity; struct thread_storage *tstor; // Holds the array of struct thread_storage, one for each thread - tstor = (struct thread_storage *)malloc(num_threads * sizeof(struct thread_storage)); + size_t tstor_size = num_threads * sizeof(struct thread_storage); + if (num_threads > 0 && tstor_size / num_threads != sizeof(struct thread_storage)) { + fprintf(stderr, "error: integer overflow in thread storage allocation\n"); + return -1; + } + tstor = (struct thread_storage *)malloc(tstor_size); if (!tstor) { - perror("could not allocate memory for strocut thread_storage array\n"); + perror("could not allocate memory for struct thread_storage array\n"); + return -1; } statst.tstor = tstor; // The stats thread needs to know how to access the socket for each packet worker - global_thread_stall = (struct thread_stall *)malloc((num_threads + 1) * sizeof(struct thread_stall)); + size_t stall_size = (num_threads + 1) * sizeof(struct thread_stall); + if (num_threads > SIZE_MAX - 1 || stall_size / (num_threads + 1) != sizeof(struct thread_stall)) { + fprintf(stderr, "error: integer overflow in thread stall allocation\n"); + free(tstor); + return -1; + } + global_thread_stall = (struct thread_stall *)malloc(stall_size); if (!global_thread_stall) { perror("could not allocate memory for global thread stall structs\n"); + free(tstor); + return -1; } for (int i = 0; i <= num_threads; i++) { global_thread_stall[i].used = 0; diff --git a/src/capture.c b/src/capture.c index 3809ae43..02fc8ca9 100644 --- a/src/capture.c +++ b/src/capture.c @@ -16,7 +16,8 @@ */ enum status bind_and_dispatch(struct mercury_config *, mercury_context, - struct output_file *) { + struct output_file *, + struct cap_stats *) { fprintf(stderr, "error: packet capture is unavailable; AF_PACKET TPACKETv3 not present\n"); diff --git a/src/intercept.cc b/src/intercept.cc index 238d6bf7..851573cb 100644 --- a/src/intercept.cc +++ b/src/intercept.cc @@ -525,7 +525,8 @@ struct daemon_output : public output { perror("opening datagram socket"); } name.sun_family = AF_UNIX; - strcpy(name.sun_path, socket_name); + strncpy(name.sun_path, socket_name, sizeof(name.sun_path) - 1); + name.sun_path[sizeof(name.sun_path) - 1] = '\0'; } void write_buffer(struct buffer_stream &buf) { diff --git a/src/intercept_server.cc b/src/intercept_server.cc index d1ee113c..192a13df 100644 --- a/src/intercept_server.cc +++ b/src/intercept_server.cc @@ -61,7 +61,8 @@ int main(int argc, char *argv[]) { // struct sockaddr_un name; name.sun_family = AF_UNIX; - strcpy(name.sun_path, SOCKET_PATH); + strncpy(name.sun_path, SOCKET_PATH, sizeof(name.sun_path) - 1); + name.sun_path[sizeof(name.sun_path) - 1] = '\0'; // if an old copy of the named socket is still around, remove it // diff --git a/src/output.c b/src/output.c index 8657e261..60ae2ffa 100644 --- a/src/output.c +++ b/src/output.c @@ -16,6 +16,12 @@ #define gettid() syscall(SYS_gettid) #endif +/* macOS doesn't have gettid(), use pthread_self() instead */ +#ifdef __APPLE__ +#include +#define gettid() ((pid_t)(uintptr_t)pthread_self()) +#endif + #include #include #include diff --git a/src/pcap.h b/src/pcap.h index 39d85280..75b2c0c1 100644 --- a/src/pcap.h +++ b/src/pcap.h @@ -75,12 +75,12 @@ namespace pcap { if (*this == magic_nsec || alt == magic_nsec) { char errmsg_buf[34] = "unsupported file magic: "; - sprintf(errmsg_buf + 25, "%08x", this->value()); + snprintf(errmsg_buf + 25, 9, "%08x", this->value()); throw std::runtime_error(errmsg_buf); } char errmsg_buf[34] = "unrecognized file magic: "; - sprintf(errmsg_buf + 25, "%08x", this->value()); + snprintf(errmsg_buf + 25, 9, "%08x", this->value()); throw std::runtime_error(errmsg_buf); } } diff --git a/src/python_interface.c b/src/python_interface.c index bbff0bef..6e1aeb48 100644 --- a/src/python_interface.c +++ b/src/python_interface.c @@ -46,7 +46,7 @@ int init_python() { buf[i] = '\0'; } } - sprintf(buf2, "sys.path.append(\"%s\")", buf); + snprintf(buf2, sizeof(buf2), "sys.path.append(\"%s\")", buf); PyRun_SimpleString(buf2); if (PyErr_Occurred()) { PyErr_Print(); diff --git a/src/signal_handling.c b/src/signal_handling.c index 3a0644ed..b615af15 100644 --- a/src/signal_handling.c +++ b/src/signal_handling.c @@ -63,6 +63,7 @@ __attribute__((noreturn)) void sig_backtrace (int signal_arg) { } /* Find an execution context to restore */ +#ifdef __linux__ pthread_t tid = pthread_self(); int tnum = 0; @@ -80,6 +81,7 @@ __attribute__((noreturn)) void sig_backtrace (int signal_arg) { tnum++; } +#endif /* We are never supposed to get to the end of this signal handler since * we longjmp out From 84c3e11983cb32264cd6b027be7e4c9d49f68f69 Mon Sep 17 00:00:00 2001 From: David Liedle Date: Sat, 9 Aug 2025 17:27:18 -0600 Subject: [PATCH 2/3] fix: improve code quality and fix memory leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improvements: - Fix format string warnings for uint64_t on macOS (use PRIu64) - Fix pthread_t format specifiers (use %p for opaque pointer) - Add proper NULL checks for fopen() to prevent segfaults - Fix file descriptor leaks in pcap_file_io.c error paths - Update .gitignore with build artifacts - Add missing includes for inttypes.h Memory safety fixes: - Close files on error in pcap_file_io.c - Check fopen() return values in batch_gcd.cc and pkcs8.cc - Prevent NULL pointer dereference in write_pem() calls These changes improve robustness and prevent resource leaks that could lead to crashes or resource exhaustion in long-running processes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .gitignore | 24 +++++++++++++++++++++++- src/batch_gcd.cc | 7 ++++++- src/libmerc_test.c | 3 ++- src/output.c | 11 ++++++----- src/pcap_file_io.c | 4 ++++ src/pkcs8.cc | 14 ++++++++++++-- 6 files changed, 53 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index ec76511e..1fe07c49 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,26 @@ unit_tests/pcaps/*.pcap *.mk *.tgz src/libmerc/asn1/oid.cc -src/libmerc/asn1/oid.h \ No newline at end of file +src/libmerc/asn1/oid.h + +# Build artifacts +src/cert_analyze +src/intercept_server +src/libmerc/.depend +src/libmerc/__.SYMDEF* +src/libmerc/lctrie/.d/ +src/libmerc/lctrie/liblctrie.a +src/libmerc_test +src/libmerc_util +src/unit_test +src/libmerc/*.o +src/libmerc/asn1/*.o +src/libmerc/libmerc.a +src/libmerc/libmerc.so +unit_tests/Makefile +unit_tests/*.o +unit_tests/mercury_test + +# macOS specific +.DS_Store +*.dSYM/ \ No newline at end of file diff --git a/src/batch_gcd.cc b/src/batch_gcd.cc index f33c40d0..3157d9b1 100644 --- a/src/batch_gcd.cc +++ b/src/batch_gcd.cc @@ -794,7 +794,12 @@ class pem_mpz_reader : public mpz_reader { ++linenum; if (*next_line_to_write == linenum) { std::string filename = base_filename + "-line-" + std::to_string(linenum) + ".cert.pem"; - write_pem(fopen(filename.c_str(), "w+"), pemdata.data, pemdata.length(), "CERTIFICATE"); + FILE *fp = fopen(filename.c_str(), "w+"); + if (fp != NULL) { + write_pem(fp, pemdata.data, pemdata.length(), "CERTIFICATE"); + } else { + fprintf(stderr, "error: could not open file %s for writing\n", filename.c_str()); + } ++next_line_to_write; } } diff --git a/src/libmerc_test.c b/src/libmerc_test.c index 723fa58c..3ede4896 100644 --- a/src/libmerc_test.c +++ b/src/libmerc_test.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "libmerc/libmerc.h" @@ -224,7 +225,7 @@ void print_out_analysis_context(const struct analysis_context *c) { &os_info_len)) { const struct os_information *os = os_info; for (unsigned int i=0; i < os_info_len; i++) { - fprintf(stdout, "OS and prevalence: %s\t%lu\n", os->os_name, os->os_prevalence); + fprintf(stdout, "OS and prevalence: %s\t%" PRIu64 "\n", os->os_name, os->os_prevalence); os++; } } else { diff --git a/src/output.c b/src/output.c index 60ae2ffa..f247849b 100644 --- a/src/output.c +++ b/src/output.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "output.h" #include "pcap_file_io.h" // for write_pcap_file_header() #include "libmerc/utils.h" @@ -47,7 +48,7 @@ void thread_queues_init(struct thread_queues *tqs, int n, float frac) { } if (qlen < 8 * LLQ_MAX_MSG_SIZE) { - fprintf(stderr, "Only able to allocate output queue lengths of %lu (minimum %d)\n", qlen, 8 * LLQ_MAX_MSG_SIZE); + fprintf(stderr, "Only able to allocate output queue lengths of %" PRIu64 " (minimum %d)\n", qlen, 8 * LLQ_MAX_MSG_SIZE); exit(255); } @@ -334,7 +335,7 @@ void *output_thread_func(void *arg) { out_ctx->kpid = gettid(); if (out_ctx->from_network == 1) { - fprintf(stderr, "[OUTPUT] Thread with pthread id %lu (PID %u) started...\n", out_ctx->tid, out_ctx->kpid); + fprintf(stderr, "[OUTPUT] Thread with pthread id %p (PID %u) started...\n", (void*)out_ctx->tid, out_ctx->kpid); } int err; @@ -439,7 +440,7 @@ void *output_thread_func(void *arg) { if (drops > 0) { total_drops += drops; - fprintf(stderr, "[OUTPUT] Output queue %d reported %lu drops\n", q, drops); + fprintf(stderr, "[OUTPUT] Output queue %d reported %" PRIu64 " drops\n", q, drops); /* Subtract all the drops we just counted */ __sync_sub_and_fetch(&(out_ctx->qs.queue[q].drops), drops); @@ -448,7 +449,7 @@ void *output_thread_func(void *arg) { if (drops_trunc > 0) { total_drops_trunc += drops_trunc; - fprintf(stderr, "[OUTPUT] Output queue %d reported %lu truncations\n", q, drops_trunc); + fprintf(stderr, "[OUTPUT] Output queue %d reported %" PRIu64 " truncations\n", q, drops_trunc); /* Subtract all the drops we just counted */ __sync_sub_and_fetch(&(out_ctx->qs.queue[q].drops_trunc), drops_trunc); @@ -481,7 +482,7 @@ void *output_thread_func(void *arg) { } if (out_ctx->from_network == 1) { - fprintf(stderr, "[OUTPUT] Thread with pthread id %lu (PID %u) exiting...\n", out_ctx->tid, out_ctx->kpid); + fprintf(stderr, "[OUTPUT] Thread with pthread id %p (PID %u) exiting...\n", (void*)out_ctx->tid, out_ctx->kpid); } return NULL; diff --git a/src/pcap_file_io.c b/src/pcap_file_io.c index 1670cb4d..41a50db9 100644 --- a/src/pcap_file_io.c +++ b/src/pcap_file_io.c @@ -132,6 +132,8 @@ enum status pcap_file_open(struct pcap_file *f, f->fd = fileno(f->file_ptr); // save file descriptor if (f->fd < 0) { printf("%s: error getting file descriptor for pcap file %s\n", strerror(errno), fname); + fclose(f->file_ptr); + f->file_ptr = NULL; return status_err; /* system call failed */ } @@ -188,6 +190,8 @@ enum status pcap_file_open(struct pcap_file *f, f->fd = fileno(f->file_ptr); // save file descriptor if (f->fd < 0) { printf("%s: error getting file descriptor for read file %s\n", strerror(errno), fname); + fclose(f->file_ptr); + f->file_ptr = NULL; return status_err; /* system call failed */ } diff --git a/src/pkcs8.cc b/src/pkcs8.cc index a25be0ed..d296f912 100644 --- a/src/pkcs8.cc +++ b/src/pkcs8.cc @@ -306,7 +306,12 @@ int main(int argc, char *argv[]) { if (!compare(pemdata2, out)) { fprintf(stderr, "error: mismatch between input and output\n"); } std::string outfile_name{"outfile"}; outfile_name += argv[1]; - write_pem(fopen(outfile_name.c_str(), "w"), out.data, out.length()); + FILE *fp = fopen(outfile_name.c_str(), "w"); + if (fp != NULL) { + write_pem(fp, out.data, out.length()); + } else { + fprintf(stderr, "error: could not open file %s for writing\n", outfile_name.c_str()); + } return 0; // EARLY RETURN @@ -367,7 +372,12 @@ int main(int argc, char *argv[]) { fprintf(stdout, "result length: %lu\n", result2.length()); fprintf(stdout, "result: "); result2.fprint_hex(stdout); fputc('\n', stdout); - write_pem(fopen("pkcs8.pem", "w+"), result2.data, result2.length()); + FILE *fp2 = fopen("pkcs8.pem", "w+"); + if (fp2 != NULL) { + write_pem(fp2, result2.data, result2.length()); + } else { + fprintf(stderr, "error: could not open file pkcs8.pem for writing\n"); + } output_buffer<4096> buf; json_object o{&buf}; From 86519492d53bb722bccae4b2246f2a1a977963c1 Mon Sep 17 00:00:00 2001 From: David Liedle Date: Sat, 9 Aug 2025 17:38:18 -0600 Subject: [PATCH 3/3] refactor: eliminate memory leaks and improve code quality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major improvements: - Add cleanup_resources() function to properly free memory on error paths - Replace exit() calls with proper error returns in af_packet_v3.c - Fix TODO memory leaks in addr.cc by freeing resources on error - Fix thread safety issues with rand() and localtime() - Initialize unused fields in dnp3.h to suppress warnings - Add proper casting to suppress blocked_count warning in queue.h Thread safety fixes: - Replace rand() with rand_r() using thread-local seed - Replace localtime() with localtime_r() for thread safety - Fix random seed initialization with pthread_self() hash This completes the code quality improvements, making Mercury more robust and production-ready with proper resource management and thread safety. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- src/af_packet_v3.c | 45 +++++++++++++++++++++++++++++++++------------ src/json_file_io.c | 4 +++- src/libmerc/addr.cc | 6 ++++-- src/libmerc/dnp3.h | 2 ++ src/libmerc/queue.h | 8 ++++++-- src/output.c | 4 +++- src/rnd_pkt_drop.c | 9 ++++++++- 7 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/af_packet_v3.c b/src/af_packet_v3.c index 38a5ac16..bc9d3f94 100644 --- a/src/af_packet_v3.c +++ b/src/af_packet_v3.c @@ -842,6 +842,17 @@ void *packet_capture_thread_func(void *arg) { return NULL; } +/* Helper function to clean up allocated resources */ +static void cleanup_resources(struct thread_storage *tstor, int num_threads) { + if (tstor != NULL) { + free(tstor); + } + if (global_thread_stall != NULL) { + free(global_thread_stall); + global_thread_stall = NULL; + } +} + enum status bind_and_dispatch(struct mercury_config *cfg, mercury_context mc, struct output_file *out_ctx, @@ -850,12 +861,12 @@ enum status bind_and_dispatch(struct mercury_config *cfg, /* sanity check memory fractions */ if (cfg->buffer_fraction < 0.0 || cfg->buffer_fraction > 1.0 ) { fprintf(stdout, "error: refusing to allocate buffer fraction %.3f\n", cfg->buffer_fraction); - exit(255); + return status_err; } if (cfg->io_balance_frac < 0.0 || cfg->io_balance_frac > 1.0 ) { fprintf(stdout, "error: refusing to balance io buffers with %.3f\n", cfg->io_balance_frac); - exit(255); + return status_err; } /* initialize the ring limits from the configuration */ @@ -936,13 +947,15 @@ enum status bind_and_dispatch(struct mercury_config *cfg, uint32_t thread_ring_blockcount = thread_ring_size / thread_ring_blocksize; if (thread_ring_blockcount < rl.af_min_blocks) { fprintf(stderr, "Error: only able to allocate %u blocks per thread (minimum %u)\n", thread_ring_blockcount, rl.af_min_blocks); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } /* blocks must be a multiple of the framesize */ if (thread_ring_blocksize % rl.af_framesize != 0) { fprintf(stderr, "Error: computed thread blocksize (%u) is not a multiple of the framesize (%u)\n", thread_ring_blocksize, rl.af_framesize); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } if ((uint64_t)num_threads * (uint64_t)thread_ring_blockcount * (uint64_t)thread_ring_blocksize < rl.af_desired_memory) { @@ -978,14 +991,16 @@ enum status bind_and_dispatch(struct mercury_config *cfg, err = pthread_attr_init(&(tstor[thread].thread_attributes)); if (err) { fprintf(stderr, "%s: error initializing attributes for thread %d\n", strerror(err), thread); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } pthread_mutexattr_t m_attr; err = pthread_mutexattr_init(&m_attr); if (err) { fprintf(stderr, "%s: error initializing block streak mutex attributes for thread %d\n", strerror(err), thread); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } memcpy(&(tstor[thread].ring_params), &thread_ring_req, sizeof(thread_ring_req)); @@ -994,7 +1009,8 @@ enum status bind_and_dispatch(struct mercury_config *cfg, if (err != 0) { fprintf(stderr, "error creating dedicated socket for thread %d\n", thread); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } } @@ -1024,7 +1040,8 @@ enum status bind_and_dispatch(struct mercury_config *cfg, err = pthread_create(&(statst.tid), NULL, stats_thread_func, &statst); if (err != 0) { perror("error creating stats thread"); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } for (int thread = 0; thread < num_threads; thread++) { @@ -1032,13 +1049,15 @@ enum status bind_and_dispatch(struct mercury_config *cfg, err = pthread_attr_init(&thread_attributes); if (err) { fprintf(stderr, "%s: error initializing attributes for thread %d\n", strerror(err), thread); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } err = pthread_create(&(tstor[thread].tid), &thread_attributes, packet_capture_thread_func, &(tstor[thread])); if (err) { fprintf(stderr, "%s: error creating af_packet capture thread %d\n", strerror(err), thread); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } } @@ -1047,7 +1066,8 @@ enum status bind_and_dispatch(struct mercury_config *cfg, err = pthread_cond_broadcast(&(out_ctx->t_output_c)); /* Wake up output */ if (err != 0) { printf("%s: error broadcasting all clear on output start condition\n", strerror(err)); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } /* At this point all threads are started but they're waiting on @@ -1057,7 +1077,8 @@ enum status bind_and_dispatch(struct mercury_config *cfg, err = pthread_cond_broadcast(&t_start_c); // Wake up all the waiting threads if (err != 0) { printf("%s: error broadcasting all clear on clean start condition\n", strerror(err)); - exit(255); + cleanup_resources(tstor, num_threads); + return status_err; } /* Wait for the stats thread to close (which only happens on a sigint/sigterm) */ diff --git a/src/json_file_io.c b/src/json_file_io.c index 97e08245..d5251454 100644 --- a/src/json_file_io.c +++ b/src/json_file_io.c @@ -43,8 +43,10 @@ enum status json_file_rotate(struct json_file *jf) { char time_str[128]; struct timeval now; + struct tm timeinfo; gettimeofday(&now, NULL); - strftime(time_str, sizeof(time_str) - 1, "%Y%m%d%H%M%S", localtime(&now.tv_sec)); + localtime_r(&now.tv_sec, &timeinfo); + strftime(time_str, sizeof(time_str) - 1, "%Y%m%d%H%M%S", &timeinfo); status = filename_append(outfile, outfile, "-", time_str); if (status) { return status; diff --git a/src/libmerc/addr.cc b/src/libmerc/addr.cc index 261f5f37..262bcfaf 100644 --- a/src/libmerc/addr.cc +++ b/src/libmerc/addr.cc @@ -258,13 +258,15 @@ void subnet_data::process_final() { if (tmp != NULL) { prefix = tmp; } else { - return; // TODO: leak check + free(prefix); + return; // cleaned up } // allocate a buffer for the IP stats lct_ip_stats_t *stats = (lct_ip_stats_t *) calloc(num, sizeof(lct_ip_stats_t)); if (!stats) { - return; // TODO: leak check + free(prefix); + return; // cleaned up } // count which subnets are prefixes of other subnets diff --git a/src/libmerc/dnp3.h b/src/libmerc/dnp3.h index 044d5239..a73fc122 100644 --- a/src/libmerc/dnp3.h +++ b/src/libmerc/dnp3.h @@ -289,6 +289,8 @@ class dnp3_app { public: dnp3_app(datum seg_data): + is_resp{false}, + outstation_resp{false}, data{seg_data}, app_hdr{data}, valid{data.is_not_empty()} diff --git a/src/libmerc/queue.h b/src/libmerc/queue.h index ae7ebd25..7f06ffff 100644 --- a/src/libmerc/queue.h +++ b/src/libmerc/queue.h @@ -72,8 +72,12 @@ class message_queue { usleep(SLEEP_MICROSEC); m_lock.lock(); } - //fprintf(stderr, "%s: message_queue %p blocked for %lu microseconds\n", - // __func__, (void *)this, blocked_count * SLEEP_MICROSEC); +#ifdef DEBUG_QUEUE_BLOCKING + fprintf(stderr, "%s: message_queue %p blocked for %lu microseconds\n", + __func__, (void *)this, blocked_count * SLEEP_MICROSEC); +#else + (void)blocked_count; // suppress unused variable warning +#endif } else { err_count++; //fprintf(stderr, "%s: message_queue %p is full\n", __func__, (void *)this); diff --git a/src/output.c b/src/output.c index f247849b..256237db 100644 --- a/src/output.c +++ b/src/output.c @@ -136,8 +136,10 @@ enum status open_outfile(struct output_file *ojf, bool is_pri) { char time_str[128]; struct timeval now; + struct tm timeinfo; gettimeofday(&now, NULL); - strftime(time_str, sizeof(time_str) - 1, "%Y%m%d%H%M%S", localtime(&now.tv_sec)); + localtime_r(&now.tv_sec, &timeinfo); + strftime(time_str, sizeof(time_str) - 1, "%Y%m%d%H%M%S", &timeinfo); status = filename_append(outfile, outfile, "-", time_str); if (status) { ojf->file_error = true; diff --git a/src/rnd_pkt_drop.c b/src/rnd_pkt_drop.c index eaeede96..5880a171 100644 --- a/src/rnd_pkt_drop.c +++ b/src/rnd_pkt_drop.c @@ -9,6 +9,9 @@ */ #include +#include +#include +#include int rnd_pkt_drop_percent_accept = 0; /* default */ @@ -35,7 +38,11 @@ int increment_percent_accept(int incr) { int select_random(int percent) { /* get random number in the range of 1 to 101 */ - int random_num = rand() % 101 + 1; + static __thread unsigned int seed = 0; + if (seed == 0) { + seed = (unsigned int)time(NULL) ^ (unsigned int)(uintptr_t)pthread_self(); + } + int random_num = rand_r(&seed) % 101 + 1; /** * from the random_num, determine if we want to select this packet