1 //! This library provides access to the Bluetooth socket. Included are ways to bind to specific
2 //! channels (i.e. control / user) and send/receive + encode/decode MGMT commands and events.
3 
4 use std::mem;
5 use std::os::unix::io::{AsRawFd, RawFd};
6 
7 use libc;
8 use log::debug;
9 use num_derive::{FromPrimitive, ToPrimitive};
10 use num_traits::cast::{FromPrimitive, ToPrimitive};
11 
12 /// Socket protocol constant for HCI.
13 const BTPROTO_HCI: u8 = 1;
14 
15 /// Non-existent HCI device for binding BT sockets.
16 pub const HCI_DEV_NONE: u16 = 0xFFFF;
17 
18 /// Bindable configurations for open HCI sockets.
19 #[derive(ToPrimitive)]
20 #[repr(u16)]
21 pub enum HciChannels {
22     Raw = 0,
23     User = 1,
24     Monitor = 2,
25     Control = 3,
26     Logging = 4,
27 
28     Unbound = 0xFFFF,
29 }
30 
31 impl From<HciChannels> for u16 {
from(item: HciChannels) -> Self32     fn from(item: HciChannels) -> Self {
33         item.to_u16().unwrap()
34     }
35 }
36 
37 #[repr(C)]
38 struct SockAddrHci {
39     hci_family: libc::sa_family_t,
40     hci_dev: u16,
41     hci_channel: u16,
42 }
43 
44 /// Maximum size of a MGMT command or event packet.
45 const MGMT_PKT_DATA_MAX: usize = 1024;
46 
47 /// Size of MGMT packet header.
48 const MGMT_PKT_HEADER_SIZE: usize = 6;
49 
50 /// Total size of MGMT packet.
51 pub const MGMT_PKT_SIZE_MAX: usize = MGMT_PKT_HEADER_SIZE + MGMT_PKT_DATA_MAX;
52 
53 /// Represents a MGMT packet (either command or event) in the raw form that can
54 /// be read from or written to the MGMT socket.
55 #[derive(Debug)]
56 pub struct MgmtPacket {
57     opcode: u16,
58     index: u16,
59     len: u16,
60     data: Vec<u8>,
61 }
62 
63 impl MgmtPacket {
write_to_wire(&self) -> Vec<u8>64     fn write_to_wire(&self) -> Vec<u8> {
65         let mut v: Vec<u8> = Vec::new();
66 
67         v.extend_from_slice(self.opcode.to_le_bytes().as_slice());
68         v.extend_from_slice(self.index.to_le_bytes().as_slice());
69         v.extend_from_slice(self.len.to_le_bytes().as_slice());
70         v.extend_from_slice(self.data.as_slice());
71 
72         v
73     }
74 }
75 
76 #[derive(FromPrimitive, ToPrimitive)]
77 pub enum MgmtCommandOpcode {
78     ReadIndexList = 0x3,
79     FlossNotifySuspendState = 0x103,
80 }
81 
82 impl From<MgmtCommandOpcode> for u16 {
from(item: MgmtCommandOpcode) -> Self83     fn from(item: MgmtCommandOpcode) -> Self {
84         item.to_u16().unwrap()
85     }
86 }
87 
88 impl TryFrom<u16> for MgmtCommandOpcode {
89     type Error = ();
90 
try_from(item: u16) -> Result<Self, Self::Error>91     fn try_from(item: u16) -> Result<Self, Self::Error> {
92         match MgmtCommandOpcode::from_u16(item) {
93             Some(v) => Ok(v),
94             None => Err(()),
95         }
96     }
97 }
98 
99 pub enum MgmtCommand {
100     ReadIndexList,
101     FlossNotifySuspendState(u16, bool),
102 }
103 
104 impl From<MgmtCommand> for MgmtPacket {
from(item: MgmtCommand) -> Self105     fn from(item: MgmtCommand) -> Self {
106         match item {
107             MgmtCommand::ReadIndexList => MgmtPacket {
108                 opcode: MgmtCommandOpcode::ReadIndexList.into(),
109                 index: HCI_DEV_NONE,
110                 len: 0,
111                 data: Vec::new(),
112             },
113             MgmtCommand::FlossNotifySuspendState(hci_index, suspended) => MgmtPacket {
114                 opcode: MgmtCommandOpcode::FlossNotifySuspendState.into(),
115                 index: HCI_DEV_NONE,
116                 len: MGMT_NOTIFY_SUSPEND_STATE_SIZE,
117                 data: MgmtCpNotifySuspendState::new(hci_index, u8::from(suspended)).to_data(),
118             },
119         }
120     }
121 }
122 
123 #[derive(FromPrimitive, ToPrimitive, Debug)]
124 pub enum MgmtEventOpcode {
125     CommandComplete = 0x1,
126     IndexAdded = 0x4,
127     IndexRemoved = 0x5,
128 }
129 
130 impl TryFrom<u16> for MgmtEventOpcode {
131     type Error = ();
132 
try_from(item: u16) -> Result<Self, Self::Error>133     fn try_from(item: u16) -> Result<Self, Self::Error> {
134         match MgmtEventOpcode::from_u16(item) {
135             Some(v) => Ok(v),
136             None => Err(()),
137         }
138     }
139 }
140 
141 #[derive(Debug)]
142 pub enum MgmtCommandResponse {
143     // This is a meta enum that is only used to indicate that the remaining data
144     // for this response has been dropped.
145     DataUnused,
146 
147     ReadIndexList { num_intf: u16, interfaces: Vec<u16> },
148 }
149 
150 #[derive(Debug)]
151 pub enum MgmtEvent {
152     /// Command completion event.
153     CommandComplete { opcode: u16, status: u8, response: MgmtCommandResponse },
154 
155     /// HCI device was added.
156     IndexAdded(u16),
157 
158     /// HCI device was removed.
159     IndexRemoved(u16),
160 }
161 
162 #[derive(Debug)]
163 pub struct MgmtCpNotifySuspendState {
164     hci_id: u16,
165     suspended: u8,
166 }
167 
168 pub const MGMT_NOTIFY_SUSPEND_STATE_SIZE: u16 = 0x3;
169 
170 impl MgmtCpNotifySuspendState {
new(hci_id: u16, suspended: u8) -> Self171     pub fn new(hci_id: u16, suspended: u8) -> Self {
172         MgmtCpNotifySuspendState { hci_id, suspended }
173     }
174 
to_data(&self) -> Vec<u8>175     pub fn to_data(&self) -> Vec<u8> {
176         let mut v: Vec<u8> = Vec::new();
177         v.extend_from_slice(self.hci_id.to_le_bytes().as_slice());
178         v.extend_from_slice(self.suspended.to_le_bytes().as_slice());
179         v
180     }
181 }
182 
183 impl TryFrom<MgmtPacket> for MgmtEvent {
184     type Error = ();
185 
try_from(item: MgmtPacket) -> Result<Self, Self::Error>186     fn try_from(item: MgmtPacket) -> Result<Self, Self::Error> {
187         MgmtEventOpcode::try_from(item.opcode).and_then(|ev| {
188             Ok(match ev {
189                 MgmtEventOpcode::CommandComplete => {
190                     // Minimum 3 bytes required for opcode + status
191                     if item.data.len() < 3 {
192                         debug!("CommandComplete packet too small: {}", item.data.len());
193                         return Err(());
194                     }
195 
196                     let (opcode_arr, rest) = item.data.split_at(std::mem::size_of::<u16>());
197 
198                     let opcode = u16::from_le_bytes(opcode_arr.try_into().unwrap());
199                     let status = rest[0];
200                     let (_, rest) = rest.split_at(std::mem::size_of::<u8>());
201 
202                     let response = if let Ok(op) = MgmtCommandOpcode::try_from(opcode) {
203                         match op {
204                             MgmtCommandOpcode::ReadIndexList => {
205                                 if rest.len() < 2 {
206                                     debug!("ReadIndexList packet too small: {}", rest.len());
207                                     return Err(());
208                                 }
209 
210                                 let (len_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
211                                 let len = u16::from_le_bytes(len_arr.try_into().unwrap());
212 
213                                 let explen = (len as usize) * 2usize;
214                                 if rest.len() < explen {
215                                     debug!(
216                                         "ReadIndexList len malformed: expect = {}, actual = {}",
217                                         explen,
218                                         rest.len()
219                                     );
220                                     return Err(());
221                                 }
222 
223                                 let interfaces: Vec<u16> = rest
224                                     .iter()
225                                     .step_by(2)
226                                     .zip(rest.iter().skip(1).step_by(2))
227                                     .map(|bytes| u16::from_le_bytes([*bytes.0, *bytes.1]))
228                                     .collect();
229 
230                                 MgmtCommandResponse::ReadIndexList { num_intf: len, interfaces }
231                             }
232                             MgmtCommandOpcode::FlossNotifySuspendState => {
233                                 MgmtCommandResponse::DataUnused
234                             }
235                         }
236                     } else {
237                         MgmtCommandResponse::DataUnused
238                     };
239 
240                     MgmtEvent::CommandComplete { opcode, status, response }
241                 }
242                 MgmtEventOpcode::IndexAdded => MgmtEvent::IndexAdded(item.index),
243                 MgmtEventOpcode::IndexRemoved => MgmtEvent::IndexRemoved(item.index),
244             })
245         })
246     }
247 }
248 
249 /// This struct is used to keep track of an open Bluetooth MGMT socket and it's
250 /// current state. It is meant to be used in two ways: call MGMT commands that
251 /// don't have a open hci device requirement or support being called when the
252 /// device is userchannel owned.
253 #[repr(C)]
254 pub struct BtSocket {
255     sock_fd: i32,
256     channel_type: HciChannels,
257 }
258 
259 // Close given file descriptor.
close_fd(fd: i32) -> i32260 fn close_fd(fd: i32) -> i32 {
261     unsafe { libc::close(fd) }
262 }
263 
264 impl Drop for BtSocket {
drop(&mut self)265     fn drop(&mut self) {
266         if self.has_valid_fd() {
267             close_fd(self.sock_fd);
268         }
269     }
270 }
271 
272 impl Default for BtSocket {
default() -> Self273     fn default() -> Self {
274         BtSocket { sock_fd: -1, channel_type: HciChannels::Unbound }
275     }
276 }
277 
278 impl BtSocket {
new() -> Self279     pub fn new() -> Self {
280         Default::default()
281     }
282 
283     /// Is the current file descriptor valid?
has_valid_fd(&self) -> bool284     pub fn has_valid_fd(&self) -> bool {
285         self.sock_fd >= 0
286     }
287 
288     /// Open raw socket to Bluetooth. This should be the first thing called.
open(&mut self) -> i32289     pub fn open(&mut self) -> i32 {
290         if self.has_valid_fd() {
291             return self.sock_fd;
292         }
293 
294         unsafe {
295             let sockfd = libc::socket(
296                 libc::PF_BLUETOOTH,
297                 libc::SOCK_RAW | libc::SOCK_NONBLOCK,
298                 BTPROTO_HCI.into(),
299             );
300 
301             if sockfd >= 0 {
302                 self.sock_fd = sockfd;
303             }
304 
305             sockfd
306         }
307     }
308 
309     /// Bind socket to a specific HCI channel type.
bind_channel(&mut self, channel: HciChannels, hci_dev: u16) -> i32310     pub fn bind_channel(&mut self, channel: HciChannels, hci_dev: u16) -> i32 {
311         unsafe {
312             let addr = SockAddrHci {
313                 // AF_BLUETOOTH can always be cast into u16
314                 hci_family: libc::sa_family_t::try_from(libc::AF_BLUETOOTH).unwrap(),
315                 hci_dev,
316                 hci_channel: channel.into(),
317             };
318 
319             libc::bind(
320                 self.sock_fd,
321                 (&addr as *const SockAddrHci) as *const libc::sockaddr,
322                 mem::size_of::<SockAddrHci>() as u32,
323             )
324         }
325     }
326 
327     /// Take ownership of the file descriptor owned by this context. The caller
328     /// is responsible for closing the underlying socket if it is open (this is
329     /// intended to be used with something like AsyncFd).
take_fd(&mut self) -> i32330     pub fn take_fd(&mut self) -> i32 {
331         let fd = self.sock_fd;
332         self.sock_fd = -1;
333 
334         fd
335     }
336 
read_mgmt_packet(&mut self) -> Option<MgmtPacket>337     pub fn read_mgmt_packet(&mut self) -> Option<MgmtPacket> {
338         if !self.has_valid_fd() {
339             return None;
340         }
341 
342         unsafe {
343             let mut buf: [u8; MGMT_PKT_SIZE_MAX] = [0; MGMT_PKT_SIZE_MAX];
344             let mut bytes_read;
345             loop {
346                 bytes_read = libc::read(
347                     self.sock_fd,
348                     buf.as_mut_ptr() as *mut libc::c_void,
349                     MGMT_PKT_SIZE_MAX,
350                 );
351 
352                 // Retry if -EINTR
353                 let retry = (bytes_read == -1)
354                     && std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EINTR;
355 
356                 if !retry {
357                     break;
358                 }
359             }
360 
361             // Exit early on error.
362             if bytes_read == -1 {
363                 debug!(
364                     "read_mgmt_packet failed with errno {}",
365                     std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
366                 );
367                 return None;
368             }
369 
370             if bytes_read < (MGMT_PKT_HEADER_SIZE as isize) {
371                 debug!("read_mgmt_packet got {} bytes (not enough for header)", bytes_read);
372                 return None;
373             }
374 
375             let data_size: usize =
376                 (bytes_read - (MGMT_PKT_HEADER_SIZE as isize)).try_into().unwrap();
377 
378             let (opcode_arr, rest) = buf.split_at(std::mem::size_of::<u16>());
379             let (index_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
380             let (len_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
381             let data_arr = rest;
382 
383             Some(MgmtPacket {
384                 opcode: u16::from_le_bytes(opcode_arr.try_into().unwrap()),
385                 index: u16::from_le_bytes(index_arr.try_into().unwrap()),
386                 len: u16::from_le_bytes(len_arr.try_into().unwrap()),
387                 data: match data_size {
388                     x if x > 0 => data_arr[..x].to_vec(),
389                     _ => Vec::new(),
390                 },
391             })
392         }
393     }
394 
write_mgmt_packet(&mut self, packet: MgmtPacket) -> isize395     pub fn write_mgmt_packet(&mut self, packet: MgmtPacket) -> isize {
396         let wire_data = packet.write_to_wire();
397         unsafe {
398             let mut bytes_written;
399             loop {
400                 bytes_written = libc::write(
401                     self.sock_fd,
402                     wire_data.as_slice().as_ptr() as *const libc::c_void,
403                     wire_data.len(),
404                 );
405 
406                 // Retry if -EINTR
407                 let retry = bytes_written == -1
408                     && std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EINTR;
409 
410                 if !retry {
411                     break;
412                 }
413             }
414 
415             bytes_written
416         }
417     }
418 }
419 
420 impl AsRawFd for BtSocket {
as_raw_fd(&self) -> RawFd421     fn as_raw_fd(&self) -> RawFd {
422         self.sock_fd
423     }
424 }
425 
426 #[cfg(test)]
427 mod tests {
428     use super::*;
429 
430     #[test]
mgmt_tryfrom_indexlist()431     fn mgmt_tryfrom_indexlist() {
432         let mut packet = MgmtPacket {
433             opcode: MgmtEventOpcode::CommandComplete.to_u16().unwrap(),
434             index: 0,
435             len: 0,
436             // CommandComplete consists of opcode (u16), status (u8) and the response.
437             // ReadIndexList consists of u16 (num intf) and Vec<u16> (interfaces).
438             // Return a few values to test the parser.
439             data: vec![
440                 /*opcode*/ 0x03, 0x00, /*status*/ 0x0, /*num_intf*/ 0x03, 0x00,
441                 /*interfaces*/ 0x00, 0x00, 0x05, 0x00, 0xef, 0xbe,
442             ],
443         };
444         packet.len = packet.data.len().try_into().unwrap_or(0);
445 
446         let event = packet.try_into();
447         assert!(event.is_ok(), "Packet doesn't parse into event.");
448         if let Ok(ev) = event {
449             if let MgmtEvent::CommandComplete { opcode, status, response } = ev {
450                 assert_eq!(opcode, 0x3);
451                 assert_eq!(status, 0x0);
452                 if let MgmtCommandResponse::ReadIndexList { num_intf, interfaces } = response {
453                     assert_eq!(3, num_intf);
454                     assert_eq!(vec![0x0, 0x5, 0xbeef], interfaces);
455                 } else {
456                     panic!("Command Response is not ReadIndexList");
457                 }
458             } else {
459                 panic!("Event is not Command Complete");
460             }
461         }
462     }
463 }
464