diff options
Diffstat (limited to 'lib/libpcap/libpcap/pcapng.c')
-rw-r--r-- | lib/libpcap/libpcap/pcapng.c | 1405 |
1 files changed, 1405 insertions, 0 deletions
diff --git a/lib/libpcap/libpcap/pcapng.c b/lib/libpcap/libpcap/pcapng.c new file mode 100644 index 0000000..1f1dfd0 --- /dev/null +++ b/lib/libpcap/libpcap/pcapng.c @@ -0,0 +1,1405 @@ +/* + * Copyright (c) 2012-2015 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <sys/param.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <err.h> +#include <sysexits.h> +#include <unistd.h> +#include <sys/uio.h> +#include "pcapng-private.h" + +#include "pcap-int.h" +#include "pcap-common.h" +#include "sf-pcapng.h" +#include "pcap-util.h" + +/* + * Block cursor - used when processing the contents of a block. + * Contains a pointer into the data being processed and a count + * of bytes remaining in the block. + */ +struct block_cursor { + u_char *data; + size_t data_remaining; + bpf_u_int32 block_type; +}; + + +#define PAD_32BIT(x) ((x + 3) & ~3) +#define PAD_64BIT(x) ((x + 7) & ~7) +#define PADDED_OPTION_LEN(x) ((x) ? PAD_32BIT(x) + sizeof(struct pcapng_option_header) : 0) + + +void * +pcap_ng_block_header_ptr(pcapng_block_t block) +{ + return (block->pcapng_bufptr); +} + +void * +pcap_ng_block_fields_ptr(pcapng_block_t block) +{ + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header)); +} + +void * +pcap_ng_block_data_ptr(pcapng_block_t block) +{ + if (block->pcapng_data_is_external) + return (block->pcapng_data_ptr); + else + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len); +} + +void * +pcap_ng_block_records_ptr(pcapng_block_t block) +{ + if (block->pcapng_block_type != PCAPNG_BT_NRB) + return (NULL); + + if (block->pcapng_data_is_external) + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len); + else + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len + + block->pcapng_data_len); +} + +void * +pcap_ng_block_options_ptr(pcapng_block_t block) +{ + if (block->pcapng_block_type == PCAPNG_BT_SPB) + return (NULL); + + if (block->pcapng_data_is_external) + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len + + block->pcapng_records_len); + else + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len + + block->pcapng_data_len + + block->pcapng_records_len); +} + +void * +pcap_ng_block_trailer_ptr(pcapng_block_t block) +{ + if (block->pcapng_data_is_external) + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len + + block->pcapng_records_len + + block->pcapng_options_len); + else + return (block->pcapng_bufptr + + sizeof(struct pcapng_block_header) + + block->pcapng_fields_len + + block->pcapng_data_len + + block->pcapng_records_len + + block->pcapng_options_len); +} + +pcapng_block_t +pcap_ng_block_alloc(size_t len) +{ + size_t totallen; + u_char *ptr; + struct pcapng_block *block; + + if (len > pcap_ng_block_size_max()) + return (NULL); + + /* + * The internal block structure is prepended + */ + totallen = PAD_64BIT(sizeof(struct pcapng_block)) + len; + ptr = malloc(totallen); + if (ptr == NULL) + return (NULL); + + block = (struct pcapng_block *)ptr; + bzero(block, sizeof(struct pcapng_block)); + + block->pcapng_bufptr = ptr + PAD_64BIT(sizeof(struct pcapng_block)); + block->pcapng_buflen = len; + + return (block); +} + +size_t +pcap_ng_block_size_max() +{ + return (2 * MAXIMUM_SNAPLEN); +} + +void +pcap_ng_free_block(pcapng_block_t block) +{ + free(block); +} + +bpf_u_int32 +pcap_ng_block_get_type(pcapng_block_t block) +{ + return (block->pcapng_block_type); +} + +bpf_u_int32 +pcap_ng_block_get_len(pcapng_block_t block) +{ + return ((bpf_u_int32)block->pcapng_block_len); +} + +int +pcap_ng_block_is_swapped(pcapng_block_t block) +{ + return (block->pcapng_block_swapped); +} + +int +pcapng_update_block_length(pcapng_block_t block) +{ + block->pcapng_block_len = sizeof(struct pcapng_block_header) + + block->pcapng_fields_len + + block->pcapng_data_len + + block->pcapng_records_len + + block->pcapng_options_len + + sizeof(struct pcapng_block_trailer); + + if (block->pcapng_block_len > block->pcapng_buflen) { + errx(EX_SOFTWARE, "%s block len %lu greater than buffer size %lu", + __func__, block->pcapng_block_len, block->pcapng_buflen); + } + + return (0); +} + +int +pcap_ng_block_reset(pcapng_block_t block, bpf_u_int32 type) +{ + bzero(&block->block_fields_, sizeof(block->block_fields_)); + + switch (type) { + case PCAPNG_BT_SHB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_section_header_fields); + + block->pcap_ng_shb_fields.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC; + block->pcap_ng_shb_fields.major_version = PCAPNG_VERSION_MAJOR; + block->pcap_ng_shb_fields.minor_version = PCAPNG_VERSION_MINOR; + block->pcap_ng_shb_fields.section_length = (uint64_t)-1; + break; + + case PCAPNG_BT_IDB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_interface_description_fields); + break; + + case PCAPNG_BT_PB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_packet_fields); + break; + + case PCAPNG_BT_SPB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_simple_packet_fields); + break; + + case PCAPNG_BT_NRB: + block->pcapng_block_type = type; + block->pcapng_fields_len = 0; + break; + + case PCAPNG_BT_ISB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_interface_statistics_fields); + break; + + case PCAPNG_BT_EPB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_enhanced_packet_fields); + break; + + case PCAPNG_BT_PIB: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_process_information_fields); + break; + + case PCAPNG_BT_OSEV: + block->pcapng_block_type = type; + block->pcapng_fields_len = sizeof(struct pcapng_os_event_fields); + break; + + case PCAPNG_BT_DSB: + block->pcapng_block_type = PCAPNG_BT_DSB; + block->pcapng_fields_len = sizeof(struct pcapng_decryption_secrets_fields); + break; + + default: + return (PCAP_ERROR); + } + + block->pcapng_data_ptr = NULL; + block->pcapng_data_len = 0; + block->pcapng_cap_len = 0; + block->pcapng_data_is_external = 0; + + block->pcapng_records_len = 0; + + block->pcapng_options_len = 0; + + pcapng_update_block_length(block); + + return (0); +} + +struct pcapng_section_header_fields * +pcap_ng_get_section_header_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_SHB) + return &block->pcap_ng_shb_fields; + else + return NULL; +} + +struct pcapng_interface_description_fields * +pcap_ng_get_interface_description_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_IDB) + return &block->pcap_ng_idb_fields; + else + return NULL; +} + +struct pcapng_enhanced_packet_fields * +pcap_ng_get_enhanced_packet_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_EPB) + return &block->pcap_ng_epb_fields; + else + return NULL; +} + +struct pcapng_simple_packet_fields * +pcap_ng_get_simple_packet_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_SPB) + return &block->pcap_ng_spb_fields; + else + return NULL; +} + +struct pcapng_packet_fields * +pcap_ng_get_packet_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_PB) + return &block->pcap_ng_opb_fields; + else + return NULL; +} + +struct pcapng_interface_statistics_fields * +pcap_ng_get_interface_statistics_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_ISB) + return &block->pcap_ng_isb_fields; + else + return NULL; +} + +struct pcapng_process_information_fields * +pcap_ng_get_process_information_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_PIB) + return &block->pcap_ng_pib_fields; + else + return NULL; +} + +struct pcapng_os_event_fields * +pcap_ng_get_os_event_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_OSEV) + return &block->pcap_ng_osev_fields; + else + return NULL; +} + +struct pcapng_decryption_secrets_fields * +pcap_ng_get_decryption_secrets_fields(pcapng_block_t block) +{ + if (block != NULL && block->pcapng_block_type == PCAPNG_BT_DSB) + return &block->pcap_ng_dsb_fields; + else + return NULL; +} + +int +pcap_ng_block_does_support_data(pcapng_block_t block) +{ + switch (block->pcapng_block_type) { + case PCAPNG_BT_PB: + case PCAPNG_BT_SPB: + case PCAPNG_BT_EPB: + case PCAPNG_BT_OSEV: + case PCAPNG_BT_DSB: + return (1); + /* NOT REACHED */ + + default: + break; + } + return (0); +} + +void * +pcap_ng_block_packet_get_data_ptr(pcapng_block_t block) +{ + if (pcap_ng_block_does_support_data(block) == 0) + return (NULL); + + return (block->pcapng_data_ptr); +} + +bpf_u_int32 +pcap_ng_block_packet_get_data_len(pcapng_block_t block) +{ + if (pcap_ng_block_does_support_data(block) == 0) + return (0); + + return (block->pcapng_cap_len); +} + +bpf_u_int32 +pcap_ng_block_packet_copy_data(pcapng_block_t block, const void *ptr, + bpf_u_int32 caplen) +{ + bpf_u_int32 padding_len = PAD_32BIT(caplen) - caplen; + + if (pcap_ng_block_does_support_data(block) == 0) + return (PCAP_ERROR); + + if (block->pcapng_block_len + PAD_32BIT(caplen) > block->pcapng_buflen) { + warnx("%s block len %lu greater than buffer size %lu", + __func__, block->pcapng_block_len, block->pcapng_buflen); + return (PCAP_ERROR); + } + /* + * Move the name records and options if necessary + */ + if (block->pcapng_records_len > 0 || block->pcapng_options_len > 0) { + u_char *tmp = pcap_ng_block_records_ptr(block) ? + pcap_ng_block_records_ptr(block) : + pcap_ng_block_options_ptr(block); + size_t len = block->pcapng_records_len + block->pcapng_options_len; + int32_t offset = PAD_32BIT(caplen) - (int32_t)block->pcapng_data_len; + + bcopy(tmp, tmp + offset, len); + } + + /* + * TBD: if records or options exist, should move them or error out + */ + block->pcapng_data_is_external = 0; + block->pcapng_data_ptr = pcap_ng_block_data_ptr(block); + bcopy(ptr, block->pcapng_data_ptr, caplen); + if (padding_len > 0) + bzero(block->pcapng_data_ptr + caplen, padding_len); + block->pcapng_cap_len = caplen; + block->pcapng_data_len = PAD_32BIT(caplen); + + pcapng_update_block_length(block); + + return (0); +} + +bpf_u_int32 +pcap_ng_block_packet_set_data(pcapng_block_t block, const void *ptr, + bpf_u_int32 caplen) +{ + if (pcap_ng_block_does_support_data(block) == 0) + return (PCAP_ERROR); + + block->pcapng_data_is_external = 1; + block->pcapng_data_ptr = (u_char *)ptr; + block->pcapng_cap_len = caplen; + block->pcapng_data_len = PAD_32BIT(caplen); + + pcapng_update_block_length(block); + + return (0); +} + +int +pcap_ng_block_add_option_with_value(pcapng_block_t block, u_short code, + const void *value, u_short value_len) +{ + size_t optlen = sizeof(struct pcapng_option_header) + PAD_32BIT(value_len); + struct pcapng_option_header *opt_header; + bpf_u_int32 padding_len = PAD_32BIT(value_len) - value_len; + u_char *buffer; + u_char *block_option_ptr = pcap_ng_block_options_ptr(block); + + if (block_option_ptr == NULL) { + warnx("%s options not supported for block type %u", + __func__, block->pcapng_block_type); + return (PCAP_ERROR); + } + + if (optlen + block->pcapng_block_len > block->pcapng_buflen) { + warnx("%s block len %lu greater than buffer size %lu", + __func__, block->pcapng_block_len, block->pcapng_buflen); + return (PCAP_ERROR); + } + + opt_header = (struct pcapng_option_header *)(block_option_ptr + block->pcapng_options_len); + /* Insert before the end of option */ + if (block->pcapng_options_len > 0) + opt_header -= 1; + opt_header->option_code = code; + opt_header->option_length = value_len; + + buffer = (u_char *)(opt_header + 1); + + bcopy(value, buffer, value_len); + + if (padding_len > 0) + bzero(buffer + value_len, padding_len); + + /* Add end of option when first option added */ + if (block->pcapng_options_len == 0) + block->pcapng_options_len = sizeof(struct pcapng_option_header); + + block->pcapng_options_len += optlen; + + /* Set the end of option at the end of the options */ + opt_header = (struct pcapng_option_header *)(block_option_ptr + block->pcapng_options_len); + opt_header -= 1; + opt_header->option_code = PCAPNG_OPT_ENDOFOPT; + opt_header->option_length = 0; + + pcapng_update_block_length(block); + + return (0); +} + +int +pcap_ng_block_add_option_with_string(pcapng_block_t block, u_short code, const char *str) +{ + return (pcap_ng_block_add_option_with_value(block, code, str, strlen(str) + 1)); +} + +int +pcap_ng_block_add_option_with_uuid(pcapng_block_t block, u_short code, const uuid_t uu) +{ + return (pcap_ng_block_add_option_with_value(block, code, uu, sizeof(uuid_t))); +} + + +static struct pcapng_option_header * +get_opthdr_from_block_data(struct pcapng_option_header *opthdr, int swapped, + struct block_cursor *cursor, char *errbuf) +{ + struct pcapng_option_header *optp; + + optp = get_from_block_data(cursor, sizeof(*opthdr), errbuf); + if (optp == NULL) { + /* + * Option header is cut short. + */ + return (NULL); + } + *opthdr = *optp; + /* + * Byte-swap it if necessary. + */ + if (swapped) { + opthdr->option_code = SWAPSHORT(opthdr->option_code); + opthdr->option_length = SWAPSHORT(opthdr->option_length); + } + + return (opthdr); +} + +static void * +get_optvalue_from_block_data(struct block_cursor *cursor, + struct pcapng_option_header *opthdr, char *errbuf) +{ + size_t padded_option_len; + void *optvalue; + + /* Pad option length to 4-byte boundary */ + padded_option_len = opthdr->option_length; + padded_option_len = ((padded_option_len + 3)/4)*4; + + optvalue = get_from_block_data(cursor, padded_option_len, errbuf); + if (optvalue == NULL) { + /* + * Option value is cut short. + */ + return (NULL); + } + + return (optvalue); +} + + +int +pcap_ng_block_get_option(pcapng_block_t block, u_short code, struct pcapng_option_info *option_info) +{ + struct pcapng_option_header opthdr; + int swapped; + int num_of_options = 0; + struct block_cursor cursor; + static char errbuf[PCAP_ERRBUF_SIZE + 1]; + + if (option_info == NULL) + return (PCAP_ERROR); + if (block->pcapng_options_len == 0) + goto done; + + swapped = block->pcapng_block_swapped; + + cursor.block_type = block->pcapng_block_type; + cursor.data = pcap_ng_block_options_ptr(block); + cursor.data_remaining = block->pcapng_options_len; + + while (get_opthdr_from_block_data(&opthdr, swapped, &cursor, errbuf)) { + void *value = get_optvalue_from_block_data(&cursor, &opthdr, errbuf); + + /* + * If option is cut short we cannot parse it, give up + */ + if (opthdr.option_length != 0 && value == NULL) + break; + + if (code == opthdr.option_code) { + option_info->code = opthdr.option_code; + option_info->length = opthdr.option_length; + option_info->value = value; + + num_of_options = 1; + break; + } + /* + * Detect end of option delimiter + */ + if (opthdr.option_code == PCAPNG_OPT_ENDOFOPT) + break; + } + +done: + return (num_of_options); +} + +int +pcnapng_block_iterate_options(pcapng_block_t block, + pcapng_option_iterator_func opt_iterator_func, + void *context) +{ + struct pcapng_option_header opthdr; + int swapped; + int num_of_options = 0; + struct block_cursor cursor; + static char errbuf[PCAP_ERRBUF_SIZE + 1]; + + if (block == NULL || opt_iterator_func == NULL) + return (PCAP_ERROR); + swapped = block->pcapng_block_swapped; + + cursor.block_type = block->pcapng_block_type; + cursor.data = pcap_ng_block_options_ptr(block); + cursor.data_remaining = block->pcapng_options_len; + + while (get_opthdr_from_block_data(&opthdr, swapped, &cursor, errbuf)) { + void *value = get_optvalue_from_block_data(&cursor, &opthdr, errbuf); + struct pcapng_option_info option_info; + + /* + * If option is cut short we cannot parse it, give up + */ + if (opthdr.option_length != 0 && value == NULL) + break; + + option_info.code = opthdr.option_code; + option_info.length = opthdr.option_length; + option_info.value = value; + + num_of_options++; + + opt_iterator_func(block, &option_info, context); + + /* + * Detect end of option delimiter + */ + if (opthdr.option_code == PCAPNG_OPT_ENDOFOPT) + break; + } + +done: + return (num_of_options); +} + +int +pcnapng_block_iterate_name_records(pcapng_block_t block, + pcapng_name_record_iterator_func record_iterator_func, + void *context) +{ + struct pcapng_record_header recordhdr; + int swapped; + int num_of_records = 0; + struct block_cursor cursor; + static char errbuf[PCAP_ERRBUF_SIZE + 1]; + + if (block == NULL || record_iterator_func == NULL) + return (PCAP_ERROR); + swapped = block->pcapng_block_swapped; + + cursor.block_type = block->pcapng_block_type; + cursor.data = pcap_ng_block_records_ptr(block); + cursor.data_remaining = block->pcapng_records_len; + + /* + * Note that we take advantage of the fact that name record headers + * have the same layout as option headers + */ + while (get_opthdr_from_block_data((struct pcapng_option_header *)&recordhdr, + swapped, &cursor, errbuf)) { + struct pcapng_name_record_info record_info; + void *value = + get_optvalue_from_block_data(&cursor, + (struct pcapng_option_header *)&recordhdr, + errbuf); + + /* + * If record is cut short we cannot parse it, give up + */ + if (recordhdr.record_length != 0 && value == NULL) + break; + + record_info.code = recordhdr.record_type; + record_info.length = recordhdr.record_length; + record_info.value = value; + + num_of_records++; + + /* + * Detect end of option delimiter + */ + if (record_info.code == PCAPNG_NRES_ENDOFRECORD) + break; + } + +done: + return (num_of_records); +} + +int +pcap_ng_block_add_name_record_common(pcapng_block_t block, uint32_t type, + size_t addrlen, void *addr, const char **names) +{ + size_t names_len = 0; + int i; + const char *p; + size_t record_len = 0; + struct pcapng_record_header *record_hdr; + size_t padding_len; + u_char *buffer; + size_t offset; + u_char *block_records_ptr = pcap_ng_block_records_ptr(block); + + if (block_records_ptr == NULL) + return (PCAP_ERROR); + + for (i = 0; ; i++) { + p = names[i]; + if (p == NULL || *p == 0) + break; + names_len += strlen(p) + 1; + } + + record_len = sizeof(struct pcapng_record_header) + addrlen + PAD_32BIT(names_len); + if (record_len + block->pcapng_block_len > block->pcapng_buflen) { + warnx("%s block len %lu greater than buffer size %lu", + __func__, block->pcapng_block_len, block->pcapng_buflen); + return (PCAP_ERROR); + } + + /* + * Move the options if necessary + */ + if (block->pcapng_options_len > 0) { + u_char *tmp = pcap_ng_block_options_ptr(block); + + bcopy(tmp, tmp + record_len, block->pcapng_options_len); + } + + padding_len = PAD_32BIT(names_len) - names_len; + + record_hdr = (struct pcapng_record_header*)(block_records_ptr + block->pcapng_records_len); + if (block->pcapng_records_len > 0) + record_hdr -= 1; + record_hdr->record_type = type; + record_hdr->record_length = addrlen + PAD_32BIT(names_len); + + buffer = (u_char *)(record_hdr + 1); + bcopy(addr, buffer, addrlen); + offset = addrlen; + for (i = 0; ; i++) { + p = names[i]; + if (p == NULL || *p == 0) + break; + u_short slen = strlen(p) + 1; + bcopy(p, buffer, slen); + offset += slen; + } + if (padding_len > 0) + bzero(buffer + offset, padding_len); + + block->pcapng_records_len += record_len; + + pcapng_update_block_length(block); + + return (0); +} + +int +pcap_ng_block_add_name_record_with_ip4(pcapng_block_t block, + struct in_addr *in4, + const char **names) +{ + if (block->pcapng_block_type != PCAPNG_BT_NRB) + return (PCAP_ERROR); + + return pcap_ng_block_add_name_record_common(block, + PCAPNG_NRES_IP4RECORD, + sizeof(struct in_addr), + in4, + names); +} + +int +pcap_ng_block_add_name_record_with_ip6(pcapng_block_t block, + struct in6_addr *in6, + const char **names) +{ + if (block->pcapng_block_type != PCAPNG_BT_NRB) + return (PCAP_ERROR); + + return pcap_ng_block_add_name_record_common(block, + PCAPNG_NRES_IP4RECORD, + sizeof(struct in6_addr), + in6, + names); +} + +bpf_u_int32 +pcap_ng_externalize_block(void *buffer, size_t buflen, pcapng_block_t block) +{ + struct pcapng_block_header block_header; + struct pcapng_block_trailer block_trailer; + bpf_u_int32 bytes_written = 0; + u_char *ptr; + + if (buffer == NULL || buflen < block->pcapng_block_len) + return (0); + + ptr = buffer; + block_header.block_type = block->pcapng_block_type; + block_header.total_length = (bpf_u_int32)block->pcapng_block_len; + bcopy(&block_header, ptr + bytes_written, sizeof(struct pcapng_block_header)); + bytes_written += sizeof(struct pcapng_block_header); + + switch (block->pcapng_block_type) { + case PCAPNG_BT_SHB: + case PCAPNG_BT_IDB: + case PCAPNG_BT_PB: + case PCAPNG_BT_SPB: + case PCAPNG_BT_NRB: + case PCAPNG_BT_ISB: + case PCAPNG_BT_EPB: + case PCAPNG_BT_PIB: + case PCAPNG_BT_OSEV: + if (block->pcapng_block_type == PCAPNG_BT_PB) { + if(block->pcap_ng_opb_fields.caplen == 0) + block->pcap_ng_opb_fields.caplen = block->pcapng_cap_len; + if(block->pcap_ng_opb_fields.len == 0) + block->pcap_ng_opb_fields.len = block->pcapng_cap_len; + } + if (block->pcapng_block_type == PCAPNG_BT_SPB) { + if(block->pcap_ng_spb_fields.len == 0) + block->pcap_ng_spb_fields.len = block->pcapng_cap_len; + } + if (block->pcapng_block_type == PCAPNG_BT_EPB) { + if(block->pcap_ng_epb_fields.caplen == 0) + block->pcap_ng_epb_fields.caplen = block->pcapng_cap_len; + if(block->pcap_ng_epb_fields.len == 0) + block->pcap_ng_epb_fields.len = block->pcapng_cap_len; + } + if (block->pcapng_block_type == PCAPNG_BT_OSEV) { + if(block->pcap_ng_osev_fields.len == 0) + block->pcap_ng_osev_fields.len = block->pcapng_cap_len; + } + + if (block->pcapng_fields_len > 0) { + bcopy(&block->pcap_ng_shb_fields, ptr + bytes_written, block->pcapng_fields_len); + bytes_written += block->pcapng_fields_len; + } + break; + default: + /* Unknown block */ + return (0); + break; + } + + + if (block->pcapng_data_len > 0) { + bpf_u_int32 padding_len = PAD_32BIT(block->pcapng_cap_len) - block->pcapng_cap_len; + + bcopy(block->pcapng_data_ptr, ptr + bytes_written, block->pcapng_cap_len); + bytes_written += block->pcapng_cap_len; + + if (padding_len > 0) { + bzero(ptr + bytes_written, padding_len); + bytes_written += padding_len; + } + } + + if (block->pcapng_records_len > 0) { + bcopy(pcap_ng_block_records_ptr(block), ptr + bytes_written, block->pcapng_records_len); + bytes_written += block->pcapng_records_len; + } + if (block->pcapng_options_len > 0) { + bcopy(pcap_ng_block_options_ptr(block), ptr + bytes_written, block->pcapng_options_len); + bytes_written += block->pcapng_options_len; + } + + block_trailer.total_length = (bpf_u_int32)block->pcapng_block_len; + bcopy(&block_trailer, ptr + bytes_written, bytes_written); + bytes_written += sizeof(struct pcapng_block_trailer); + + return (bytes_written); +} + +bpf_u_int32 +pcap_ng_dump_block(pcap_dumper_t *p, pcapng_block_t block) +{ + struct pcapng_block_header *block_header; + struct pcapng_block_trailer *block_trailer; + bpf_u_int32 bytes_written = 0; + struct iovec iov[4]; + int iovcnt; + char data_padding[3] = { 0, 0, 0 }; + + block_header = (struct pcapng_block_header *)pcap_ng_block_header_ptr(block); + block_header->block_type = block->pcapng_block_type; + block_header->total_length = (bpf_u_int32)block->pcapng_block_len; + + switch (block->pcapng_block_type) { + case PCAPNG_BT_SHB: + case PCAPNG_BT_IDB: + case PCAPNG_BT_PB: + case PCAPNG_BT_NRB: + case PCAPNG_BT_ISB: + case PCAPNG_BT_EPB: + case PCAPNG_BT_PIB: + case PCAPNG_BT_OSEV: + case PCAPNG_BT_DSB: + if (block->pcapng_block_type == PCAPNG_BT_PB) { + if (block->pcap_ng_opb_fields.caplen == 0) + block->pcap_ng_opb_fields.caplen = block->pcapng_cap_len; + if (block->pcap_ng_opb_fields.len == 0) + block->pcap_ng_opb_fields.len = block->pcapng_cap_len; + } + if (block->pcapng_block_type == PCAPNG_BT_SPB) { + if (block->pcap_ng_spb_fields.len == 0) + block->pcap_ng_spb_fields.len = block->pcapng_cap_len; + } + if (block->pcapng_block_type == PCAPNG_BT_EPB) { + if (block->pcap_ng_epb_fields.caplen == 0) + block->pcap_ng_epb_fields.caplen = block->pcapng_cap_len; + if (block->pcap_ng_epb_fields.len == 0) + block->pcap_ng_epb_fields.len = block->pcapng_cap_len; + } + + // Copy the fixed fields if any + if (block->pcapng_fields_len > 0) { + bcopy(&block->pcap_ng_shb_fields, pcap_ng_block_fields_ptr(block), block->pcapng_fields_len); + } + break; + default: + /* Unknown block */ + return (0); + break; + } + + block_trailer = pcap_ng_block_trailer_ptr(block); + block_trailer->total_length = block_header->total_length; + + iovcnt = 0; + iov[iovcnt].iov_len = sizeof(struct pcapng_block_header) + block->pcapng_fields_len; + iov[iovcnt].iov_base = block->pcapng_bufptr; + iovcnt++; + + if (block->pcapng_data_len > 0) { + bpf_u_int32 padding_len = PAD_32BIT(block->pcapng_cap_len) - block->pcapng_cap_len; + + iov[iovcnt].iov_len = block->pcapng_cap_len; + iov[iovcnt].iov_base = block->pcapng_data_ptr; + iovcnt++; + + /* This is suboptimal... */ + if (padding_len > 0) { + iov[iovcnt].iov_len = padding_len; + iov[iovcnt].iov_base = data_padding; + iovcnt++; + } + } + /* + * The name records, options and block trailer are contiguous + */ + iov[iovcnt].iov_len = block->pcapng_records_len + + block->pcapng_options_len + + sizeof(struct pcapng_block_trailer); + if (block->pcapng_records_len > 0) + iov[iovcnt].iov_base = pcap_ng_block_records_ptr(block); + else if (block->pcapng_options_len > 0) + iov[iovcnt].iov_base = pcap_ng_block_options_ptr(block); + else + iov[iovcnt].iov_base = block_trailer; + iovcnt++; + + bytes_written += writev(p->f->_file, iov, iovcnt); + + return (bytes_written); +} + +int +pcap_ng_block_internalize_common(pcapng_block_t *pblock, pcap_t *p, u_char *raw_block) +{ + pcapng_block_t block = NULL; + struct pcapng_block_header bh = *(struct pcapng_block_header *)raw_block; + struct block_cursor cursor; + int swapped = 0; + + if (pblock == NULL || raw_block == NULL) + return (PCAP_ERROR); + + if (p != NULL) + swapped = p->swapped; + + if (swapped) { + bh.block_type = SWAPLONG(bh.block_type); + bh.total_length = SWAPLONG(bh.total_length); + } + + switch (bh.block_type) { + case PCAPNG_BT_SHB: + pcap_ng_init_section_info(p); + break; + case PCAPNG_BT_IDB: + case PCAPNG_BT_PB: + case PCAPNG_BT_SPB: + case PCAPNG_BT_NRB: + case PCAPNG_BT_ISB: + case PCAPNG_BT_EPB: + case PCAPNG_BT_PIB: + case PCAPNG_BT_OSEV: + case PCAPNG_BT_DSB: + break; + default: + (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "%s: Unknown block type length %u", + __func__, bh.block_type); + goto fail; + } + /* Check the length is reasonable, limit to 1 MBytes */ + if (bh.total_length > 1024 * 1024) { + (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "%s: Block total length %u is greater than 16 MB", + __func__, bh.total_length); + goto fail; + } + + /* + * Some ntar files from wireshark.org do not round up the total block length to + * a multiple of 4 bytes -- they must ignore the 32 bit alignment of the block body! + */ + bh.total_length = PAD_32BIT(bh.total_length); + + if (*pblock == NULL) { + block = pcap_ng_block_alloc(bh.total_length); + if (block == NULL) { + (void) snprintf(p->errbuf, PCAP_ERRBUF_SIZE, + "%s: Unknown block type %u", + __func__, bh.block_type); + goto fail; + } + } else { + block = *pblock; + } + block->pcapng_bufptr = raw_block; + block->pcapng_buflen = bh.total_length; + block->pcapng_buf_is_external = 1; + pcap_ng_block_reset(block, bh.block_type); + block->pcapng_block_len = bh.total_length; + block->pcapng_block_swapped = swapped; + + cursor.data = raw_block + sizeof(struct pcapng_block_header); + cursor.data_remaining = bh.total_length - + sizeof(struct pcapng_block_header) - + sizeof(struct pcapng_block_trailer); + cursor.block_type = bh.block_type; + + switch (bh.block_type) { + case PCAPNG_BT_SHB: { + struct pcapng_section_header_fields *shbp = pcap_ng_get_section_header_fields(block); + struct pcapng_section_header_fields *rawshb; + + rawshb = get_from_block_data(&cursor, sizeof(struct pcapng_section_header_fields), p->errbuf); + if (rawshb == NULL) + goto fail; + + shbp->byte_order_magic = rawshb->byte_order_magic; + shbp->major_version = rawshb->major_version; + shbp->minor_version = rawshb->minor_version; + shbp->section_length = rawshb->section_length; + if (swapped) { + shbp->byte_order_magic = SWAPLONG(shbp->byte_order_magic); + shbp->major_version = SWAPSHORT(shbp->major_version); + shbp->minor_version = SWAPSHORT(shbp->minor_version); + shbp->section_length = SWAPLONGLONG(shbp->section_length); + } + + break; + } + case PCAPNG_BT_IDB: { + struct pcapng_interface_description_fields *idbp = pcap_ng_get_interface_description_fields(block); + struct pcapng_interface_description_fields *rawidb; + + rawidb = get_from_block_data(&cursor, sizeof(struct pcapng_interface_description_fields), p->errbuf); + if (rawidb == NULL) + goto fail; + + idbp->idb_linktype = rawidb->idb_linktype; + idbp->idb_reserved = rawidb->idb_reserved; + idbp->idb_snaplen = rawidb->idb_snaplen; + if (swapped) { + idbp->idb_linktype = SWAPSHORT(idbp->idb_linktype); + idbp->idb_reserved = SWAPSHORT(idbp->idb_reserved); + idbp->idb_snaplen = SWAPLONG(idbp->idb_snaplen); + } + + break; + } + case PCAPNG_BT_ISB: { + struct pcapng_interface_statistics_fields *isbp = pcap_ng_get_interface_statistics_fields(block); + struct pcapng_interface_statistics_fields *rawisb; + + rawisb = get_from_block_data(&cursor, sizeof(struct pcapng_interface_statistics_fields), p->errbuf); + if (rawisb == NULL) + goto fail; + + isbp->interface_id = rawisb->interface_id; + isbp->timestamp_high = rawisb->timestamp_high; + isbp->timestamp_low = rawisb->timestamp_low; + if (swapped) { + isbp->interface_id = SWAPSHORT(isbp->interface_id); + isbp->timestamp_high = SWAPLONG(isbp->timestamp_high); + isbp->timestamp_low = SWAPLONG(isbp->timestamp_low); + } + + break; + } + case PCAPNG_BT_EPB: { + struct pcapng_enhanced_packet_fields *epbp = pcap_ng_get_enhanced_packet_fields(block); + struct pcapng_enhanced_packet_fields *rawepb; + void *data; + + rawepb = get_from_block_data(&cursor, sizeof(struct pcapng_enhanced_packet_fields), p->errbuf); + if (rawepb == NULL) + goto fail; + + epbp->interface_id = rawepb->interface_id; + epbp->timestamp_high = rawepb->timestamp_high; + epbp->timestamp_low = rawepb->timestamp_low; + epbp->caplen = rawepb->caplen; + epbp->len = rawepb->len; + if (swapped) { + epbp->interface_id = SWAPLONG(epbp->interface_id); + epbp->timestamp_high = SWAPLONG(epbp->timestamp_high); + epbp->timestamp_low = SWAPLONG(epbp->timestamp_low); + epbp->caplen = SWAPLONG(epbp->caplen); + epbp->len = SWAPLONG(epbp->len); + } + data = get_from_block_data(&cursor, PAD_32BIT(epbp->caplen), p->errbuf); + if (data == NULL) + goto fail; + block->pcapng_data_is_external = 0; + block->pcapng_data_ptr = (u_char *)data; + block->pcapng_cap_len = epbp->caplen; + block->pcapng_data_len = PAD_32BIT(epbp->caplen); + + break; + } + case PCAPNG_BT_SPB: { + struct pcapng_simple_packet_fields *spbp = pcap_ng_get_simple_packet_fields(block); + struct pcapng_simple_packet_fields *rawspb; + void *data; + uint32_t caplen; + + rawspb = get_from_block_data(&cursor, sizeof(struct pcapng_simple_packet_fields), p->errbuf); + if (rawspb == NULL) + goto fail; + + spbp->len = rawspb->len; + if (swapped) { + spbp->len = SWAPLONG(spbp->len); + } + caplen = bh.total_length - sizeof(struct pcapng_simple_packet_fields) - + sizeof(struct pcapng_block_header) - sizeof(struct pcapng_block_trailer); + if (caplen > spbp->len) + caplen = spbp->len; + data = get_from_block_data(&cursor, PAD_32BIT(caplen), p->errbuf); + if (data == NULL) + goto fail; + block->pcapng_data_is_external = 0; + block->pcapng_data_ptr = (u_char *)data; + block->pcapng_cap_len = caplen; + block->pcapng_data_len = PAD_32BIT(caplen); + + break; + } + case PCAPNG_BT_PB: { + struct pcapng_packet_fields *pbp = pcap_ng_get_packet_fields(block); + struct pcapng_packet_fields *rawpb; + void *data; + + rawpb = get_from_block_data(&cursor, sizeof(struct pcapng_packet_fields), p->errbuf); + if (rawpb == NULL) + goto fail; + + pbp->interface_id = rawpb->interface_id; + pbp->drops_count = rawpb->drops_count; + pbp->timestamp_high = rawpb->timestamp_high; + pbp->timestamp_low = rawpb->timestamp_low; + pbp->caplen = rawpb->caplen; + pbp->len = rawpb->len; + if (swapped) { + /* these were written in opposite byte order */ + pbp->interface_id = SWAPSHORT(pbp->interface_id); + pbp->drops_count = SWAPSHORT(pbp->drops_count); + pbp->timestamp_high = SWAPLONG(pbp->timestamp_high); + pbp->timestamp_low = SWAPLONG(pbp->timestamp_low); + pbp->caplen = SWAPLONG(pbp->caplen); + pbp->len = SWAPLONG(pbp->len); + } + + data = get_from_block_data(&cursor, PAD_32BIT(pbp->caplen), p->errbuf); + if (data == NULL) + goto fail; + block->pcapng_data_is_external = 0; + block->pcapng_data_ptr = (u_char *)data; + block->pcapng_cap_len = pbp->caplen; + block->pcapng_data_len = PAD_32BIT(pbp->caplen); + + break; + } + case PCAPNG_BT_PIB: { + struct pcapng_process_information_fields *pibp = pcap_ng_get_process_information_fields(block); + struct pcapng_process_information_fields *rawpib; + + rawpib = get_from_block_data(&cursor, sizeof(struct pcapng_process_information_fields), p->errbuf); + if (rawpib == NULL) + goto fail; + + pibp->process_id = rawpib->process_id; + if (swapped) { + pibp->process_id = SWAPSHORT(rawpib->process_id); + } + break; + } + case PCAPNG_BT_NRB: { + struct pcapng_record_header *rh; + + while (1) { + size_t record_len; + + rh = get_from_block_data(&cursor, sizeof(struct pcapng_record_header), p->errbuf); + if (rh == NULL) + goto fail; + + if (swapped) + record_len = SWAPSHORT(rh->record_length); + else + record_len = rh->record_length; + + if (get_from_block_data(&cursor, PCAPNG_ROUNDUP32(record_len), p->errbuf) == NULL) + goto fail; + + block->pcapng_records_len += sizeof(struct pcapng_record_header) + + PCAPNG_ROUNDUP32(record_len); + + if (rh->record_type == PCAPNG_NRES_ENDOFRECORD) + break; + } + break; + } + case PCAPNG_BT_OSEV: { + struct pcapng_os_event_fields *osevp = pcap_ng_get_os_event_fields(block); + struct pcapng_os_event_fields *rawosevp; + void *data; + uint32_t caplen; + + rawosevp = get_from_block_data(&cursor, sizeof(struct pcapng_os_event_fields), p->errbuf); + if (rawosevp == NULL) + goto fail; + + osevp->type = rawosevp->type; + osevp->timestamp_high = rawosevp->timestamp_high; + osevp->timestamp_low = rawosevp->timestamp_low; + osevp->len = rawosevp->len; + if (swapped) { + osevp->type = SWAPLONG(osevp->type); + osevp->timestamp_high = SWAPLONG(osevp->timestamp_high); + osevp->timestamp_low = SWAPLONG(osevp->timestamp_low); + osevp->len = SWAPLONG(osevp->len); + } + caplen = bh.total_length - sizeof(struct pcapng_os_event_fields) - + sizeof(struct pcapng_block_header) - sizeof(struct pcapng_block_trailer); + if (caplen > osevp->len) + caplen = osevp->len; + data = get_from_block_data(&cursor, PAD_32BIT(caplen), p->errbuf); + if (data == NULL) + goto fail; + block->pcapng_data_is_external = 0; + block->pcapng_data_ptr = (u_char *)data; + block->pcapng_cap_len = caplen; + block->pcapng_data_len = PAD_32BIT(caplen); + break; + } + case PCAPNG_BT_DSB: { + struct pcapng_decryption_secrets_fields *dsp = pcap_ng_get_decryption_secrets_fields(block); + struct pcapng_decryption_secrets_fields *rawdsp; + void *data; + uint32_t caplen; + + rawdsp = get_from_block_data(&cursor, sizeof(struct pcapng_decryption_secrets_fields), p->errbuf); + if (rawdsp == NULL) + goto fail; + dsp->secrets_type = rawdsp->secrets_type; + dsp->secrets_length = rawdsp->secrets_length; + if (swapped) { + dsp->secrets_type = SWAPLONG(dsp->secrets_type); + dsp->secrets_length = SWAPLONG(dsp->secrets_length); + } + + caplen = bh.total_length - sizeof(struct pcapng_decryption_secrets_fields) - + sizeof(struct pcapng_block_header) - sizeof(struct pcapng_block_trailer); + if (caplen > dsp->secrets_length) + caplen = dsp->secrets_length; + data = get_from_block_data(&cursor, PAD_32BIT(caplen), p->errbuf); + if (data == NULL) + goto fail; + block->pcapng_data_is_external = 0; + block->pcapng_data_ptr = (u_char *)data; + block->pcapng_cap_len = caplen; + block->pcapng_data_len = PAD_32BIT(caplen); + break; + } + default: + goto fail; + } + + /* + * Finally compute the length of the options as options come last in blocks + */ + while (1) { + size_t optlen; + struct pcapng_option_header *opt; + + opt = get_from_block_data(&cursor, sizeof(struct pcapng_option_header), p->errbuf); + /* + * No, or no more options + */ + if (opt == NULL) + break; + + if (swapped) + optlen = SWAPSHORT(opt->option_length); + else + optlen = opt->option_length; + + if (get_from_block_data(&cursor, PCAPNG_ROUNDUP32(optlen), p->errbuf) == NULL) + goto fail; + + block->pcapng_options_len += sizeof(struct pcapng_option_header) + PCAPNG_ROUNDUP32(optlen); + + if (opt->option_code == PCAPNG_OPT_ENDOFOPT) + break; + } + + /* Success */ + if (*pblock == NULL) + *pblock = block; + return (0); + +fail: + if (*pblock == NULL && block != NULL) + pcap_ng_free_block(block); + return (PCAP_ERROR); +} + +int +pcap_ng_block_init_with_raw_block(pcapng_block_t block, pcap_t *p, u_char *raw_block) +{ + return pcap_ng_block_internalize_common(&block, p, raw_block); +} + +pcapng_block_t +pcap_ng_block_alloc_with_raw_block(pcap_t *p, u_char *raw_block) +{ + pcapng_block_t block = NULL; + + if (pcap_ng_block_internalize_common(&block, p, raw_block) == 0) + return (block); + else + return (NULL); +} |