| Index: device/usb/webusb_descriptors.cc
|
| diff --git a/device/usb/webusb_descriptors.cc b/device/usb/webusb_descriptors.cc
|
| index 43ad170f44da0ae2e052199e7b9221b946c9af6b..27a3d38ea8f8ae6531284945373424e4475a2162 100644
|
| --- a/device/usb/webusb_descriptors.cc
|
| +++ b/device/usb/webusb_descriptors.cc
|
| @@ -5,9 +5,19 @@
|
| #include <stddef.h>
|
|
|
| #include <iterator>
|
| +#include <map>
|
| +#include <set>
|
|
|
| +#include "base/barrier_closure.h"
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| #include "base/logging.h"
|
| +#include "components/device_event_log/device_event_log.h"
|
| +#include "device/usb/usb_device_handle.h"
|
| #include "device/usb/webusb_descriptors.h"
|
| +#include "net/base/io_buffer.h"
|
| +
|
| +using net::IOBufferWithSize;
|
|
|
| namespace device {
|
|
|
| @@ -15,6 +25,8 @@ namespace {
|
|
|
| // These constants are defined by the Universal Serial Device 3.0 Specification
|
| // Revision 1.0.
|
| +const uint8_t kGetDescriptorRequest = 0x06;
|
| +
|
| const uint8_t kBosDescriptorType = 0x0F;
|
| const uint8_t kDeviceCapabilityDescriptorType = 0x10;
|
|
|
| @@ -22,136 +34,325 @@ const uint8_t kPlatformDevCapabilityType = 0x05;
|
|
|
| // These constants are defined by the WebUSB specification:
|
| // http://wicg.github.io/webusb/
|
| +const uint8_t kGetAllowedOriginsRequest = 0x01;
|
| +const uint8_t kGetUrlRequest = 0x02;
|
| +
|
| const uint8_t kWebUsbCapabilityUUID[16] = {
|
| // Little-endian encoding of {3408b638-09a9-47a0-8bfd-a0768815b665}.
|
| 0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
|
| 0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65};
|
|
|
| -const uint8_t kDescriptorSetDescriptorType = 0x00;
|
| -const uint8_t kConfigurationSubsetDescriptorType = 0x01;
|
| -const uint8_t kFunctionSubsetDescriptorType = 0x02;
|
| -const uint8_t kUrlDescriptorType = 0x03;
|
| +const int kControlTransferTimeout = 60000; // 1 minute
|
| +
|
| +using ReadWebUsbDescriptorsCallback =
|
| + base::Callback<void(scoped_ptr<WebUsbAllowedOrigins> allowed_origins,
|
| + const GURL& landing_page)>;
|
| +
|
| +using ReadWebUsbAllowedOriginsCallback =
|
| + base::Callback<void(scoped_ptr<WebUsbAllowedOrigins> allowed_origins)>;
|
| +
|
| +// Parses a WebUSB Function Subset Header:
|
| +// http://wicg.github.io/webusb/#dfn-function-subset-header
|
| +//
|
| +// 0 1 2 3
|
| +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | length | type | 1st interface | origin[0] |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | origin[1] | ...
|
| +// +-+-+-+-+-+-+-+-+-+-+-+------
|
| +bool ParseFunction(WebUsbFunctionSubset* function,
|
| + std::vector<uint8_t>::const_iterator* it,
|
| + std::vector<uint8_t>::const_iterator end) {
|
| + const uint8_t kDescriptorType = 0x02;
|
| + const uint8_t kDescriptorMinLength = 3;
|
|
|
| -bool ParseUrl(GURL* url,
|
| - std::vector<uint8_t>::const_iterator* it,
|
| - std::vector<uint8_t>::const_iterator end) {
|
| - // These conditions must be guaranteed by the caller.
|
| - DCHECK(*it != end);
|
| + // If this isn't the end of the buffer then there must be at least one byte
|
| + // available, the length of the descriptor.
|
| + if (*it == end)
|
| + return false;
|
| uint8_t length = (*it)[0];
|
| - DCHECK_LE(length, std::distance(*it, end));
|
| - DCHECK_GE(length, 2);
|
| - DCHECK_EQ((*it)[1], kUrlDescriptorType);
|
|
|
| - if (length == 2) {
|
| + // Is this a valid Function Subset Header? It must be long enough, fit within
|
| + // the buffer and have the right descriptor type.
|
| + if (length < kDescriptorMinLength || std::distance(*it, end) < length ||
|
| + (*it)[1] != kDescriptorType) {
|
| return false;
|
| }
|
|
|
| - const char* str = reinterpret_cast<const char*>(&(*it)[2]);
|
| - *url = GURL(std::string(str, length - 2));
|
| - if (!url->is_valid()) {
|
| - return false;
|
| + function->first_interface = (*it)[2];
|
| +
|
| + // Everything after the mandatory fields are origin indicies.
|
| + std::advance(*it, kDescriptorMinLength);
|
| + uint8_t num_origins = length - kDescriptorMinLength;
|
| + function->origin_ids.reserve(num_origins);
|
| + for (size_t i = 0; i < num_origins; ++i) {
|
| + uint8_t index = *(*it)++;
|
| + if (index == 0)
|
| + return false;
|
| + function->origin_ids.push_back(index);
|
| }
|
|
|
| - std::advance(*it, length);
|
| return true;
|
| }
|
|
|
| -bool ParseFunction(WebUsbFunctionSubset* function,
|
| - std::vector<uint8_t>::const_iterator* it,
|
| - std::vector<uint8_t>::const_iterator end) {
|
| - // These conditions must be guaranteed by the caller.
|
| - DCHECK(*it != end);
|
| +// Parses a WebUSB Configuration Subset Header:
|
| +// http://wicg.github.io/webusb/#dfn-configuration-subset-header
|
| +//
|
| +// 0 1 2 3
|
| +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | length | type | config value | num functions |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | origin[0] | origin[1] | ...
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+------
|
| +bool ParseConfiguration(WebUsbConfigurationSubset* configuration,
|
| + std::vector<uint8_t>::const_iterator* it,
|
| + std::vector<uint8_t>::const_iterator end) {
|
| + const uint8_t kDescriptorType = 0x01;
|
| + const uint8_t kDescriptorMinLength = 4;
|
| +
|
| + // If this isn't the end of the buffer then there must be at least one byte
|
| + // available, the length of the descriptor.
|
| + if (*it == end)
|
| + return false;
|
| uint8_t length = (*it)[0];
|
| - DCHECK_LE(length, std::distance(*it, end));
|
| - DCHECK_GE(length, 2);
|
| - DCHECK_EQ((*it)[1], kFunctionSubsetDescriptorType);
|
|
|
| - if (length != 5) {
|
| + // Is this a valid Configuration Subset Header? It must be long enough, fit
|
| + // within the buffer and have the right descriptor type.
|
| + if (length < kDescriptorMinLength || std::distance(*it, end) < length ||
|
| + (*it)[1] != kDescriptorType) {
|
| return false;
|
| }
|
|
|
| - function->first_interface = (*it)[2];
|
| + configuration->configuration_value = (*it)[2];
|
| + uint8_t num_functions = (*it)[3];
|
| +
|
| + // The next |length - 4| bytes after the mandatory fields are origin indicies.
|
| + std::advance(*it, kDescriptorMinLength);
|
| + uint8_t num_origins = length - kDescriptorMinLength;
|
| + configuration->origin_ids.reserve(num_origins);
|
| + for (size_t i = 0; i < num_origins; ++i) {
|
| + uint8_t index = *(*it)++;
|
| + if (index == 0)
|
| + return false;
|
| + configuration->origin_ids.push_back(index);
|
| + }
|
|
|
| - // Validate the Function Subset header.
|
| - uint16_t total_length = (*it)[3] + ((*it)[4] << 8);
|
| - if (length > total_length || total_length > std::distance(*it, end)) {
|
| - return false;
|
| + // |num_functions| function descriptors then follow the descriptor.
|
| + for (size_t i = 0; i < num_functions; ++i) {
|
| + WebUsbFunctionSubset function;
|
| + if (!ParseFunction(&function, it, end))
|
| + return false;
|
| + configuration->functions.push_back(function);
|
| }
|
|
|
| - end = *it + total_length;
|
| - std::advance(*it, length);
|
| + return true;
|
| +}
|
|
|
| - while (*it != end) {
|
| - uint8_t length = (*it)[0];
|
| - if (length < 2 || std::distance(*it, end) < length) {
|
| - return false;
|
| +void OnDoneReadingUrls(scoped_ptr<WebUsbAllowedOrigins> allowed_origins,
|
| + uint8_t landing_page_id,
|
| + scoped_ptr<std::map<uint8_t, GURL>> url_map,
|
| + const ReadWebUsbDescriptorsCallback& callback) {
|
| + for (uint8_t origin_id : allowed_origins->origin_ids) {
|
| + const auto& it = url_map->find(origin_id);
|
| + if (it != url_map->end())
|
| + allowed_origins->origins.push_back(it->second.GetOrigin());
|
| + }
|
| +
|
| + for (auto& configuration : allowed_origins->configurations) {
|
| + for (uint8_t origin_id : configuration.origin_ids) {
|
| + const auto& it = url_map->find(origin_id);
|
| + if (it != url_map->end())
|
| + configuration.origins.push_back(it->second.GetOrigin());
|
| }
|
|
|
| - uint8_t type = (*it)[1];
|
| - if (type == kUrlDescriptorType) {
|
| - GURL url;
|
| - if (!ParseUrl(&url, it, end)) {
|
| - return false;
|
| + for (auto& function : configuration.functions) {
|
| + for (uint8_t origin_id : function.origin_ids) {
|
| + const auto& it = url_map->find(origin_id);
|
| + if (it != url_map->end())
|
| + function.origins.push_back(it->second.GetOrigin());
|
| }
|
| - function->origins.push_back(url.GetOrigin());
|
| - } else {
|
| - return false;
|
| }
|
| }
|
|
|
| - return true;
|
| + GURL landing_page;
|
| + if (landing_page_id != 0)
|
| + landing_page = (*url_map)[landing_page_id];
|
| +
|
| + callback.Run(std::move(allowed_origins), landing_page);
|
| }
|
|
|
| -bool ParseConfiguration(WebUsbConfigurationSubset* configuration,
|
| - std::vector<uint8_t>::const_iterator* it,
|
| - std::vector<uint8_t>::const_iterator end) {
|
| - // These conditions must be guaranteed by the caller.
|
| - DCHECK(*it != end);
|
| - uint8_t length = (*it)[0];
|
| - DCHECK_LE(length, std::distance(*it, end));
|
| - DCHECK_GE(length, 2);
|
| - DCHECK_EQ((*it)[1], kConfigurationSubsetDescriptorType);
|
| +void OnReadUrlDescriptor(std::map<uint8_t, GURL>* url_map,
|
| + uint8_t index,
|
| + const base::Closure& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED) {
|
| + USB_LOG(EVENT) << "Failed to read WebUSB URL descriptor: " << index;
|
| + callback.Run();
|
| + return;
|
| + }
|
|
|
| - if (length != 5) {
|
| - return false;
|
| + GURL url;
|
| + if (ParseWebUsbUrlDescriptor(
|
| + std::vector<uint8_t>(buffer->data(), buffer->data() + length),
|
| + &url)) {
|
| + (*url_map)[index] = url;
|
| }
|
| + callback.Run();
|
| +}
|
|
|
| - configuration->configuration_value = (*it)[2];
|
| +// Reads the descriptor with |index| from the device, adds the value to
|
| +// |url_map| and then runs |callback|.
|
| +void ReadUrlDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + uint8_t vendor_code,
|
| + std::map<uint8_t, GURL>* url_map,
|
| + uint8_t index,
|
| + const base::Closure& callback) {
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(255);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE,
|
| + vendor_code, index, kGetUrlRequest, buffer, buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadUrlDescriptor, url_map, index, callback));
|
| +}
|
|
|
| - // Validate the Configuration Subset header.
|
| - uint16_t total_length = (*it)[3] + ((*it)[4] << 8);
|
| - if (length > total_length || total_length > std::distance(*it, end)) {
|
| - return false;
|
| +// Reads URL descriptors from the device so that it can fill |allowed_origins|
|
| +// with the GURLs matching the indicies already collected.
|
| +void ReadUrlDescriptors(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + uint8_t vendor_code,
|
| + uint8_t landing_page_id,
|
| + const ReadWebUsbDescriptorsCallback& callback,
|
| + scoped_ptr<WebUsbAllowedOrigins> allowed_origins) {
|
| + if (!allowed_origins) {
|
| + callback.Run(nullptr, GURL());
|
| + return;
|
| }
|
|
|
| - end = *it + total_length;
|
| - std::advance(*it, length);
|
| + std::set<uint8_t> to_request;
|
| + if (landing_page_id != 0)
|
| + to_request.insert(landing_page_id);
|
|
|
| - while (*it != end) {
|
| - uint8_t length = (*it)[0];
|
| - if (length < 2 || std::distance(*it, end) < length) {
|
| - return false;
|
| + to_request.insert(allowed_origins->origin_ids.begin(),
|
| + allowed_origins->origin_ids.end());
|
| + for (auto& config : allowed_origins->configurations) {
|
| + to_request.insert(config.origin_ids.begin(), config.origin_ids.end());
|
| + for (auto& function : config.functions) {
|
| + to_request.insert(function.origin_ids.begin(), function.origin_ids.end());
|
| }
|
| + }
|
|
|
| - uint8_t type = (*it)[1];
|
| - if (type == kFunctionSubsetDescriptorType) {
|
| - WebUsbFunctionSubset function;
|
| - if (!ParseFunction(&function, it, end)) {
|
| - return false;
|
| - }
|
| - configuration->functions.push_back(function);
|
| - } else if (type == kUrlDescriptorType) {
|
| - GURL url;
|
| - if (!ParseUrl(&url, it, end)) {
|
| - return false;
|
| - }
|
| - configuration->origins.push_back(url.GetOrigin());
|
| - } else {
|
| - return false;
|
| - }
|
| + scoped_ptr<std::map<uint8_t, GURL>> url_map(new std::map<uint8_t, GURL>());
|
| + std::map<uint8_t, GURL>* url_map_ptr = url_map.get();
|
| + base::Closure barrier = base::BarrierClosure(
|
| + static_cast<int>(to_request.size()),
|
| + base::Bind(&OnDoneReadingUrls, base::Passed(&allowed_origins),
|
| + landing_page_id, base::Passed(&url_map), callback));
|
| +
|
| + for (uint8_t index : to_request) {
|
| + ReadUrlDescriptor(device_handle, vendor_code, url_map_ptr, index, barrier);
|
| }
|
| +}
|
|
|
| - return true;
|
| +void OnReadWebUsbAllowedOrigins(
|
| + const ReadWebUsbAllowedOriginsCallback& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED) {
|
| + USB_LOG(EVENT) << "Failed to read WebUSB allowed origins.";
|
| + callback.Run(nullptr);
|
| + return;
|
| + }
|
| +
|
| + scoped_ptr<WebUsbAllowedOrigins> allowed_origins(new WebUsbAllowedOrigins());
|
| + if (allowed_origins->Parse(
|
| + std::vector<uint8_t>(buffer->data(), buffer->data() + length))) {
|
| + callback.Run(std::move(allowed_origins));
|
| + } else {
|
| + callback.Run(nullptr);
|
| + }
|
| +}
|
| +
|
| +void OnReadWebUsbAllowedOriginsHeader(
|
| + scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const ReadWebUsbAllowedOriginsCallback& callback,
|
| + uint8_t vendor_code,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED || length != 4) {
|
| + USB_LOG(EVENT) << "Failed to read WebUSB allowed origins header.";
|
| + callback.Run(nullptr);
|
| + return;
|
| + }
|
| +
|
| + uint16_t new_length = buffer->data()[2] | (buffer->data()[3] << 8);
|
| + scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE,
|
| + vendor_code, 0, kGetAllowedOriginsRequest, new_buffer, new_buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadWebUsbAllowedOrigins, callback));
|
| +}
|
| +
|
| +void ReadWebUsbAllowedOrigins(
|
| + scoped_refptr<UsbDeviceHandle> device_handle,
|
| + uint8_t vendor_code,
|
| + const ReadWebUsbAllowedOriginsCallback& callback) {
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(4);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE,
|
| + vendor_code, 0, kGetAllowedOriginsRequest, buffer, buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadWebUsbAllowedOriginsHeader, device_handle, callback,
|
| + vendor_code));
|
| +}
|
| +
|
| +void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const ReadWebUsbDescriptorsCallback& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED) {
|
| + USB_LOG(EVENT) << "Failed to read BOS descriptor.";
|
| + callback.Run(nullptr, GURL());
|
| + return;
|
| + }
|
| +
|
| + WebUsbPlatformCapabilityDescriptor descriptor;
|
| + if (!descriptor.ParseFromBosDescriptor(
|
| + std::vector<uint8_t>(buffer->data(), buffer->data() + length))) {
|
| + callback.Run(nullptr, GURL());
|
| + return;
|
| + }
|
| +
|
| + ReadWebUsbAllowedOrigins(
|
| + device_handle, descriptor.vendor_code,
|
| + base::Bind(&ReadUrlDescriptors, device_handle, descriptor.vendor_code,
|
| + descriptor.landing_page_id, callback));
|
| +}
|
| +
|
| +void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const ReadWebUsbDescriptorsCallback& callback,
|
| + UsbTransferStatus status,
|
| + scoped_refptr<net::IOBuffer> buffer,
|
| + size_t length) {
|
| + if (status != USB_TRANSFER_COMPLETED || length != 5) {
|
| + USB_LOG(EVENT) << "Failed to read BOS descriptor header.";
|
| + callback.Run(nullptr, GURL());
|
| + return;
|
| + }
|
| +
|
| + uint16_t new_length = buffer->data()[2] | (buffer->data()[3] << 8);
|
| + scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE,
|
| + kGetDescriptorRequest, kBosDescriptorType << 8, 0, new_buffer,
|
| + new_buffer->size(), kControlTransferTimeout,
|
| + base::Bind(&OnReadBosDescriptor, device_handle, callback));
|
| }
|
|
|
| } // namespace
|
| @@ -165,49 +366,59 @@ WebUsbConfigurationSubset::WebUsbConfigurationSubset()
|
|
|
| WebUsbConfigurationSubset::~WebUsbConfigurationSubset() {}
|
|
|
| -WebUsbDescriptorSet::WebUsbDescriptorSet() {}
|
| -
|
| -WebUsbDescriptorSet::~WebUsbDescriptorSet() {}
|
| -
|
| -bool WebUsbDescriptorSet::Parse(const std::vector<uint8_t>& bytes) {
|
| - if (bytes.size() < 4) {
|
| +WebUsbAllowedOrigins::WebUsbAllowedOrigins() {}
|
| +
|
| +WebUsbAllowedOrigins::~WebUsbAllowedOrigins() {}
|
| +
|
| +// Parses a WebUSB Allowed Origins Header:
|
| +// http://wicg.github.io/webusb/#dfn-allowed-origins-header
|
| +//
|
| +// 0 1 2 3
|
| +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | length | type | total length |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | num configs | origin[0] | origin[1] | ...
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+------
|
| +bool WebUsbAllowedOrigins::Parse(const std::vector<uint8_t>& bytes) {
|
| + const uint8_t kDescriptorType = 0x00;
|
| + const uint8_t kDescriptorMinLength = 5;
|
| +
|
| + // The buffer must be at least the length of this descriptor's mandatory
|
| + // fields.
|
| + if (bytes.size() < kDescriptorMinLength)
|
| return false;
|
| - }
|
|
|
| - // Validate the descriptor set header.
|
| + // Validate that the length of this descriptor and the total length of the
|
| + // entire block of descriptors is consistent with the length of the buffer.
|
| + uint8_t length = bytes[0];
|
| uint16_t total_length = bytes[2] + (bytes[3] << 8);
|
| - if (bytes[0] != 4 || // bLength
|
| - bytes[1] != kDescriptorSetDescriptorType || // bDescriptorType
|
| - 4 > total_length || total_length > bytes.size()) { // wTotalLength
|
| + if (length < 5 || length > bytes.size() || // bLength
|
| + bytes[1] != kDescriptorType || // bDescriptorType
|
| + total_length < length || total_length > bytes.size()) { // wTotalLength
|
| return false;
|
| }
|
|
|
| std::vector<uint8_t>::const_iterator it = bytes.begin();
|
| - std::vector<uint8_t>::const_iterator end = it + total_length;
|
| - std::advance(it, 4);
|
| -
|
| - while (it != bytes.end()) {
|
| - uint8_t length = it[0];
|
| - if (length < 2 || std::distance(it, end) < length) {
|
| + uint8_t num_configurations = bytes[4];
|
| +
|
| + // The next |length - 5| bytes after the mandatory fields are origin indicies.
|
| + std::advance(it, kDescriptorMinLength);
|
| + uint8_t num_origins = length - kDescriptorMinLength;
|
| + origin_ids.reserve(num_origins);
|
| + for (size_t i = 0; i < num_origins; ++i) {
|
| + uint8_t index = *it++;
|
| + if (index == 0)
|
| return false;
|
| - }
|
| + origin_ids.push_back(index);
|
| + }
|
|
|
| - uint8_t type = it[1];
|
| - if (type == kConfigurationSubsetDescriptorType) {
|
| - WebUsbConfigurationSubset configuration;
|
| - if (!ParseConfiguration(&configuration, &it, end)) {
|
| - return false;
|
| - }
|
| - configurations.push_back(configuration);
|
| - } else if (type == kUrlDescriptorType) {
|
| - GURL url;
|
| - if (!ParseUrl(&url, &it, end)) {
|
| - return false;
|
| - }
|
| - origins.push_back(url.GetOrigin());
|
| - } else {
|
| + // |num_configurations| configuration descriptors then follow the descriptor.
|
| + for (size_t i = 0; i < num_configurations; ++i) {
|
| + WebUsbConfigurationSubset configuration;
|
| + if (!ParseConfiguration(&configuration, &it, bytes.end()))
|
| return false;
|
| - }
|
| + configurations.push_back(configuration);
|
| }
|
|
|
| return true;
|
| @@ -240,7 +451,6 @@ bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor(
|
| std::advance(it, 5);
|
|
|
| uint8_t length = 0;
|
| - bool found_vendor_code = false;
|
| for (size_t i = 0; i < num_device_caps; ++i, std::advance(it, length)) {
|
| if (it == end) {
|
| return false;
|
| @@ -270,8 +480,8 @@ bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor(
|
| continue;
|
| }
|
|
|
| - if (length < 23) {
|
| - // The WebUSB capability descriptor must be at least 23 bytes (to allow
|
| + if (length < 22) {
|
| + // The WebUSB capability descriptor must be at least 22 bytes (to allow
|
| // for future versions).
|
| return false;
|
| }
|
| @@ -281,24 +491,74 @@ bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor(
|
| continue;
|
| }
|
|
|
| - // Version 1.0 only defines a single field, bVendorCode.
|
| + // Version 1.0 defines two fields for a total length of 24 bytes.
|
| + if (length != 24) {
|
| + return false;
|
| + }
|
| +
|
| vendor_code = it[22];
|
| - found_vendor_code = true;
|
| + landing_page_id = it[23];
|
| + return true;
|
| }
|
|
|
| - return found_vendor_code;
|
| + return false;
|
| }
|
|
|
| +// Parses a WebUSB URL Descriptor:
|
| +// http://wicg.github.io/webusb/#dfn-url-descriptor
|
| +//
|
| +// 0 1 2 3
|
| +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | length | type | prefix | data[0] |
|
| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
| +// | data[1] | ...
|
| +// +-+-+-+-+-+-+-+-+-+-+-+------
|
| bool ParseWebUsbUrlDescriptor(const std::vector<uint8_t>& bytes, GURL* output) {
|
| - if (bytes.size() < 2) {
|
| + const uint8_t kDescriptorType = 0x03;
|
| + const uint8_t kDescriptorMinLength = 3;
|
| +
|
| + if (bytes.size() < kDescriptorMinLength) {
|
| return false;
|
| }
|
| +
|
| + // Validate that the length is consistent and fits within the buffer.
|
| uint8_t length = bytes[0];
|
| - if (length != bytes.size() || bytes[1] != kUrlDescriptorType) {
|
| + if (length < kDescriptorMinLength || length < bytes.size() ||
|
| + bytes[1] != kDescriptorType) {
|
| return false;
|
| }
|
| - std::vector<uint8_t>::const_iterator it = bytes.begin();
|
| - return ParseUrl(output, &it, bytes.end());
|
| +
|
| + // Look up the URL prefix and append the rest of the data in the descriptor.
|
| + std::string url;
|
| + switch (bytes[2]) {
|
| + case 0:
|
| + url.append("http://");
|
| + break;
|
| + case 1:
|
| + url.append("https://");
|
| + break;
|
| + default:
|
| + return false;
|
| + }
|
| + url.append(reinterpret_cast<const char*>(bytes.data() + 3), length - 3);
|
| +
|
| + *output = GURL(url);
|
| + if (!output->is_valid()) {
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle,
|
| + const ReadWebUsbDescriptorsCallback& callback) {
|
| + scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(5);
|
| + device_handle->ControlTransfer(
|
| + USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE,
|
| + kGetDescriptorRequest, kBosDescriptorType << 8, 0, buffer, buffer->size(),
|
| + kControlTransferTimeout,
|
| + base::Bind(&OnReadBosDescriptorHeader, device_handle, callback));
|
| }
|
|
|
| } // namespace device
|
|
|