Skip to main content

tailtalk_packets/
aarp.rs

1use byteorder::{BigEndian, ReadBytesExt};
2use bytes::{Buf, BufMut, BytesMut};
3use std::io::{Cursor, Error, Read};
4
5const AARP_MIN_LEN: usize = 28;
6
7pub type EthernetMac = [u8; 6];
8
9#[derive(Debug)]
10pub enum AarpError {
11    InvalidSize,
12    UnknownOpcode(u16),
13    StdIoError(Error),
14}
15
16impl From<Error> for AarpError {
17    fn from(err: Error) -> AarpError {
18        AarpError::StdIoError(err)
19    }
20}
21
22#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
23pub struct AppleTalkAddress {
24    pub network_number: u16,
25    pub node_number: u8,
26}
27
28impl AppleTalkAddress {
29    pub fn decode(address_bytes: [u8; 4]) -> Self {
30        let network_number = u16::from_be_bytes([address_bytes[1], address_bytes[2]]);
31        let node_number = address_bytes[3];
32
33        AppleTalkAddress {
34            network_number,
35            node_number,
36        }
37    }
38
39    pub fn encode(&self, encoded_address: &mut [u8; 4]) {
40        encoded_address[0] = 0;
41        encoded_address[1..=2].copy_from_slice(&self.network_number.to_be_bytes());
42        encoded_address[3] = self.node_number;
43    }
44
45    pub fn matches(&self, other: &AppleTalkAddress, source: AddressSource) -> bool {
46        match source {
47            AddressSource::LocalTalk => self.node_number == other.node_number,
48            AddressSource::EtherTalkPhase1 => self.node_number == other.node_number,
49            AddressSource::EtherTalkPhase2 => self == other,
50        }
51    }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum AddressSource {
56    EtherTalkPhase2,
57    EtherTalkPhase1,
58    LocalTalk,
59}
60
61#[repr(u16)]
62#[derive(Debug, PartialEq, Eq, Copy, Clone)]
63pub enum AarpOpcode {
64    Request = 1,
65    Response = 2,
66    Probe = 3,
67}
68
69#[derive(Debug, PartialEq, Eq)]
70pub struct AarpPacket {
71    pub hardware_type: u16,
72    pub protocol_type: u16,
73    pub hardware_size: u8, // Always 6 for EtherTalk networks - Only case supported
74    pub protocol_size: u8, // Always 4 for typical AppleTalk networks
75    pub opcode: AarpOpcode,
76    pub sender_addr: EthernetMac,
77    pub sender_protocol: AppleTalkAddress,
78    pub target_addr: EthernetMac,
79    pub target_protocol: AppleTalkAddress,
80}
81
82impl AarpPacket {
83    pub const LEN: usize = 28;
84
85    pub fn parse(buf: &[u8]) -> Result<Self, AarpError> {
86        if buf.len() < AARP_MIN_LEN {
87            return Err(AarpError::InvalidSize);
88        }
89
90        let mut cursor = Cursor::new(buf);
91        let hardware_type = cursor.read_u16::<BigEndian>()?;
92        let protocol_type = cursor.read_u16::<BigEndian>()?;
93        let hardware_size = cursor.read_u8()?;
94        let protocol_size = cursor.read_u8()?;
95        let opcode = {
96            let opcode = cursor.read_u16::<BigEndian>()?;
97
98            match opcode {
99                1 => AarpOpcode::Request,
100                2 => AarpOpcode::Response,
101                3 => AarpOpcode::Probe,
102                _ => return Err(AarpError::UnknownOpcode(opcode)),
103            }
104        };
105
106        let mut sender_addr: [u8; 6] = [0u8; 6];
107        cursor.read_exact(&mut sender_addr)?;
108
109        let mut protocol: [u8; 4] = [0u8; 4];
110        cursor.read_exact(&mut protocol)?;
111        let sender_protocol = AppleTalkAddress::decode(protocol);
112
113        let mut target_addr: [u8; 6] = [0u8; 6];
114        cursor.read_exact(&mut target_addr)?;
115
116        cursor.read_exact(&mut protocol)?;
117        let target_protocol = AppleTalkAddress::decode(protocol);
118
119        Ok(Self {
120            hardware_type,
121            protocol_type,
122            hardware_size,
123            protocol_size,
124            opcode,
125            sender_addr,
126            sender_protocol,
127            target_addr,
128            target_protocol,
129        })
130    }
131
132    pub fn to_bytes(&self, buffer: &mut [u8]) -> usize {
133        let mut buf = BytesMut::with_capacity(buffer.len());
134        buf.put_u16(self.hardware_type);
135        buf.put_u16(self.protocol_type);
136        buf.put_u8(self.hardware_size);
137        buf.put_u8(self.protocol_size);
138        buf.put_u16(self.opcode as u16);
139
140        buf.put_slice(&self.sender_addr);
141
142        let mut sender_protocol_encoded = [0u8; 4];
143        self.sender_protocol.encode(&mut sender_protocol_encoded);
144        buf.put_slice(&sender_protocol_encoded);
145
146        buf.put_slice(&self.target_addr);
147
148        let mut target_protocol_encoded = [0u8; 4];
149        self.target_protocol.encode(&mut target_protocol_encoded);
150        buf.put_slice(&target_protocol_encoded);
151
152        let used = buf.chunk();
153        buffer[..used.len()].copy_from_slice(used);
154
155        used.len()
156    }
157}
158
159#[cfg(test)]
160mod tests {
161    use super::*;
162    use assert_hex::assert_eq_hex;
163
164    #[test]
165    fn test_parse_aarp() {
166        let test_data: &[u8] = &[
167            0x00, 0x01, 0x80, 0x9b, 0x06, 0x04, 0x00, 0x03, 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe3,
168            0x00, 0xff, 0x54, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x54, 0x44,
169        ];
170
171        let packet = AarpPacket::parse(test_data).expect("failed to parse");
172
173        assert_eq_hex!(packet.hardware_type, 1);
174        assert_eq_hex!(packet.protocol_type, 0x809b);
175        assert_eq_hex!(packet.hardware_size, 6);
176        assert_eq_hex!(packet.protocol_size, 4);
177        assert_eq_hex!(packet.opcode, AarpOpcode::Probe);
178        assert_eq_hex!(packet.sender_addr, [0x00u8, 0x0c, 0x29, 0x0d, 0x56, 0xe3]);
179        assert_eq_hex!(packet.sender_protocol.network_number, 65364);
180        assert_eq_hex!(packet.sender_protocol.node_number, 68);
181        assert_eq_hex!(packet.target_addr, [0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00]);
182        assert_eq_hex!(packet.target_protocol.network_number, 65364);
183        assert_eq_hex!(packet.target_protocol.node_number, 68);
184    }
185
186    #[test]
187    fn test_generate_aarp() {
188        let test_pkt = AarpPacket {
189            hardware_type: 1,
190            protocol_type: 0x809b,
191            hardware_size: 6,
192            protocol_size: 4,
193            opcode: AarpOpcode::Probe,
194            sender_addr: [0x00u8, 0x0c, 0x29, 0x0d, 0x56, 0xe3],
195            sender_protocol: AppleTalkAddress {
196                network_number: 65310,
197                node_number: 248,
198            },
199            target_addr: [0x00u8, 0x00, 0x00, 0x00, 0x00, 0x00],
200            target_protocol: AppleTalkAddress {
201                network_number: 65310,
202                node_number: 248,
203            },
204        };
205
206        let mut test_buf: [u8; 100] = [0u8; 100];
207
208        let pkt_size = test_pkt.to_bytes(&mut test_buf);
209        let sized = &test_buf[..pkt_size];
210        let expected_bin_data = &[
211            0x00u8, 0x01, 0x80, 0x9b, 0x06, 0x04, 0x00, 0x03, 0x00, 0x0c, 0x29, 0x0d, 0x56, 0xe3,
212            0x00, 0xff, 0x1e, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1e, 0xf8,
213        ];
214
215        assert_eq_hex!(sized, expected_bin_data);
216    }
217
218    #[test]
219    fn test_dogfood() {
220        let test_pkt = AarpPacket {
221            hardware_type: 1,
222            protocol_type: 0x809b,
223            hardware_size: 6,
224            protocol_size: 4,
225            opcode: AarpOpcode::Request,
226            sender_addr: [0x00u8, 0x0c, 0x29, 0x0d, 0x56, 0xe3],
227            sender_protocol: AppleTalkAddress {
228                network_number: 12345,
229                node_number: 100,
230            },
231            target_addr: [0x00u8, 0x01, 0x02, 0x03, 0x04, 0x05],
232            target_protocol: AppleTalkAddress {
233                network_number: 54321,
234                node_number: 200,
235            },
236        };
237
238        let mut test_buf: [u8; 100] = [0u8; 100];
239        let pkt_size = test_pkt.to_bytes(&mut test_buf);
240        let sized = &test_buf[..pkt_size];
241
242        let parsed = AarpPacket::parse(sized).expect("failed to parse");
243
244        assert_eq!(test_pkt, parsed);
245    }
246}