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<'a, I: Iterator<Item = ByGroupAttData<'a>>> {
items: I,
}
impl<'a, I: Iterator<Item = ByGroupAttData<'a>>> ReadByGroupRsp<'a, I> {
fn encode(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();
for att in self.items {
trace!("read by group rsp: {:?}", att);
if let Err(_) = att.to_bytes(writer) {
break;
}
let used = left - writer.space_left();
if let Some(expected_size) = size {
if used != expected_size {
break;
}
} else {
size = Some(used);
}
}
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 Attributes {
fn attributes(&mut self) -> &[Attribute];
}
pub struct NoAttributes;
impl Attributes for NoAttributes {
fn attributes(&mut self) -> &[Attribute] {
&[]
}
}
pub struct AttributeServer<A: Attributes> {
attrs: A,
}
impl<A: Attributes> AttributeServer<A> {
pub fn new(attrs: A) -> Self {
Self { attrs }
}
}
impl<A: Attributes> 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()?;
let requested_attrs = self
.attrs
.attributes()
.iter()
.filter(|att| att.att_type == group_type && range.contains(att.handle))
.map(|att| {
ByGroupAttData {
handle: att.handle,
end_group_handle: att.handle,
value: att.value,
}
});
let result = responder.respond_with(|writer| {
if requested_attrs.clone().next().is_none() {
Err(AttError {
code: ErrorCode::AttributeNotFound,
handle: AttHandle::NULL,
}
.into())
} else {
ReadByGroupRsp {
items: requested_attrs,
}
.encode(writer)?;
Ok(())
}
});
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: Attributes> 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: Attributes> 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(())
}
}