/** * Dump relayed or locally-emitted packet. * If ``from'' is NULL, packet was emitted locally. */ static void dump_packet_from_to(struct dump *dump, const struct gnutella_node *from, const struct gnutella_node *to, const pmsg_t *mb) { struct dump_header dh_to; struct dump_header dh_from; g_assert(to != NULL); g_assert(mb != NULL); g_assert(pmsg_read_base(mb) == pmsg_start(mb)); if (!dump_initialize(dump)) return; /* * This is only for Gnutella packets, leave DHT messages out. */ if (GTA_MSG_DHT == gnutella_header_get_function(pmsg_start(mb))) return; if (!ipset_contains_addr(&dump_tx_to_addrs, to->addr, TRUE)) return; if (NULL == from) { struct gnutella_node local; local.peermode = NODE_IS_UDP(to) ? NODE_P_UDP : NODE_P_NORMAL; local.addr = listen_addr(); local.port = GNET_PROPERTY(listen_port); if (!ipset_contains_addr(&dump_tx_from_addrs, local.addr, TRUE)) return; dump_header_set(&dh_from, &local); } else { if (!ipset_contains_addr(&dump_tx_from_addrs, from->addr, TRUE)) return; dump_header_set(&dh_from, from); } dump_header_set(&dh_to, to); dh_to.data[0] |= DH_F_TO; if (pmsg_prio(mb) != PMSG_P_DATA) dh_to.data[0] |= DH_F_CTRL; dump_append(dump, dh_to.data, sizeof dh_to.data); dump_append(dump, dh_from.data, sizeof dh_from.data); dump_append(dump, pmsg_read_base(mb), pmsg_size(mb)); dump_flush(dump); }
/** * Creates an iovec from a singly-linked list of pmsg_t buffers. * It should be freed via hfree(). * * NOTE: The iovec will hold no more than MAX_IOV_COUNT items. That means * the iovec might not cover the whole buffered data. This limit * is applied because writev() could fail with EINVAL otherwise * which would simply add more unnecessary complexity. */ iovec_t * pmsg_slist_to_iovec(slist_t *slist, int *iovcnt_ptr, size_t *size_ptr) { iovec_t *iov; size_t held = 0; int n; g_assert(slist); n = slist_length(slist); if (n > 0) { slist_iter_t *iter; int i; n = MIN(n, MAX_IOV_COUNT); iov = halloc(n * sizeof *iov); iter = slist_iter_before_head(slist); for (i = 0; i < n; i++) { pmsg_t *mb; size_t size; mb = slist_iter_next(iter); pmsg_check_consistency(mb); size = pmsg_size(mb); g_assert(size > 0); held += size; iovec_set(&iov[i], deconstify_pointer(pmsg_read_base(mb)), size); } slist_iter_free(&iter); } else { iov = NULL; } if (iovcnt_ptr) { *iovcnt_ptr = MAX(0, n); } if (size_ptr) { *size_ptr = held; } return iov; }
/** * Dechunk more data from the input buffer `mb'. * @returns dechunked data in a new buffer, or NULL if no more data. */ static pmsg_t * dechunk_data(rxdrv_t *rx, pmsg_t *mb) { struct attr *attr = rx->opaque; const char *error_str, *src; size_t size; /* * Prepare call to parse_chunk(). */ size = pmsg_size(mb); src = pmsg_read_base(mb); while (size > 0) { size_t ret; g_assert(CHUNK_STATE_ERROR != attr->state); /* * Copy avoidance: if the data we got fits into the current chunk size, * then we don't have to parse anything: all the data belong to the * current chunk, so we can simply pass them to the upper layer. */ if (CHUNK_STATE_DATA == attr->state) { pmsg_t *nmb; nmb = pmsg_clone(mb); if (size < attr->data_remain) { /* The complete chunk data is forwarded to the upper layer */ mb->m_rptr += size; attr->data_remain -= size; } else { /* Only the first ``data_remain'' bytes are forwarded */ mb->m_rptr += attr->data_remain; nmb->m_wptr = deconstify_pointer(&nmb->m_rptr[attr->data_remain]); attr->data_remain = 0; attr->state = CHUNK_STATE_DATA_CRLF; } if (GNET_PROPERTY(rx_debug) > 9) g_debug("dechunk_data: returning chunk of %u bytes", pmsg_size(nmb)); return nmb; } g_assert(size > 0); g_assert(CHUNK_STATE_DATA != attr->state); /* * Parse chunk headers */ ret = parse_chunk(rx, src, size, &error_str); if (0 == ret) { /* * We can't continue if we meet a dechunking error. Signal * our user so that the connection is terminated. */ errno = EIO; attr->cb->chunk_error(rx->owner, "dechunk() failed: %s", error_str); g_warning("dechunk_data(): %s", error_str); break; } g_assert(ret <= size); size -= ret; mb->m_rptr += ret; /* Read that far */ } return NULL; /* No more data */ }