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