diff --git a/dumpcap.c b/dumpcap.c index cc420ba..6fff51f 100644 --- a/dumpcap.c +++ b/dumpcap.c @@ -108,6 +108,7 @@ */ #include "wiretap/libpcap.h" #include "wiretap/pcapng_module.h" +#include "wiretap/pcapng.h" /**#define DEBUG_DUMPCAP**/ /**#define DEBUG_CHILD_DUMPCAP**/ @@ -270,8 +271,15 @@ typedef struct _capture_src { /**< capture pipe (unix only "input file") */ gboolean from_cap_pipe; /**< TRUE if we are capturing data from a capture pipe */ gboolean from_cap_socket; /**< TRUE if we're capturing from socket */ - struct pcap_hdr cap_pipe_hdr; /**< Pcap header when capturing from a pipe */ - struct pcaprec_modified_hdr cap_pipe_rechdr; /**< Pcap record header when capturing from a pipe */ + gboolean from_pcapng; /**< TRUE if we're capturing from pcapng format */ + union { + struct pcap_hdr cap_pipe_hdr; /**< Pcap header when capturing from a pipe */ + struct pcapng_section_header_block_s pcapng_pipe_shb; /**< Pcapng header when capturing from a pipe */ + }; + union { + struct pcaprec_modified_hdr cap_pipe_rechdr; /**< Pcap record header when capturing from a pipe */ + struct pcapng_block_header_s pcapng_pipe_bh; /**< Pcapng block header when capturing from a pipe */ + }; #ifdef _WIN32 HANDLE cap_pipe_h; /**< The handle of the capture pipe */ #endif @@ -388,6 +396,8 @@ static void capture_loop_write_packet_cb(u_char *pcap_src_p, const struct pcap_p const u_char *pd); static void capture_loop_queue_packet_cb(u_char *pcap_src_p, const struct pcap_pkthdr *phdr, const u_char *pd); +static void capture_loop_write_pcapng_shb(u_char *pcap_src_p, const u_char *pd); +static void capture_loop_write_pcapng_block(u_char *pcap_src_p, const u_char *pd); static void capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, int err, gboolean is_close); @@ -1452,6 +1462,10 @@ cap_pipe_close(int pipe_fd, gboolean from_socket _U_) #endif } +/* Some forward declarations for breaking up cap_pipe_open_live for pcap and pcapng formats */ +static void pcap_pipe_open_live(int fd, capture_src *pcap_src, struct pcap_hdr *hdr, char *errmsg, int errmsgl); +static void pcapng_pipe_open_live(int fd, capture_src *pcap_src, struct pcapng_section_header_block_s *hdr, char *errmsg, int errmsgl); + /* Mimic pcap_open_live() for pipe captures * We check if "pipename" is "-" (stdin), a AF_UNIX socket, or a FIFO, @@ -1462,7 +1476,7 @@ cap_pipe_close(int pipe_fd, gboolean from_socket _U_) static void cap_pipe_open_live(char *pipename, capture_src *pcap_src, - struct pcap_hdr *hdr, + void *hdr, char *errmsg, int errmsgl) { #ifndef _WIN32 @@ -1774,16 +1788,38 @@ cap_pipe_open_live(char *pipename, pcap_src->cap_pipe_modified = TRUE; break; case BLOCK_TYPE_SHB: - /* This isn't pcap, it's pcapng. We don't yet support - reading it. */ - g_snprintf(errmsg, errmsgl, "Capturing from a pipe doesn't support pcapng format."); - goto error; + pcap_src->pcapng_pipe_bh.block_type = BLOCK_TYPE_SHB; + pcap_src->from_pcapng = TRUE; + global_capture_opts.use_pcapng = TRUE; + pcapng_pipe_open_live(fd, pcap_src, (struct pcapng_section_header_block_s *) hdr, errmsg, errmsgl); + return; default: /* Not a pcap type we know about, or not pcap at all. */ g_snprintf(errmsg, errmsgl, "Unrecognized libpcap format or not libpcap data."); goto error; } + pcap_pipe_open_live(fd, pcap_src, (struct pcap_hdr *) hdr, errmsg, errmsgl); + return; +error: + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); + pcap_src->cap_pipe_err = PIPERR; + cap_pipe_close(fd, pcap_src->from_cap_socket); + pcap_src->cap_pipe_fd = -1; +#ifdef _WIN32 + pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE; +#endif +} + +static void +pcap_pipe_open_live(int fd, + capture_src *pcap_src, + struct pcap_hdr *hdr, + char *errmsg, int errmsgl) +{ + size_t bytes_read; + ssize_t b; + int sel_ret; #ifdef _WIN32 if (pcap_src->from_cap_socket) #endif @@ -1829,7 +1865,6 @@ cap_pipe_open_live(char *pipename, } } #endif - if (pcap_src->cap_pipe_byte_swapped) { /* Byte-swap the header fields about which we care. */ hdr->version_major = GUINT16_SWAP_LE_BE(hdr->version_major); @@ -1850,7 +1885,8 @@ cap_pipe_open_live(char *pipename, pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD; if (hdr->version_major < 2) { - g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format"); + g_snprintf(errmsg, errmsgl, "Unable to read old libpcap format version %d.%d", + hdr->version_major, hdr->version_minor); goto error; } @@ -1869,11 +1905,190 @@ error: #endif } +static void +pcapng_read_shb(capture_src *pcap_src, + struct pcapng_section_header_block_s *hdr, + char *errmsg, + int errmsgl) +{ + size_t bytes_read; + ssize_t b; + +#ifdef _WIN32 + if (pcap_src->from_cap_socket) +#endif + { + /* Read the section header */ + bytes_read = 0; + while (bytes_read < sizeof(struct pcapng_section_header_block_s)) { + b = cap_pipe_read(pcap_src->cap_pipe_fd, ((char *)hdr)+bytes_read, + sizeof(struct pcapng_section_header_block_s) - bytes_read, + pcap_src->from_cap_socket); + if (b <= 0) { + if (b == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe header during open."); + else + g_snprintf(errmsg, errmsgl, "Error on pipe header during open: %s.", + g_strerror(errno)); + goto error; + } + bytes_read += b; + } + } +#ifdef _WIN32 + else { + pcap_src->cap_pipe_buf = (char *) hdr; + pcap_src->cap_pipe_bytes_read = 0; + pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_section_header_block_s); + g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf); + g_async_queue_pop(pcap_src->cap_pipe_done_q); + if (pcap_src->cap_pipe_bytes_read <= 0) { + if (pcap_src->cap_pipe_bytes_read == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe section header during open."); + else + g_snprintf(errmsg, errmsgl, "Error on pipe section header during open: %s.", + g_strerror(errno)); + goto error; + } + } +#endif + + switch (hdr->magic) + { + case PCAPNG_MAGIC: + pcap_src->cap_pipe_byte_swapped = FALSE; + break; + case PCAPNG_SWAPPED_MAGIC: + pcap_src->cap_pipe_byte_swapped = TRUE; + break; + default: + /* Not a pcapng type we know about, or not pcapng at all. */ + g_snprintf(errmsg, errmsgl, "Unrecognized pcapng format or not pcapng data."); + goto error; + } + + if (pcap_src->cap_pipe_byte_swapped) { + /* Byte-swap the header fields about which we care. */ + hdr->version_major = GUINT16_SWAP_LE_BE(hdr->version_major); + hdr->version_minor = GUINT16_SWAP_LE_BE(hdr->version_minor); + hdr->section_length = GUINT64_SWAP_LE_BE(hdr->section_length); + pcap_src->pcapng_pipe_bh.block_total_length = GUINT32_SWAP_LE_BE(pcap_src->pcapng_pipe_bh.block_total_length); + } + + pcap_src->cap_pipe_max_pkt_size = WTAP_MAX_PACKET_SIZE_STANDARD; + + /* Setup state to capture the rest of the section header block */ + pcap_src->cap_pipe_state = STATE_READ_DATA; + pcap_src->cap_pipe_bytes_to_read = pcap_src->pcapng_pipe_bh.block_total_length + - sizeof(struct pcapng_block_header_s) + - sizeof(struct pcapng_section_header_block_s); + pcap_src->cap_pipe_bytes_read = 0; + + return; + +error: + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng_read_shb: error %s", errmsg); + pcap_src->cap_pipe_err = PIPERR; + cap_pipe_close(pcap_src->cap_pipe_fd, pcap_src->from_cap_socket); + pcap_src->cap_pipe_fd = -1; +#ifdef _WIN32 + pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE; +#endif +} + +static void +pcapng_pipe_open_live(int fd, + capture_src *pcap_src, + struct pcapng_section_header_block_s *hdr, + char *errmsg, + int errmsgl) +{ + size_t bytes_read; + ssize_t b; + int sel_ret; + + if (pcap_src->pcapng_pipe_bh.block_type != BLOCK_TYPE_SHB) { + g_snprintf(errmsg, errmsgl, "Expected Section Header Block instead of 0x%X.", + pcap_src->pcapng_pipe_bh.block_type); + goto error; + } +#ifdef _WIN32 + if (pcap_src->from_cap_socket) +#endif + { + /* read the block header total length */ + bytes_read = sizeof(guint32); + while (bytes_read < sizeof(struct pcapng_block_header_s)) { + if (fd == -1) { + g_snprintf(errmsg, errmsgl, "Invalid file descriptor."); + goto error; + } + + sel_ret = cap_pipe_select(fd); + if (sel_ret < 0) { + g_snprintf(errmsg, errmsgl, + "Unexpected error from select: %s.", g_strerror(errno)); + goto error; + } else if (sel_ret > 0) { + b = cap_pipe_read(fd, ((char *)&pcap_src->pcapng_pipe_bh)+bytes_read, + sizeof(struct pcapng_block_header_s)-bytes_read, + pcap_src->from_cap_socket); + if (b <= 0) { + if (b == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe section during open."); + else + g_snprintf(errmsg, errmsgl, "Error on pipe section during open: %s.", + g_strerror(errno)); + goto error; + } + bytes_read += b; + } + } + } +#ifdef _WIN32 + else { +#if GLIB_CHECK_VERSION(2,31,0) + g_thread_new("cap_pipe_open_live", &cap_thread_read, pcap_src); +#else + g_thread_create(&cap_thread_read, pcap_src, FALSE, NULL); +#endif + + pcap_src->cap_pipe_buf = (char *) &pcap_src->pcapng_pipe_bh.block_total_length; + pcap_src->cap_pipe_bytes_read = 0; + pcap_src->cap_pipe_bytes_to_read = sizeof(pcap_src->pcapng_pipe_bh.block_total_length); + /* We don't have to worry about cap_pipe_read_mtx here */ + g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf); + g_async_queue_pop(pcap_src->cap_pipe_done_q); + if (pcap_src->cap_pipe_bytes_read <= 0) { + if (pcap_src->cap_pipe_bytes_read == 0) + g_snprintf(errmsg, errmsgl, "End of file on pipe block_total_length during open."); + else + g_snprintf(errmsg, errmsgl, "Error on pipe block_total_length during open: %s.", + g_strerror(errno)); + goto error; + } + } +#endif + + pcap_src->cap_pipe_err = PIPOK; + pcap_src->cap_pipe_fd = fd; + pcapng_read_shb(pcap_src, hdr, errmsg, errmsgl); + return; + +error: + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "cap_pipe_open_live: error %s", errmsg); + pcap_src->cap_pipe_err = PIPERR; + cap_pipe_close(fd, pcap_src->from_cap_socket); + pcap_src->cap_pipe_fd = -1; +#ifdef _WIN32 + pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE; +#endif +} /* We read one record from the pipe, take care of byte order in the record * header, write the record to the capture file, and update capture statistics. */ static int -cap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl) +pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl) { struct pcap_pkthdr phdr; enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR, @@ -2118,6 +2333,256 @@ cap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsg return -1; } +static int +pcapng_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, int errmsgl) +{ + enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR, + PD_ERR } result; +#ifdef _WIN32 +#if !GLIB_CHECK_VERSION(2,31,18) + GTimeVal wait_time; +#endif + gpointer q_status; + wchar_t *err_str; +#endif + ssize_t b; + guint new_bufsize; + +#ifdef LOG_CAPTURE_VERBOSE + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng_pipe_dispatch"); +#endif + + switch (pcap_src->cap_pipe_state) { + + case STATE_EXPECT_REC_HDR: +#ifdef _WIN32 + if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) { +#endif + + pcap_src->cap_pipe_state = STATE_READ_REC_HDR; + pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_block_header_s); + pcap_src->cap_pipe_bytes_read = 0; + +#ifdef _WIN32 + pcap_src->cap_pipe_buf = (char *) &pcap_src->pcapng_pipe_bh; + g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf); + g_mutex_unlock(pcap_src->cap_pipe_read_mtx); + } +#endif + /* Fall through */ + + case STATE_READ_REC_HDR: +#ifdef _WIN32 + if (pcap_src->from_cap_socket) +#endif + { + b = cap_pipe_read(pcap_src->cap_pipe_fd, ((char *)&pcap_src->pcapng_pipe_bh)+pcap_src->cap_pipe_bytes_read, + pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read, pcap_src->from_cap_socket); + if (b <= 0) { + if (b == 0) + result = PD_PIPE_EOF; + else + result = PD_PIPE_ERR; + break; + } + pcap_src->cap_pipe_bytes_read += b; + } +#ifdef _WIN32 + else { +#if GLIB_CHECK_VERSION(2,31,18) + q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT); +#else + g_get_current_time(&wait_time); + g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); + q_status = g_async_queue_timed_pop(pcap_src->cap_pipe_done_q, &wait_time); +#endif + if (pcap_src->cap_pipe_err == PIPEOF) { + result = PD_PIPE_EOF; + break; + } else if (pcap_src->cap_pipe_err == PIPERR) { + result = PD_PIPE_ERR; + break; + } + if (!q_status) { + return 0; + } + } +#endif + if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) + return 0; + result = PD_REC_HDR_READ; + break; + + case STATE_EXPECT_DATA: +#ifdef _WIN32 + if (g_mutex_trylock(pcap_src->cap_pipe_read_mtx)) { +#endif + + pcap_src->cap_pipe_state = STATE_READ_DATA; + pcap_src->cap_pipe_bytes_to_read = pcap_src->pcapng_pipe_bh.block_total_length + - sizeof(struct pcapng_block_header_s); + pcap_src->cap_pipe_bytes_read = 0; + +#ifdef _WIN32 + pcap_src->cap_pipe_buf = pcap_src->cap_pipe_databuf; + g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf); + g_mutex_unlock(pcap_src->cap_pipe_read_mtx); + } +#endif + /* Fall through */ + + case STATE_READ_DATA: +#ifdef _WIN32 + if (pcap_src->from_cap_socket) +#endif + { + b = cap_pipe_read(pcap_src->cap_pipe_fd, + pcap_src->cap_pipe_databuf+pcap_src->cap_pipe_bytes_read, + pcap_src->cap_pipe_bytes_to_read - pcap_src->cap_pipe_bytes_read, + pcap_src->from_cap_socket); + if (b <= 0) { + if (b == 0) + result = PD_PIPE_EOF; + else + result = PD_PIPE_ERR; + break; + } + pcap_src->cap_pipe_bytes_read += b; + } +#ifdef _WIN32 + else { + +#if GLIB_CHECK_VERSION(2,31,18) + q_status = g_async_queue_timeout_pop(pcap_src->cap_pipe_done_q, PIPE_READ_TIMEOUT); +#else + g_get_current_time(&wait_time); + g_time_val_add(&wait_time, PIPE_READ_TIMEOUT); + q_status = g_async_queue_timed_pop(pcap_src->cap_pipe_done_q, &wait_time); +#endif /* GLIB_CHECK_VERSION(2,31,18) */ + if (pcap_src->cap_pipe_err == PIPEOF) { + result = PD_PIPE_EOF; + break; + } else if (pcap_src->cap_pipe_err == PIPERR) { + result = PD_PIPE_ERR; + break; + } + if (!q_status) { + return 0; + } + } +#endif /* _WIN32 */ + if (pcap_src->cap_pipe_bytes_read < pcap_src->cap_pipe_bytes_to_read) + return 0; + result = PD_DATA_READ; + break; + + default: + g_snprintf(errmsg, errmsgl, "pcapng_pipe_dispatch: invalid state"); + result = PD_ERR; + + } /* switch (pcap_src->cap_pipe_state) */ + + /* + * We've now read as much data as we were expecting, so process it. + */ + switch (result) { + + case PD_REC_HDR_READ: + if (pcap_src->pcapng_pipe_bh.block_type == BLOCK_TYPE_SHB) { + pcapng_read_shb(pcap_src, &pcap_src->pcapng_pipe_shb, errmsg, errmsgl); + return 1; + } + + /* We've read the header. Take care of byte order. */ + if (pcap_src->cap_pipe_byte_swapped) { + /* Byte-swap the record header fields. */ + pcap_src->pcapng_pipe_bh.block_type = GUINT32_SWAP_LE_BE(pcap_src->pcapng_pipe_bh.block_type); + pcap_src->pcapng_pipe_bh.block_total_length = GUINT32_SWAP_LE_BE(pcap_src->pcapng_pipe_bh.block_total_length); + } + if (pcap_src->pcapng_pipe_bh.block_total_length > pcap_src->cap_pipe_max_pkt_size) { + /* + * The record contains more data than the advertised/allowed in the + * pcapng header, do not try to read more data (do not change to + * STATE_EXPECT_DATA) as that would not fit in the buffer and + * instead stop with an error. + */ + g_snprintf(errmsg, errmsgl, "Frame %u too long (%d bytes)", + ld->packet_count+1, pcap_src->pcapng_pipe_bh.block_total_length); + break; + } + + if (pcap_src->pcapng_pipe_bh.block_total_length > pcap_src->cap_pipe_databuf_size) { + /* + * Grow the buffer to the packet size, rounded up to a power of + * 2. + */ + new_bufsize = pcap_src->pcapng_pipe_bh.block_total_length; + /* + * http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + */ + new_bufsize--; + new_bufsize |= new_bufsize >> 1; + new_bufsize |= new_bufsize >> 2; + new_bufsize |= new_bufsize >> 4; + new_bufsize |= new_bufsize >> 8; + new_bufsize |= new_bufsize >> 16; + new_bufsize++; + pcap_src->cap_pipe_databuf = (guchar*)g_realloc(pcap_src->cap_pipe_databuf, new_bufsize); + pcap_src->cap_pipe_databuf_size = new_bufsize; + } + + /* The record always has at least the block total length following the header */ + if (pcap_src->pcapng_pipe_bh.block_total_length < sizeof(struct pcapng_block_header_s)+sizeof(guint32)) { + g_snprintf(errmsg, errmsgl, "malformed pcapng block_total_length < minimum"); + pcap_src->cap_pipe_err = PIPEOF; + return -1; + } + pcap_src->cap_pipe_state = STATE_EXPECT_DATA; + return 0; + + case PD_DATA_READ: + /* If this is a section header block then write out the parsed data with the received data */ + if (pcap_src->pcapng_pipe_bh.block_type == BLOCK_TYPE_SHB) { + capture_loop_write_pcapng_shb((u_char *)pcap_src, pcap_src->cap_pipe_databuf); + pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR; + return 1; + } + if (use_threads) { + //capture_loop_queue_pcapng_block((u_char *)pcap_src, pcap_src->cap_pipe_databuf); + g_snprintf(errmsg, errmsgl, + "capture_loop_queue_pcapng_block not implemented for threads."); + return -1; + } else { + capture_loop_write_pcapng_block((u_char *)pcap_src, pcap_src->cap_pipe_databuf); + } + pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR; + return 1; + + case PD_PIPE_EOF: + pcap_src->cap_pipe_err = PIPEOF; + return -1; + + case PD_PIPE_ERR: +#ifdef _WIN32 + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), 0, (LPTSTR) &err_str, 0, NULL); + g_snprintf(errmsg, errmsgl, + "Error reading from pipe: %s (error %d)", + utf_16to8(err_str), GetLastError()); + LocalFree(err_str); +#else + g_snprintf(errmsg, errmsgl, "Error reading from pipe: %s", + g_strerror(errno)); +#endif + /* Fall through */ + case PD_ERR: + break; + } + + pcap_src->cap_pipe_err = PIPERR; + /* Return here rather than inside the switch to prevent GCC warning */ + return -1; +} /** Open the capture input file (pcap or capture pipe). * Returns TRUE if it succeeds, FALSE otherwise. */ @@ -2199,34 +2664,16 @@ capture_loop_open_input(capture_options *capture_opts, loop_data *ld, "Could not allocate memory."); return FALSE; } - pcap_src->received = 0; - pcap_src->dropped = 0; - pcap_src->flushed = 0; - pcap_src->pcap_h = NULL; + memset(pcap_src, 0, sizeof(capture_src)); #ifdef MUST_DO_SELECT pcap_src->pcap_fd = -1; #endif - pcap_src->pcap_err = FALSE; pcap_src->interface_id = i; - pcap_src->tid = NULL; - pcap_src->snaplen = 0; pcap_src->linktype = -1; - pcap_src->ts_nsec = FALSE; - pcap_src->from_cap_pipe = FALSE; - pcap_src->from_cap_socket = FALSE; - memset(&pcap_src->cap_pipe_hdr, 0, sizeof(struct pcap_hdr)); - memset(&pcap_src->cap_pipe_rechdr, 0, sizeof(struct pcaprec_modified_hdr)); #ifdef _WIN32 pcap_src->cap_pipe_h = INVALID_HANDLE_VALUE; #endif pcap_src->cap_pipe_fd = -1; - pcap_src->cap_pipe_modified = FALSE; - pcap_src->cap_pipe_byte_swapped = FALSE; -#ifdef _WIN32 - pcap_src->cap_pipe_buf = NULL; -#endif - pcap_src->cap_pipe_bytes_to_read = 0; - pcap_src->cap_pipe_bytes_read = 0; pcap_src->cap_pipe_state = STATE_EXPECT_REC_HDR; pcap_src->cap_pipe_err = PIPOK; #ifdef _WIN32 @@ -2475,6 +2922,11 @@ capture_loop_init_output(capture_options *capture_opts, loop_data *ld, char *err GString *cpu_info_str; GString *os_info_str; + /* If our source is also pcapng then we will just write the source blocks */ + pcap_src = g_array_index(ld->pcaps, capture_src *, 0); + if (pcap_src->from_pcapng) { + return TRUE; + } cpu_info_str = g_string_new(""); os_info_str = g_string_new(""); get_cpu_info(cpu_info_str); @@ -2649,7 +3101,11 @@ capture_loop_dispatch(loop_data *ld, * "select()" says we can read from the pipe without blocking */ #endif - inpkts = cap_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len); + if (pcap_src->from_pcapng) { + inpkts = pcapng_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len); + } else { + inpkts = pcap_pipe_dispatch(ld, pcap_src, errmsg, errmsg_len); + } if (inpkts < 0) { ld->go = FALSE; } @@ -2978,6 +3434,12 @@ do_file_switch_or_stop(capture_options *capture_opts, GString *cpu_info_str; GString *os_info_str; + /* If our source is also pcapng then we will just write the source blocks */ + pcap_src = g_array_index(global_ld.pcaps, capture_src *, 0); + if (pcap_src->from_pcapng) { + return TRUE; + } + cpu_info_str = g_string_new(""); os_info_str = g_string_new(""); get_cpu_info(cpu_info_str); @@ -3648,6 +4110,113 @@ capture_loop_get_errmsg(char *errmsg, int errmsglen, const char *fname, } } +/* one pcapng block was captured, process it */ +static void +capture_loop_write_pcapng_shb(u_char *pcap_src_p, const u_char *pd) +{ + capture_src *pcap_src = (capture_src *) (void *) pcap_src_p; + int err; + + if (!global_capture_opts.use_pcapng) { + return; + } + /* We may be called multiple times from pcap_dispatch(); if we've set + the "stop capturing" flag, ignore this packet, as we're not + supposed to be saving any more packets. */ + if (!global_ld.go) { + pcap_src->flushed++; + return; + } + + if (global_ld.pdh) { + gboolean successful; + + /* We're supposed to write the packet to a file; do so. + If this fails, set "ld->go" to FALSE, to stop the capture, and set + "ld->err" to the error. */ + successful = pcapng_rewrite_session_header_block(global_ld.pdh, + pcap_src->pcapng_pipe_shb.magic, + pcap_src->pcapng_pipe_shb.version_major, + pcap_src->pcapng_pipe_shb.version_minor, + pcap_src->pcapng_pipe_bh.block_total_length, + pd, + &global_ld.bytes_written, &err); + + fflush(global_ld.pdh); + if (!successful) { + global_ld.go = FALSE; + global_ld.err = err; +#if 0 + pcap_src->dropped++; + } else { +#if defined(DEBUG_DUMPCAP) || defined(DEBUG_CHILD_DUMPCAP) + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Wrote a packet of length %d captured on interface %u.", + phdr->caplen, pcap_src->interface_id); +#endif + global_ld.packet_count++; + pcap_src->received++; + /* if the user told us to stop after x packets, do we already have enough? */ + if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) { + global_ld.go = FALSE; + } +#endif /* 0 */ + } + } +} + + +/* one pcapng block was captured, process it */ +static void +capture_loop_write_pcapng_block(u_char *pcap_src_p, const u_char *pd) +{ + capture_src *pcap_src = (capture_src *) (void *) pcap_src_p; + int err; + + if (!global_capture_opts.use_pcapng) { + return; + } + /* We may be called multiple times from pcap_dispatch(); if we've set + the "stop capturing" flag, ignore this packet, as we're not + supposed to be saving any more packets. */ + if (!global_ld.go) { + pcap_src->flushed++; + return; + } + + if (global_ld.pdh) { + gboolean successful; + + /* We're supposed to write the packet to a file; do so. + If this fails, set "ld->go" to FALSE, to stop the capture, and set + "ld->err" to the error. */ + successful = pcapng_rewrite_general_block(global_ld.pdh, + pcap_src->pcapng_pipe_shb.magic, + pcap_src->pcapng_pipe_bh.block_type, + pcap_src->pcapng_pipe_bh.block_total_length, + pd, + &global_ld.bytes_written, &err); + + fflush(global_ld.pdh); + if (!successful) { + global_ld.go = FALSE; + global_ld.err = err; + pcap_src->dropped++; + } else if (pcap_src->pcapng_pipe_bh.block_type == BLOCK_TYPE_EPB) { +#if defined(DEBUG_DUMPCAP) || defined(DEBUG_CHILD_DUMPCAP) + g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_INFO, + "Wrote a packet of length %d captured on interface %u.", + phdr->caplen, pcap_src->interface_id); +#endif + global_ld.packet_count++; + pcap_src->received++; + /* if the user told us to stop after x packets, do we already have enough? */ + if ((global_ld.packet_max > 0) && (global_ld.packet_count >= global_ld.packet_max)) { + global_ld.go = FALSE; + } + } + } +} /* one packet was captured, process it */ static void diff --git a/wiretap/pcapng.h b/wiretap/pcapng.h index 36e14a4..1c2cf87 100644 --- a/wiretap/pcapng.h +++ b/wiretap/pcapng.h @@ -25,6 +25,12 @@ #include "wtap.h" #include "ws_symbol_export.h" +#define PCAPNG_MAGIC 0x1A2B3C4D +#define PCAPNG_SWAPPED_MAGIC 0xD4C3B2A1 + +#define PCAPNG_MAJOR_VERSION 1 +#define PCAPNG_MINOR_VERSION 0 + /* pcapng: common block header file encoding for every block type */ typedef struct pcapng_block_header_s { guint32 block_type; diff --git a/writecap/pcapio.c b/writecap/pcapio.c index abc0af9..2fc253a 100644 --- a/writecap/pcapio.c +++ b/writecap/pcapio.c @@ -284,6 +284,85 @@ pcapng_write_string_option(FILE* pfile, return TRUE; } +extern gboolean +pcapng_rewrite_general_block(FILE* pfile, + guint32 magic, + guint32 block_type, + guint32 block_total_length, + const char *data, + guint64 *bytes_written, + int *err) +{ + guint32 bytes_to_write = block_total_length; + + /* write the general block header */ + if (magic != PCAPNG_MAGIC) { + block_type = GUINT32_SWAP_LE_BE(block_type); + block_total_length = GUINT32_SWAP_LE_BE(block_total_length); + } + + if (!write_to_file(pfile, (const guint8*)&block_type, sizeof(guint32), bytes_written, err)) { + return FALSE; + } + if (!write_to_file(pfile, (const guint8*)&block_total_length, sizeof(guint32), bytes_written, err)) { + return FALSE; + } + + bytes_to_write -= 2*sizeof(guint32); + + /* write the rest of the data including trailing block total length */ + return write_to_file(pfile, (const guint8*)data, bytes_to_write, bytes_written, err); +} + +gboolean +pcapng_rewrite_session_header_block(FILE* pfile, /**< Write information */ + guint32 magic, + guint16 version_major, + guint16 version_minor, + guint32 block_total_length, + const char *data, + guint64 *bytes_written, /**< Number of written bytes */ + int *err /**< Error type */ + ) +{ + guint32 write_total_length; + guint32 bytes_to_write = block_total_length; + struct shb shb; + + /* Size of base header */ + if (magic == PCAPNG_MAGIC) { + write_total_length = block_total_length; + + /* write shb header */ + shb.block_type = SECTION_HEADER_BLOCK_TYPE; + shb.block_total_length = write_total_length; + shb.byte_order_magic = magic; + shb.major_version = version_major; + shb.minor_version = version_minor; + shb.section_length = ~0ULL; + } + else { + write_total_length = GUINT32_SWAP_LE_BE(block_total_length); + + /* write shb header */ + shb.block_type = SECTION_HEADER_BLOCK_TYPE; + shb.block_total_length = write_total_length; + shb.byte_order_magic = PCAPNG_SWAPPED_MAGIC; + shb.major_version = GUINT16_SWAP_LE_BE(version_major); + shb.minor_version = GUINT16_SWAP_LE_BE(version_minor); + shb.section_length = ~0ULL; + } + + if (!write_to_file(pfile, (const guint8*)&shb, sizeof(struct shb), bytes_written, err)) { + return FALSE; + } + + bytes_to_write -= sizeof(struct shb); + + /* write the rest of the data including trailing block total length */ + return write_to_file(pfile, (const guint8*)data, bytes_to_write, bytes_written, err); +} + gboolean pcapng_write_session_header_block(FILE* pfile, const char *comment, diff --git a/writecap/pcapio.h b/writecap/pcapio.h index 9263024..f51c62e 100644 --- a/writecap/pcapio.h +++ b/writecap/pcapio.h @@ -43,6 +43,26 @@ libpcap_write_packet(FILE* pfile, /* Writing pcap-ng files */ +extern gboolean +pcapng_rewrite_general_block(FILE* pfile, + guint32 magic, + guint32 block_type, + guint32 block_total_length, + const char *data, + guint64 *bytes_written, + int *err); + +extern gboolean +pcapng_rewrite_session_header_block(FILE* pfile, /**< Write information */ + guint32 magic, + guint16 version_major, + guint16 version_minor, + guint32 block_total_length, + const char *data, + guint64 *bytes_written, /**< Number of written bytes */ + int *err /**< Error type */ + ); + /** Write a section header block (SHB) * */