aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordiogo464 <[email protected]>2025-09-25 17:32:25 +0100
committerdiogo464 <[email protected]>2025-09-25 17:32:25 +0100
commit5bf4135847954bec6b8c90ef2996439783ae8056 (patch)
tree4f0bef15b7ee3d1c03333962a73b8f8a75e9a335 /src
booting debian
Diffstat (limited to 'src')
-rw-r--r--src/main.rs756
1 files changed, 756 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..87ea283
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,756 @@
1use std::io::{BufRead, Cursor, Read, Result, Write};
2use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
3
4const FLAG_BROADCAST: u16 = 1 << 15;
5
6const OPTION_CODE_PAD: u8 = 0;
7const OPTION_CODE_END: u8 = 255;
8const OPTION_CODE_VENDOR_CLASS_IDENTIFIER: u8 = 60;
9const OPTION_CODE_USER_CLASS_INFORMATION: u8 = 77;
10
11const MAGIC_COOKIE: [u8; 4] = [0x63, 0x82, 0x53, 0x63];
12
13//const BOOT_FILE_NAME: &[u8] = b"pxelinux.0";
14//const BOOT_FILE_NAME: &[u8] = b"debian-installer/amd64/bootnetx64.efi";
15const BOOT_FILE_NAME: &[u8] = b"ipxe.efi";
16const BOOT_FILE_NAME_IPXE: &[u8] = b"test.ipxe";
17
18const LOCAL_IPV4: Ipv4Addr = Ipv4Addr::new(192, 168, 2, 184);
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21enum BootOp {
22 Request,
23 Reply,
24}
25
26impl BootOp {
27 pub const OP_REQUEST: u8 = 1;
28 pub const OP_REPLY: u8 = 2;
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32enum HardwareType {
33 Ethernet,
34}
35
36impl HardwareType {
37 pub const TYPE_ETHER: u8 = 1;
38 pub const LEN_ETHER: u8 = 6;
39}
40
41#[derive(Debug, Clone)]
42enum DhcpOption {
43 Pad,
44 End,
45 VendorClassIdentifier(String),
46 UserClassInformation(String),
47 Unknown { code: u8, data: Vec<u8> },
48}
49
50#[derive(Debug)]
51struct DhcpPacket {
52 op: BootOp,
53 htype: HardwareType,
54 hlen: u8,
55 hops: u8, // should be zero
56 xid: u32,
57 secs: u16,
58 flags: u16,
59 ciaddr: Ipv4Addr,
60 yiaddr: Ipv4Addr,
61 siaddr: Ipv4Addr,
62 giaddr: Ipv4Addr,
63 chaddr: [u8; 16],
64 sname: [u8; 64],
65 file: [u8; 128],
66 options: Vec<DhcpOption>,
67}
68
69fn read_u8(cursor: &mut Cursor<&[u8]>) -> Result<u8> {
70 let mut buf = [0u8; 1];
71 cursor.read_exact(&mut buf)?;
72 Ok(buf[0])
73}
74
75fn read_u16(cursor: &mut Cursor<&[u8]>) -> Result<u16> {
76 let mut buf = [0u8; 2];
77 cursor.read_exact(&mut buf)?;
78 Ok(u16::from_be_bytes(buf))
79}
80
81fn read_u32(cursor: &mut Cursor<&[u8]>) -> Result<u32> {
82 let mut buf = [0u8; 4];
83 cursor.read_exact(&mut buf)?;
84 Ok(u32::from_be_bytes(buf))
85}
86
87fn read_arr<const N: usize>(cursor: &mut Cursor<&[u8]>) -> Result<[u8; N]> {
88 let mut buf = [0u8; N];
89 cursor.read_exact(&mut buf)?;
90 Ok(buf)
91}
92
93fn read_null_terminated_vec(cursor: &mut Cursor<&[u8]>) -> Result<Vec<u8>> {
94 let mut buf = Vec::default();
95 cursor.read_until(0, &mut buf)?;
96 buf.pop();
97 Ok(buf)
98}
99
100fn read_null_terminated_string(cursor: &mut Cursor<&[u8]>) -> Result<String> {
101 let buf = read_null_terminated_vec(cursor)?;
102 Ok(String::from_utf8(buf).unwrap())
103}
104
105fn read_len8_prefixed_vec(cursor: &mut Cursor<&[u8]>) -> Result<Vec<u8>> {
106 let len = read_u8(cursor)?;
107 let mut buf = vec![0u8; len as usize];
108 cursor.read_exact(&mut buf)?;
109 Ok(buf)
110}
111
112fn read_len8_prefixed_string(cursor: &mut Cursor<&[u8]>) -> Result<String> {
113 let buf = read_len8_prefixed_vec(cursor)?;
114 Ok(String::from_utf8(buf).unwrap())
115}
116
117fn read_ipv4(cursor: &mut Cursor<&[u8]>) -> Result<Ipv4Addr> {
118 Ok(Ipv4Addr::from_octets(read_arr(cursor)?))
119}
120
121fn read_op(cursor: &mut Cursor<&[u8]>) -> Result<BootOp> {
122 let v = read_u8(cursor)?;
123 match v {
124 BootOp::OP_REQUEST => Ok(BootOp::Request),
125 BootOp::OP_REPLY => Ok(BootOp::Reply),
126 _ => Err(std::io::Error::new(
127 std::io::ErrorKind::InvalidData,
128 "invalid boot op",
129 )),
130 }
131}
132
133fn read_htype(cursor: &mut Cursor<&[u8]>) -> Result<HardwareType> {
134 match read_u8(cursor)? {
135 HardwareType::TYPE_ETHER => Ok(HardwareType::Ethernet),
136 _ => Err(std::io::Error::new(
137 std::io::ErrorKind::InvalidData,
138 "invalid hardware type",
139 )),
140 }
141}
142
143fn read_option(cursor: &mut Cursor<&[u8]>) -> Result<DhcpOption> {
144 let code = read_u8(cursor)?;
145 Ok(match code {
146 OPTION_CODE_PAD => DhcpOption::Pad,
147 OPTION_CODE_END => DhcpOption::End,
148 OPTION_CODE_VENDOR_CLASS_IDENTIFIER => {
149 DhcpOption::VendorClassIdentifier(read_len8_prefixed_string(cursor)?)
150 }
151 OPTION_CODE_USER_CLASS_INFORMATION => {
152 DhcpOption::UserClassInformation(read_len8_prefixed_string(cursor)?)
153 }
154 _ => {
155 let len = read_u8(cursor)?;
156 let mut data = vec![0u8; usize::from(len)];
157 cursor.read_exact(&mut data)?;
158 DhcpOption::Unknown { code, data }
159 }
160 })
161}
162
163fn parse_packet(buf: &[u8]) -> Result<DhcpPacket> {
164 let mut cursor = Cursor::new(buf);
165 let mut packet = DhcpPacket {
166 op: read_op(&mut cursor)?,
167 htype: read_htype(&mut cursor)?,
168 hlen: read_u8(&mut cursor)?,
169 hops: read_u8(&mut cursor)?,
170 xid: read_u32(&mut cursor)?,
171 secs: read_u16(&mut cursor)?,
172 flags: read_u16(&mut cursor)?,
173 ciaddr: read_ipv4(&mut cursor)?,
174 yiaddr: read_ipv4(&mut cursor)?,
175 siaddr: read_ipv4(&mut cursor)?,
176 giaddr: read_ipv4(&mut cursor)?,
177 chaddr: read_arr(&mut cursor)?,
178 sname: read_arr(&mut cursor)?,
179 file: read_arr(&mut cursor)?,
180 options: Default::default(),
181 };
182
183 let magic = read_arr::<4>(&mut cursor)?;
184 assert_eq!(magic, MAGIC_COOKIE);
185
186 while cursor.position() < buf.len() as u64 {
187 let option = read_option(&mut cursor)?;
188 packet.options.push(option);
189 }
190
191 Ok(packet)
192}
193
194fn write_buf(writer: &mut Vec<u8>, buf: &[u8]) -> Result<()> {
195 writer.write_all(buf)
196}
197
198fn write_u8(writer: &mut Vec<u8>, v: u8) -> Result<()> {
199 write_buf(writer, &[v])
200}
201
202fn write_u16(writer: &mut Vec<u8>, v: u16) -> Result<()> {
203 let buf = u16::to_be_bytes(v);
204 write_buf(writer, &buf)
205}
206
207fn write_u32(writer: &mut Vec<u8>, v: u32) -> Result<()> {
208 let buf = u32::to_be_bytes(v);
209 write_buf(writer, &buf)
210}
211
212fn write_ipv4(writer: &mut Vec<u8>, v: Ipv4Addr) -> Result<()> {
213 write_buf(writer, &v.octets())
214}
215
216fn write_boot_packet(
217 xid: u32,
218 chaddr: [u8; 16],
219 client_uuid: Option<Vec<u8>>,
220 ipxe: bool,
221) -> Result<Vec<u8>> {
222 let mut writer = Vec::default();
223 write_u8(&mut writer, BootOp::OP_REPLY)?;
224 write_u8(&mut writer, HardwareType::TYPE_ETHER)?;
225 write_u8(&mut writer, 6)?;
226 write_u8(&mut writer, 0)?;
227 write_u32(&mut writer, xid)?;
228 write_u16(&mut writer, 0)?;
229 write_u16(&mut writer, FLAG_BROADCAST)?;
230 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // ciaddr
231 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // yiaddr
232 write_ipv4(&mut writer, LOCAL_IPV4)?; // siaddr (TFTP server)
233 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // giaddr
234 write_buf(&mut writer, &chaddr)?;
235 write_buf(&mut writer, &[0u8; 64])?;
236 write_buf(&mut writer, &[0u8; 128])?;
237 write_buf(&mut writer, &MAGIC_COOKIE)?;
238
239 // Option 53: DHCP Message Type (DHCPOFFER)
240 write_u8(&mut writer, 53)?;
241 write_u8(&mut writer, 1)?;
242 write_u8(&mut writer, 2)?; // DHCPOFFER
243
244 // Option 54: DHCP Server Identifier
245 write_u8(&mut writer, 54)?;
246 write_u8(&mut writer, 4)?;
247 write_ipv4(&mut writer, LOCAL_IPV4)?; // Your server IP
248
249 // Option 60: Vendor Class Identifier
250 const PXE_CLIENT: &[u8] = b"PXEClient";
251 write_u8(&mut writer, 60)?;
252 write_u8(&mut writer, PXE_CLIENT.len() as u8)?;
253 write_buf(&mut writer, PXE_CLIENT)?;
254
255 // Option 97: Client Machine Identifier (UUID from client)
256 if let Some(uuid) = client_uuid {
257 write_u8(&mut writer, 97)?;
258 write_u8(&mut writer, uuid.len() as u8)?;
259 write_buf(&mut writer, &uuid)?;
260 }
261
262 // TFTP server name
263 const SERVER_NAME: &[u8] = b"diogos-air";
264 write_u8(&mut writer, 66)?;
265 write_u8(&mut writer, SERVER_NAME.len() as u8)?;
266 write_buf(&mut writer, SERVER_NAME)?;
267
268 write_u8(&mut writer, 67)?;
269 if !ipxe {
270 write_u8(&mut writer, BOOT_FILE_NAME.len() as u8)?;
271 write_buf(&mut writer, BOOT_FILE_NAME)?;
272 } else {
273 write_u8(&mut writer, BOOT_FILE_NAME_IPXE.len() as u8)?;
274 write_buf(&mut writer, BOOT_FILE_NAME_IPXE)?;
275 }
276
277 // Option 255: End
278 write_u8(&mut writer, 255)?;
279
280 Ok(writer)
281}
282
283fn write_boot_ack(xid: u32, chaddr: [u8; 16], client_uuid: Option<Vec<u8>>) -> Result<Vec<u8>> {
284 let mut writer = Vec::default();
285 write_u8(&mut writer, BootOp::OP_REPLY)?;
286 write_u8(&mut writer, HardwareType::TYPE_ETHER)?;
287 write_u8(&mut writer, 6)?;
288 write_u8(&mut writer, 0)?;
289 write_u32(&mut writer, xid)?;
290 write_u16(&mut writer, 0)?;
291 write_u16(&mut writer, 0)?;
292 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // ciaddr
293 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // yiaddr
294 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // siaddr (TFTP server)
295 write_ipv4(&mut writer, Ipv4Addr::UNSPECIFIED)?; // giaddr
296 write_buf(&mut writer, &chaddr)?;
297 write_buf(&mut writer, &[0u8; 64])?;
298 write_buf(&mut writer, &[0u8; 128])?;
299 write_buf(&mut writer, &MAGIC_COOKIE)?;
300
301 // Option 53: DHCP Message Type (DHCPOFFER)
302 write_u8(&mut writer, 53)?;
303 write_u8(&mut writer, 1)?;
304 write_u8(&mut writer, 5)?; // DHCPACK
305
306 // Option 54: DHCP Server Identifier
307 write_u8(&mut writer, 54)?;
308 write_u8(&mut writer, 4)?;
309 write_ipv4(&mut writer, LOCAL_IPV4)?; // Your server IP
310
311 // Option 60: Vendor Class Identifier
312 const PXE_CLIENT: &[u8] = b"PXEClient";
313 write_u8(&mut writer, 60)?;
314 write_u8(&mut writer, PXE_CLIENT.len() as u8)?;
315 write_buf(&mut writer, PXE_CLIENT)?;
316
317 // Option 97: Client Machine Identifier (UUID from client)
318 if let Some(uuid) = client_uuid {
319 write_u8(&mut writer, 97)?;
320 write_u8(&mut writer, uuid.len() as u8)?;
321 write_buf(&mut writer, &uuid)?;
322 }
323
324 // TFTP server name
325 const SERVER_NAME: &[u8] = b"diogos-air";
326 write_u8(&mut writer, 66)?;
327 write_u8(&mut writer, SERVER_NAME.len() as u8)?;
328 write_buf(&mut writer, SERVER_NAME)?;
329
330 // TFTP file name
331 write_u8(&mut writer, 67)?;
332 write_u8(&mut writer, BOOT_FILE_NAME.len() as u8)?;
333 write_buf(&mut writer, BOOT_FILE_NAME)?;
334
335 write_u8(&mut writer, 71)?;
336 write_u8(&mut writer, 4)?;
337 write_buf(&mut writer, &[0, 0, 0, 0])?;
338
339 // Option 255: End
340 write_u8(&mut writer, 255)?;
341
342 Ok(writer)
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
346struct InvalidTftpOp(u16);
347
348impl std::fmt::Display for InvalidTftpOp {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 write!(f, "invalid tftp opcode '{}'", self.0)
351 }
352}
353
354impl std::error::Error for InvalidTftpOp {}
355
356#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
357enum TftpOp {
358 ReadRequest,
359 WriteRequest,
360 Data,
361 Ack,
362 Error,
363 Oack,
364}
365
366impl Into<u16> for TftpOp {
367 fn into(self) -> u16 {
368 match self {
369 TftpOp::ReadRequest => 1,
370 TftpOp::WriteRequest => 2,
371 TftpOp::Data => 3,
372 TftpOp::Ack => 4,
373 TftpOp::Error => 5,
374 TftpOp::Oack => 6,
375 }
376 }
377}
378
379impl TryFrom<u16> for TftpOp {
380 type Error = InvalidTftpOp;
381
382 fn try_from(value: u16) -> std::result::Result<Self, InvalidTftpOp> {
383 match value {
384 1 => Ok(Self::ReadRequest),
385 2 => Ok(Self::WriteRequest),
386 3 => Ok(Self::Data),
387 4 => Ok(Self::Ack),
388 5 => Ok(Self::Error),
389 6 => Ok(Self::Oack),
390 unknown => Err(InvalidTftpOp(unknown)),
391 }
392 }
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
396enum TftpMode {
397 NetAscii,
398 Octet,
399 Mail,
400}
401
402#[derive(Debug)]
403struct TftpRequestPacket {
404 filename: String,
405 mode: String,
406 tsize: Option<u64>,
407 blksize: Option<u64>,
408}
409
410#[derive(Debug)]
411struct TftpDataPacket {
412 block: u16,
413 data: Vec<u8>,
414}
415
416#[derive(Debug)]
417struct TftpAckPacket {
418 block: u16,
419}
420
421#[derive(Debug)]
422struct TftpErrorPacket {
423 code: u16,
424 message: String,
425}
426
427fn tftp_request_packet_parse(cursor: &mut Cursor<&[u8]>) -> Result<TftpRequestPacket> {
428 let filename = read_null_terminated_string(cursor)?;
429 let mode = read_null_terminated_string(cursor)?;
430 let mut tsize = None;
431 let mut blksize = None;
432 while let Ok(opt_name) = read_null_terminated_string(cursor) {
433 if opt_name.is_empty() {
434 break;
435 }
436 let opt_data = read_null_terminated_string(cursor)?;
437 match opt_name.as_str() {
438 "tsize" => tsize = Some(opt_data.parse::<u64>().unwrap()),
439 "blksize" => blksize = Some(opt_data.parse::<u64>().unwrap()),
440 _ => eprintln!("unknown tftp request option '{opt_name}'"),
441 }
442 }
443
444 Ok(TftpRequestPacket {
445 filename,
446 mode,
447 tsize,
448 blksize,
449 })
450}
451
452fn tftp_data_packet_write(writer: &mut Vec<u8>, block: u16, data: Vec<u8>) -> Result<()> {
453 write_u16(writer, TftpOp::Data.into())?;
454 write_u16(writer, block)?;
455 write_buf(writer, &data)?;
456 Ok(())
457}
458
459fn tftp_oack_packet_write(
460 writer: &mut Vec<u8>,
461 tsize: Option<u64>,
462 blksize: Option<u64>,
463) -> Result<()> {
464 write_u16(writer, TftpOp::Oack.into())?;
465
466 // Only include options that were requested by the client
467 if let Some(blksize_val) = blksize {
468 write_buf(writer, b"blksize")?;
469 write_u8(writer, 0)?; // null terminator
470 let blksize_str = blksize_val.to_string();
471 write_buf(writer, blksize_str.as_bytes())?;
472 write_u8(writer, 0)?; // null terminator
473 }
474
475 if let Some(tsize_val) = tsize {
476 write_buf(writer, b"tsize")?;
477 write_u8(writer, 0)?; // null terminator
478 let tsize_str = tsize_val.to_string();
479 write_buf(writer, tsize_str.as_bytes())?;
480 write_u8(writer, 0)?; // null terminator
481 }
482
483 Ok(())
484}
485
486#[derive(Debug)]
487enum TftpPacket {
488 Request(TftpRequestPacket),
489 Data(TftpDataPacket),
490 Ack(TftpAckPacket),
491 Error(TftpErrorPacket),
492}
493
494fn tftp_packet_parse(cursor: &mut Cursor<&[u8]>) -> Result<TftpPacket> {
495 let op = TftpOp::try_from(read_u16(cursor)?).unwrap();
496 match op {
497 TftpOp::ReadRequest | TftpOp::WriteRequest => {
498 let filename = read_null_terminated_string(cursor)?;
499 let mode = read_null_terminated_string(cursor)?;
500 let mut tsize = None;
501 let mut blksize = None;
502
503 while let Ok(opt_name) = read_null_terminated_string(cursor) {
504 if opt_name.is_empty() {
505 break;
506 }
507 let opt_data = read_null_terminated_string(cursor)?;
508 match opt_name.as_str() {
509 "tsize" => tsize = Some(opt_data.parse::<u64>().unwrap()),
510 "blksize" => blksize = Some(opt_data.parse::<u64>().unwrap()),
511 _ => eprintln!("unknown tftp request option '{opt_name}'"),
512 }
513 }
514
515 Ok(TftpPacket::Request(TftpRequestPacket {
516 filename,
517 mode,
518 tsize,
519 blksize,
520 }))
521 }
522 TftpOp::Data => {
523 let block = read_u16(cursor)?;
524 let mut data = Vec::new();
525 cursor.read_to_end(&mut data)?;
526 Ok(TftpPacket::Data(TftpDataPacket { block, data }))
527 }
528 TftpOp::Ack => {
529 let block = read_u16(cursor)?;
530 Ok(TftpPacket::Ack(TftpAckPacket { block }))
531 }
532 TftpOp::Error => {
533 let code = read_u16(cursor)?;
534 let message = read_null_terminated_string(cursor)?;
535 Ok(TftpPacket::Error(TftpErrorPacket { code, message }))
536 }
537 TftpOp::Oack => {
538 // OACK parsing not implemented for now
539 Err(std::io::Error::new(
540 std::io::ErrorKind::Unsupported,
541 "OACK parsing not implemented",
542 ))
543 }
544 }
545}
546
547fn main() {
548 let socket67 = UdpSocket::bind("0.0.0.0:67").unwrap();
549 socket67.set_broadcast(true).unwrap();
550 socket67.set_nonblocking(true).unwrap();
551
552 let socket69 = UdpSocket::bind("0.0.0.0:69").unwrap();
553 socket69.set_broadcast(false).unwrap();
554 socket69.set_nonblocking(true).unwrap();
555
556 let socket4011 = UdpSocket::bind("0.0.0.0:4011").unwrap();
557 socket4011.set_broadcast(true).unwrap();
558 socket4011.set_nonblocking(true).unwrap();
559
560 let mut last_blksize = 512u64;
561 let mut current_file = String::new();
562
563 loop {
564 let mut buf = [0u8; 1500];
565
566 // Try port 67 first
567 if let Ok((n, addr)) = socket67.recv_from(&mut buf) {
568 println!("Received {} bytes from {} on port 67", n, addr);
569 handle_packet(&buf[..n], &socket67);
570 } else if let Ok((n, addr)) = socket4011.recv_from(&mut buf) {
571 println!("Received {} bytes from {} on port 4011", n, addr);
572 handle_packet_4011(&buf[..n], &socket4011, addr);
573 } else if let Ok((n, addr)) = socket69.recv_from(&mut buf) {
574 let mut cursor = Cursor::new(&buf[..n]);
575
576 let packet = tftp_packet_parse(&mut cursor).unwrap();
577 println!("Received TFTP request from {addr}: {packet:#?}");
578
579 let mut response = Vec::default();
580 match packet {
581 TftpPacket::Request(tftp_request_packet) => {
582 println!(
583 "Request options: tsize={:?}, blksize={:?}",
584 tftp_request_packet.tsize, tftp_request_packet.blksize
585 );
586
587 let filepath = format!("tftp/{}", tftp_request_packet.filename);
588 current_file = filepath.clone();
589 let meta = std::fs::metadata(&filepath).unwrap();
590 let actual_file_size = meta.len();
591
592 // Only send OACK if client sent options
593 if tftp_request_packet.tsize.is_some() || tftp_request_packet.blksize.is_some()
594 {
595 if let Some(blksize) = tftp_request_packet.blksize {
596 last_blksize = blksize;
597 }
598
599 let tsize_response = if tftp_request_packet.tsize.is_some() {
600 Some(actual_file_size)
601 } else {
602 None
603 };
604
605 tftp_oack_packet_write(
606 &mut response,
607 tsize_response,
608 tftp_request_packet.blksize,
609 )
610 .unwrap();
611 } else {
612 // No options, send first data block directly
613 let contents = std::fs::read(&filepath).unwrap();
614 let block_size = 512;
615 let first_block = if contents.len() > block_size {
616 contents[..block_size].to_vec()
617 } else {
618 contents
619 };
620
621 tftp_data_packet_write(&mut response, 1, first_block).unwrap();
622 }
623 }
624 TftpPacket::Data(tftp_data_packet) => {
625 println!("Received DATA packet: block {}", tftp_data_packet.block);
626 }
627 TftpPacket::Ack(tftp_ack_packet) => {
628 println!("Received ACK packet: block {}", tftp_ack_packet.block);
629
630 let contents = std::fs::read(&current_file).unwrap();
631 let next_block = tftp_ack_packet.block + 1;
632 let start_offset = (next_block - 1) as u64 * last_blksize;
633 let end_offset = next_block as u64 * last_blksize;
634 let prev_start_offset = (next_block.saturating_sub(2)) as u64 * last_blksize;
635 let prev_remain = contents.len() - prev_start_offset as usize;
636 if prev_remain as u64 >= last_blksize || tftp_ack_packet.block == 0 {
637 let end = std::cmp::min(end_offset as usize, contents.len());
638 let block_data = contents[start_offset as usize..end].to_vec();
639 println!("sending tftp data packet with {} bytes", block_data.len());
640 tftp_data_packet_write(&mut response, next_block, block_data).unwrap();
641 }
642 }
643 TftpPacket::Error(tftp_error_packet) => {
644 println!(
645 "Received ERROR packet: code {}, message: {}",
646 tftp_error_packet.code, tftp_error_packet.message
647 );
648 }
649 }
650 //
651 // let filepath = format!("tftp/{}", request.filename);
652 // let meta = std::fs::metadata(&filepath).unwrap();
653 // let contents = std::fs::read(&filepath).unwrap();
654 // let mut response = Vec::default();
655 if !response.is_empty() {
656 socket69.send_to(&response, addr).unwrap();
657 }
658 } else {
659 std::thread::sleep(std::time::Duration::from_millis(10));
660 }
661 }
662}
663
664fn handle_packet(buf: &[u8], socket: &UdpSocket) {
665 match parse_packet(buf) {
666 Ok(packet) => {
667 println!("Parsed DHCP packet: XID={:08x}", packet.xid);
668
669 // Check if it's a PXE client and extract client UUID
670 let mut is_pxe = false;
671 let mut client_uuid = None;
672 let mut is_ipxe = false;
673
674 for option in &packet.options {
675 match option {
676 DhcpOption::VendorClassIdentifier(vendor_class) => {
677 println!("Vendor class: {}", vendor_class);
678 if vendor_class.contains("PXEClient") {
679 is_pxe = true;
680 }
681 }
682 DhcpOption::UserClassInformation(user_class) => {
683 println!("User class: {}", user_class);
684 is_ipxe = true;
685 }
686 DhcpOption::Unknown { code: 97, data } => {
687 println!("Found client machine identifier");
688 client_uuid = Some(data.clone());
689 }
690 _ => {}
691 }
692 }
693
694 if is_pxe {
695 println!("Responding to PXE client with DHCPOFFER");
696 let response =
697 write_boot_packet(packet.xid, packet.chaddr, client_uuid, is_ipxe).unwrap();
698 socket
699 .send_to(&response, SocketAddrV4::new(Ipv4Addr::BROADCAST, 68))
700 .unwrap();
701 } else {
702 println!("Not a PXE client, ignoring");
703 }
704 }
705 Err(e) => {
706 println!("Failed to parse packet: {}", e);
707 }
708 }
709}
710
711fn handle_packet_4011(buf: &[u8], socket: &UdpSocket, sender_addr: SocketAddr) {
712 match parse_packet(buf) {
713 Ok(packet) => {
714 println!("Parsed DHCP packet on 4011: XID={:08x}", packet.xid);
715
716 // Extract client UUID
717 let mut client_uuid = None;
718 for option in &packet.options {
719 if let DhcpOption::Unknown { code: 97, data } = option {
720 client_uuid = Some(data.clone());
721 break;
722 }
723 }
724
725 println!("Responding with DHCPACK");
726 let response = write_boot_ack(packet.xid, packet.chaddr, client_uuid).unwrap();
727 socket.send_to(&response, sender_addr).unwrap();
728 }
729 Err(e) => {
730 println!("Failed to parse packet on 4011: {}", e);
731 }
732 }
733}
734
735const DHCP_PACKET_PAYLOAD: &'static [u8] = &[
736 0x1, 0x1, 0x6, 0x0, 0xf1, 0x25, 0x7c, 0x21, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
737 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2b, 0x67, 0x3f, 0xda, 0x70, 0x0, 0x0,
738 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
739 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
740 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
741 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
742 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
743 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
744 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
745 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
746 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
747 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
748 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x63, 0x82, 0x53, 0x63, 0x35, 0x1, 0x1, 0x39,
749 0x2, 0x5, 0xc0, 0x37, 0x23, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0xc, 0xd, 0xf, 0x11, 0x12, 0x16,
750 0x17, 0x1c, 0x28, 0x29, 0x2a, 0x2b, 0x32, 0x33, 0x36, 0x3a, 0x3b, 0x3c, 0x42, 0x43, 0x61, 0x80,
751 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x61, 0x11, 0x0, 0xcc, 0xfc, 0x32, 0x1b, 0xce, 0x2a,
752 0xb2, 0x11, 0xa8, 0x5c, 0xb1, 0xac, 0x38, 0x38, 0x10, 0xf, 0x5e, 0x3, 0x1, 0x3, 0x10, 0x5d,
753 0x2, 0x0, 0x7, 0x3c, 0x20, 0x50, 0x58, 0x45, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x3a, 0x41,
754 0x72, 0x63, 0x68, 0x3a, 0x30, 0x30, 0x30, 0x30, 0x37, 0x3a, 0x55, 0x4e, 0x44, 0x49, 0x3a, 0x30,
755 0x30, 0x33, 0x30, 0x31, 0x36, 0xff,
756];