• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }
80 
81 impl From<MgmtCommandOpcode> for u16 {
from(item: MgmtCommandOpcode) -> Self82     fn from(item: MgmtCommandOpcode) -> Self {
83         item.to_u16().unwrap()
84     }
85 }
86 
87 impl TryFrom<u16> for MgmtCommandOpcode {
88     type Error = ();
89 
try_from(item: u16) -> Result<Self, Self::Error>90     fn try_from(item: u16) -> Result<Self, Self::Error> {
91         match MgmtCommandOpcode::from_u16(item) {
92             Some(v) => Ok(v),
93             None => Err(()),
94         }
95     }
96 }
97 
98 pub enum MgmtCommand {
99     ReadIndexList,
100 }
101 
102 impl From<MgmtCommand> for MgmtPacket {
from(item: MgmtCommand) -> Self103     fn from(item: MgmtCommand) -> Self {
104         match item {
105             MgmtCommand::ReadIndexList => MgmtPacket {
106                 opcode: MgmtCommandOpcode::ReadIndexList.into(),
107                 index: HCI_DEV_NONE,
108                 len: 0,
109                 data: Vec::new(),
110             },
111         }
112     }
113 }
114 
115 #[derive(FromPrimitive, ToPrimitive, Debug)]
116 pub enum MgmtEventOpcode {
117     CommandComplete = 0x1,
118     IndexAdded = 0x4,
119     IndexRemoved = 0x5,
120 }
121 
122 impl TryFrom<u16> for MgmtEventOpcode {
123     type Error = ();
124 
try_from(item: u16) -> Result<Self, Self::Error>125     fn try_from(item: u16) -> Result<Self, Self::Error> {
126         match MgmtEventOpcode::from_u16(item) {
127             Some(v) => Ok(v),
128             None => Err(()),
129         }
130     }
131 }
132 
133 #[derive(Debug)]
134 pub enum MgmtCommandResponse {
135     // This is a meta enum that is only used to indicate that the remaining data
136     // for this response has been dropped.
137     DataUnused,
138 
139     ReadIndexList { num_intf: u16, interfaces: Vec<u16> },
140 }
141 
142 #[derive(Debug)]
143 pub enum MgmtEvent {
144     /// Command completion event.
145     CommandComplete { opcode: u16, status: u8, response: MgmtCommandResponse },
146 
147     /// HCI device was added.
148     IndexAdded(u16),
149 
150     /// HCI device was removed.
151     IndexRemoved(u16),
152 }
153 
154 impl TryFrom<MgmtPacket> for MgmtEvent {
155     type Error = ();
156 
try_from(item: MgmtPacket) -> Result<Self, Self::Error>157     fn try_from(item: MgmtPacket) -> Result<Self, Self::Error> {
158         MgmtEventOpcode::try_from(item.opcode).and_then(|ev| {
159             Ok(match ev {
160                 MgmtEventOpcode::CommandComplete => {
161                     // Minimum 3 bytes required for opcode + status
162                     if item.data.len() < 3 {
163                         debug!("CommandComplete packet too small: {}", item.data.len());
164                         return Err(());
165                     }
166 
167                     let (opcode_arr, rest) = item.data.split_at(std::mem::size_of::<u16>());
168 
169                     let opcode = u16::from_le_bytes(opcode_arr.try_into().unwrap());
170                     let status = rest[0];
171                     let (_, rest) = rest.split_at(std::mem::size_of::<u8>());
172 
173                     let response = if let Ok(op) = MgmtCommandOpcode::try_from(opcode) {
174                         match op {
175                             MgmtCommandOpcode::ReadIndexList => {
176                                 if rest.len() < 2 {
177                                     debug!("ReadIndexList packet too small: {}", rest.len());
178                                     return Err(());
179                                 }
180 
181                                 let (len_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
182                                 let len = u16::from_le_bytes(len_arr.try_into().unwrap());
183 
184                                 let explen = (len as usize) * 2usize;
185                                 if rest.len() < explen {
186                                     debug!(
187                                         "ReadIndexList len malformed: expect = {}, actual = {}",
188                                         explen,
189                                         rest.len()
190                                     );
191                                     return Err(());
192                                 }
193 
194                                 let interfaces: Vec<u16> = rest
195                                     .iter()
196                                     .step_by(2)
197                                     .zip(rest.iter().skip(1).step_by(2))
198                                     .map(|bytes| u16::from_le_bytes([*bytes.0, *bytes.1]))
199                                     .collect();
200 
201                                 MgmtCommandResponse::ReadIndexList { num_intf: len, interfaces }
202                             }
203                         }
204                     } else {
205                         MgmtCommandResponse::DataUnused
206                     };
207 
208                     MgmtEvent::CommandComplete { opcode, status, response }
209                 }
210                 MgmtEventOpcode::IndexAdded => MgmtEvent::IndexAdded(item.index),
211                 MgmtEventOpcode::IndexRemoved => MgmtEvent::IndexRemoved(item.index),
212             })
213         })
214     }
215 }
216 
217 /// This struct is used to keep track of an open Bluetooth MGMT socket and it's
218 /// current state. It is meant to be used in two ways: call MGMT commands that
219 /// don't have a open hci device requirement or support being called when the
220 /// device is userchannel owned.
221 #[repr(C)]
222 pub struct BtSocket {
223     sock_fd: i32,
224     channel_type: HciChannels,
225 }
226 
227 // Close given file descriptor.
close_fd(fd: i32) -> i32228 fn close_fd(fd: i32) -> i32 {
229     unsafe { libc::close(fd) }
230 }
231 
232 impl Drop for BtSocket {
drop(&mut self)233     fn drop(&mut self) {
234         if self.has_valid_fd() {
235             close_fd(self.sock_fd);
236         }
237     }
238 }
239 
240 impl BtSocket {
new() -> Self241     pub fn new() -> Self {
242         BtSocket { sock_fd: -1, channel_type: HciChannels::Unbound }
243     }
244 
245     /// Is the current file descriptor valid?
has_valid_fd(&self) -> bool246     pub fn has_valid_fd(&self) -> bool {
247         self.sock_fd >= 0
248     }
249 
250     /// Open raw socket to Bluetooth. This should be the first thing called.
open(&mut self) -> i32251     pub fn open(&mut self) -> i32 {
252         if self.has_valid_fd() {
253             return self.sock_fd;
254         }
255 
256         unsafe {
257             let sockfd = libc::socket(
258                 libc::PF_BLUETOOTH,
259                 libc::SOCK_RAW | libc::SOCK_NONBLOCK,
260                 BTPROTO_HCI.into(),
261             );
262 
263             if sockfd >= 0 {
264                 self.sock_fd = sockfd;
265             }
266 
267             sockfd
268         }
269     }
270 
271     /// Bind socket to a specific HCI channel type.
bind_channel(&mut self, channel: HciChannels, hci_dev: u16) -> i32272     pub fn bind_channel(&mut self, channel: HciChannels, hci_dev: u16) -> i32 {
273         unsafe {
274             let addr = SockAddrHci {
275                 // AF_BLUETOOTH can always be cast into u16
276                 hci_family: libc::sa_family_t::try_from(libc::AF_BLUETOOTH).unwrap(),
277                 hci_dev,
278                 hci_channel: channel.into(),
279             };
280 
281             return libc::bind(
282                 self.sock_fd,
283                 (&addr as *const SockAddrHci) as *const libc::sockaddr,
284                 mem::size_of::<SockAddrHci>() as u32,
285             );
286         }
287     }
288 
289     /// Take ownership of the file descriptor owned by this context. The caller
290     /// is responsible for closing the underlying socket if it is open (this is
291     /// intended to be used with something like AsyncFd).
take_fd(&mut self) -> i32292     pub fn take_fd(&mut self) -> i32 {
293         let fd = self.sock_fd;
294         self.sock_fd = -1;
295 
296         fd
297     }
298 
read_mgmt_packet(&mut self) -> Option<MgmtPacket>299     pub fn read_mgmt_packet(&mut self) -> Option<MgmtPacket> {
300         if !self.has_valid_fd() {
301             return None;
302         }
303 
304         unsafe {
305             let mut buf: [u8; MGMT_PKT_SIZE_MAX] = [0; MGMT_PKT_SIZE_MAX];
306             let mut bytes_read;
307             loop {
308                 bytes_read = libc::read(
309                     self.sock_fd,
310                     buf.as_mut_ptr() as *mut libc::c_void,
311                     MGMT_PKT_SIZE_MAX,
312                 );
313 
314                 // Retry if -EINTR
315                 let retry = (bytes_read == -1)
316                     && std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EINTR;
317 
318                 if !retry {
319                     break;
320                 }
321             }
322 
323             // Exit early on error.
324             if bytes_read == -1 {
325                 debug!(
326                     "read_mgmt_packet failed with errno {}",
327                     std::io::Error::last_os_error().raw_os_error().unwrap_or(0)
328                 );
329                 return None;
330             }
331 
332             if bytes_read < (MGMT_PKT_HEADER_SIZE as isize) {
333                 debug!("read_mgmt_packet got {} bytes (not enough for header)", bytes_read);
334                 return None;
335             }
336 
337             let data_size: usize =
338                 (bytes_read - (MGMT_PKT_HEADER_SIZE as isize)).try_into().unwrap();
339 
340             let (opcode_arr, rest) = buf.split_at(std::mem::size_of::<u16>());
341             let (index_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
342             let (len_arr, rest) = rest.split_at(std::mem::size_of::<u16>());
343             let data_arr = rest;
344 
345             Some(MgmtPacket {
346                 opcode: u16::from_le_bytes(opcode_arr.try_into().unwrap()),
347                 index: u16::from_le_bytes(index_arr.try_into().unwrap()),
348                 len: u16::from_le_bytes(len_arr.try_into().unwrap()),
349                 data: match data_size {
350                     x if x > 0 => data_arr[..x].iter().map(|x| *x).collect::<Vec<u8>>(),
351                     _ => Vec::new(),
352                 },
353             })
354         }
355     }
356 
write_mgmt_packet(&mut self, packet: MgmtPacket) -> isize357     pub fn write_mgmt_packet(&mut self, packet: MgmtPacket) -> isize {
358         let wire_data = packet.write_to_wire();
359         unsafe {
360             let mut bytes_written;
361             loop {
362                 bytes_written = libc::write(
363                     self.sock_fd,
364                     wire_data.as_slice().as_ptr() as *const libc::c_void,
365                     wire_data.len(),
366                 );
367 
368                 // Retry if -EINTR
369                 let retry = bytes_written == -1
370                     && std::io::Error::last_os_error().raw_os_error().unwrap_or(0) == libc::EINTR;
371 
372                 if !retry {
373                     break;
374                 }
375             }
376 
377             bytes_written
378         }
379     }
380 }
381 
382 impl AsRawFd for BtSocket {
as_raw_fd(&self) -> RawFd383     fn as_raw_fd(&self) -> RawFd {
384         self.sock_fd
385     }
386 }
387 
388 #[cfg(test)]
389 mod tests {
390     use super::*;
391 
392     #[test]
mgmt_tryfrom_indexlist()393     fn mgmt_tryfrom_indexlist() {
394         let mut packet = MgmtPacket {
395             opcode: MgmtEventOpcode::CommandComplete.to_u16().unwrap(),
396             index: 0,
397             len: 0,
398             // CommandComplete consists of opcode (u16), status (u8) and the response.
399             // ReadIndexList consists of u16 (num intf) and Vec<u16> (interfaces).
400             // Return a few values to test the parser.
401             data: vec![
402                 /*opcode*/ 0x03, 0x00, /*status*/ 0x0, /*num_intf*/ 0x03, 0x00,
403                 /*interfaces*/ 0x00, 0x00, 0x05, 0x00, 0xef, 0xbe,
404             ],
405         };
406         packet.len = packet.data.len().try_into().unwrap_or(0);
407 
408         let event = packet.try_into();
409         assert_eq!(true, event.is_ok(), "Packet doesn't parse into event.");
410         if let Ok(ev) = event {
411             if let MgmtEvent::CommandComplete { opcode, status, response } = ev {
412                 assert_eq!(opcode, 0x3);
413                 assert_eq!(status, 0x0);
414                 if let MgmtCommandResponse::ReadIndexList { num_intf, interfaces } = response {
415                     assert_eq!(3, num_intf);
416                     assert_eq!(vec![0x0, 0x5, 0xbeef], interfaces);
417                 } else {
418                     panic!("Command Response is not ReadIndexList");
419                 }
420             } else {
421                 panic!("Event is not Command Complete");
422             }
423         }
424     }
425 }
426