awdl_frame_parser/tlvs/dns_sd/service_response_tlv/
mod.rs

1pub mod dns_record;
2
3use crate::{
4    common::{AWDLDnsName, AWDLStr, ReadLabelIterator},
5    tlvs::{AWDLTLVType, AwdlTlv},
6};
7
8use dns_record::AWDLDnsRecord;
9
10use scroll::{
11    ctx::{MeasureWith, TryFromCtx, TryIntoCtx},
12    Endian, Pread, Pwrite,
13};
14
15#[derive(Clone, Copy, Debug, Hash)]
16/// This TLV contains data about services offered by the peer.
17pub struct ServiceResponseTLV<'a, I> {
18    /// The fullname of the service.
19    pub name: AWDLDnsName<I>,
20
21    /// The DNS record contained in this response.
22    pub record: AWDLDnsRecord<'a, I>,
23}
24impl<I> AwdlTlv for ServiceResponseTLV<'_, I> {
25    const TLV_TYPE: AWDLTLVType = AWDLTLVType::ServiceResponse;
26}
27impl<'a, I: IntoIterator<Item = AWDLStr<'a>> + Clone> Eq for ServiceResponseTLV<'a, I> {}
28impl<'a, LhsIterator, RhsIterator> PartialEq<ServiceResponseTLV<'a, RhsIterator>>
29    for ServiceResponseTLV<'a, LhsIterator>
30where
31    LhsIterator: IntoIterator<Item = AWDLStr<'a>> + Clone,
32    RhsIterator: IntoIterator<Item = AWDLStr<'a>> + Clone,
33{
34    fn eq(&self, other: &ServiceResponseTLV<'a, RhsIterator>) -> bool {
35        self.name == other.name && self.record == other.record
36    }
37}
38impl<'a, I> MeasureWith<()> for ServiceResponseTLV<'a, I>
39where
40    I: IntoIterator<Item = AWDLStr<'a>> + Clone,
41{
42    fn measure_with(&self, ctx: &()) -> usize {
43        6 + self.name.measure_with(ctx) + self.record.measure_with(ctx)
44    }
45}
46impl<'a> TryFromCtx<'a> for ServiceResponseTLV<'a, ReadLabelIterator<'a>> {
47    type Error = scroll::Error;
48    fn try_from_ctx(from: &'a [u8], _ctx: ()) -> Result<(Self, usize), Self::Error> {
49        let mut offset = 0;
50
51        let length = from.gread_with::<u16>(&mut offset, Endian::Little)? as usize;
52        let name = from
53            .gread_with::<&'a [u8]>(&mut offset, length - 1)?
54            .pread(0)?;
55        let record = from.gread(&mut offset)?;
56        Ok((Self { name, record }, offset))
57    }
58}
59impl<'a, I> TryIntoCtx for ServiceResponseTLV<'a, I>
60where
61    I: IntoIterator<Item = AWDLStr<'a>> + Clone,
62{
63    type Error = scroll::Error;
64    fn try_into_ctx(self, buf: &mut [u8], _ctx: ()) -> Result<usize, Self::Error> {
65        let mut offset = 0;
66        buf.gwrite_with::<u16>(
67            self.name.measure_with(&()) as u16 + 1,
68            &mut offset,
69            Endian::Little,
70        )?;
71        buf.gwrite(self.name, &mut offset)?;
72        buf.gwrite(self.record, &mut offset)?;
73        Ok(offset)
74    }
75}
76
77/// The default service response tlv returned by reading.
78pub type DefaultServiceResponseTLV<'a> = ServiceResponseTLV<'a, ReadLabelIterator<'a>>;
79
80#[cfg(test)]
81mod service_response_tests {
82    use alloc::vec;
83    use scroll::{ctx::MeasureWith, Pread, Pwrite};
84
85    use crate::{
86        common::{AWDLDnsCompression, AWDLDnsName, ReadLabelIterator},
87        tlvs::dns_sd::{dns_record::AWDLDnsRecord, ServiceResponseTLV},
88    };
89
90    #[test]
91    fn test_service_response_tlv_ptr() {
92        let bytes = &include_bytes!("../../../../test_bins/service_response_tlv_ptr.bin")[3..];
93
94        let service_response_tlv = bytes
95            .pread::<ServiceResponseTLV<ReadLabelIterator>>(0)
96            .unwrap();
97
98        assert_eq!(
99            service_response_tlv,
100            ServiceResponseTLV {
101                name: AWDLDnsName {
102                    labels: vec!["_airplay-p2p".into()],
103                    domain: AWDLDnsCompression::TcpLocal,
104                    ..Default::default()
105                },
106                record: AWDLDnsRecord::PTR {
107                    domain_name: AWDLDnsName {
108                        labels: vec!["34FD6A0C9A42@1.021".into()],
109                        domain: AWDLDnsCompression::Null,
110                        ..Default::default()
111                    }
112                }
113            }
114        );
115        let mut buf = vec![0x00; service_response_tlv.measure_with(&())];
116        buf.as_mut_slice().pwrite(service_response_tlv, 0).unwrap();
117        assert_eq!(buf, bytes);
118    }
119    #[test]
120    fn test_service_response_tlv_srv() {
121        let bytes = &include_bytes!("../../../../test_bins/service_response_tlv_srv.bin")[3..];
122
123        let service_response_tlv = bytes
124            .pread::<ServiceResponseTLV<ReadLabelIterator>>(0)
125            .unwrap();
126
127        assert_eq!(
128            service_response_tlv,
129            ServiceResponseTLV {
130                name: AWDLDnsName {
131                    labels: vec!["34fd6a0c9a42@1.021".into(), "_airplay-p2p".into()],
132                    domain: AWDLDnsCompression::TcpLocal,
133                    ..Default::default()
134                },
135                record: AWDLDnsRecord::SRV {
136                    priority: 0,
137                    weight: 0,
138                    port: 7000,
139                    target: AWDLDnsName {
140                        labels: vec!["dcc83dc2-fae7-4043-8c7a-a8b6bf49eaad".into()],
141                        domain: AWDLDnsCompression::Local,
142                        ..Default::default()
143                    }
144                }
145            }
146        );
147        let mut buf = vec![0x00; service_response_tlv.measure_with(&())];
148        buf.as_mut_slice().pwrite(service_response_tlv, 0).unwrap();
149        assert_eq!(buf, bytes);
150    }
151    #[test]
152    fn test_service_response_tlv_txt() {
153        let bytes = &include_bytes!("../../../../test_bins/service_response_tlv_txt.bin")[3..];
154
155        let service_response_tlv = bytes
156            .pread::<ServiceResponseTLV<ReadLabelIterator>>(0)
157            .unwrap();
158
159        assert_eq!(
160            service_response_tlv,
161            ServiceResponseTLV {
162                name: AWDLDnsName {
163                    labels: vec!["6dba48462242".into()],
164                    domain: AWDLDnsCompression::AirDropTcpLocal,
165                    ..Default::default()
166                },
167                record: AWDLDnsRecord::TXT {
168                    txt_record: alloc::vec!["flags=999".into()]
169                }
170            }
171        );
172        let mut buf = vec![0x00; service_response_tlv.measure_with(&())];
173        buf.as_mut_slice().pwrite(service_response_tlv, 0).unwrap();
174        assert_eq!(buf, bytes);
175    }
176}