/* usbredirparser.c usb redirection protocol parser Copyright 2010-2012 Red Hat, Inc. Red Hat Authors: Hans de Goede This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ #include "config.h" #include #include #include #include #include "usbredirproto-compat.h" #include "usbredirparser.h" #include "usbredirfilter.h" /* Put *some* upper limit on bulk transfer sizes */ #define MAX_BULK_TRANSFER_SIZE (128u * 1024u * 1024u) /* Locking convenience macros */ #define LOCK(parser) \ do { \ if ((parser)->lock) \ (parser)->callb.lock_func((parser)->lock); \ } while (0) #define UNLOCK(parser) \ do { \ if ((parser)->lock) \ (parser)->callb.unlock_func((parser)->lock); \ } while (0) struct usbredirparser_buf { uint8_t *buf; int pos; int len; struct usbredirparser_buf *next; }; struct usbredirparser_priv { struct usbredirparser callb; int flags; int have_peer_caps; uint32_t our_caps[USB_REDIR_CAPS_SIZE]; uint32_t peer_caps[USB_REDIR_CAPS_SIZE]; void *lock; union { struct usb_redir_header header; struct usb_redir_header_32bit_id header_32bit_id; }; uint8_t type_header[288]; int header_read; int type_header_len; int type_header_read; uint8_t *data; int data_len; int data_read; int to_skip; struct usbredirparser_buf *write_buf; int write_buf_count; }; static void #if defined __GNUC__ __attribute__((format(printf, 3, 4))) #endif va_log(struct usbredirparser_priv *parser, int verbose, const char *fmt, ...) { char buf[512]; va_list ap; int n; n = sprintf(buf, "usbredirparser: "); va_start(ap, fmt); vsnprintf(buf + n, sizeof(buf) - n, fmt, ap); va_end(ap); parser->callb.log_func(parser->callb.priv, verbose, buf); } #define ERROR(...) va_log(parser, usbredirparser_error, __VA_ARGS__) #define WARNING(...) va_log(parser, usbredirparser_warning, __VA_ARGS__) #define INFO(...) va_log(parser, usbredirparser_info, __VA_ARGS__) #define DEBUG(...) va_log(parser, usbredirparser_debug, __VA_ARGS__) #if 0 /* Can be enabled and called from random place to test serialization */ static void serialize_test(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; struct usbredirparser_buf *wbuf, *next_wbuf; uint8_t *data; int len; if (usbredirparser_serialize(parser_pub, &data, &len)) return; wbuf = parser->write_buf; while (wbuf) { next_wbuf = wbuf->next; free(wbuf->buf); free(wbuf); wbuf = next_wbuf; } parser->write_buf = NULL; parser->write_buf_count = 0; free(parser->data); parser->data = NULL; parser->type_header_len = parser->data_len = parser->have_peer_caps = 0; usbredirparser_unserialize(parser_pub, data, len); free(data); } #endif static void usbredirparser_queue(struct usbredirparser *parser, uint32_t type, uint64_t id, void *type_header_in, uint8_t *data_in, int data_len); static int usbredirparser_caps_get_cap(struct usbredirparser_priv *parser, uint32_t *caps, int cap); struct usbredirparser *usbredirparser_create(void) { return calloc(1, sizeof(struct usbredirparser_priv)); } static void usbredirparser_verify_caps(struct usbredirparser_priv *parser, uint32_t *caps, const char *desc) { if (usbredirparser_caps_get_cap(parser, caps, usb_redir_cap_bulk_streams) && !usbredirparser_caps_get_cap(parser, caps, usb_redir_cap_ep_info_max_packet_size)) { ERROR("error %s caps contains cap_bulk_streams without" "cap_ep_info_max_packet_size", desc); caps[0] &= ~(1 << usb_redir_cap_bulk_streams); } } void usbredirparser_init(struct usbredirparser *parser_pub, const char *version, uint32_t *caps, int caps_len, int flags) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; struct usb_redir_hello_header hello = { { 0 }, }; parser->flags = (flags & ~usbredirparser_fl_no_hello); if (parser->callb.alloc_lock_func) { parser->lock = parser->callb.alloc_lock_func(); } snprintf(hello.version, sizeof(hello.version), "%s", version); if (caps_len > USB_REDIR_CAPS_SIZE) { caps_len = USB_REDIR_CAPS_SIZE; } memcpy(parser->our_caps, caps, caps_len * sizeof(uint32_t)); /* libusbredirparser handles sending the ack internally */ if (!(flags & usbredirparser_fl_usb_host)) usbredirparser_caps_set_cap(parser->our_caps, usb_redir_cap_device_disconnect_ack); usbredirparser_verify_caps(parser, parser->our_caps, "our"); if (!(flags & usbredirparser_fl_no_hello)) usbredirparser_queue(parser_pub, usb_redir_hello, 0, &hello, (uint8_t *)parser->our_caps, USB_REDIR_CAPS_SIZE * sizeof(uint32_t)); } void usbredirparser_destroy(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; struct usbredirparser_buf *wbuf, *next_wbuf; wbuf = parser->write_buf; while (wbuf) { next_wbuf = wbuf->next; free(wbuf->buf); free(wbuf); wbuf = next_wbuf; } if (parser->lock) parser->callb.free_lock_func(parser->lock); free(parser); } static int usbredirparser_caps_get_cap(struct usbredirparser_priv *parser, uint32_t *caps, int cap) { if (cap / 32 >= USB_REDIR_CAPS_SIZE) { ERROR("error request for out of bounds cap: %d", cap); return 0; } if (caps[cap / 32] & (1 << (cap % 32))) { return 1; } else { return 0; } } void usbredirparser_caps_set_cap(uint32_t *caps, int cap) { caps[cap / 32] |= 1 << (cap % 32); } int usbredirparser_have_peer_caps(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; return parser->have_peer_caps; } int usbredirparser_peer_has_cap(struct usbredirparser *parser_pub, int cap) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; return usbredirparser_caps_get_cap(parser, parser->peer_caps, cap); } int usbredirparser_have_cap(struct usbredirparser *parser_pub, int cap) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; return usbredirparser_caps_get_cap(parser, parser->our_caps, cap); } static int usbredirparser_using_32bits_ids(struct usbredirparser *parser_pub) { return !usbredirparser_have_cap(parser_pub, usb_redir_cap_64bits_ids) || !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_64bits_ids); } static void usbredirparser_handle_hello(struct usbredirparser *parser_pub, struct usb_redir_hello_header *hello, uint8_t *data, int data_len) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; uint32_t *peer_caps = (uint32_t *)data; char buf[64]; int i; if (parser->have_peer_caps) { ERROR("Received second hello message, ignoring"); return; } /* In case hello->version is not 0 terminated (which would be a protocol violation)_ */ strncpy(buf, hello->version, sizeof(buf)); buf[sizeof(buf)-1] = '\0'; memset(parser->peer_caps, 0, sizeof(parser->peer_caps)); if (data_len > sizeof(parser->peer_caps)) { data_len = sizeof(parser->peer_caps); } for (i = 0; i < data_len / sizeof(uint32_t); i++) { parser->peer_caps[i] = peer_caps[i]; } usbredirparser_verify_caps(parser, parser->peer_caps, "peer"); parser->have_peer_caps = 1; free(data); INFO("Peer version: %s, using %d-bits ids", buf, usbredirparser_using_32bits_ids(parser_pub) ? 32 : 64); /* Added in 0.3.2, so no guarantee it is there */ if (parser->callb.hello_func) parser->callb.hello_func(parser->callb.priv, hello); } static int usbredirparser_get_header_len(struct usbredirparser *parser_pub) { if (usbredirparser_using_32bits_ids(parser_pub)) return sizeof(struct usb_redir_header_32bit_id); else return sizeof(struct usb_redir_header); } static int usbredirparser_get_type_header_len( struct usbredirparser *parser_pub, int32_t type, int send) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; int command_for_host = 0; if (parser->flags & usbredirparser_fl_usb_host) { command_for_host = 1; } if (send) { command_for_host = !command_for_host; } switch (type) { case usb_redir_hello: return sizeof(struct usb_redir_hello_header); case usb_redir_device_connect: if (!command_for_host) { if (usbredirparser_have_cap(parser_pub, usb_redir_cap_connect_device_version) && usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_connect_device_version)) { return sizeof(struct usb_redir_device_connect_header); } else { return sizeof(struct usb_redir_device_connect_header_no_device_version); } } else { return -1; } case usb_redir_device_disconnect: if (!command_for_host) { return 0; } else { return -1; } case usb_redir_reset: if (command_for_host) { return 0; /* No packet type specific header */ } else { return -1; } case usb_redir_interface_info: if (!command_for_host) { return sizeof(struct usb_redir_interface_info_header); } else { return -1; } case usb_redir_ep_info: if (!command_for_host) { if (usbredirparser_have_cap(parser_pub, usb_redir_cap_bulk_streams) && usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_bulk_streams)) { return sizeof(struct usb_redir_ep_info_header); } else if (usbredirparser_have_cap(parser_pub, usb_redir_cap_ep_info_max_packet_size) && usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_ep_info_max_packet_size)) { return sizeof(struct usb_redir_ep_info_header_no_max_streams); } else { return sizeof(struct usb_redir_ep_info_header_no_max_pktsz); } } else { return -1; } case usb_redir_set_configuration: if (command_for_host) { return sizeof(struct usb_redir_set_configuration_header); } else { return -1; /* Should never be send to a guest */ } case usb_redir_get_configuration: if (command_for_host) { return 0; /* No packet type specific header */ } else { return -1; } case usb_redir_configuration_status: if (!command_for_host) { return sizeof(struct usb_redir_configuration_status_header); } else { return -1; } case usb_redir_set_alt_setting: if (command_for_host) { return sizeof(struct usb_redir_set_alt_setting_header); } else { return -1; } case usb_redir_get_alt_setting: if (command_for_host) { return sizeof(struct usb_redir_get_alt_setting_header); } else { return -1; } case usb_redir_alt_setting_status: if (!command_for_host) { return sizeof(struct usb_redir_alt_setting_status_header); } else { return -1; } case usb_redir_start_iso_stream: if (command_for_host) { return sizeof(struct usb_redir_start_iso_stream_header); } else { return -1; } case usb_redir_stop_iso_stream: if (command_for_host) { return sizeof(struct usb_redir_stop_iso_stream_header); } else { return -1; } case usb_redir_iso_stream_status: if (!command_for_host) { return sizeof(struct usb_redir_iso_stream_status_header); } else { return -1; } case usb_redir_start_interrupt_receiving: if (command_for_host) { return sizeof(struct usb_redir_start_interrupt_receiving_header); } else { return -1; } case usb_redir_stop_interrupt_receiving: if (command_for_host) { return sizeof(struct usb_redir_stop_interrupt_receiving_header); } else { return -1; } case usb_redir_interrupt_receiving_status: if (!command_for_host) { return sizeof(struct usb_redir_interrupt_receiving_status_header); } else { return -1; } case usb_redir_alloc_bulk_streams: if (command_for_host) { return sizeof(struct usb_redir_alloc_bulk_streams_header); } else { return -1; } case usb_redir_free_bulk_streams: if (command_for_host) { return sizeof(struct usb_redir_free_bulk_streams_header); } else { return -1; } case usb_redir_bulk_streams_status: if (!command_for_host) { return sizeof(struct usb_redir_bulk_streams_status_header); } else { return -1; } case usb_redir_cancel_data_packet: if (command_for_host) { return 0; /* No packet type specific header */ } else { return -1; } case usb_redir_filter_reject: if (command_for_host) { return 0; } else { return -1; } case usb_redir_filter_filter: return 0; case usb_redir_device_disconnect_ack: if (command_for_host) { return 0; } else { return -1; } case usb_redir_start_bulk_receiving: if (command_for_host) { return sizeof(struct usb_redir_start_bulk_receiving_header); } else { return -1; } case usb_redir_stop_bulk_receiving: if (command_for_host) { return sizeof(struct usb_redir_stop_bulk_receiving_header); } else { return -1; } case usb_redir_bulk_receiving_status: if (!command_for_host) { return sizeof(struct usb_redir_bulk_receiving_status_header); } else { return -1; } case usb_redir_control_packet: return sizeof(struct usb_redir_control_packet_header); case usb_redir_bulk_packet: if (usbredirparser_have_cap(parser_pub, usb_redir_cap_32bits_bulk_length) && usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_32bits_bulk_length)) { return sizeof(struct usb_redir_bulk_packet_header); } else { return sizeof(struct usb_redir_bulk_packet_header_16bit_length); } case usb_redir_iso_packet: return sizeof(struct usb_redir_iso_packet_header); case usb_redir_interrupt_packet: return sizeof(struct usb_redir_interrupt_packet_header); case usb_redir_buffered_bulk_packet: if (!command_for_host) { return sizeof(struct usb_redir_buffered_bulk_packet_header); } else { return -1; } default: return -1; } } /* Note this function only checks if extra data is allowed for the packet type being read at all, a check if it is actually allowed given the direction of the packet + ep is done in _erify_type_header */ static int usbredirparser_expect_extra_data(struct usbredirparser_priv *parser) { switch (parser->header.type) { case usb_redir_hello: /* For the variable length capabilities array */ case usb_redir_filter_filter: case usb_redir_control_packet: case usb_redir_bulk_packet: case usb_redir_iso_packet: case usb_redir_interrupt_packet: case usb_redir_buffered_bulk_packet: return 1; default: return 0; } } static int usbredirparser_verify_bulk_recv_cap( struct usbredirparser *parser_pub, int send) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; if ((send && !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_bulk_receiving)) || (!send && !usbredirparser_have_cap(parser_pub, usb_redir_cap_bulk_receiving))) { ERROR("error bulk_receiving without cap_bulk_receiving"); return 0; } return 1; /* Verify ok */ } static int usbredirparser_verify_type_header( struct usbredirparser *parser_pub, int32_t type, void *header, uint8_t *data, int data_len, int send) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; int command_for_host = 0, expect_extra_data = 0; int length = 0, ep = -1; if (parser->flags & usbredirparser_fl_usb_host) { command_for_host = 1; } if (send) { command_for_host = !command_for_host; } switch (type) { case usb_redir_interface_info: { struct usb_redir_interface_info_header *intf_info = header; if (intf_info->interface_count > 32) { ERROR("error interface_count > 32"); return 0; } break; } case usb_redir_start_interrupt_receiving: { struct usb_redir_start_interrupt_receiving_header *start_int = header; if (!(start_int->endpoint & 0x80)) { ERROR("start int receiving on non input ep %02x", start_int->endpoint); return 0; } break; } case usb_redir_stop_interrupt_receiving: { struct usb_redir_stop_interrupt_receiving_header *stop_int = header; if (!(stop_int->endpoint & 0x80)) { ERROR("stop int receiving on non input ep %02x", stop_int->endpoint); return 0; } break; } case usb_redir_interrupt_receiving_status: { struct usb_redir_interrupt_receiving_status_header *int_status = header; if (!(int_status->endpoint & 0x80)) { ERROR("int receiving status for non input ep %02x", int_status->endpoint); return 0; } break; } case usb_redir_filter_reject: if ((send && !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_filter)) || (!send && !usbredirparser_have_cap(parser_pub, usb_redir_cap_filter))) { ERROR("error filter_reject without cap_filter"); return 0; } break; case usb_redir_filter_filter: if ((send && !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_filter)) || (!send && !usbredirparser_have_cap(parser_pub, usb_redir_cap_filter))) { ERROR("error filter_filter without cap_filter"); return 0; } if (data_len < 1) { ERROR("error filter_filter without data"); return 0; } if (data[data_len - 1] != 0) { ERROR("error non 0 terminated filter_filter data"); return 0; } break; case usb_redir_device_disconnect_ack: if ((send && !usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_device_disconnect_ack)) || (!send && !usbredirparser_have_cap(parser_pub, usb_redir_cap_device_disconnect_ack))) { ERROR("error device_disconnect_ack without cap_device_disconnect_ack"); return 0; } break; case usb_redir_start_bulk_receiving: { struct usb_redir_start_bulk_receiving_header *start_bulk = header; if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) { return 0; } if (start_bulk->bytes_per_transfer > MAX_BULK_TRANSFER_SIZE) { ERROR("start bulk receiving length exceeds limits %u > %u", start_bulk->bytes_per_transfer, MAX_BULK_TRANSFER_SIZE); return 0; } if (!(start_bulk->endpoint & 0x80)) { ERROR("start bulk receiving on non input ep %02x", start_bulk->endpoint); return 0; } break; } case usb_redir_stop_bulk_receiving: { struct usb_redir_stop_bulk_receiving_header *stop_bulk = header; if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) { return 0; } if (!(stop_bulk->endpoint & 0x80)) { ERROR("stop bulk receiving on non input ep %02x", stop_bulk->endpoint); return 0; } break; } case usb_redir_bulk_receiving_status: { struct usb_redir_bulk_receiving_status_header *bulk_status = header; if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) { return 0; } if (!(bulk_status->endpoint & 0x80)) { ERROR("bulk receiving status for non input ep %02x", bulk_status->endpoint); return 0; } break; } case usb_redir_control_packet: length = ((struct usb_redir_control_packet_header *)header)->length; ep = ((struct usb_redir_control_packet_header *)header)->endpoint; break; case usb_redir_bulk_packet: { struct usb_redir_bulk_packet_header *bulk_packet = header; if (usbredirparser_have_cap(parser_pub, usb_redir_cap_32bits_bulk_length) && usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_32bits_bulk_length)) { length = (bulk_packet->length_high << 16) | bulk_packet->length; } else { length = bulk_packet->length; if (!send) bulk_packet->length_high = 0; } if ((uint32_t)length > MAX_BULK_TRANSFER_SIZE) { ERROR("bulk transfer length exceeds limits %u > %u", (uint32_t)length, MAX_BULK_TRANSFER_SIZE); return 0; } ep = bulk_packet->endpoint; break; } case usb_redir_iso_packet: length = ((struct usb_redir_iso_packet_header *)header)->length; ep = ((struct usb_redir_iso_packet_header *)header)->endpoint; break; case usb_redir_interrupt_packet: length = ((struct usb_redir_interrupt_packet_header *)header)->length; ep = ((struct usb_redir_interrupt_packet_header *)header)->endpoint; break; case usb_redir_buffered_bulk_packet: { struct usb_redir_buffered_bulk_packet_header *buf_bulk_pkt = header; length = buf_bulk_pkt->length; if (!usbredirparser_verify_bulk_recv_cap(parser_pub, send)) { return 0; } if ((uint32_t)length > MAX_BULK_TRANSFER_SIZE) { ERROR("buffered bulk transfer length exceeds limits %u > %u", (uint32_t)length, MAX_BULK_TRANSFER_SIZE); return 0; } ep = buf_bulk_pkt->endpoint; break; } } if (ep != -1) { if (((ep & 0x80) && !command_for_host) || (!(ep & 0x80) && command_for_host)) { expect_extra_data = 1; } if (expect_extra_data) { if (data_len != length) { ERROR("error data len %d != header len %d ep %02X", data_len, length, ep); return 0; } } else { if (data || data_len) { ERROR("error unexpected extra data ep %02X", ep); return 0; } switch (type) { case usb_redir_iso_packet: ERROR("error iso packet send in wrong direction"); return 0; case usb_redir_interrupt_packet: if (command_for_host) { ERROR("error interrupt packet send in wrong direction"); return 0; } break; case usb_redir_buffered_bulk_packet: ERROR("error buffered bulk packet send in wrong direction"); return 0; } } } return 1; /* Verify ok */ } static void usbredirparser_call_type_func(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; uint64_t id; if (usbredirparser_using_32bits_ids(parser_pub)) id = parser->header_32bit_id.id; else id = parser->header.id; switch (parser->header.type) { case usb_redir_hello: usbredirparser_handle_hello(parser_pub, (struct usb_redir_hello_header *)parser->type_header, parser->data, parser->data_len); break; case usb_redir_device_connect: parser->callb.device_connect_func(parser->callb.priv, (struct usb_redir_device_connect_header *)parser->type_header); break; case usb_redir_device_disconnect: parser->callb.device_disconnect_func(parser->callb.priv); if (usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_device_disconnect_ack)) usbredirparser_queue(parser_pub, usb_redir_device_disconnect_ack, 0, NULL, NULL, 0); break; case usb_redir_reset: parser->callb.reset_func(parser->callb.priv); break; case usb_redir_interface_info: parser->callb.interface_info_func(parser->callb.priv, (struct usb_redir_interface_info_header *)parser->type_header); break; case usb_redir_ep_info: parser->callb.ep_info_func(parser->callb.priv, (struct usb_redir_ep_info_header *)parser->type_header); break; case usb_redir_set_configuration: parser->callb.set_configuration_func(parser->callb.priv, id, (struct usb_redir_set_configuration_header *)parser->type_header); break; case usb_redir_get_configuration: parser->callb.get_configuration_func(parser->callb.priv, id); break; case usb_redir_configuration_status: parser->callb.configuration_status_func(parser->callb.priv, id, (struct usb_redir_configuration_status_header *)parser->type_header); break; case usb_redir_set_alt_setting: parser->callb.set_alt_setting_func(parser->callb.priv, id, (struct usb_redir_set_alt_setting_header *)parser->type_header); break; case usb_redir_get_alt_setting: parser->callb.get_alt_setting_func(parser->callb.priv, id, (struct usb_redir_get_alt_setting_header *)parser->type_header); break; case usb_redir_alt_setting_status: parser->callb.alt_setting_status_func(parser->callb.priv, id, (struct usb_redir_alt_setting_status_header *)parser->type_header); break; case usb_redir_start_iso_stream: parser->callb.start_iso_stream_func(parser->callb.priv, id, (struct usb_redir_start_iso_stream_header *)parser->type_header); break; case usb_redir_stop_iso_stream: parser->callb.stop_iso_stream_func(parser->callb.priv, id, (struct usb_redir_stop_iso_stream_header *)parser->type_header); break; case usb_redir_iso_stream_status: parser->callb.iso_stream_status_func(parser->callb.priv, id, (struct usb_redir_iso_stream_status_header *)parser->type_header); break; case usb_redir_start_interrupt_receiving: parser->callb.start_interrupt_receiving_func(parser->callb.priv, id, (struct usb_redir_start_interrupt_receiving_header *) parser->type_header); break; case usb_redir_stop_interrupt_receiving: parser->callb.stop_interrupt_receiving_func(parser->callb.priv, id, (struct usb_redir_stop_interrupt_receiving_header *) parser->type_header); break; case usb_redir_interrupt_receiving_status: parser->callb.interrupt_receiving_status_func(parser->callb.priv, id, (struct usb_redir_interrupt_receiving_status_header *) parser->type_header); break; case usb_redir_alloc_bulk_streams: parser->callb.alloc_bulk_streams_func(parser->callb.priv, id, (struct usb_redir_alloc_bulk_streams_header *)parser->type_header); break; case usb_redir_free_bulk_streams: parser->callb.free_bulk_streams_func(parser->callb.priv, id, (struct usb_redir_free_bulk_streams_header *)parser->type_header); break; case usb_redir_bulk_streams_status: parser->callb.bulk_streams_status_func(parser->callb.priv, id, (struct usb_redir_bulk_streams_status_header *)parser->type_header); break; case usb_redir_cancel_data_packet: parser->callb.cancel_data_packet_func(parser->callb.priv, id); break; case usb_redir_filter_reject: parser->callb.filter_reject_func(parser->callb.priv); break; case usb_redir_filter_filter: { struct usbredirfilter_rule *rules; int r, count; r = usbredirfilter_string_to_rules((char *)parser->data, ",", "|", &rules, &count); if (r) { ERROR("error parsing filter (%d), ignoring filter message", r); break; } parser->callb.filter_filter_func(parser->callb.priv, rules, count); break; } case usb_redir_device_disconnect_ack: parser->callb.device_disconnect_ack_func(parser->callb.priv); break; case usb_redir_start_bulk_receiving: parser->callb.start_bulk_receiving_func(parser->callb.priv, id, (struct usb_redir_start_bulk_receiving_header *) parser->type_header); break; case usb_redir_stop_bulk_receiving: parser->callb.stop_bulk_receiving_func(parser->callb.priv, id, (struct usb_redir_stop_bulk_receiving_header *) parser->type_header); break; case usb_redir_bulk_receiving_status: parser->callb.bulk_receiving_status_func(parser->callb.priv, id, (struct usb_redir_bulk_receiving_status_header *) parser->type_header); break; case usb_redir_control_packet: parser->callb.control_packet_func(parser->callb.priv, id, (struct usb_redir_control_packet_header *)parser->type_header, parser->data, parser->data_len); break; case usb_redir_bulk_packet: parser->callb.bulk_packet_func(parser->callb.priv, id, (struct usb_redir_bulk_packet_header *)parser->type_header, parser->data, parser->data_len); break; case usb_redir_iso_packet: parser->callb.iso_packet_func(parser->callb.priv, id, (struct usb_redir_iso_packet_header *)parser->type_header, parser->data, parser->data_len); break; case usb_redir_interrupt_packet: parser->callb.interrupt_packet_func(parser->callb.priv, id, (struct usb_redir_interrupt_packet_header *)parser->type_header, parser->data, parser->data_len); break; case usb_redir_buffered_bulk_packet: parser->callb.buffered_bulk_packet_func(parser->callb.priv, id, (struct usb_redir_buffered_bulk_packet_header *)parser->type_header, parser->data, parser->data_len); break; } } int usbredirparser_do_read(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; int r, header_len, type_header_len, data_len; uint8_t *dest; header_len = usbredirparser_get_header_len(parser_pub); /* Skip forward to next packet (only used in error conditions) */ while (parser->to_skip > 0) { uint8_t buf[65536]; r = (parser->to_skip > sizeof(buf)) ? sizeof(buf) : parser->to_skip; r = parser->callb.read_func(parser->callb.priv, buf, r); if (r <= 0) return r; parser->to_skip -= r; } /* Consume data until read would block or returns an error */ while (1) { if (parser->header_read < header_len) { r = header_len - parser->header_read; dest = (uint8_t *)&parser->header + parser->header_read; } else if (parser->type_header_read < parser->type_header_len) { r = parser->type_header_len - parser->type_header_read; dest = parser->type_header + parser->type_header_read; } else { r = parser->data_len - parser->data_read; dest = parser->data + parser->data_read; } if (r > 0) { r = parser->callb.read_func(parser->callb.priv, dest, r); if (r <= 0) { return r; } } if (parser->header_read < header_len) { parser->header_read += r; if (parser->header_read == header_len) { type_header_len = usbredirparser_get_type_header_len(parser_pub, parser->header.type, 0); if (type_header_len < 0) { ERROR("error invalid usb-redir packet type: %u", parser->header.type); parser->to_skip = parser->header.length; parser->header_read = 0; return -2; } /* This should never happen */ if (type_header_len > sizeof(parser->type_header)) { ERROR("error type specific header buffer too small, please report!!"); parser->to_skip = parser->header.length; parser->header_read = 0; return -2; } if ((int)parser->header.length < type_header_len || ((int)parser->header.length > type_header_len && !usbredirparser_expect_extra_data(parser))) { ERROR("error invalid packet type %u length: %u", parser->header.type, parser->header.length); parser->to_skip = parser->header.length; parser->header_read = 0; return -2; } data_len = parser->header.length - type_header_len; if (data_len) { parser->data = malloc(data_len); if (!parser->data) { ERROR("Out of memory allocating data buffer"); parser->to_skip = parser->header.length; parser->header_read = 0; return -2; } } parser->type_header_len = type_header_len; parser->data_len = data_len; } } else if (parser->type_header_read < parser->type_header_len) { parser->type_header_read += r; } else { parser->data_read += r; if (parser->data_read == parser->data_len) { r = usbredirparser_verify_type_header(parser_pub, parser->header.type, parser->type_header, parser->data, parser->data_len, 0); if (r) usbredirparser_call_type_func(parser_pub); parser->header_read = 0; parser->type_header_len = 0; parser->type_header_read = 0; parser->data_len = 0; parser->data_read = 0; parser->data = NULL; if (!r) return -2; /* header len may change if this was an hello packet */ header_len = usbredirparser_get_header_len(parser_pub); } } } } int usbredirparser_has_data_to_write(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; return parser->write_buf_count; } int usbredirparser_do_write(struct usbredirparser *parser_pub) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; struct usbredirparser_buf* wbuf; int w, ret = 0; LOCK(parser); for (;;) { wbuf = parser->write_buf; if (!wbuf) break; w = wbuf->len - wbuf->pos; w = parser->callb.write_func(parser->callb.priv, wbuf->buf + wbuf->pos, w); if (w <= 0) { ret = w; break; } /* See usbredirparser_write documentation */ if ((parser->flags & usbredirparser_fl_write_cb_owns_buffer) && w != wbuf->len) abort(); wbuf->pos += w; if (wbuf->pos == wbuf->len) { parser->write_buf = wbuf->next; if (!(parser->flags & usbredirparser_fl_write_cb_owns_buffer)) free(wbuf->buf); free(wbuf); parser->write_buf_count--; } } UNLOCK(parser); return ret; } void usbredirparser_free_write_buffer(struct usbredirparser *parser, uint8_t *data) { free(data); } void usbredirparser_free_packet_data(struct usbredirparser *parser, uint8_t *data) { free(data); } static void usbredirparser_queue(struct usbredirparser *parser_pub, uint32_t type, uint64_t id, void *type_header_in, uint8_t *data_in, int data_len) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; uint8_t *buf, *type_header_out, *data_out; struct usb_redir_header *header; struct usbredirparser_buf *wbuf, *new_wbuf; int header_len, type_header_len; header_len = usbredirparser_get_header_len(parser_pub); type_header_len = usbredirparser_get_type_header_len(parser_pub, type, 1); if (type_header_len < 0) { /* This should never happen */ ERROR("error packet type unknown with internal call, please report!!"); return; } if (!usbredirparser_verify_type_header(parser_pub, type, type_header_in, data_in, data_len, 1)) { ERROR("error usbredirparser_send_* call invalid params, please report!!"); return; } new_wbuf = calloc(1, sizeof(*new_wbuf)); buf = malloc(header_len + type_header_len + data_len); if (!new_wbuf || !buf) { ERROR("Out of memory allocating buffer to send packet, dropping!"); free(new_wbuf); free(buf); return; } new_wbuf->buf = buf; new_wbuf->len = header_len + type_header_len + data_len; header = (struct usb_redir_header *)buf; type_header_out = buf + header_len; data_out = type_header_out + type_header_len; header->type = type; header->length = type_header_len + data_len; if (usbredirparser_using_32bits_ids(parser_pub)) ((struct usb_redir_header_32bit_id *)header)->id = id; else header->id = id; memcpy(type_header_out, type_header_in, type_header_len); memcpy(data_out, data_in, data_len); LOCK(parser); if (!parser->write_buf) { parser->write_buf = new_wbuf; } else { /* limiting the write_buf's stack depth is our users responsibility */ wbuf = parser->write_buf; while (wbuf->next) wbuf = wbuf->next; wbuf->next = new_wbuf; } parser->write_buf_count++; UNLOCK(parser); } void usbredirparser_send_device_connect(struct usbredirparser *parser, struct usb_redir_device_connect_header *device_connect) { usbredirparser_queue(parser, usb_redir_device_connect, 0, device_connect, NULL, 0); } void usbredirparser_send_device_disconnect(struct usbredirparser *parser) { usbredirparser_queue(parser, usb_redir_device_disconnect, 0, NULL, NULL, 0); } void usbredirparser_send_reset(struct usbredirparser *parser) { usbredirparser_queue(parser, usb_redir_reset, 0, NULL, NULL, 0); } void usbredirparser_send_interface_info(struct usbredirparser *parser, struct usb_redir_interface_info_header *interface_info) { usbredirparser_queue(parser, usb_redir_interface_info, 0, interface_info, NULL, 0); } void usbredirparser_send_ep_info(struct usbredirparser *parser, struct usb_redir_ep_info_header *ep_info) { usbredirparser_queue(parser, usb_redir_ep_info, 0, ep_info, NULL, 0); } void usbredirparser_send_set_configuration(struct usbredirparser *parser, uint64_t id, struct usb_redir_set_configuration_header *set_configuration) { usbredirparser_queue(parser, usb_redir_set_configuration, id, set_configuration, NULL, 0); } void usbredirparser_send_get_configuration(struct usbredirparser *parser, uint64_t id) { usbredirparser_queue(parser, usb_redir_get_configuration, id, NULL, NULL, 0); } void usbredirparser_send_configuration_status(struct usbredirparser *parser, uint64_t id, struct usb_redir_configuration_status_header *configuration_status) { usbredirparser_queue(parser, usb_redir_configuration_status, id, configuration_status, NULL, 0); } void usbredirparser_send_set_alt_setting(struct usbredirparser *parser, uint64_t id, struct usb_redir_set_alt_setting_header *set_alt_setting) { usbredirparser_queue(parser, usb_redir_set_alt_setting, id, set_alt_setting, NULL, 0); } void usbredirparser_send_get_alt_setting(struct usbredirparser *parser, uint64_t id, struct usb_redir_get_alt_setting_header *get_alt_setting) { usbredirparser_queue(parser, usb_redir_get_alt_setting, id, get_alt_setting, NULL, 0); } void usbredirparser_send_alt_setting_status(struct usbredirparser *parser, uint64_t id, struct usb_redir_alt_setting_status_header *alt_setting_status) { usbredirparser_queue(parser, usb_redir_alt_setting_status, id, alt_setting_status, NULL, 0); } void usbredirparser_send_start_iso_stream(struct usbredirparser *parser, uint64_t id, struct usb_redir_start_iso_stream_header *start_iso_stream) { usbredirparser_queue(parser, usb_redir_start_iso_stream, id, start_iso_stream, NULL, 0); } void usbredirparser_send_stop_iso_stream(struct usbredirparser *parser, uint64_t id, struct usb_redir_stop_iso_stream_header *stop_iso_stream) { usbredirparser_queue(parser, usb_redir_stop_iso_stream, id, stop_iso_stream, NULL, 0); } void usbredirparser_send_iso_stream_status(struct usbredirparser *parser, uint64_t id, struct usb_redir_iso_stream_status_header *iso_stream_status) { usbredirparser_queue(parser, usb_redir_iso_stream_status, id, iso_stream_status, NULL, 0); } void usbredirparser_send_start_interrupt_receiving(struct usbredirparser *parser, uint64_t id, struct usb_redir_start_interrupt_receiving_header *start_interrupt_receiving) { usbredirparser_queue(parser, usb_redir_start_interrupt_receiving, id, start_interrupt_receiving, NULL, 0); } void usbredirparser_send_stop_interrupt_receiving(struct usbredirparser *parser, uint64_t id, struct usb_redir_stop_interrupt_receiving_header *stop_interrupt_receiving) { usbredirparser_queue(parser, usb_redir_stop_interrupt_receiving, id, stop_interrupt_receiving, NULL, 0); } void usbredirparser_send_interrupt_receiving_status(struct usbredirparser *parser, uint64_t id, struct usb_redir_interrupt_receiving_status_header *interrupt_receiving_status) { usbredirparser_queue(parser, usb_redir_interrupt_receiving_status, id, interrupt_receiving_status, NULL, 0); } void usbredirparser_send_alloc_bulk_streams(struct usbredirparser *parser, uint64_t id, struct usb_redir_alloc_bulk_streams_header *alloc_bulk_streams) { usbredirparser_queue(parser, usb_redir_alloc_bulk_streams, id, alloc_bulk_streams, NULL, 0); } void usbredirparser_send_free_bulk_streams(struct usbredirparser *parser, uint64_t id, struct usb_redir_free_bulk_streams_header *free_bulk_streams) { usbredirparser_queue(parser, usb_redir_free_bulk_streams, id, free_bulk_streams, NULL, 0); } void usbredirparser_send_bulk_streams_status(struct usbredirparser *parser, uint64_t id, struct usb_redir_bulk_streams_status_header *bulk_streams_status) { usbredirparser_queue(parser, usb_redir_bulk_streams_status, id, bulk_streams_status, NULL, 0); } void usbredirparser_send_cancel_data_packet(struct usbredirparser *parser, uint64_t id) { usbredirparser_queue(parser, usb_redir_cancel_data_packet, id, NULL, NULL, 0); } void usbredirparser_send_filter_reject(struct usbredirparser *parser) { if (!usbredirparser_peer_has_cap(parser, usb_redir_cap_filter)) return; usbredirparser_queue(parser, usb_redir_filter_reject, 0, NULL, NULL, 0); } void usbredirparser_send_filter_filter(struct usbredirparser *parser_pub, const struct usbredirfilter_rule *rules, int rules_count) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; char *str; if (!usbredirparser_peer_has_cap(parser_pub, usb_redir_cap_filter)) return; str = usbredirfilter_rules_to_string(rules, rules_count, ",", "|"); if (!str) { ERROR("error creating filter string, not sending filter"); return; } usbredirparser_queue(parser_pub, usb_redir_filter_filter, 0, NULL, (uint8_t *)str, strlen(str) + 1); free(str); } void usbredirparser_send_start_bulk_receiving(struct usbredirparser *parser, uint64_t id, struct usb_redir_start_bulk_receiving_header *start_bulk_receiving) { usbredirparser_queue(parser, usb_redir_start_bulk_receiving, id, start_bulk_receiving, NULL, 0); } void usbredirparser_send_stop_bulk_receiving(struct usbredirparser *parser, uint64_t id, struct usb_redir_stop_bulk_receiving_header *stop_bulk_receiving) { usbredirparser_queue(parser, usb_redir_stop_bulk_receiving, id, stop_bulk_receiving, NULL, 0); } void usbredirparser_send_bulk_receiving_status(struct usbredirparser *parser, uint64_t id, struct usb_redir_bulk_receiving_status_header *bulk_receiving_status) { usbredirparser_queue(parser, usb_redir_bulk_receiving_status, id, bulk_receiving_status, NULL, 0); } /* Data packets: */ void usbredirparser_send_control_packet(struct usbredirparser *parser, uint64_t id, struct usb_redir_control_packet_header *control_header, uint8_t *data, int data_len) { usbredirparser_queue(parser, usb_redir_control_packet, id, control_header, data, data_len); } void usbredirparser_send_bulk_packet(struct usbredirparser *parser, uint64_t id, struct usb_redir_bulk_packet_header *bulk_header, uint8_t *data, int data_len) { usbredirparser_queue(parser, usb_redir_bulk_packet, id, bulk_header, data, data_len); } void usbredirparser_send_iso_packet(struct usbredirparser *parser, uint64_t id, struct usb_redir_iso_packet_header *iso_header, uint8_t *data, int data_len) { usbredirparser_queue(parser, usb_redir_iso_packet, id, iso_header, data, data_len); } void usbredirparser_send_interrupt_packet(struct usbredirparser *parser, uint64_t id, struct usb_redir_interrupt_packet_header *interrupt_header, uint8_t *data, int data_len) { usbredirparser_queue(parser, usb_redir_interrupt_packet, id, interrupt_header, data, data_len); } void usbredirparser_send_buffered_bulk_packet(struct usbredirparser *parser, uint64_t id, struct usb_redir_buffered_bulk_packet_header *buffered_bulk_header, uint8_t *data, int data_len) { usbredirparser_queue(parser, usb_redir_buffered_bulk_packet, id, buffered_bulk_header, data, data_len); } /****** Serialization support ******/ #define USBREDIRPARSER_SERIALIZE_MAGIC 0x55525031 #define USBREDIRPARSER_SERIALIZE_BUF_SIZE 65536 /* Serialization format, send and receiving endian are expected to be the same! uint32 MAGIC: 0x55525031 ascii: URP1 (UsbRedirParser version 1) uint32 len: length of the entire serialized state, including MAGIC uint32 our_caps_len uint32 our_caps[our_caps_len] uint32 peer_caps_len uint32 peer_caps[peer_caps_len] uint32 to_skip uint32 header_read uint8 header[header_read] uint32 type_header_read uint8 type_header[type_header_read] uint32 data_read uint8 data[data_read] uint32 write_buf_count: followed by write_buf_count times: uint32 write_buf_len uint8 write_buf_data[write_buf_len] */ static int serialize_alloc(struct usbredirparser_priv *parser, uint8_t **state, uint8_t **pos, uint32_t *remain, uint32_t needed) { uint8_t *old_state = *state; uint32_t used, size; if (*remain >= needed) return 0; used = *pos - *state; size = (used + needed + USBREDIRPARSER_SERIALIZE_BUF_SIZE - 1) & ~(USBREDIRPARSER_SERIALIZE_BUF_SIZE - 1); *state = realloc(*state, size); if (!*state) { free(old_state); ERROR("Out of memory allocating serialization buffer"); return -1; } *pos = *state + used; *remain = size - used; return 0; } static int serialize_int(struct usbredirparser_priv *parser, uint8_t **state, uint8_t **pos, uint32_t *remain, uint32_t val, const char *desc) { DEBUG("serializing int %08x : %s", val, desc); if (serialize_alloc(parser, state, pos, remain, sizeof(uint32_t))) return -1; memcpy(*pos, &val, sizeof(uint32_t)); *pos += sizeof(uint32_t); *remain -= sizeof(uint32_t); return 0; } static int unserialize_int(struct usbredirparser_priv *parser, uint8_t **pos, uint32_t *remain, uint32_t *val, const char *desc) { if (*remain < sizeof(uint32_t)) { ERROR("error buffer underrun while unserializing state"); return -1; } memcpy(val, *pos, sizeof(uint32_t)); *pos += sizeof(uint32_t); *remain -= sizeof(uint32_t); DEBUG("unserialized int %08x : %s", *val, desc); return 0; } static int serialize_data(struct usbredirparser_priv *parser, uint8_t **state, uint8_t **pos, uint32_t *remain, uint8_t *data, uint32_t len, const char *desc) { DEBUG("serializing %d bytes of %s data", len, desc); if (len >= 8) DEBUG("First 8 bytes of %s: %02x %02x %02x %02x %02x %02x %02x %02x", desc, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); if (serialize_alloc(parser, state, pos, remain, sizeof(uint32_t) + len)) return -1; memcpy(*pos, &len, sizeof(uint32_t)); *pos += sizeof(uint32_t); *remain -= sizeof(uint32_t); memcpy(*pos, data, len); *pos += len; *remain -= len; return 0; } /* If *data == NULL, allocs buffer dynamically, else len_in_out must contain the length of the passed in buffer. */ static int unserialize_data(struct usbredirparser_priv *parser, uint8_t **pos, uint32_t *remain, uint8_t **data, uint32_t *len_in_out, const char *desc) { uint32_t len; if (*remain < sizeof(uint32_t)) { ERROR("error buffer underrun while unserializing state"); return -1; } memcpy(&len, *pos, sizeof(uint32_t)); *pos += sizeof(uint32_t); *remain -= sizeof(uint32_t); if (*remain < len) { ERROR("error buffer underrun while unserializing state"); return -1; } if (*data == NULL && len > 0) { *data = malloc(len); if (!*data) { ERROR("Out of memory allocating unserialize buffer"); return -1; } } else { if (*len_in_out < len) { ERROR("error buffer overrun while unserializing state"); return -1; } } memcpy(*data, *pos, len); *pos += len; *remain -= len; *len_in_out = len; DEBUG("unserialized %d bytes of %s data", len, desc); if (len >= 8) DEBUG("First 8 bytes of %s: %02x %02x %02x %02x %02x %02x %02x %02x", desc, (*data)[0], (*data)[1], (*data)[2], (*data)[3], (*data)[4], (*data)[5], (*data)[6], (*data)[7]); return 0; } int usbredirparser_serialize(struct usbredirparser *parser_pub, uint8_t **state_dest, int *state_len) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; struct usbredirparser_buf *wbuf; uint8_t *write_buf_count_pos, *state = NULL, *pos = NULL; uint32_t write_buf_count = 0, len, remain = 0; *state_dest = NULL; *state_len = 0; if (serialize_int(parser, &state, &pos, &remain, USBREDIRPARSER_SERIALIZE_MAGIC, "magic")) return -1; /* To be replaced with length later */ if (serialize_int(parser, &state, &pos, &remain, 0, "length")) return -1; if (serialize_data(parser, &state, &pos, &remain, (uint8_t *)parser->our_caps, USB_REDIR_CAPS_SIZE * sizeof(int32_t), "our_caps")) return -1; if (parser->have_peer_caps) { if (serialize_data(parser, &state, &pos, &remain, (uint8_t *)parser->peer_caps, USB_REDIR_CAPS_SIZE * sizeof(int32_t), "peer_caps")) return -1; } else { if (serialize_int(parser, &state, &pos, &remain, 0, "peer_caps_len")) return -1; } if (serialize_int(parser, &state, &pos, &remain, parser->to_skip, "skip")) return -1; if (serialize_data(parser, &state, &pos, &remain, (uint8_t *)&parser->header, parser->header_read, "header")) return -1; if (serialize_data(parser, &state, &pos, &remain, parser->type_header, parser->type_header_read, "type_header")) return -1; if (serialize_data(parser, &state, &pos, &remain, parser->data, parser->data_read, "packet-data")) return -1; write_buf_count_pos = pos; /* To be replaced with write_buf_count later */ if (serialize_int(parser, &state, &pos, &remain, 0, "write_buf_count")) return -1; wbuf = parser->write_buf; while (wbuf) { if (serialize_data(parser, &state, &pos, &remain, wbuf->buf + wbuf->pos, wbuf->len - wbuf->pos, "write-buf")) return -1; write_buf_count++; wbuf = wbuf->next; } /* Patch in write_buf_count */ memcpy(write_buf_count_pos, &write_buf_count, sizeof(int32_t)); /* Patch in length */ len = pos - state; memcpy(state + sizeof(int32_t), &len, sizeof(int32_t)); *state_dest = state; *state_len = len; return 0; } int usbredirparser_unserialize(struct usbredirparser *parser_pub, uint8_t *state, int len) { struct usbredirparser_priv *parser = (struct usbredirparser_priv *)parser_pub; struct usbredirparser_buf *wbuf, **next; uint32_t orig_caps[USB_REDIR_CAPS_SIZE]; uint8_t *data; uint32_t i, l, header_len, remain = len; if (unserialize_int(parser, &state, &remain, &i, "magic")) return -1; if (i != USBREDIRPARSER_SERIALIZE_MAGIC) { ERROR("error unserialize magic mismatch"); return -1; } if (unserialize_int(parser, &state, &remain, &i, "length")) return -1; if (i != len) { ERROR("error unserialize length mismatch"); return -1; } data = (uint8_t *)parser->our_caps; i = USB_REDIR_CAPS_SIZE * sizeof(int32_t); memcpy(orig_caps, parser->our_caps, i); if (unserialize_data(parser, &state, &remain, &data, &i, "our_caps")) return -1; for (i =0; i < USB_REDIR_CAPS_SIZE; i++) { if (parser->our_caps[i] != orig_caps[i]) { /* orig_caps is our original settings * parser->our_caps is off the wire. * We want to allow reception from an older * usbredir that doesn't have all our features. */ if (parser->our_caps[i] & ~orig_caps[i]) { /* Source has a cap we don't */ ERROR("error unserialize caps mismatch ours: %x recv: %x", orig_caps[i], parser->our_caps[i]); return -1; } else { /* We've got a cap the source doesn't - that's OK */ WARNING("unserialize missing some caps; ours: %x recv: %x", orig_caps[i], parser->our_caps[i]); } } } data = (uint8_t *)parser->peer_caps; i = USB_REDIR_CAPS_SIZE * sizeof(int32_t); if (unserialize_data(parser, &state, &remain, &data, &i, "peer_caps")) return -1; if (i) parser->have_peer_caps = 1; if (unserialize_int(parser, &state, &remain, &i, "skip")) return -1; parser->to_skip = i; header_len = usbredirparser_get_header_len(parser_pub); data = (uint8_t *)&parser->header; i = header_len; if (unserialize_data(parser, &state, &remain, &data, &i, "header")) return -1; parser->header_read = i; /* Set various length field froms the header (if we've a header) */ if (parser->header_read == header_len) { int type_header_len = usbredirparser_get_type_header_len(parser_pub, parser->header.type, 0); if (type_header_len < 0 || type_header_len > sizeof(parser->type_header) || parser->header.length < type_header_len || (parser->header.length > type_header_len && !usbredirparser_expect_extra_data(parser))) { ERROR("error unserialize packet header invalid"); return -1; } parser->type_header_len = type_header_len; parser->data_len = parser->header.length - type_header_len; } data = parser->type_header; i = parser->type_header_len; if (unserialize_data(parser, &state, &remain, &data, &i, "type_header")) return -1; parser->type_header_read = i; if (parser->data_len) { parser->data = malloc(parser->data_len); if (!parser->data) { ERROR("Out of memory allocating unserialize buffer"); return -1; } } i = parser->data_len; if (unserialize_data(parser, &state, &remain, &parser->data, &i, "data")) return -1; parser->data_read = i; /* Get the write buffer count and the write buffers */ if (unserialize_int(parser, &state, &remain, &i, "write_buf_count")) return -1; next = &parser->write_buf; while (i) { wbuf = calloc(1, sizeof(*wbuf)); if (!wbuf) { ERROR("Out of memory allocating unserialize buffer"); return -1; } *next = wbuf; l = 0; if (unserialize_data(parser, &state, &remain, &wbuf->buf, &l, "wbuf")) return -1; wbuf->len = l; next = &wbuf->next; i--; } if (remain) { ERROR("error unserialize %d bytes of extraneous state data", remain); return -1; } return 0; }