Skip to main content

tailtalk_packets/afp/
mod.rs

1mod bitmap;
2mod commands;
3mod types;
4mod util;
5
6// Re-export all public items
7pub use bitmap::*;
8pub use commands::*;
9pub use types::*;
10pub use util::*;
11
12#[cfg(test)]
13mod tests {
14    use super::*;
15
16    #[test]
17    fn test_afp_status_round_trip() {
18        let status = FPGetSrvrInfo {
19            machine_type: MacString::from("Macintosh"),
20            afp_versions: vec![
21                AfpVersion::Version1_1,
22                AfpVersion::Version2,
23                AfpVersion::Version2_1,
24            ],
25            uams: vec![AfpUam::NoUserAuthent, AfpUam::CleartxtPasswrd],
26            volume_icon: Some([0xAA; 256]), // Dummy icon
27            flags: 0x0001,                  // CopyFile
28            server_name: MacString::from("Test Server"),
29        };
30
31        let bytes = status.to_bytes().expect("Serialization failed");
32
33        assert_eq!(bytes.len(), 368);
34
35        let parsed = FPGetSrvrInfo::parse(&bytes).expect("Parsing failed");
36
37        assert_eq!(status, parsed);
38    }
39
40    #[test]
41    fn test_afp_status_no_icon() {
42        let status = FPGetSrvrInfo {
43            machine_type: MacString::from("Macintosh"),
44            afp_versions: vec![AfpVersion::Version2],
45            uams: vec![AfpUam::NoUserAuthent],
46            volume_icon: None,
47            flags: 0,
48            server_name: MacString::from("Mini"),
49        };
50
51        let bytes = status.to_bytes().expect("Serialization failed");
52        let parsed = FPGetSrvrInfo::parse(&bytes).expect("Parsing failed");
53
54        assert_eq!(status, parsed);
55    }
56
57    #[test]
58    fn test_afp_status_binary() {
59        // Packet from a PowerBook G3 server running an AFP share
60        let test_data: &[u8] = &[
61            0x0, 0x2b, 0x0, 0x35, 0x0, 0x63, 0x0, 0x9d, 0x80, 0xb, 0xc, 0x50, 0x6f, 0x77, 0x65,
62            0x72, 0x42, 0x6f, 0x6f, 0x6b, 0x20, 0x47, 0x33, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
63            0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
64            0x9, 0x4d, 0x61, 0x63, 0x69, 0x6e, 0x74, 0x6f, 0x73, 0x68, 0x3, 0xe, 0x41, 0x46, 0x50,
65            0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x31, 0x2e, 0x31, 0xe, 0x41, 0x46,
66            0x50, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x2e, 0x30, 0xe, 0x41,
67            0x46, 0x50, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x32, 0x2e, 0x31, 0x3,
68            0x10, 0x43, 0x6c, 0x65, 0x61, 0x72, 0x74, 0x78, 0x74, 0x20, 0x70, 0x61, 0x73, 0x73,
69            0x77, 0x72, 0x64, 0x10, 0x52, 0x61, 0x6e, 0x64, 0x6e, 0x75, 0x6d, 0x20, 0x65, 0x78,
70            0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x16, 0x32, 0x2d, 0x57, 0x61, 0x79, 0x20, 0x52,
71            0x61, 0x6e, 0x64, 0x6e, 0x75, 0x6d, 0x20, 0x65, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67,
72            0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x2, 0x9f, 0xe0,
73            0x0, 0x4, 0x50, 0x30, 0x0, 0x8, 0x30, 0x28, 0x0, 0x10, 0x10, 0x3c, 0x7, 0xa0, 0x8, 0x4,
74            0x18, 0x7f, 0x4, 0x4, 0x10, 0x0, 0x82, 0x4, 0x10, 0x0, 0x81, 0x4, 0x10, 0x0, 0x82, 0x4,
75            0x10, 0x0, 0x84, 0x4, 0x10, 0x0, 0x88, 0x4, 0x10, 0x0, 0x90, 0x4, 0x10, 0x0, 0xb0, 0x4,
76            0x10, 0x0, 0xd0, 0x4, 0xff, 0xff, 0xff, 0xff, 0x40, 0x0, 0x0, 0x2, 0x3f, 0xff, 0xff,
77            0xfc, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x5, 0x0,
78            0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0x8, 0x80, 0x0, 0x0, 0x8, 0x80, 0x0, 0x0, 0xf, 0x80,
79            0x0, 0x0, 0xa, 0x80, 0xbf, 0xff, 0xf2, 0x74, 0x0, 0x0, 0x5, 0x0, 0xbf, 0xff, 0xf8,
80            0xf4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x3, 0x9f, 0xe0,
81            0x0, 0x7, 0xdf, 0xf0, 0x0, 0xf, 0xff, 0xf8, 0x0, 0x1f, 0xff, 0xfc, 0x7, 0xbf, 0xff,
82            0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f,
83            0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff,
84            0xfc, 0x1f, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x7f,
85            0xff, 0xff, 0xfe, 0x3f, 0xff, 0xff, 0xfc, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0,
86            0x0, 0x7, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0,
87            0xf, 0x80, 0x0, 0x0, 0xf, 0x80, 0x0, 0x0, 0xf, 0x80, 0xbf, 0xff, 0xff, 0xf4, 0xbf,
88            0xff, 0xfd, 0xf4, 0xbf, 0xff, 0xf8, 0xf4,
89        ];
90
91        let packet = FPGetSrvrInfo::parse(test_data).expect("Failed to parse test data");
92
93        // Verify the parsed data is correct
94        assert_eq!(packet.machine_type.as_str(), "Macintosh");
95        assert_eq!(packet.afp_versions.len(), 3);
96        assert_eq!(packet.uams.len(), 3);
97        assert_eq!(packet.server_name.as_str(), "PowerBook G3");
98
99        // Re-serialize and verify it can be parsed back
100        let encoded = packet.to_bytes().expect("Failed to encode packet");
101        let reparsed = FPGetSrvrInfo::parse(&encoded).expect("Failed to reparse encoded data");
102
103        // Verify round-trip produces the same structure
104        assert_eq!(packet, reparsed);
105    }
106
107    #[test]
108    fn test_fplogin_no_auth() {
109        // Test FPLogin with NoUserAuthent
110        let login = FPLogin {
111            afp_version: AfpVersion::Version2,
112            auth: FPLoginAuth::NoUserAuthent,
113        };
114
115        let encoded = login.to_bytes().expect("Failed to encode");
116        let decoded = FPLogin::parse(&encoded).expect("Failed to parse");
117
118        assert_eq!(login, decoded);
119        assert_eq!(decoded.afp_version, AfpVersion::Version2);
120        assert_eq!(decoded.auth, FPLoginAuth::NoUserAuthent);
121    }
122
123    #[test]
124    fn test_fplogin_cleartext() {
125        // Test FPLogin with CleartxtPasswrd
126        let mut password = [0u8; 8];
127        password[..4].copy_from_slice(b"pass");
128
129        let login = FPLogin {
130            afp_version: AfpVersion::Version2_1,
131            auth: FPLoginAuth::CleartxtPasswrd {
132                username: MacString::from("testuser"),
133                password,
134            },
135        };
136
137        let encoded = login.to_bytes().expect("Failed to encode");
138        let decoded = FPLogin::parse(&encoded).expect("Failed to parse");
139
140        assert_eq!(login, decoded);
141        assert_eq!(decoded.afp_version, AfpVersion::Version2_1);
142
143        if let FPLoginAuth::CleartxtPasswrd {
144            username,
145            password: pwd,
146        } = decoded.auth
147        {
148            assert_eq!(username.as_str(), "testuser");
149            assert_eq!(&pwd[..4], b"pass");
150            assert_eq!(&pwd[4..], &[0, 0, 0, 0]); // Verify padding
151        } else {
152            panic!("Expected CleartxtPasswrd auth");
153        }
154    }
155
156    #[test]
157    fn test_fplogin_round_trip() {
158        // Test round-trip for both auth types
159        let test_cases = vec![
160            FPLogin {
161                afp_version: AfpVersion::Version1,
162                auth: FPLoginAuth::NoUserAuthent,
163            },
164            FPLogin {
165                afp_version: AfpVersion::Version2,
166                auth: FPLoginAuth::CleartxtPasswrd {
167                    username: MacString::from("admin"),
168                    password: *b"secret\0\0",
169                },
170            },
171        ];
172
173        for original in test_cases {
174            let encoded = original.to_bytes().expect("Failed to encode");
175            let decoded = FPLogin::parse(&encoded).expect("Failed to parse");
176            assert_eq!(original, decoded);
177        }
178    }
179
180    #[test]
181    fn test_fplogin_errors() {
182        // Test empty buffer
183        assert!(FPLogin::parse(&[]).is_err());
184
185        // Test buffer too small for AFP version
186        assert!(FPLogin::parse(&[5]).is_err());
187
188        // Test invalid AFP version
189        let invalid_version = vec![
190            10, b'B', b'a', b'd', b'V', b'e', b'r', b's', b'i', b'o', b'n', 16, b'N', b'o', b' ',
191            b'U', b's', b'e', b'r', b' ', b'A', b'u', b't', b'h', b'e', b'n', b't',
192        ];
193        assert!(FPLogin::parse(&invalid_version).is_err());
194
195        // Test invalid UAM
196        let invalid_uam = vec![
197            14, b'A', b'F', b'P', b'V', b'e', b'r', b's', b'i', b'o', b'n', b' ', b'2', b'.', b'0',
198            10, b'B', b'a', b'd', b'U', b'A', b'M', b'N', b'a', b'm', b'e',
199        ];
200        assert!(FPLogin::parse(&invalid_uam).is_err());
201
202        // Test CleartxtPasswrd with missing password data
203        let missing_password = vec![
204            14, b'A', b'F', b'P', b'V', b'e', b'r', b's', b'i', b'o', b'n', b' ', b'2', b'.', b'0',
205            16, b'C', b'l', b'e', b'a', b'r', b't', b'x', b't', b' ', b'P', b'a', b's', b's', b'w',
206            b'r', b'd', 4, b'u', b's', b'e', b'r',
207            // Missing 8 bytes for password
208        ];
209        assert!(FPLogin::parse(&missing_password).is_err());
210    }
211
212    #[test]
213    fn test_fplogin_long_username() {
214        // Test username longer than 255 characters gets truncated
215        let long_username = "a".repeat(300);
216        let mut password = [0u8; 8];
217        password[..4].copy_from_slice(b"test");
218
219        let login = FPLogin {
220            afp_version: AfpVersion::Version2,
221            auth: FPLoginAuth::CleartxtPasswrd {
222                username: MacString::from(long_username.clone()),
223                password,
224            },
225        };
226
227        let encoded = login.to_bytes().expect("Failed to encode");
228        let decoded = FPLogin::parse(&encoded).expect("Failed to parse");
229
230        // Username should be truncated to 255 characters
231        if let FPLoginAuth::CleartxtPasswrd { username, .. } = decoded.auth {
232            assert_eq!(username.len(), 255);
233            assert_eq!(username.as_str(), "a".repeat(255));
234        } else {
235            panic!("Expected CleartxtPasswrd auth");
236        }
237    }
238}