diff options
| author | diogo464 <[email protected]> | 2025-10-11 11:34:59 +0100 |
|---|---|---|
| committer | diogo464 <[email protected]> | 2025-10-11 11:34:59 +0100 |
| commit | 521218ce06fbb7bd518eb6a069406936079e3ec2 (patch) | |
| tree | 862e84ec23175119abb7652e197a4113e7fcc31b /src/main.rs | |
| parent | 89db26c86bf48a4c527778fc254765a38b7e9085 (diff) | |
initial working version
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 408 |
1 files changed, 263 insertions, 145 deletions
diff --git a/src/main.rs b/src/main.rs index 51bbd77..c179ac0 100644 --- a/src/main.rs +++ b/src/main.rs | |||
| @@ -1,3 +1,4 @@ | |||
| 1 | #![feature(gethostname)] | ||
| 1 | #![feature(cursor_split)] | 2 | #![feature(cursor_split)] |
| 2 | pub mod dhcp; | 3 | pub mod dhcp; |
| 3 | pub mod tftp; | 4 | pub mod tftp; |
| @@ -8,13 +9,273 @@ use std::{ | |||
| 8 | net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}, | 9 | net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket}, |
| 9 | }; | 10 | }; |
| 10 | 11 | ||
| 12 | use clap::Parser; | ||
| 11 | use ipnet::Ipv4Net; | 13 | use ipnet::Ipv4Net; |
| 12 | 14 | ||
| 13 | use crate::dhcp::{DhcpOption, DhcpPacket}; | 15 | use crate::dhcp::{DhcpOption, DhcpPacket}; |
| 14 | 16 | ||
| 15 | const LOCAL_IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 1, 103); | 17 | const BOOT_FILE_X64_BIOS: &'static str = "netboot.xyz.kpxe"; |
| 16 | const LOCAL_HOSTNAME: &'static str = "Diogos-Air"; | 18 | const BOOT_FILE_X64_EFI: &'static str = "netboot.xyz.efi"; |
| 19 | const BOOT_FILE_A64_EFI: &'static str = "netboot.xyz-arm64.efi"; | ||
| 20 | const MENU_FILE: &'static str = "menu.ipxe"; | ||
| 17 | 21 | ||
| 22 | #[derive(Debug, Parser)] | ||
| 23 | struct Cli { | ||
| 24 | #[clap(long)] | ||
| 25 | hostname: Option<String>, | ||
| 26 | |||
| 27 | #[clap(long, default_value = "0.0.0.0")] | ||
| 28 | listen_address: Ipv4Addr, | ||
| 29 | |||
| 30 | #[clap(long, default_value = "67")] | ||
| 31 | dhcp_port: u16, | ||
| 32 | |||
| 33 | #[clap(long, default_value = "4011")] | ||
| 34 | proxy_dhcp_port: u16, | ||
| 35 | |||
| 36 | #[clap(long, default_value = "69")] | ||
| 37 | tftp_port: u16, | ||
| 38 | } | ||
| 39 | |||
| 40 | struct Context { | ||
| 41 | local_hostname: String, | ||
| 42 | local_address: Ipv4Addr, | ||
| 43 | } | ||
| 44 | |||
| 45 | fn main() { | ||
| 46 | let cli = Cli::parse(); | ||
| 47 | |||
| 48 | let dhcp_sockaddr = SocketAddrV4::new(cli.listen_address, cli.dhcp_port); | ||
| 49 | let pdhcp_sockaddr = SocketAddrV4::new(cli.listen_address, cli.proxy_dhcp_port); | ||
| 50 | let tftp_sockaddr = SocketAddrV4::new(cli.listen_address, cli.tftp_port); | ||
| 51 | |||
| 52 | let hostname = match cli.hostname { | ||
| 53 | Some(hostname) => hostname, | ||
| 54 | None => { | ||
| 55 | let hostname = std::net::hostname().expect("unable to obtain local machine's hostname"); | ||
| 56 | hostname | ||
| 57 | .into_string() | ||
| 58 | .expect("unable to convert local machine's hostname to utf-8 string") | ||
| 59 | } | ||
| 60 | }; | ||
| 61 | let local_ip_address = if cli.listen_address == Ipv4Addr::UNSPECIFIED { | ||
| 62 | let interfaces = list_network_interfaces().expect("unable to list network interfaces"); | ||
| 63 | let mut chosen = None; | ||
| 64 | for interface in interfaces { | ||
| 65 | if interface.address.is_loopback() { | ||
| 66 | continue; | ||
| 67 | } | ||
| 68 | chosen = Some((interface.interface, interface.address)); | ||
| 69 | break; | ||
| 70 | } | ||
| 71 | |||
| 72 | let (name, addr) = | ||
| 73 | chosen.expect("unable to find network interface with non-loopback IPv4 address"); | ||
| 74 | println!("using local address {} from interface {}", addr, name); | ||
| 75 | addr | ||
| 76 | } else { | ||
| 77 | cli.listen_address | ||
| 78 | }; | ||
| 79 | |||
| 80 | println!("local hostname = {hostname}"); | ||
| 81 | println!("local address = {local_ip_address}"); | ||
| 82 | |||
| 83 | let context = Context { | ||
| 84 | local_hostname: hostname, | ||
| 85 | local_address: local_ip_address, | ||
| 86 | }; | ||
| 87 | |||
| 88 | let socket_dhcp = UdpSocket::bind(dhcp_sockaddr).unwrap(); | ||
| 89 | socket_dhcp.set_broadcast(true).unwrap(); | ||
| 90 | socket_dhcp.set_nonblocking(true).unwrap(); | ||
| 91 | |||
| 92 | let socket_pdhcp = UdpSocket::bind(pdhcp_sockaddr).unwrap(); | ||
| 93 | socket_pdhcp.set_broadcast(true).unwrap(); | ||
| 94 | socket_pdhcp.set_nonblocking(true).unwrap(); | ||
| 95 | |||
| 96 | let socket_tftp = UdpSocket::bind(tftp_sockaddr).unwrap(); | ||
| 97 | socket_tftp.set_broadcast(false).unwrap(); | ||
| 98 | socket_tftp.set_nonblocking(true).unwrap(); | ||
| 99 | |||
| 100 | let tftp_filesystem = tftp::StaticFileSystem::new(&[ | ||
| 101 | (BOOT_FILE_X64_BIOS, include_bytes!("../netboot.xyz.kpxe")), | ||
| 102 | (BOOT_FILE_X64_EFI, include_bytes!("../netboot.xyz.efi")), | ||
| 103 | ( | ||
| 104 | BOOT_FILE_A64_EFI, | ||
| 105 | include_bytes!("../netboot.xyz-arm64.efi"), | ||
| 106 | ), | ||
| 107 | (MENU_FILE, include_bytes!("../menu.ipxe")), | ||
| 108 | ]); | ||
| 109 | let mut tftp_server = tftp::Server::default(); | ||
| 110 | |||
| 111 | loop { | ||
| 112 | let mut buf = [0u8; 1500]; | ||
| 113 | |||
| 114 | if let Ok((n, addr)) = socket_dhcp.recv_from(&mut buf) { | ||
| 115 | println!("Received {} bytes from {} on port 67", n, addr); | ||
| 116 | handle_packet(&context, &buf[..n], &socket_dhcp); | ||
| 117 | } | ||
| 118 | |||
| 119 | if let Ok((n, addr)) = socket_pdhcp.recv_from(&mut buf) { | ||
| 120 | println!("Received {} bytes from {} on port 4011", n, addr); | ||
| 121 | handle_packet_4011(&context, &buf[..n], &socket_pdhcp, addr); | ||
| 122 | } | ||
| 123 | |||
| 124 | if let Ok((n, addr)) = socket_tftp.recv_from(&mut buf) { | ||
| 125 | println!("Received {} bytes from {} on port 4011", n, addr); | ||
| 126 | match tftp_server.process(&tftp_filesystem, addr, &buf) { | ||
| 127 | tftp::ServerCommand::Send(tftp_packet) => { | ||
| 128 | let mut output = Vec::default(); | ||
| 129 | tftp_packet.write(&mut output).unwrap(); | ||
| 130 | socket_tftp.send_to(&output, addr).unwrap(); | ||
| 131 | } | ||
| 132 | tftp::ServerCommand::Ignore => {} | ||
| 133 | } | ||
| 134 | } | ||
| 135 | |||
| 136 | std::thread::sleep(std::time::Duration::from_millis(1)); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | fn handle_packet(context: &Context, buf: &[u8], socket: &UdpSocket) { | ||
| 141 | let packet = match dhcp::parse_packet(buf) { | ||
| 142 | Ok(packet) => packet, | ||
| 143 | Err(err) => { | ||
| 144 | eprintln!("failed to parse DHCP packet: {err}"); | ||
| 145 | return; | ||
| 146 | } | ||
| 147 | }; | ||
| 148 | |||
| 149 | println!("Parsed DHCP packet: XID={:08x}", packet.xid); | ||
| 150 | |||
| 151 | // Check if it's a PXE client and extract client UUID | ||
| 152 | let mut pxe_class = None; | ||
| 153 | let mut client_uuid = None; | ||
| 154 | let mut is_ipxe = false; | ||
| 155 | |||
| 156 | for option in &packet.options { | ||
| 157 | match option { | ||
| 158 | DhcpOption::VendorClassIdentifier(vendor_class) => { | ||
| 159 | if let Ok(class) = dhcp::PxeClassIdentifier::try_from(vendor_class.as_slice()) { | ||
| 160 | println!("{class}"); | ||
| 161 | pxe_class = Some(class); | ||
| 162 | } | ||
| 163 | } | ||
| 164 | DhcpOption::UserClassInformation(user_class) => { | ||
| 165 | if user_class == dhcp::USER_CLASS_IPXE { | ||
| 166 | is_ipxe = true; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | DhcpOption::ClientMachineIdentifier(uuid) => { | ||
| 170 | client_uuid = Some(uuid.clone()); | ||
| 171 | } | ||
| 172 | _ => {} | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | let pxe_client_class = match pxe_class { | ||
| 177 | Some(dhcp::PxeClassIdentifier::Client(class)) => class, | ||
| 178 | _ => { | ||
| 179 | println!("Not a PXE client, ignoring"); | ||
| 180 | return; | ||
| 181 | } | ||
| 182 | }; | ||
| 183 | |||
| 184 | println!("Responding to PXE client with DHCPOFFER"); | ||
| 185 | let mut response_buf = Vec::default(); | ||
| 186 | let response = DhcpPacket::new_boot( | ||
| 187 | packet.xid, | ||
| 188 | packet.chaddr, | ||
| 189 | client_uuid, | ||
| 190 | context.local_address, | ||
| 191 | context.local_hostname.clone(), | ||
| 192 | match is_ipxe { | ||
| 193 | true => MENU_FILE.to_string(), | ||
| 194 | false => match pxe_client_class.architecture { | ||
| 195 | dhcp::SystemArchitecture::IntelX86pc => BOOT_FILE_X64_BIOS.to_string(), | ||
| 196 | dhcp::SystemArchitecture::EfiARM64 => BOOT_FILE_A64_EFI.to_string(), | ||
| 197 | dhcp::SystemArchitecture::EfiX86_64 | dhcp::SystemArchitecture::EfiBC => { | ||
| 198 | BOOT_FILE_X64_EFI.to_string() | ||
| 199 | } | ||
| 200 | _ => { | ||
| 201 | eprintln!( | ||
| 202 | "unsupported architecture {:?}", | ||
| 203 | pxe_client_class.architecture | ||
| 204 | ); | ||
| 205 | return; | ||
| 206 | } | ||
| 207 | }, | ||
| 208 | }, | ||
| 209 | ); | ||
| 210 | response.write(&mut response_buf).unwrap(); | ||
| 211 | socket | ||
| 212 | .send_to(&response_buf, SocketAddrV4::new(Ipv4Addr::BROADCAST, 68)) | ||
| 213 | .unwrap(); | ||
| 214 | } | ||
| 215 | |||
| 216 | fn handle_packet_4011(context: &Context, buf: &[u8], socket: &UdpSocket, sender_addr: SocketAddr) { | ||
| 217 | let packet = match dhcp::parse_packet(buf) { | ||
| 218 | Ok(packet) => packet, | ||
| 219 | Err(err) => { | ||
| 220 | println!("Failed to parse packet on 4011: {}", err); | ||
| 221 | return; | ||
| 222 | } | ||
| 223 | }; | ||
| 224 | |||
| 225 | println!("Parsed DHCP packet on 4011: XID={:08x}", packet.xid); | ||
| 226 | |||
| 227 | // Extract client UUID | ||
| 228 | let mut client_uuid = None; | ||
| 229 | for option in &packet.options { | ||
| 230 | if let DhcpOption::ClientMachineIdentifier(uuid) = option { | ||
| 231 | client_uuid = Some(uuid.clone()); | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | let mut client_class = None; | ||
| 237 | for option in &packet.options { | ||
| 238 | if let DhcpOption::VendorClassIdentifier(vendor_class) = option { | ||
| 239 | if let Ok(dhcp::PxeClassIdentifier::Client(class)) = | ||
| 240 | dhcp::PxeClassIdentifier::try_from(vendor_class.as_slice()) | ||
| 241 | { | ||
| 242 | println!("{class}"); | ||
| 243 | client_class = Some(class); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
| 247 | let client_class = match client_class { | ||
| 248 | Some(class) => class, | ||
| 249 | None => return, | ||
| 250 | }; | ||
| 251 | |||
| 252 | let file = match client_class.architecture { | ||
| 253 | dhcp::SystemArchitecture::IntelX86pc => BOOT_FILE_X64_BIOS.to_string(), | ||
| 254 | dhcp::SystemArchitecture::EfiARM64 => BOOT_FILE_A64_EFI.to_string(), | ||
| 255 | dhcp::SystemArchitecture::EfiX86_64 | dhcp::SystemArchitecture::EfiBC => { | ||
| 256 | BOOT_FILE_X64_EFI.to_string() | ||
| 257 | } | ||
| 258 | _ => { | ||
| 259 | eprintln!("unsupported architecture {:?}", client_class.architecture); | ||
| 260 | return; | ||
| 261 | } | ||
| 262 | }; | ||
| 263 | |||
| 264 | println!("Responding with DHCPACK"); | ||
| 265 | let mut response_buf = Vec::default(); | ||
| 266 | let response = DhcpPacket::new_boot_ack( | ||
| 267 | packet.xid, | ||
| 268 | packet.chaddr, | ||
| 269 | client_uuid, | ||
| 270 | context.local_address, | ||
| 271 | context.local_hostname.clone(), | ||
| 272 | file, | ||
| 273 | ); | ||
| 274 | response.write(&mut response_buf).unwrap(); | ||
| 275 | socket.send_to(&response_buf, sender_addr).unwrap(); | ||
| 276 | } | ||
| 277 | |||
| 278 | #[allow(unused)] | ||
| 18 | #[derive(Debug, Clone)] | 279 | #[derive(Debug, Clone)] |
| 19 | struct InterfaceAddr { | 280 | struct InterfaceAddr { |
| 20 | interface: String, | 281 | interface: String, |
| @@ -68,146 +329,3 @@ fn list_network_interfaces() -> Result<Vec<InterfaceAddr>> { | |||
| 68 | Ok(interfaces) | 329 | Ok(interfaces) |
| 69 | } | 330 | } |
| 70 | } | 331 | } |
| 71 | |||
| 72 | fn main() { | ||
| 73 | let socket67 = UdpSocket::bind("0.0.0.0:67").unwrap(); | ||
| 74 | socket67.set_broadcast(true).unwrap(); | ||
| 75 | socket67.set_nonblocking(true).unwrap(); | ||
| 76 | |||
| 77 | let socket4011 = UdpSocket::bind("0.0.0.0:4011").unwrap(); | ||
| 78 | socket4011.set_broadcast(true).unwrap(); | ||
| 79 | socket4011.set_nonblocking(true).unwrap(); | ||
| 80 | |||
| 81 | std::thread::spawn(|| { | ||
| 82 | tftp::serve("tftp").unwrap(); | ||
| 83 | }); | ||
| 84 | |||
| 85 | loop { | ||
| 86 | let mut buf = [0u8; 1500]; | ||
| 87 | |||
| 88 | // Try port 67 first | ||
| 89 | if let Ok((n, addr)) = socket67.recv_from(&mut buf) { | ||
| 90 | println!("Received {} bytes from {} on port 67", n, addr); | ||
| 91 | handle_packet(&buf[..n], &socket67); | ||
| 92 | } else if let Ok((n, addr)) = socket4011.recv_from(&mut buf) { | ||
| 93 | println!("Received {} bytes from {} on port 4011", n, addr); | ||
| 94 | handle_packet_4011(&buf[..n], &socket4011, addr); | ||
| 95 | } else { | ||
| 96 | std::thread::sleep(std::time::Duration::from_millis(10)); | ||
| 97 | } | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | fn handle_packet(buf: &[u8], socket: &UdpSocket) { | ||
| 102 | match dhcp::parse_packet(buf) { | ||
| 103 | Ok(packet) => { | ||
| 104 | println!("Parsed DHCP packet: XID={:08x}", packet.xid); | ||
| 105 | |||
| 106 | // Check if it's a PXE client and extract client UUID | ||
| 107 | let mut is_pxe = false; | ||
| 108 | let mut client_uuid = None; | ||
| 109 | let mut is_ipxe = false; | ||
| 110 | |||
| 111 | for option in &packet.options { | ||
| 112 | match option { | ||
| 113 | DhcpOption::VendorClassIdentifier(vendor_class) => { | ||
| 114 | println!("Vendor class: {}", vendor_class); | ||
| 115 | if vendor_class.contains("PXEClient") { | ||
| 116 | is_pxe = true; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | DhcpOption::UserClassInformation(user_class) => { | ||
| 120 | println!("User class: {}", user_class); | ||
| 121 | is_ipxe = true; | ||
| 122 | } | ||
| 123 | DhcpOption::Unknown { code: 97, data } => { | ||
| 124 | println!("Found client machine identifier"); | ||
| 125 | client_uuid = Some(data.clone()); | ||
| 126 | } | ||
| 127 | _ => {} | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | if is_pxe { | ||
| 132 | println!("Responding to PXE client with DHCPOFFER"); | ||
| 133 | let mut response_buf = Vec::default(); | ||
| 134 | let response = DhcpPacket::new_boot( | ||
| 135 | packet.xid, | ||
| 136 | packet.chaddr, | ||
| 137 | client_uuid.unwrap(), | ||
| 138 | LOCAL_IPV4, | ||
| 139 | LOCAL_HOSTNAME.to_string(), | ||
| 140 | match is_ipxe { | ||
| 141 | true => "test.ipxe".to_string(), | ||
| 142 | false => "ipxe.efi".to_string(), | ||
| 143 | }, | ||
| 144 | ); | ||
| 145 | response.write(&mut response_buf).unwrap(); | ||
| 146 | socket | ||
| 147 | .send_to(&response_buf, SocketAddrV4::new(Ipv4Addr::BROADCAST, 68)) | ||
| 148 | .unwrap(); | ||
| 149 | } else { | ||
| 150 | println!("Not a PXE client, ignoring"); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | Err(e) => { | ||
| 154 | println!("Failed to parse packet: {}", e); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | } | ||
| 158 | |||
| 159 | fn handle_packet_4011(buf: &[u8], socket: &UdpSocket, sender_addr: SocketAddr) { | ||
| 160 | match dhcp::parse_packet(buf) { | ||
| 161 | Ok(packet) => { | ||
| 162 | println!("Parsed DHCP packet on 4011: XID={:08x}", packet.xid); | ||
| 163 | |||
| 164 | // Extract client UUID | ||
| 165 | let mut client_uuid = None; | ||
| 166 | for option in &packet.options { | ||
| 167 | if let DhcpOption::Unknown { code: 97, data } = option { | ||
| 168 | client_uuid = Some(data.clone()); | ||
| 169 | break; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | println!("Responding with DHCPACK"); | ||
| 174 | let mut response_buf = Vec::default(); | ||
| 175 | let response = DhcpPacket::new_boot_ack( | ||
| 176 | packet.xid, | ||
| 177 | packet.chaddr, | ||
| 178 | client_uuid.unwrap(), | ||
| 179 | LOCAL_IPV4, | ||
| 180 | LOCAL_HOSTNAME.to_string(), | ||
| 181 | "ipxe.efi".to_string(), | ||
| 182 | ); | ||
| 183 | response.write(&mut response_buf).unwrap(); | ||
| 184 | socket.send_to(&response_buf, sender_addr).unwrap(); | ||
| 185 | } | ||
| 186 | Err(e) => { | ||
| 187 | println!("Failed to parse packet on 4011: {}", e); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | } | ||
| 191 | |||
| 192 | const DHCP_PACKET_PAYLOAD: &'static [u8] = &[ | ||
| 193 | 0x1, 0x1, 0x6, 0x0, 0xf1, 0x25, 0x7c, 0x21, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 194 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x67, 0x3f, 0xda, 0x70, 0x0, 0x0, | ||
| 195 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 196 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 197 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 198 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 199 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 200 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 201 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 202 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 203 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 204 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, | ||
| 205 | 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x63, 0x82, 0x53, 0x63, 0x35, 0x1, 0x1, 0x39, | ||
| 206 | 0x2, 0x5, 0xc0, 0x37, 0x23, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xc, 0xd, 0xf, 0x11, 0x12, 0x16, | ||
| 207 | 0x17, 0x1c, 0x28, 0x29, 0x2a, 0x2b, 0x32, 0x33, 0x36, 0x3a, 0x3b, 0x3c, 0x42, 0x43, 0x61, 0x80, | ||
| 208 | 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x61, 0x11, 0x0, 0xcc, 0xfc, 0x32, 0x1b, 0xce, 0x2a, | ||
| 209 | 0xb2, 0x11, 0xa8, 0x5c, 0xb1, 0xac, 0x38, 0x38, 0x10, 0xf, 0x5e, 0x3, 0x1, 0x3, 0x10, 0x5d, | ||
| 210 | 0x2, 0x0, 0x7, 0x3c, 0x20, 0x50, 0x58, 0x45, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3a, 0x41, | ||
| 211 | 0x72, 0x63, 0x68, 0x3a, 0x30, 0x30, 0x30, 0x30, 0x37, 0x3a, 0x55, 0x4e, 0x44, 0x49, 0x3a, 0x30, | ||
| 212 | 0x30, 0x33, 0x30, 0x31, 0x36, 0xff, | ||
| 213 | ]; | ||
