mod handle;
mod uuid;
use {
self::handle::*,
crate::{
bytes::*,
l2cap::{L2CAPResponder, Protocol, ProtocolObj},
utils::HexSlice,
Error,
},
};
pub use self::handle::AttHandle;
pub use self::uuid::AttUuid;
enum_with_unknown! {
#[derive(Debug, Copy, Clone)]
enum Opcode(u8) {
ErrorRsp = 0x01,
ExchangeMtuReq = 0x02,
ExchangeMtuRsp = 0x03,
FindInformationReq = 0x04,
FindInformationRsp = 0x05,
FindByTypeReq = 0x06,
FindByTypeRsp = 0x07,
ReadByTypeReq = 0x08,
ReadByTypeRsp = 0x09,
ReadReq = 0x0A,
ReadRsp = 0x0B,
ReadBlobReq = 0x0C,
ReadBlobRsp = 0x0D,
ReadMultipleReq = 0x0E,
ReadMultipleRsp = 0x0F,
ReadByGroupReq = 0x10,
ReadByGroupRsp = 0x11,
WriteReq = 0x12,
WriteRsp = 0x13,
WriteCommand = 0x52,
SignedWriteCommand = 0xD2,
PrepareWriteReq = 0x16,
PrepareWriteRsp = 0x17,
ExecuteWriteReq = 0x18,
ExecuteWriteRsp = 0x19,
HandleValueNotification = 0x1B,
HandleValueIndication = 0x1D,
HandleValueConfirmation = 0x1E,
}
}
impl Opcode {
fn raw(&self) -> u8 {
u8::from(*self)
}
fn is_authenticated(&self) -> bool {
self.raw() & 0x80 != 0
}
fn is_command(&self) -> bool {
self.raw() & 0x40 != 0
}
}
#[derive(Debug)]
enum AttMsg<'a> {
ErrorRsp {
opcode: Opcode,
handle: AttHandle,
error_code: ErrorCode,
},
ExchangeMtuReq {
mtu: u16,
},
ExchangeMtuRsp {
mtu: u16,
},
ReadByGroupReq {
handle_range: RawHandleRange,
group_type: AttUuid,
},
Unknown {
opcode: Opcode,
params: HexSlice<&'a [u8]>,
},
}
struct ReadByGroupRsp<
F: FnMut(&mut FnMut(ByGroupAttData) -> Result<(), Error>) -> Result<(), Error>,
> {
item_fn: F,
}
impl<'a, F: FnMut(&mut FnMut(ByGroupAttData) -> Result<(), Error>) -> Result<(), Error>>
ReadByGroupRsp<F>
{
fn encode(mut self, writer: &mut ByteWriter) -> Result<(), Error> {
writer.write_u8(Opcode::ReadByGroupRsp.into())?;
let mut length = writer.split_off(1)?;
let mut size = None;
let left = writer.space_left();
(self.item_fn)(&mut |att: ByGroupAttData| {
trace!("read by group rsp: {:?}", att);
att.to_bytes(writer)?;
let used = left - writer.space_left();
if let Some(expected_size) = size {
if used != expected_size {
return Err(Error::InvalidLength);
}
} else {
size = Some(used);
}
Ok(())
})
.ok();
let size = size.expect("empty response");
assert!(size <= usize::from(u8::max_value()));
length.write_u8(size as u8).unwrap();
Ok(())
}
}
impl AttMsg<'_> {
fn opcode(&self) -> Opcode {
match self {
AttMsg::ErrorRsp { .. } => Opcode::ErrorRsp,
AttMsg::ExchangeMtuReq { .. } => Opcode::ExchangeMtuReq,
AttMsg::ExchangeMtuRsp { .. } => Opcode::ExchangeMtuRsp,
AttMsg::ReadByGroupReq { .. } => Opcode::ReadByGroupReq,
AttMsg::Unknown { opcode, .. } => *opcode,
}
}
}
#[derive(Debug)]
struct OutgoingPdu<'a>(AttMsg<'a>);
impl<'a> From<AttMsg<'a>> for OutgoingPdu<'a> {
fn from(msg: AttMsg<'a>) -> Self {
OutgoingPdu(msg)
}
}
impl<'a> FromBytes<'a> for OutgoingPdu<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
let opcode = Opcode::from(bytes.read_u8()?);
let auth = opcode.is_authenticated();
let msg = match opcode {
Opcode::ErrorRsp => AttMsg::ErrorRsp {
opcode: Opcode::from(bytes.read_u8()?),
handle: AttHandle::from_bytes(bytes)?,
error_code: ErrorCode::from(bytes.read_u8()?),
},
Opcode::ExchangeMtuReq => AttMsg::ExchangeMtuReq {
mtu: bytes.read_u16_le()?,
},
Opcode::ExchangeMtuRsp => AttMsg::ExchangeMtuRsp {
mtu: bytes.read_u16_le()?,
},
Opcode::ReadByGroupReq => AttMsg::ReadByGroupReq {
handle_range: RawHandleRange::from_bytes(bytes)?,
group_type: AttUuid::from_bytes(bytes)?,
},
_ => AttMsg::Unknown {
opcode,
params: HexSlice(bytes.read_slice(bytes.bytes_left() - if auth { 12 } else { 0 })?),
},
};
if auth {
bytes.skip(12)?;
}
Ok(OutgoingPdu(msg))
}
}
impl ToBytes for OutgoingPdu<'_> {
fn to_bytes(&self, writer: &mut ByteWriter) -> Result<(), Error> {
writer.write_u8(self.0.opcode().into())?;
match self.0 {
AttMsg::ErrorRsp {
opcode,
handle,
error_code,
} => {
writer.write_u8(opcode.into())?;
writer.write_u16_le(handle.as_u16())?;
writer.write_u8(error_code.into())?;
}
AttMsg::ExchangeMtuReq { mtu } => {
writer.write_u16_le(mtu)?;
}
AttMsg::ExchangeMtuRsp { mtu } => {
writer.write_u16_le(mtu)?;
}
AttMsg::ReadByGroupReq {
handle_range,
group_type,
} => {
handle_range.to_bytes(writer)?;
group_type.to_bytes(writer)?;
}
AttMsg::Unknown { opcode: _, params } => {
writer.write_slice(params.0)?;
}
}
if self.0.opcode().is_authenticated() {
writer.write_slice(&[0; 12])?;
}
Ok(())
}
}
#[derive(Debug)]
struct IncomingPdu<'a> {
opcode: Opcode,
params: AttMsg<'a>,
signature: Option<HexSlice<&'a [u8]>>,
}
impl<'a> FromBytes<'a> for IncomingPdu<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
let opcode = Opcode::from(bytes.read_u8()?);
let auth = opcode.is_authenticated();
Ok(Self {
opcode,
params: match opcode {
Opcode::ErrorRsp => AttMsg::ErrorRsp {
opcode: Opcode::from(bytes.read_u8()?),
handle: AttHandle::from_bytes(bytes)?,
error_code: ErrorCode::from(bytes.read_u8()?),
},
Opcode::ExchangeMtuReq => AttMsg::ExchangeMtuReq {
mtu: bytes.read_u16_le()?,
},
Opcode::ExchangeMtuRsp => AttMsg::ExchangeMtuRsp {
mtu: bytes.read_u16_le()?,
},
Opcode::ReadByGroupReq => AttMsg::ReadByGroupReq {
handle_range: RawHandleRange::from_bytes(bytes)?,
group_type: AttUuid::from_bytes(bytes)?,
},
_ => AttMsg::Unknown {
opcode,
params: HexSlice(
bytes.read_slice(bytes.bytes_left() - if auth { 12 } else { 0 })?,
),
},
},
signature: if auth {
Some(HexSlice(bytes.read_slice(12)?))
} else {
None
},
})
}
}
impl ToBytes for IncomingPdu<'_> {
fn to_bytes(&self, writer: &mut ByteWriter) -> Result<(), Error> {
writer.write_u8(self.opcode.into())?;
match self.params {
AttMsg::ErrorRsp {
opcode,
handle,
error_code,
} => {
writer.write_u8(opcode.into())?;
writer.write_u16_le(handle.as_u16())?;
writer.write_u8(error_code.into())?;
}
AttMsg::ExchangeMtuReq { mtu } => {
writer.write_u16_le(mtu)?;
}
AttMsg::ExchangeMtuRsp { mtu } => {
writer.write_u16_le(mtu)?;
}
AttMsg::ReadByGroupReq {
handle_range,
group_type,
} => {
handle_range.to_bytes(writer)?;
group_type.to_bytes(writer)?;
}
AttMsg::Unknown { opcode: _, params } => {
writer.write_slice(params.0)?;
}
}
if let Some(sig) = self.signature {
writer.write_slice(sig.0)?;
}
Ok(())
}
}
pub struct Attribute<'a> {
pub att_type: AttUuid,
pub handle: AttHandle,
pub value: HexSlice<&'a [u8]>,
pub permission: AttPermission,
}
pub struct AttPermission {
_access: AccessPermission,
_encryption: EncryptionPermission,
_authentication: AuthenticationPermission,
_authorization: AuthorizationPermission,
}
pub enum AccessPermission {
Readable,
Writeable,
ReadableWritable,
}
pub enum EncryptionPermission {
EncryptionRequired,
EncryptionNotRequired,
}
pub enum AuthenticationPermission {
AuthenticationRequired,
AuthenticationNotRequired,
}
pub enum AuthorizationPermission {
AuthorizationRequired,
AuthorizationNotRequired,
}
impl Default for AttPermission {
fn default() -> Self {
Self {
_access: AccessPermission::Readable,
_encryption: EncryptionPermission::EncryptionNotRequired,
_authentication: AuthenticationPermission::AuthenticationNotRequired,
_authorization: AuthorizationPermission::AuthorizationNotRequired,
}
}
}
pub trait AttributeProvider {
fn for_each_attr(
&mut self,
f: &mut dyn FnMut(&mut Attribute) -> Result<(), Error>,
) -> Result<(), Error>;
fn any(&mut self, filter: &mut dyn FnMut(&mut Attribute) -> bool) -> bool {
match self.for_each_attr(&mut |att| {
if filter(att) {
Err(Error::Eof)
} else {
Ok(())
}
}) {
Err(Error::Eof) => true,
_ => false,
}
}
fn is_grouping_attr(&self, uuid: AttUuid) -> bool;
fn group_end(&self, handle: AttHandle) -> Option<&Attribute>;
}
pub struct NoAttributes;
impl AttributeProvider for NoAttributes {
fn for_each_attr(
&mut self,
_: &mut dyn FnMut(&mut Attribute) -> Result<(), Error>,
) -> Result<(), Error> {
Ok(())
}
fn is_grouping_attr(&self, _uuid: AttUuid) -> bool {
false
}
fn group_end(&self, _handle: AttHandle) -> Option<&Attribute> {
None
}
}
pub struct AttributeServer<A: AttributeProvider> {
attrs: A,
}
impl<A: AttributeProvider> AttributeServer<A> {
pub fn new(attrs: A) -> Self {
Self { attrs }
}
}
impl<A: AttributeProvider> AttributeServer<A> {
fn process_request<'a>(
&mut self,
pdu: IncomingPdu,
responder: &mut L2CAPResponder,
) -> Result<(), AttError> {
struct RspError(AttError);
impl From<Error> for RspError {
fn from(e: Error) -> Self {
panic!("unexpected error: {}", e);
}
}
impl From<AttError> for RspError {
fn from(att: AttError) -> Self {
RspError(att)
}
}
match pdu.params {
AttMsg::ReadByGroupReq {
handle_range,
group_type,
} => {
let range = handle_range.check()?;
if !self.attrs.is_grouping_attr(group_type) {
return Err(AttError {
code: ErrorCode::UnsupportedGroupType,
handle: range.start(),
});
}
let mut filter =
|att: &mut Attribute| att.att_type == group_type && range.contains(att.handle);
let result = responder.respond_with(|writer| {
if self.attrs.any(&mut filter) {
ReadByGroupRsp {
item_fn: |cb: &mut FnMut(ByGroupAttData) -> Result<(), Error>| {
self.attrs.for_each_attr(&mut |att: &mut Attribute| {
if att.att_type == group_type && range.contains(att.handle) {
cb(ByGroupAttData {
handle: att.handle,
end_group_handle: att.handle,
value: att.value,
})?;
}
Ok(())
})
},
}
.encode(writer)?;
Ok(())
} else {
Err(AttError {
code: ErrorCode::AttributeNotFound,
handle: AttHandle::NULL,
}
.into())
}
});
match result {
Ok(()) => Ok(()),
Err(RspError(e)) => Err(e),
}
}
AttMsg::ExchangeMtuReq { mtu: _mtu } => {
responder
.respond(OutgoingPdu(AttMsg::ExchangeMtuRsp {
mtu: u16::from(Self::RSP_PDU_SIZE),
}))
.unwrap();
Ok(())
}
AttMsg::Unknown { .. } => {
if pdu.opcode.is_command() {
Ok(())
} else {
Err(AttError {
code: ErrorCode::RequestNotSupported,
handle: AttHandle::NULL,
})
}
}
AttMsg::ErrorRsp { .. } | AttMsg::ExchangeMtuRsp { .. } => Err(AttError {
code: ErrorCode::InvalidPdu,
handle: AttHandle::NULL,
}),
}
}
}
impl<A: AttributeProvider> ProtocolObj for AttributeServer<A> {
fn process_message(
&mut self,
message: &[u8],
mut responder: L2CAPResponder,
) -> Result<(), Error> {
let pdu = IncomingPdu::from_bytes(&mut ByteReader::new(message))?;
let opcode = pdu.opcode;
debug!("ATT msg received: {:?}", pdu);
match self.process_request(pdu, &mut responder) {
Ok(()) => Ok(()),
Err(att_error) => {
debug!("ATT error: {:?}", att_error);
responder.respond(OutgoingPdu(AttMsg::ErrorRsp {
opcode: opcode,
handle: att_error.handle,
error_code: att_error.code,
}))
}
}
}
}
impl<A: AttributeProvider> Protocol for AttributeServer<A> {
const RSP_PDU_SIZE: u8 = 23;
}
enum_with_unknown! {
#[derive(Copy, Clone, Debug)]
pub enum ErrorCode(u8) {
InvalidHandle = 0x01,
ReadNotPermitted = 0x02,
WriteNotPermitted = 0x03,
InvalidPdu = 0x04,
InsufficientAuthentication = 0x05,
RequestNotSupported = 0x06,
InvalidOffset = 0x07,
InsufficientAuthorization = 0x08,
PrepareQueueFull = 0x09,
AttributeNotFound = 0x0A,
AttributeNotLong = 0x0B,
InsufficientEncryptionKeySize = 0x0C,
InvalidAttributeValueLength = 0x0D,
UnlikelyError = 0x0E,
InsufficientEncryption = 0x0F,
UnsupportedGroupType = 0x10,
InsufficientResources = 0x11,
}
}
#[derive(Debug)]
pub struct AttError {
code: ErrorCode,
handle: AttHandle,
}
#[derive(Debug)]
pub struct ByTypeAttData<'a> {
handle: AttHandle,
value: HexSlice<&'a [u8]>,
}
impl<'a> FromBytes<'a> for ByTypeAttData<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
Ok(ByTypeAttData {
handle: AttHandle::from_bytes(bytes)?,
value: HexSlice(bytes.read_rest()),
})
}
}
impl<'a> ToBytes for ByTypeAttData<'a> {
fn to_bytes(&self, writer: &mut ByteWriter) -> Result<(), Error> {
writer.write_u16_le(self.handle.as_u16())?;
writer.write_slice(self.value.0)?;
Ok(())
}
}
#[derive(Debug, Copy, Clone)]
pub struct ByGroupAttData<'a> {
handle: AttHandle,
end_group_handle: AttHandle,
value: HexSlice<&'a [u8]>,
}
impl<'a> FromBytes<'a> for ByGroupAttData<'a> {
fn from_bytes(bytes: &mut ByteReader<'a>) -> Result<Self, Error> {
Ok(ByGroupAttData {
handle: AttHandle::from_bytes(bytes)?,
end_group_handle: AttHandle::from_bytes(bytes)?,
value: HexSlice(bytes.read_rest()),
})
}
}
impl<'a> ToBytes for ByGroupAttData<'a> {
fn to_bytes(&self, writer: &mut ByteWriter) -> Result<(), Error> {
writer.write_u16_le(self.handle.as_u16())?;
writer.write_u16_le(self.end_group_handle.as_u16())?;
if writer.space_left() >= self.value.0.len() {
writer.write_slice(self.value.0)?;
} else {
writer
.write_slice(&self.value.0[..writer.space_left()])
.unwrap();
}
Ok(())
}
}