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