servo: Merge #11580 - Allowed services support (from szeged:allowedservices); r=jdm
authorzakorgy <zakorgy@inf.u-szeged.hu>
Sun, 05 Jun 2016 08:00:50 -0500
changeset 339018 9a423065f2713f34a9f269f014cde45721ba7ab9
parent 339017 8adaceafe9974d8170646dc1114bebfb736b8b35
child 339019 a18e490685038d757f640e2bb5a3793c60b90fa8
push id31307
push usergszorc@mozilla.com
push dateSat, 04 Feb 2017 00:59:06 +0000
treeherdermozilla-central@94079d43835f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #11580 - Allowed services support (from szeged:allowedservices); r=jdm <!-- Please describe your changes on the following line: --> --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [X] These changes do not require tests because there are no webbluetooth test api implementation yet. <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> Source-Repo: https://github.com/servo/servo Source-Revision: 939da24cc8cda5396180fc3aba707794653b0824
servo/components/net/bluetooth_thread.rs
servo/components/net_traits/bluetooth_scanfilter.rs
--- a/servo/components/net/bluetooth_thread.rs
+++ b/servo/components/net/bluetooth_thread.rs
@@ -11,32 +11,33 @@ use device::bluetooth::BluetoothGATTServ
 use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
 use net_traits::bluetooth_scanfilter::{BluetoothScanfilter, BluetoothScanfilterSequence, RequestDeviceoptions};
 use net_traits::bluetooth_thread::{BluetoothCharacteristicMsg, BluetoothCharacteristicsMsg};
 use net_traits::bluetooth_thread::{BluetoothDescriptorMsg, BluetoothDescriptorsMsg};
 use net_traits::bluetooth_thread::{BluetoothDeviceMsg, BluetoothMethodMsg};
 use net_traits::bluetooth_thread::{BluetoothResult, BluetoothServiceMsg, BluetoothServicesMsg};
 use rand::{self, Rng};
 use std::borrow::ToOwned;
-use std::collections::HashMap;
+use std::collections::{HashMap, HashSet};
 use std::string::String;
 use std::thread;
 use std::time::Duration;
 #[cfg(target_os = "linux")]
 use tinyfiledialogs;
 use util::thread::spawn_named;
 
 const ADAPTER_ERROR: &'static str = "No adapter found";
 const DEVICE_ERROR: &'static str = "No device found";
 const DEVICE_MATCH_ERROR: &'static str = "No device found, that matches the given options";
 const PRIMARY_SERVICE_ERROR: &'static str = "No primary service found";
 const INCLUDED_SERVICE_ERROR: &'static str = "No included service found";
 const CHARACTERISTIC_ERROR: &'static str = "No characteristic found";
 const DESCRIPTOR_ERROR: &'static str = "No descriptor found";
 const VALUE_ERROR: &'static str = "No characteristic or descriptor found with that id";
+const SECURITY_ERROR: &'static str = "The operation is insecure";
 // The discovery session needs some time to find any nearby devices
 const DISCOVERY_TIMEOUT_MS: u64 = 1500;
 #[cfg(target_os = "linux")]
 const DIALOG_TITLE: &'static str = "Choose a device";
 #[cfg(target_os = "linux")]
 const DIALOG_COLUMN_ID: &'static str = "Id";
 #[cfg(target_os = "linux")]
 const DIALOG_COLUMN_NAME: &'static str = "Name";
@@ -134,31 +135,33 @@ pub struct BluetoothManager {
     address_to_id: HashMap<String, String>,
     service_to_device: HashMap<String, String>,
     characteristic_to_service: HashMap<String, String>,
     descriptor_to_characteristic: HashMap<String, String>,
     cached_devices: HashMap<String, BluetoothDevice>,
     cached_services: HashMap<String, BluetoothGATTService>,
     cached_characteristics: HashMap<String, BluetoothGATTCharacteristic>,
     cached_descriptors: HashMap<String, BluetoothGATTDescriptor>,
+    allowed_services: HashMap<String, HashSet<String>>,
 }
 
 impl BluetoothManager {
     pub fn new (receiver: IpcReceiver<BluetoothMethodMsg>, adapter: Option<BluetoothAdapter>) -> BluetoothManager {
         BluetoothManager {
             receiver: receiver,
             adapter: adapter,
             address_to_id: HashMap::new(),
             service_to_device: HashMap::new(),
             characteristic_to_service: HashMap::new(),
             descriptor_to_characteristic: HashMap::new(),
             cached_devices: HashMap::new(),
             cached_services: HashMap::new(),
             cached_characteristics: HashMap::new(),
             cached_descriptors: HashMap::new(),
+            allowed_services: HashMap::new(),
         }
     }
 
     fn start(&mut self) {
         while let Ok(msg) = self.receiver.recv() {
             match msg {
                 BluetoothMethodMsg::RequestDevice(options, sender) => {
                     self.request_device(options, sender)
@@ -220,17 +223,18 @@ impl BluetoothManager {
 
     fn get_and_cache_devices(&mut self, adapter: &mut BluetoothAdapter) -> Vec<BluetoothDevice> {
         let devices = adapter.get_devices().unwrap_or(vec!());
         for device in &devices {
             if let Ok(address) = device.get_address() {
                 if !self.address_to_id.contains_key(&address) {
                     let generated_id = self.generate_device_id();
                     self.address_to_id.insert(address, generated_id.clone());
-                    self.cached_devices.insert(generated_id, device.clone());
+                    self.cached_devices.insert(generated_id.clone(), device.clone());
+                    self.allowed_services.insert(generated_id, HashSet::new());
                 }
             }
         }
         self.cached_devices.iter().map(|(_, d)| d.clone()).collect()
     }
 
     fn get_device(&mut self, adapter: &mut BluetoothAdapter, device_id: &str) -> Option<&BluetoothDevice> {
         return_if_cached!(self.cached_devices, device_id);
@@ -441,16 +445,21 @@ impl BluetoothManager {
         let matched_devices: Vec<BluetoothDevice> = devices.into_iter()
                                                            .filter(|d| matches_filters(d, options.get_filters()))
                                                            .collect();
         if let Some(address) = self.select_device(matched_devices) {
             let device_id = match self.address_to_id.get(&address) {
                 Some(id) => id.clone(),
                 None => return drop(sender.send(Err(String::from(DEVICE_MATCH_ERROR)))),
             };
+            let mut services = options.get_services_set();
+            if let Some(services_set) = self.allowed_services.get(&device_id) {
+                services = services_set | &services;
+            }
+            self.allowed_services.insert(device_id.clone(), services);
             if let Some(device) = self.get_device(&mut adapter, &device_id) {
                 let message = Ok(BluetoothDeviceMsg {
                                      id: device_id,
                                      name: device.get_name().ok(),
                                      appearance: device.get_appearance().ok(),
                                      tx_power: device.get_tx_power().ok().map(|p| p as i8),
                                      rssi: device.get_rssi().ok().map(|p| p as i8),
                                  });
@@ -494,16 +503,19 @@ impl BluetoothManager {
         let _ = sender.send(Ok(connected));
     }
 
     fn get_primary_service(&mut self,
                            device_id: String,
                            uuid: String,
                            sender: IpcSender<BluetoothResult<BluetoothServiceMsg>>) {
         let mut adapter = get_adapter_or_return_error!(self, sender);
+        if !self.allowed_services.get(&device_id).map_or(false, |s| s.contains(&uuid)) {
+            return drop(sender.send(Err(String::from(SECURITY_ERROR))));
+        }
         let services = self.get_gatt_services_by_uuid(&mut adapter, &device_id, &uuid);
         if services.is_empty() {
             return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR))));
         }
         for service in services {
             if service.is_primary().unwrap_or(false) {
                 if let Ok(uuid) = service.get_uuid() {
                     return drop(sender.send(Ok(BluetoothServiceMsg {
@@ -518,17 +530,22 @@ impl BluetoothManager {
     }
 
     fn get_primary_services(&mut self,
                             device_id: String,
                             uuid: Option<String>,
                             sender: IpcSender<BluetoothResult<BluetoothServicesMsg>>) {
         let mut adapter = get_adapter_or_return_error!(self, sender);
         let services = match uuid {
-            Some(id) => self.get_gatt_services_by_uuid(&mut adapter, &device_id, &id),
+            Some(ref id) => {
+                if !self.allowed_services.get(&device_id).map_or(false, |s| s.contains(id)) {
+                    return drop(sender.send(Err(String::from(SECURITY_ERROR))))
+                }
+                self.get_gatt_services_by_uuid(&mut adapter, &device_id, id)
+            },
             None => self.get_and_cache_gatt_services(&mut adapter, &device_id),
         };
         if services.is_empty() {
             return drop(sender.send(Err(String::from(PRIMARY_SERVICE_ERROR))));
         }
         let mut services_vec = vec!();
         for service in services {
             if service.is_primary().unwrap_or(false) {
--- a/servo/components/net_traits/bluetooth_scanfilter.rs
+++ b/servo/components/net_traits/bluetooth_scanfilter.rs
@@ -1,26 +1,35 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+use std::collections::HashSet;
 use std::slice::Iter;
 
 // A device name can never be longer than 29 bytes. An adv packet is at most
 // 31 bytes long. The length and identifier of the length field take 2 bytes.
 // That leaves 29 bytes for the name.
 const MAX_NAME_LENGTH: usize = 29;
 
 #[derive(Deserialize, Serialize)]
 pub struct ServiceUUIDSequence(Vec<String>);
 
 impl ServiceUUIDSequence {
     pub fn new(vec: Vec<String>) -> ServiceUUIDSequence {
         ServiceUUIDSequence(vec)
     }
+
+    fn get_services_set(&self) -> HashSet<String> {
+        let mut set = HashSet::new();
+        for s in self.0.iter() {
+            set.insert(s.clone());
+        }
+        set
+    }
 }
 
 #[derive(Deserialize, Serialize)]
 pub struct BluetoothScanfilter {
     name: String,
     name_prefix: String,
     services: ServiceUUIDSequence,
 }
@@ -64,16 +73,24 @@ impl BluetoothScanfilterSequence {
     pub fn has_empty_or_invalid_filter(&self) -> bool {
         self.0.is_empty() ||
         self.0.iter().any(BluetoothScanfilter::is_empty_or_invalid)
     }
 
     pub fn iter(&self) -> Iter<BluetoothScanfilter> {
         self.0.iter()
     }
+
+    fn get_services_set(&self) -> HashSet<String> {
+        let mut set = HashSet::new();
+        for filter in self.iter() {
+            set = &set | &filter.services.get_services_set();
+        }
+        set
+    }
 }
 
 #[derive(Deserialize, Serialize)]
 pub struct RequestDeviceoptions {
     filters: BluetoothScanfilterSequence,
     optional_services: ServiceUUIDSequence,
 }
 
@@ -85,9 +102,13 @@ impl RequestDeviceoptions {
             filters: filters,
             optional_services: services,
         }
     }
 
     pub fn get_filters(&self) -> &BluetoothScanfilterSequence {
         &self.filters
     }
+
+    pub fn get_services_set(&self) -> HashSet<String> {
+        &self.filters.get_services_set() | &self.optional_services.get_services_set()
+    }
 }