+/*
+ * Save IDB blocks for playback whenever we change output files.
+ * Rewrite EPB and ISB interface IDs.
+ */
+static gboolean
+pcapng_adjust_block(capture_src *pcap_src, const struct pcapng_block_header_s *bh, u_char *pd)
+{
+ switch(bh->block_type) {
+ case BLOCK_TYPE_SHB:
+ {
+ if (global_ld.pcapng_passthrough) {
+ /*
+ * We have a single pcapng input. We pass the SHB through when
+ * writing a single output file and for the first ring buffer
+ * file. We need to save it for the second and subsequent ring
+ * buffer files.
+ */
+ g_free(global_ld.saved_shb);
+ global_ld.saved_shb = (guint8 *) g_memdup(pd, bh->block_total_length);
+
+ /*
+ * We're dealing with one section at a time, so we can (and must)
+ * get rid of our old IDBs.
+ */
+ for (unsigned i = 0; i < global_ld.saved_idbs->len; i++) {
+ saved_idb_t *idb_source = &g_array_index(global_ld.saved_idbs, saved_idb_t, i);
+ g_free(idb_source->idb);
+ }
+ g_array_set_size(global_ld.saved_idbs, 0);
+ } else {
+ /*
+ * We have a new SHB from this capture source. We need to keep
+ * global_ld.saved_idbs intact, so we mark IDBs we previously
+ * collected from this source as deleted.
+ */
+ for (unsigned i = 0; i < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len; i++) {
+ guint32 iface_id = g_array_index(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, guint32, i);
+ saved_idb_t *idb_source = &g_array_index(global_ld.saved_idbs, saved_idb_t, iface_id);
+ g_assert(idb_source->interface_id == pcap_src->interface_id);
+ g_free(idb_source->idb);
+ memset(idb_source, 0, sizeof(saved_idb_t));
+ idb_source->deleted = TRUE;
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: deleted pcapng IDB %u", G_STRFUNC, iface_id);
+ }
+ }
+ g_array_set_size(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, 0);
+ }
+ break;
+ case BLOCK_TYPE_IDB:
+ {
+ /*
+ * Always gather IDBs. We can remove them or mark them as deleted
+ * when we get a new SHB.
+ */
+ saved_idb_t idb_source = { 0 };
+ idb_source.interface_id = pcap_src->interface_id;
+ idb_source.idb_len = bh->block_total_length;
+ idb_source.idb = (guint8 *) g_memdup(pd, idb_source.idb_len);
+ g_array_append_val(global_ld.saved_idbs, idb_source);
+ guint32 iface_id = global_ld.saved_idbs->len - 1;
+ g_array_append_val(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, iface_id);
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: mapped pcapng IDB %u -> %u from source %u",
+ G_STRFUNC, pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len - 1, iface_id, pcap_src->interface_id);
+ }
+ break;
+ case BLOCK_TYPE_EPB:
+ case BLOCK_TYPE_ISB:
+ {
+ if (global_ld.pcapng_passthrough) {
+ /* Our input and output interface IDs are the same. */
+ break;
+ }
+ /* The interface ID is the first 32-bit field after the BH for both EPBs and ISBs. */
+ guint32 iface_id;
+ memcpy(&iface_id, pd + sizeof(struct pcapng_block_header_s), 4);
+ if (iface_id < pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len) {
+ memcpy(pd + sizeof(struct pcapng_block_header_s),
+ &g_array_index(pcap_src->cap_pipe_info.pcapng.src_iface_to_global, guint32, iface_id), 4);
+ } else {
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "%s: pcapng EPB or ISB interface id %u > max %u", G_STRFUNC, iface_id, pcap_src->cap_pipe_info.pcapng.src_iface_to_global->len);
+ return FALSE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return TRUE;
+}
+
+static void
+pcapng_pipe_open_live(int fd,
+ capture_src *pcap_src,
+ char *errmsg,
+ size_t errmsgl)
+{
+ guint32 type = BLOCK_TYPE_SHB;
+ struct pcapng_block_header_s *bh = &pcap_src->cap_pipe_info.pcapng.bh;
+
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng_pipe_open_live: fd %d", fd);
+#ifdef _WIN32
+ if (pcap_src->from_cap_socket)
+#endif
+ {
+ memcpy(pcap_src->cap_pipe_databuf, &type, sizeof(guint32));
+ /* read the rest of the pcapng general block header */
+ pcap_src->cap_pipe_bytes_read = sizeof(guint32);
+ pcap_src->cap_pipe_bytes_to_read = sizeof(struct pcapng_block_header_s);
+ pcap_src->cap_pipe_fd = fd;
+ if (cap_pipe_read_data_bytes(pcap_src, errmsg, errmsgl) < 0) {
+ goto error;
+ }
+ memcpy(bh, pcap_src->cap_pipe_databuf, sizeof(struct pcapng_block_header_s));
+ }
+#ifdef _WIN32
+ else {
+ g_thread_new("cap_pipe_open_live", &cap_thread_read, pcap_src);
+
+ bh->block_type = type;
+ pcap_src->cap_pipe_buf = (char *) &bh->block_total_length;
+ pcap_src->cap_pipe_bytes_read = 0;
+ pcap_src->cap_pipe_bytes_to_read = sizeof(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, (gulong)errmsgl,
+ "End of file on pipe block_total_length during open.");
+ else
+ g_snprintf(errmsg, (gulong)errmsgl,
+ "Error on pipe block_total_length during open: %s.",
+ g_strerror(errno));
+ goto error;
+ }
+ pcap_src->cap_pipe_bytes_read = sizeof(struct pcapng_block_header_s);
+ memcpy(pcap_src->cap_pipe_databuf, bh, sizeof(struct pcapng_block_header_s));
+ pcap_src->cap_pipe_fd = fd;
+ }
+#endif
+ if (pcapng_read_shb(pcap_src, errmsg, errmsgl)) {
+ goto error;
+ }
+
+ return;
+
+error:
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcapng_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
+pcap_pipe_dispatch(loop_data *ld, capture_src *pcap_src, char *errmsg, size_t errmsgl)
+{
+ struct pcap_pkthdr phdr;
+ enum { PD_REC_HDR_READ, PD_DATA_READ, PD_PIPE_EOF, PD_PIPE_ERR,
+ PD_ERR } result;
+#ifdef _WIN32
+ gpointer q_status;
+ wchar_t *err_str;
+#endif
+ ssize_t b;
+ guint new_bufsize;
+ pcap_pipe_info_t *pcap_info = &pcap_src->cap_pipe_info.pcap;
+
+#ifdef LOG_CAPTURE_VERBOSE
+ g_log(LOG_DOMAIN_CAPTURE_CHILD, G_LOG_LEVEL_DEBUG, "pcap_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 = pcap_src->cap_pipe_modified ?
+ sizeof(struct pcaprec_modified_hdr) : sizeof(struct pcaprec_hdr);
+ pcap_src->cap_pipe_bytes_read = 0;
+
+#ifdef _WIN32
+ pcap_src->cap_pipe_buf = (char *) &pcap_info->rechdr;
+ g_async_queue_push(pcap_src->cap_pipe_pending_q, pcap_src->cap_pipe_buf);
+ g_mutex_unlock(pcap_src->cap_pipe_read_mtx);