aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.rs296
-rw-r--r--src/tftp.rs3
2 files changed, 4 insertions, 295 deletions
diff --git a/src/main.rs b/src/main.rs
index 3d86a8e..07e4eec 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -347,223 +347,18 @@ fn write_boot_ack(xid: u32, chaddr: [u8; 16], client_uuid: Option<Vec<u8>>) -> R
347 Ok(writer) 347 Ok(writer)
348} 348}
349 349
350#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
351struct InvalidTftpOp(u16);
352
353impl std::fmt::Display for InvalidTftpOp {
354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
355 write!(f, "invalid tftp opcode '{}'", self.0)
356 }
357}
358
359impl std::error::Error for InvalidTftpOp {}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
362enum TftpOp {
363 ReadRequest,
364 WriteRequest,
365 Data,
366 Ack,
367 Error,
368 Oack,
369}
370
371impl Into<u16> for TftpOp {
372 fn into(self) -> u16 {
373 match self {
374 TftpOp::ReadRequest => 1,
375 TftpOp::WriteRequest => 2,
376 TftpOp::Data => 3,
377 TftpOp::Ack => 4,
378 TftpOp::Error => 5,
379 TftpOp::Oack => 6,
380 }
381 }
382}
383
384impl TryFrom<u16> for TftpOp {
385 type Error = InvalidTftpOp;
386
387 fn try_from(value: u16) -> std::result::Result<Self, InvalidTftpOp> {
388 match value {
389 1 => Ok(Self::ReadRequest),
390 2 => Ok(Self::WriteRequest),
391 3 => Ok(Self::Data),
392 4 => Ok(Self::Ack),
393 5 => Ok(Self::Error),
394 6 => Ok(Self::Oack),
395 unknown => Err(InvalidTftpOp(unknown)),
396 }
397 }
398}
399
400#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
401enum TftpMode {
402 NetAscii,
403 Octet,
404 Mail,
405}
406
407#[derive(Debug)]
408struct TftpRequestPacket {
409 filename: String,
410 mode: String,
411 tsize: Option<u64>,
412 blksize: Option<u64>,
413}
414
415#[derive(Debug)]
416struct TftpDataPacket {
417 block: u16,
418 data: Vec<u8>,
419}
420
421#[derive(Debug)]
422struct TftpAckPacket {
423 block: u16,
424}
425
426#[derive(Debug)]
427struct TftpErrorPacket {
428 code: u16,
429 message: String,
430}
431
432fn tftp_request_packet_parse(cursor: &mut Cursor<&[u8]>) -> Result<TftpRequestPacket> {
433 let filename = read_null_terminated_string(cursor)?;
434 let mode = read_null_terminated_string(cursor)?;
435 let mut tsize = None;
436 let mut blksize = None;
437 while let Ok(opt_name) = read_null_terminated_string(cursor) {
438 if opt_name.is_empty() {
439 break;
440 }
441 let opt_data = read_null_terminated_string(cursor)?;
442 match opt_name.as_str() {
443 "tsize" => tsize = Some(opt_data.parse::<u64>().unwrap()),
444 "blksize" => blksize = Some(opt_data.parse::<u64>().unwrap()),
445 _ => eprintln!("unknown tftp request option '{opt_name}'"),
446 }
447 }
448
449 Ok(TftpRequestPacket {
450 filename,
451 mode,
452 tsize,
453 blksize,
454 })
455}
456
457fn tftp_data_packet_write(writer: &mut Vec<u8>, block: u16, data: Vec<u8>) -> Result<()> {
458 write_u16(writer, TftpOp::Data.into())?;
459 write_u16(writer, block)?;
460 write_buf(writer, &data)?;
461 Ok(())
462}
463
464fn tftp_oack_packet_write(
465 writer: &mut Vec<u8>,
466 tsize: Option<u64>,
467 blksize: Option<u64>,
468) -> Result<()> {
469 write_u16(writer, TftpOp::Oack.into())?;
470
471 // Only include options that were requested by the client
472 if let Some(blksize_val) = blksize {
473 write_buf(writer, b"blksize")?;
474 write_u8(writer, 0)?; // null terminator
475 let blksize_str = blksize_val.to_string();
476 write_buf(writer, blksize_str.as_bytes())?;
477 write_u8(writer, 0)?; // null terminator
478 }
479
480 if let Some(tsize_val) = tsize {
481 write_buf(writer, b"tsize")?;
482 write_u8(writer, 0)?; // null terminator
483 let tsize_str = tsize_val.to_string();
484 write_buf(writer, tsize_str.as_bytes())?;
485 write_u8(writer, 0)?; // null terminator
486 }
487
488 Ok(())
489}
490
491#[derive(Debug)]
492enum TftpPacket {
493 Request(TftpRequestPacket),
494 Data(TftpDataPacket),
495 Ack(TftpAckPacket),
496 Error(TftpErrorPacket),
497}
498
499fn tftp_packet_parse(cursor: &mut Cursor<&[u8]>) -> Result<TftpPacket> {
500 let op = TftpOp::try_from(read_u16(cursor)?).unwrap();
501 match op {
502 TftpOp::ReadRequest | TftpOp::WriteRequest => {
503 let filename = read_null_terminated_string(cursor)?;
504 let mode = read_null_terminated_string(cursor)?;
505 let mut tsize = None;
506 let mut blksize = None;
507
508 while let Ok(opt_name) = read_null_terminated_string(cursor) {
509 if opt_name.is_empty() {
510 break;
511 }
512 let opt_data = read_null_terminated_string(cursor)?;
513 match opt_name.as_str() {
514 "tsize" => tsize = Some(opt_data.parse::<u64>().unwrap()),
515 "blksize" => blksize = Some(opt_data.parse::<u64>().unwrap()),
516 _ => eprintln!("unknown tftp request option '{opt_name}'"),
517 }
518 }
519
520 Ok(TftpPacket::Request(TftpRequestPacket {
521 filename,
522 mode,
523 tsize,
524 blksize,
525 }))
526 }
527 TftpOp::Data => {
528 let block = read_u16(cursor)?;
529 let mut data = Vec::new();
530 cursor.read_to_end(&mut data)?;
531 Ok(TftpPacket::Data(TftpDataPacket { block, data }))
532 }
533 TftpOp::Ack => {
534 let block = read_u16(cursor)?;
535 Ok(TftpPacket::Ack(TftpAckPacket { block }))
536 }
537 TftpOp::Error => {
538 let code = read_u16(cursor)?;
539 let message = read_null_terminated_string(cursor)?;
540 Ok(TftpPacket::Error(TftpErrorPacket { code, message }))
541 }
542 TftpOp::Oack => {
543 // OACK parsing not implemented for now
544 Err(std::io::Error::new(
545 std::io::ErrorKind::Unsupported,
546 "OACK parsing not implemented",
547 ))
548 }
549 }
550}
551
552fn main() { 350fn main() {
553 let socket67 = UdpSocket::bind("0.0.0.0:67").unwrap(); 351 let socket67 = UdpSocket::bind("0.0.0.0:67").unwrap();
554 socket67.set_broadcast(true).unwrap(); 352 socket67.set_broadcast(true).unwrap();
555 socket67.set_nonblocking(true).unwrap(); 353 socket67.set_nonblocking(true).unwrap();
556 354
557 let socket69 = UdpSocket::bind("0.0.0.0:69").unwrap();
558 socket69.set_broadcast(false).unwrap();
559 socket69.set_nonblocking(true).unwrap();
560
561 let socket4011 = UdpSocket::bind("0.0.0.0:4011").unwrap(); 355 let socket4011 = UdpSocket::bind("0.0.0.0:4011").unwrap();
562 socket4011.set_broadcast(true).unwrap(); 356 socket4011.set_broadcast(true).unwrap();
563 socket4011.set_nonblocking(true).unwrap(); 357 socket4011.set_nonblocking(true).unwrap();
564 358
565 let mut last_blksize = 512u64; 359 std::thread::spawn(|| {
566 let mut current_file = String::new(); 360 tftp::serve("tftp").unwrap();
361 });
567 362
568 loop { 363 loop {
569 let mut buf = [0u8; 1500]; 364 let mut buf = [0u8; 1500];
@@ -575,91 +370,6 @@ fn main() {
575 } else if let Ok((n, addr)) = socket4011.recv_from(&mut buf) { 370 } else if let Ok((n, addr)) = socket4011.recv_from(&mut buf) {
576 println!("Received {} bytes from {} on port 4011", n, addr); 371 println!("Received {} bytes from {} on port 4011", n, addr);
577 handle_packet_4011(&buf[..n], &socket4011, addr); 372 handle_packet_4011(&buf[..n], &socket4011, addr);
578 } else if let Ok((n, addr)) = socket69.recv_from(&mut buf) {
579 let mut cursor = Cursor::new(&buf[..n]);
580
581 let packet = tftp_packet_parse(&mut cursor).unwrap();
582 println!("Received TFTP request from {addr}: {packet:#?}");
583
584 let mut response = Vec::default();
585 match packet {
586 TftpPacket::Request(tftp_request_packet) => {
587 println!(
588 "Request options: tsize={:?}, blksize={:?}",
589 tftp_request_packet.tsize, tftp_request_packet.blksize
590 );
591
592 let filepath = format!("tftp/{}", tftp_request_packet.filename);
593 current_file = filepath.clone();
594 let meta = std::fs::metadata(&filepath).unwrap();
595 let actual_file_size = meta.len();
596
597 // Only send OACK if client sent options
598 if tftp_request_packet.tsize.is_some() || tftp_request_packet.blksize.is_some()
599 {
600 if let Some(blksize) = tftp_request_packet.blksize {
601 last_blksize = blksize;
602 }
603
604 let tsize_response = if tftp_request_packet.tsize.is_some() {
605 Some(actual_file_size)
606 } else {
607 None
608 };
609
610 tftp_oack_packet_write(
611 &mut response,
612 tsize_response,
613 tftp_request_packet.blksize,
614 )
615 .unwrap();
616 } else {
617 // No options, send first data block directly
618 let contents = std::fs::read(&filepath).unwrap();
619 let block_size = 512;
620 let first_block = if contents.len() > block_size {
621 contents[..block_size].to_vec()
622 } else {
623 contents
624 };
625
626 tftp_data_packet_write(&mut response, 1, first_block).unwrap();
627 }
628 }
629 TftpPacket::Data(tftp_data_packet) => {
630 println!("Received DATA packet: block {}", tftp_data_packet.block);
631 }
632 TftpPacket::Ack(tftp_ack_packet) => {
633 println!("Received ACK packet: block {}", tftp_ack_packet.block);
634
635 let contents = std::fs::read(&current_file).unwrap();
636 let next_block = tftp_ack_packet.block + 1;
637 let start_offset = (next_block - 1) as u64 * last_blksize;
638 let end_offset = next_block as u64 * last_blksize;
639 let prev_start_offset = (next_block.saturating_sub(2)) as u64 * last_blksize;
640 let prev_remain = contents.len() - prev_start_offset as usize;
641 if prev_remain as u64 >= last_blksize || tftp_ack_packet.block == 0 {
642 let end = std::cmp::min(end_offset as usize, contents.len());
643 let block_data = contents[start_offset as usize..end].to_vec();
644 println!("sending tftp data packet with {} bytes", block_data.len());
645 tftp_data_packet_write(&mut response, next_block, block_data).unwrap();
646 }
647 }
648 TftpPacket::Error(tftp_error_packet) => {
649 println!(
650 "Received ERROR packet: code {}, message: {}",
651 tftp_error_packet.code, tftp_error_packet.message
652 );
653 }
654 }
655 //
656 // let filepath = format!("tftp/{}", request.filename);
657 // let meta = std::fs::metadata(&filepath).unwrap();
658 // let contents = std::fs::read(&filepath).unwrap();
659 // let mut response = Vec::default();
660 if !response.is_empty() {
661 socket69.send_to(&response, addr).unwrap();
662 }
663 } else { 373 } else {
664 std::thread::sleep(std::time::Duration::from_millis(10)); 374 std::thread::sleep(std::time::Duration::from_millis(10));
665 } 375 }
diff --git a/src/tftp.rs b/src/tftp.rs
index 681d036..d986a44 100644
--- a/src/tftp.rs
+++ b/src/tftp.rs
@@ -305,7 +305,7 @@ pub fn serve(dir: impl AsRef<Path>) -> Result<()> {
305 }; 305 };
306 306
307 Some(TftpPacket::OAck(TftpOAckPacket { 307 Some(TftpPacket::OAck(TftpOAckPacket {
308 tsize: req.tsize, 308 tsize: tsize_response,
309 blksize: req.blksize, 309 blksize: req.blksize,
310 })) 310 }))
311 } else { 311 } else {
@@ -361,7 +361,6 @@ pub fn serve(dir: impl AsRef<Path>) -> Result<()> {
361 println!("Sending to {addr}: {response:#?}"); 361 println!("Sending to {addr}: {response:#?}");
362 response.write(&mut writer).unwrap(); 362 response.write(&mut writer).unwrap();
363 let (response, _) = writer.split(); 363 let (response, _) = writer.split();
364 println!("Sending {} bytes to {addr}: {response:#?}", response.len());
365 socket.send_to(&response, addr).unwrap(); 364 socket.send_to(&response, addr).unwrap();
366 } 365 }
367 } 366 }