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 BtSocket {
new() -> Self273     pub fn new() -> Self {
274         BtSocket { sock_fd: -1, channel_type: HciChannels::Unbound }
275     }
276 
277     /// Is the current file descriptor valid?
has_valid_fd(&self) -> bool278     pub fn has_valid_fd(&self) -> bool {
279         self.sock_fd >= 0
280     }
281 
282     /// Open raw socket to Bluetooth. This should be the first thing called.
open(&mut self) -> i32283     pub fn open(&mut self) -> i32 {
284         if self.has_valid_fd() {
285             return self.sock_fd;
286         }
287 
288         unsafe {
289             let sockfd = libc::socket(
290                 libc::PF_BLUETOOTH,
291                 libc::SOCK_RAW | libc::SOCK_NONBLOCK,
292                 BTPROTO_HCI.into(),
293             );
294 
295             if sockfd >= 0 {
296                 self.sock_fd = sockfd;
297             }
298 
299             sockfd
300         }
301     }
302 
303     /// Bind socket to a specific HCI channel type.
bind_channel(&mut self, channel: HciChannels, hci_dev: u16) -> i32304     pub fn bind_channel(&mut self, channel: HciChannels, hci_dev: u16) -> i32 {
305         unsafe {
306             let addr = SockAddrHci {
307                 // AF_BLUETOOTH can always be cast into u16
308                 hci_family: libc::sa_family_t::try_from(libc::AF_BLUETOOTH).unwrap(),
309                 hci_dev,
310                 hci_channel: channel.into(),
311             };
312 
313             return libc::bind(
314                 self.sock_fd,
315                 (&addr as *const SockAddrHci) as *const libc::sockaddr,
316                 mem::size_of::<SockAddrHci>() as u32,
317             );
318         }
319     }
320 
321     /// Take ownership of the file descriptor owned by this context. The caller
322     /// is responsible for closing the underlying socket if it is open (this is
323     /// intended to be used with something like AsyncFd).
take_fd(&mut self) -> i32324     pub fn take_fd(&mut self) -> i32 {
325         let fd = self.sock_fd;
326         self.sock_fd = -1;
327 
328         fd
329     }
330 
read_mgmt_packet(&mut self) -> Option<MgmtPacket>331     pub fn read_mgmt_packet(&mut self) -> Option<MgmtPacket> {
332         if !self.has_valid_fd() {
333             return None;
334         }
335 
336         unsafe {
337             let mut buf: [u8; MGMT_PKT_SIZE_MAX] = [0; MGMT_PKT_SIZE_MAX];
338             let mut bytes_read;
339             loop {
340                 bytes_read = libc::read(
341                     self.sock_fd,
342                     buf.as_mut_ptr() as *mut libc::c_void,
343                     MGMT_PKT_SIZE_MAX,
344                 );
345 
346                 // Retry if -EINTR
347                 let retry = (bytes_read == -1)
348                     && std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EINTR;
349 
350                 if !retry {
351                     break;
352                 }
353             }
354 
355             // Exit early on error.
356             if bytes_read == -1 {
357                 debug!(
358                     "read_mgmt_packet failed with errno {}",
359                     std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
360                 );
361                 return None;
362             }
363 
364             if bytes_read < (MGMT_PKT_HEADER_SIZE as isize) {
365                 debug!("read_mgmt_packet got {} bytes (not enough for header)", bytes_read);
366                 return None;
367             }
368 
369             let data_size: usize =
370                 (bytes_read - (MGMT_PKT_HEADER_SIZE as isize)).try_into().unwrap();
371 
372             let (opcode_arr, rest) = buf.split_at(std::mem::size_of::<u16>());
373             let (index_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
374             let (len_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
375             let data_arr = rest;
376 
377             Some(MgmtPacket {
378                 opcode: u16::from_le_bytes(opcode_arr.try_into().unwrap()),
379                 index: u16::from_le_bytes(index_arr.try_into().unwrap()),
380                 len: u16::from_le_bytes(len_arr.try_into().unwrap()),
381                 data: match data_size {
382                     x if x > 0 => data_arr[..x].iter().map(|x| *x).collect::<Vec<u8>>(),
383                     _ => Vec::new(),
384                 },
385             })
386         }
387     }
388 
write_mgmt_packet(&mut self, packet: MgmtPacket) -> isize389     pub fn write_mgmt_packet(&mut self, packet: MgmtPacket) -> isize {
390         let wire_data = packet.write_to_wire();
391         unsafe {
392             let mut bytes_written;
393             loop {
394                 bytes_written = libc::write(
395                     self.sock_fd,
396                     wire_data.as_slice().as_ptr() as *const libc::c_void,
397                     wire_data.len(),
398                 );
399 
400                 // Retry if -EINTR
401                 let retry = bytes_written == -1
402                     && std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EINTR;
403 
404                 if !retry {
405                     break;
406                 }
407             }
408 
409             bytes_written
410         }
411     }
412 }
413 
414 impl AsRawFd for BtSocket {
as_raw_fd(&self) -> RawFd415     fn as_raw_fd(&self) -> RawFd {
416         self.sock_fd
417     }
418 }
419 
420 #[cfg(test)]
421 mod tests {
422     use super::*;
423 
424     #[test]
mgmt_tryfrom_indexlist()425     fn mgmt_tryfrom_indexlist() {
426         let mut packet = MgmtPacket {
427             opcode: MgmtEventOpcode::CommandComplete.to_u16().unwrap(),
428             index: 0,
429             len: 0,
430             // CommandComplete consists of opcode (u16), status (u8) and the response.
431             // ReadIndexList consists of u16 (num intf) and Vec<u16> (interfaces).
432             // Return a few values to test the parser.
433             data: vec![
434                 /*opcode*/ 0x03, 0x00, /*status*/ 0x0, /*num_intf*/ 0x03, 0x00,
435                 /*interfaces*/ 0x00, 0x00, 0x05, 0x00, 0xef, 0xbe,
436             ],
437         };
438         packet.len = packet.data.len().try_into().unwrap_or(0);
439 
440         let event = packet.try_into();
441         assert_eq!(true, event.is_ok(), "Packet doesn't parse into event.");
442         if let Ok(ev) = event {
443             if let MgmtEvent::CommandComplete { opcode, status, response } = ev {
444                 assert_eq!(opcode, 0x3);
445                 assert_eq!(status, 0x0);
446                 if let MgmtCommandResponse::ReadIndexList { num_intf, interfaces } = response {
447                     assert_eq!(3, num_intf);
448                     assert_eq!(vec![0x0, 0x5, 0xbeef], interfaces);
449                 } else {
450                     panic!("Command Response is not ReadIndexList");
451                 }
452             } else {
453                 panic!("Event is not Command Complete");
454             }
455         }
456     }
457 }
458