1use core::mem::{size_of, MaybeUninit};
2use std::{
3 io::{Error, Result},
4 os::fd::{AsRawFd, RawFd},
5};
6
7pub use macaddr::MacAddr6 as MacAddress;
8pub use socket2::Type as SocketType;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(u8)]
13pub enum AddressType {
14 Public = 1,
15 Random = 2,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
20pub struct L2capSockAddr {
21 pub addr: MacAddress,
23 pub type_: AddressType,
25 pub psm: u16,
27 pub cid: u16,
29}
30
31#[derive(Clone, Copy)]
32#[repr(i32)]
33#[non_exhaustive]
34enum BtProto {
35 L2Cap = 0,
36}
37
38#[derive(Clone, Copy)]
39#[repr(u16)]
40#[non_exhaustive]
41pub enum Psm {
42 L2CapLeCidOts = 0x25,
43 L2CapLeDynStart = 0x80,
44}
45
46impl From<Psm> for u16 {
47 fn from(psm: Psm) -> u16 {
48 psm as _
49 }
50}
51
52#[derive(Clone, Copy)]
53#[repr(i32)]
54#[non_exhaustive]
55enum SockOpt {
56 BtSndMtu = 12,
57 BtRcvMtu = 13,
58}
59
60impl L2capSockAddr {
61 pub fn new(addr: MacAddress, type_: AddressType, psm: impl Into<u16>) -> Self {
63 Self {
64 addr,
65 type_,
66 psm: psm.into(),
67 cid: 0,
68 }
69 }
70}
71
72#[derive(Clone, Copy)]
73#[allow(non_camel_case_types)]
74#[repr(C)]
75struct sockaddr_l2 {
76 pub l2_family: libc::sa_family_t,
77 pub l2_psm: libc::c_ushort,
78 pub l2_bdaddr: bdaddr_t,
79 pub l2_cid: libc::c_ushort,
80 pub l2_bdaddr_type: u8,
81}
82
83impl From<&L2capSockAddr> for socket2::SockAddr {
84 fn from(sockaddr: &L2capSockAddr) -> Self {
85 let mut addr_storage: libc::sockaddr_storage = unsafe { core::mem::zeroed() };
86 let sockaddr_ref = unsafe { &mut *(&mut addr_storage as *mut _ as *mut sockaddr_l2) };
87
88 sockaddr_ref.l2_family = libc::AF_BLUETOOTH as _;
89 sockaddr_ref.l2_psm = sockaddr.psm.to_le();
90 sockaddr_ref.l2_bdaddr = bdaddr_t::from(&sockaddr.addr);
91 sockaddr_ref.l2_cid = sockaddr.cid;
92 sockaddr_ref.l2_bdaddr_type = sockaddr.type_ as _;
93
94 unsafe { socket2::SockAddr::new(addr_storage, size_of::<sockaddr_l2>() as _) }
95 }
96}
97
98impl TryFrom<socket2::SockAddr> for L2capSockAddr {
99 type Error = Error;
100 fn try_from(sockaddr: socket2::SockAddr) -> Result<Self> {
101 let addr_storage = sockaddr.as_storage();
102 let sockaddr_ref = unsafe { &*(&addr_storage as *const _ as *const sockaddr_l2) };
103
104 if sockaddr_ref.l2_family != libc::AF_BLUETOOTH as _ {
105 return Err(Error::new(
106 std::io::ErrorKind::InvalidInput,
107 "Bluetooth address family expected",
108 ));
109 }
110
111 Ok(Self {
112 addr: sockaddr_ref.l2_bdaddr.into(),
113 type_: match sockaddr_ref.l2_bdaddr_type {
114 1 => AddressType::Public,
115 2 => AddressType::Random,
116 _ => {
117 return Err(Error::new(
118 std::io::ErrorKind::InvalidInput,
119 "Unknown L2CAP address type",
120 ))
121 }
122 },
123 psm: sockaddr_ref.l2_psm,
124 cid: sockaddr_ref.l2_cid,
125 })
126 }
127}
128
129#[derive(Clone, Copy)]
130#[allow(non_camel_case_types)]
131#[repr(C, packed)]
132struct bdaddr_t {
133 pub b: [u8; 6],
134}
135
136impl From<&MacAddress> for bdaddr_t {
137 fn from(macaddr: &MacAddress) -> Self {
138 let addr = macaddr.into_array();
139
140 #[cfg(target_endian = "little")]
141 let addr = {
142 let mut addr = addr;
143 addr.reverse();
144 addr
145 };
146
147 Self { b: addr }
148 }
149}
150
151impl From<&bdaddr_t> for MacAddress {
152 fn from(bdaddr: &bdaddr_t) -> Self {
153 let addr = bdaddr.b;
154
155 #[cfg(target_endian = "little")]
156 let addr = {
157 let mut addr = addr;
158 addr.reverse();
159 addr
160 };
161
162 Self::from(addr)
163 }
164}
165
166impl From<bdaddr_t> for MacAddress {
167 fn from(bdaddr: bdaddr_t) -> Self {
168 Self::from(&bdaddr)
169 }
170}
171
172#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
174#[repr(C)]
175pub struct Security {
176 pub level: SecurityLevel,
178 pub key_size: u8,
180}
181
182pub const BT_SECURITY: i32 = 4;
183
184#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
186#[repr(u8)]
187pub enum SecurityLevel {
188 Sdp = 0,
190 #[default]
192 Low = 1,
193 Medium = 2,
195 High = 3,
197 Fips = 4,
199}
200
201pub struct L2capSocket {
202 inner: socket2::Socket,
203}
204
205impl core::ops::Deref for L2capSocket {
206 type Target = socket2::Socket;
207
208 fn deref(&self) -> &Self::Target {
209 &self.inner
210 }
211}
212
213impl core::ops::DerefMut for L2capSocket {
214 fn deref_mut(&mut self) -> &mut Self::Target {
215 &mut self.inner
216 }
217}
218
219impl L2capSocket {
220 pub fn new(type_: SocketType) -> Result<Self> {
221 let inner = socket2::Socket::new(
222 libc::AF_BLUETOOTH.into(),
223 type_,
224 Some((BtProto::L2Cap as i32).into()),
225 )?;
226 Ok(Self { inner })
227 }
228
229 pub fn bind(&self, sockaddr: &L2capSockAddr) -> Result<()> {
230 self.inner.bind(&sockaddr.into())
231 }
232
233 pub fn connect(&self, sockaddr: &L2capSockAddr) -> Result<()> {
234 self.inner.connect(&sockaddr.into())
235 }
236
237 pub fn set_nonblocking(&self, nonblocking: bool) -> Result<()> {
238 self.inner.set_nonblocking(nonblocking)
239 }
240
241 pub fn local_addr(&self) -> Result<L2capSockAddr> {
243 self.inner.local_addr().and_then(TryFrom::try_from)
244 }
245
246 pub fn peer_addr(&self) -> Result<L2capSockAddr> {
248 self.inner.peer_addr().and_then(TryFrom::try_from)
249 }
250
251 pub fn security(&self) -> Result<Security> {
252 getsockopt(&self.inner, libc::SOL_BLUETOOTH, BT_SECURITY)
253 }
254
255 pub fn set_security(&self, security: &Security) -> Result<()> {
256 setsockopt(&self.inner, libc::SOL_BLUETOOTH, BT_SECURITY, security)
257 }
258
259 pub fn recv_mtu(&self) -> Result<usize> {
260 Ok(getsockopt::<u16>(&self.inner, libc::SOL_BLUETOOTH, SockOpt::BtRcvMtu as _)? as _)
261 }
262
263 pub fn set_recv_mtu(&self, mtu: usize) -> Result<()> {
264 let mtu = mtu as u16;
265 setsockopt(
266 &self.inner,
267 libc::SOL_BLUETOOTH,
268 SockOpt::BtRcvMtu as _,
269 &mtu,
270 )
271 }
272
273 pub fn send_mtu(&self) -> Result<usize> {
274 Ok(getsockopt::<u16>(&self.inner, libc::SOL_BLUETOOTH, SockOpt::BtSndMtu as _)? as _)
275 }
276
277 pub fn set_send_mtu(&self, mtu: usize) -> Result<()> {
278 let mtu = mtu as u16;
279 setsockopt(
280 &self.inner,
281 libc::SOL_BLUETOOTH,
282 SockOpt::BtSndMtu as _,
283 &mtu,
284 )
285 }
286}
287
288impl AsRawFd for L2capSocket {
289 fn as_raw_fd(&self) -> RawFd {
290 self.inner.as_raw_fd()
291 }
292}
293
294fn getsockopt<T>(socket: &impl AsRawFd, level: libc::c_int, optname: libc::c_int) -> Result<T> {
295 let mut optval: MaybeUninit<T> = MaybeUninit::uninit();
296 let mut optlen: libc::socklen_t = size_of::<T>() as _;
297 if unsafe {
298 libc::getsockopt(
299 socket.as_raw_fd(),
300 level,
301 optname,
302 optval.as_mut_ptr() as *mut _,
303 &mut optlen,
304 )
305 } == -1
306 {
307 return Err(Error::last_os_error());
308 }
309 if optlen != size_of::<T>() as _ {
310 return Err(Error::new(std::io::ErrorKind::InvalidInput, "invalid size"));
311 }
312 let optval = unsafe { optval.assume_init() };
313 Ok(optval)
314}
315
316fn setsockopt<T>(
317 socket: &impl AsRawFd,
318 level: libc::c_int,
319 optname: libc::c_int,
320 optval: &T,
321) -> Result<()> {
322 let optlen: libc::socklen_t = size_of::<T>() as _;
323 if unsafe {
324 libc::setsockopt(
325 socket.as_raw_fd(),
326 level,
327 optname,
328 optval as *const _ as *const _,
329 optlen,
330 )
331 } == -1
332 {
333 return Err(Error::last_os_error());
334 }
335 Ok(())
336}