Revision 119
- Date:
- 2009/08/03 08:53:47
- Files:
Legend:
- Added
- Removed
- Modified
-
lib/Net/TFTPd.pm
1 package Net::TFTPd; 2 3 use 5.006; 4 use Carp; 5 use strict; 6 use warnings; 7 use IO::Socket; 8 9 require Exporter; 10 11 # modified for supporting small block sizes, O.Z. 15.08.2007 12 use constant TFTP_MIN_BLKSIZE => 8; 13 use constant TFTP_DEFAULT_BLKSIZE => 512; 14 use constant TFTP_MAX_BLKSIZE => 65464; 15 use constant TFTP_MIN_TIMEOUT => 1; 16 use constant TFTP_MAX_TIMEOUT => 60; 17 use constant TFTP_DEFAULT_PORT => 69; 18 19 use constant TFTP_OPCODE_RRQ => 1; 20 use constant TFTP_OPCODE_WRQ => 2; 21 use constant TFTP_OPCODE_DATA => 3; 22 use constant TFTP_OPCODE_ACK => 4; 23 use constant TFTP_OPCODE_ERROR => 5; 24 use constant TFTP_OPCODE_OACK => 6; 25 26 # Type Op # Format without header 27 # 28 # 2 bytes string 1 byte string 1 byte 29 # ------------------------------------------------- 30 # RRQ/ | 01/02 | Filename | 0 | Mode | 0 | 31 # WRQ ------------------------------------------------- 32 # 2 bytes 2 bytes n bytes 33 # ----------------------------------- 34 # DATA | 03 | Block # | Data | 35 # ----------------------------------- 36 # 2 bytes 2 bytes 37 # ---------------------- 38 # ACK | 04 | Block # | 39 # ---------------------- 40 # 2 bytes 2 bytes string 1 byte 41 # ------------------------------------------ 42 # ERROR | 05 | ErrorCode | ErrMsg | 0 | 43 # ------------------------------------------ 44 45 our %OPCODES = ( 46 1 => 'RRQ', 47 2 => 'WRQ', 48 3 => 'DATA', 49 4 => 'ACK', 50 5 => 'ERROR', 51 6 => 'OACK', 52 'RRQ' => TFTP_OPCODE_RRQ, 53 'WRQ' => TFTP_OPCODE_WRQ, 54 'DATA' => TFTP_OPCODE_DATA, 55 'ACK' => TFTP_OPCODE_ACK, 56 'ERROR' => TFTP_OPCODE_ERROR, 57 'OACK' => TFTP_OPCODE_OACK 58 ); 59 60 my %ERRORS = ( 61 0 => 'Not defined, see error message (if any)', 62 1 => 'File not found', 63 2 => 'Access violation', 64 3 => 'Disk full or allocation exceeded', 65 4 => 'Illegal TFTP operation', 66 5 => 'Unknown transfer ID', 67 6 => 'File already exists', 68 7 => 'No such user', 69 8 => 'Option negotiation' 70 ); 71 72 our @ISA = qw(Exporter); 73 74 # Items to export into callers namespace by default. Note: do not export 75 # names by default without a very good reason. Use EXPORT_OK instead. 76 # Do not simply export all your public functions/methods/constants. 77 78 # This allows declaration use Net::TFTPd ':all'; 79 # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK 80 # will save memory. 81 our %EXPORT_TAGS = ( 82 'all' => [ qw( %OPCODES ) ] 83 ); 84 85 our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); 86 87 our @EXPORT = qw( ); 88 89 our $VERSION = '0.04'; 90 91 our $LASTERROR; 92 93 my $debug; 94 95 # 96 # Usage: $tftpdOBJ = Net::TFTPd->new( ['RootDir' => 'path/to/files' | 'FileName' => 'path/to/file'] ); 97 # return the tftpdOBJ object if success or undef if error 98 # 99 sub new 100 { 101 # create the future TFTPd object 102 my $self = shift; 103 my $class = ref($self) || $self; 104 105 # read parameters 106 my %cfg = @_; 107 108 # setting defaults 109 $cfg{'FileName'} or $cfg{'RootDir'} or croak "Usage: \$tftpdOBJ = Net::TFTPd->new(['RootDir' => 'path/to/files' | 'FileName' => 'path/to/file'] [, [ LocalPort => portnum ] [, ...]] );"; 110 111 if($cfg{'RootDir'} and not -d($cfg{'RootDir'}) ) 112 { 113 $LASTERROR = sprintf 'RootDir \'%s\' not found or is not a valid directory name\n', $cfg{'RootDir'}; 114 return(undef); 115 } 116 117 if($cfg{'FileName'} and not -e($cfg{'FileName'}) ) 118 { 119 $LASTERROR = sprintf 'FileName \'%s\' not found or is not a valid filename\n', $cfg{'FileName'}; 120 return(undef); 121 } 122 123 my %params = ( 124 'Proto' => 'udp', 125 'LocalPort' => $cfg{'LocalPort'} || TFTP_DEFAULT_PORT, 126 ); 127 128 # bind only to specified address 129 if($cfg{'LocalAddr'}) 130 { 131 $params{'LocalAddr'} = $cfg{'LocalAddr'}; 132 } 133 134 if(my $udpserver = IO::Socket::INET->new(%params)) 135 { 136 #removed for using this module with IO v. 1.2301 under SUSE 10.1, O.Z. 15.08.2007 137 # $udpserver->setsockopt(SOL_SOCKET, SO_RCVBUF, 0); 138 # $udpserver->setsockopt(SOL_SOCKET, SO_SNDBUF, 0); 139 140 return bless { 141 'LocalPort' => TFTP_DEFAULT_PORT, 142 'Timeout' => 10, 143 'ACKtimeout' => 4, 144 'ACKretries' => 4, 145 'Readable' => 1, 146 'Writable' => 0, 147 'CallBack' => undef, 148 'BlkSize' => TFTP_DEFAULT_BLKSIZE, 149 'Debug' => 0, 150 %cfg, # merge user parameters 151 '_UDPSERVER_' => $udpserver 152 }, $class; 153 } 154 else 155 { 156 $LASTERROR = "Error opening socket for listener: $@\n"; 157 return(undef); 158 } 159 } 160 161 # 162 # Usage: $tftpdOBJ->waitRQ($timeout); 163 # return requestOBJ if success, 0 if $timeout elapsed, undef if error 164 # 165 sub waitRQ 166 { 167 # the tftpd object 168 # my $tftpd = shift; 169 170 my $self = shift; 171 my $class = ref($self) || $self; 172 # return bless {}, $class; 173 174 # clone the object 175 my $request; 176 foreach my $key (keys(%{$self})) 177 { 178 # everything but '_xxx_' 179 $key =~ /^\_.+\_$/ and next; 180 $request->{$key} = $self->{$key}; 181 } 182 183 # use $timeout or default from $tftpdOBJ 184 my $Timeout = shift || $request->{'Timeout'}; 185 186 my $udpserver = $self->{'_UDPSERVER_'}; 187 188 my ($datagram, $opcode, $datain); 189 190 # vars for IO select 191 my ($rin, $rout, $ein, $eout) = ('', '', '', ''); 192 vec($rin, fileno($udpserver), 1) = 1; 193 194 # check if a message is waiting 195 if (select($rout=$rin, undef, $eout=$ein, $Timeout)) 196 { 197 # read the message 198 if($udpserver->recv($datagram, TFTP_MAX_BLKSIZE + 4)) 199 { 200 # decode the message 201 ($opcode, $datain) = unpack("na*", $datagram); 202 203 $request->{'_REQUEST_'}{'OPCODE'} = $opcode; 204 205 # get peer port and address 206 my($peerport, $peeraddr) = sockaddr_in($udpserver->peername); 207 $request->{'_REQUEST_'}{'PeerPort'} = $peerport; 208 $request->{'_REQUEST_'}{'PeerAddr'} = inet_ntoa($peeraddr); 209 210 # get filename and transfer mode 211 my @datain = split("\0", $datain); 212 213 $request->{'_REQUEST_'}{'FileName'} = shift(@datain); 214 $request->{'_REQUEST_'}{'Mode'} = uc(shift(@datain)); 215 $request->{'_REQUEST_'}{'BlkSize'} = TFTP_DEFAULT_BLKSIZE; 216 $request->{'_REQUEST_'}{'LASTACK'} = 0; 217 $request->{'_REQUEST_'}{'PREVACK'} = -1; 218 # counter for transferred bytes 219 $request->{'_REQUEST_'}{'TotalBytes'} = 0; 220 221 if(scalar(@datain) >= 2) 222 { 223 $request->{'_REQUEST_'}{'RFC2347'} = { @datain }; 224 } 225 226 return bless $request, $class; 227 } 228 else 229 { 230 $! = $udpserver->sockopt(SO_ERROR); 231 $LASTERROR = sprintf "Socket RECV error: %s\n", $!; 232 return(undef); 233 } 234 } 235 else 236 { 237 $LASTERROR = "Timed out waiting for RRQ/WRQ"; 238 return(0); 239 } 240 } 241 242 # 243 # Usage: $requestOBJ->processRQ(); 244 # return 1 if success, undef if error 245 # 246 sub processRQ 247 { 248 # the request object 249 my $self = shift; 250 251 if(defined($self->newSOCK())) 252 { 253 # modified for supporting NETASCII transfers on 25/05/2009 254 if(($self->{'_REQUEST_'}{'Mode'} ne 'OCTET') && ($self->{'_REQUEST_'}{'Mode'} ne 'NETASCII')) 255 { 256 #request is not OCTET 257 $LASTERROR = sprintf "%s transfer mode is not supported\n", $self->{'_REQUEST_'}{'Mode'}; 258 $self->sendERR(0, $LASTERROR); 259 return(undef); 260 } 261 262 # new socket opened successfully 263 if($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_RRQ) 264 { 265 ################# 266 # opcode is RRQ # 267 ################# 268 if($self->{'Readable'}) 269 { 270 # read is permitted 271 if($self->{'_REQUEST_'}{'FileName'} =~ /\.\.[\\\/]/) 272 { 273 # requested file contains '..\' or '../' 274 $LASTERROR = sprintf 'Access to \'%s\' is not permitted to %s', $self->{'_REQUEST_'}{'FileName'}, $self->{'_REQUEST_'}{'PeerAddr'}; 275 $self->sendERR(2); 276 return(undef); 277 } 278 279 if(defined($self->checkFILE())) 280 { 281 # file is present 282 if(defined($self->negotiateOPTS())) 283 { 284 # RFC 2347 options negotiated 285 if(defined($self->openFILE())) 286 { 287 # file opened for read, start the transfer 288 if(defined($self->sendFILE())) 289 { 290 # file sent successfully 291 return(1); 292 } 293 else 294 { 295 # error sending file 296 return(undef); 297 } 298 } 299 else 300 { 301 # error opening file 302 return(undef); 303 } 304 } 305 else 306 { 307 # error negotiating options 308 $LASTERROR = "TFTP error 8: Option negotiation\n"; 309 $self->sendERR(8); 310 return(undef); 311 } 312 } 313 else 314 { 315 # file not found 316 $LASTERROR = sprintf 'File \'%s\' not found', $self->{'_REQUEST_'}{'FileName'}; 317 $self->sendERR(1); 318 return(undef); 319 } 320 } 321 else 322 { 323 # if server is not readable 324 $LASTERROR = "TFTP Error: Access violation"; 325 $self->sendERR(2); 326 return(undef); 327 } 328 } 329 elsif($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_WRQ) 330 { 331 ################# 332 # opcode is WRQ # 333 ################# 334 if($self->{'Writable'}) 335 { 336 # write is permitted 337 if($self->{'_REQUEST_'}{'FileName'} =~ /\.\.[\\\/]/) 338 { 339 # requested file contains '..\' or '../' 340 $LASTERROR = sprintf 'Access to \'%s\' is not permitted to %s', $self->{'_REQUEST_'}{'FileName'}, $self->{'_REQUEST_'}{'PeerAddr'}; 341 $self->sendERR(2); 342 return(undef); 343 } 344 345 if(!defined($self->checkFILE())) 346 { 347 # RFC 2347 options negotiated 348 if(defined($self->openFILE())) 349 { 350 # file is not present 351 if(defined($self->negotiateOPTS())) 352 { 353 # file opened for write, start the transfer 354 if(defined($self->recvFILE())) 355 { 356 # file received successfully 357 return(1); 358 } 359 else 360 { 361 # error receiving file 362 return(undef); 363 } 364 } 365 else 366 { 367 # error negotiating options 368 $LASTERROR = "TFTP error 8: Option negotiation\n"; 369 $self->sendERR(8); 370 return(undef); 371 } 372 } 373 else 374 { 375 # error opening file 376 $self->sendERR(3); 377 return(undef); 378 } 379 } 380 else 381 { 382 # file not found 383 $LASTERROR = sprintf 'File \'%s\' already exists', $self->{'_REQUEST_'}{'FileName'}; 384 $self->sendERR(6); 385 return(undef); 386 } 387 } 388 else 389 { 390 # if server is not writable 391 $LASTERROR = "TFTP Error: Access violation"; 392 $self->sendERR(2); 393 return(undef); 394 } 395 } 396 else 397 { 398 ################# 399 # other opcodes # 400 ################# 401 $LASTERROR = sprintf "Opcode %d not supported as request", $self->{'_REQUEST_'}{'OPCODE'}; 402 $self->sendERR(4); 403 return(undef); 404 } 405 } 406 else 407 { 408 return(undef); 409 } 410 } 411 412 # 413 # Usage: $requestOBJ->getTotalBytes(); 414 # returns the number of bytes transferred by the request 415 # 416 sub getTotalBytes 417 { 418 # the request object 419 my $self = shift; 420 421 return $self->{'_REQUEST_'}{'TotalBytes'}; 422 } 423 424 # 425 # Usage: $requestOBJ->getFileName(); 426 # returns the requested file name 427 # 428 sub getFileName 429 { 430 # the request object 431 my $self = shift; 432 433 return $self->{'_REQUEST_'}{'FileName'}; 434 } 435 436 # 437 # Usage: $requestOBJ->getMode(); 438 # returns the transfer mode for the request 439 # 440 sub getMode 441 { 442 # the request object 443 my $self = shift; 444 445 return $self->{'_REQUEST_'}{'Mode'}; 446 } 447 448 # 449 # Usage: $requestOBJ->getPeerAddr(); 450 # returns the address of the requesting client 451 # 452 sub getPeerAddr 453 { 454 # the request object 455 my $self = shift; 456 457 return $self->{'_REQUEST_'}{'PeerAddr'}; 458 } 459 460 # 461 # Usage: $requestOBJ->getPeerPort(); 462 # returns the port of the requesting client 463 # 464 sub getPeerPort 465 { 466 # the request object 467 my $self = shift; 468 469 return $self->{'_REQUEST_'}{'PeerPort'}; 470 } 471 472 # 473 # Usage: $requestOBJ->getBlkSize(); 474 # returns the block size used for the transfer 475 # 476 sub getBlkSize 477 { 478 # the request object 479 my $self = shift; 480 481 return $self->{'_REQUEST_'}{'BlkSize'}; 482 } 483 484 # 485 # Usage: $requestOBJ->newSOCK(); 486 # return 1 if success or undef if error 487 # 488 sub newSOCK 489 { 490 # the request object 491 my $self = shift; 492 493 # set parameters for the new socket 494 my %params = ( 495 'Proto' => 'udp', 496 'PeerPort' => $self->{'_REQUEST_'}{'PeerPort'}, 497 'PeerAddr' => $self->{'_REQUEST_'}{'PeerAddr'} 498 ); 499 500 # bind only to specified address 501 if($self->{'Address'}) 502 { 503 $params{'LocalAddr'} = $self->{'Address'}; 504 } 505 506 # open socket 507 if(my $udpserver = IO::Socket::INET->new(%params)) 508 { 509 #removed for using this module with IO v. 1.2301 under SUSE 10.1, O.Z. 15.08.2007 510 # $udpserver->setsockopt(SOL_SOCKET, SO_RCVBUF, 0); 511 # $udpserver->setsockopt(SOL_SOCKET, SO_SNDBUF, 0); 512 513 $self->{'_UDPSERVER_'} = $udpserver; 514 return(1); 515 } 516 else 517 { 518 $LASTERROR = "Error opening socket for reply: $@\n"; 519 return(undef); 520 } 521 } 522 523 524 # 525 # Usage: $requestOBJ->negotiateOPTS(); 526 # return 1 if success or undef if error 527 # 528 sub negotiateOPTS 529 { 530 # the request object 531 my $self = shift; 532 533 if($self->{'_REQUEST_'}{'RFC2347'}) 534 { 535 # parse RFC 2347 options if present 536 foreach my $option (keys(%{ $self->{'_REQUEST_'}{'RFC2347'} })) 537 { 538 if(uc($option) eq 'BLKSIZE') 539 { 540 # Negotiate the blocksize 541 if($self->{'_REQUEST_'}{'RFC2347'}{$option} > TFTP_MAX_BLKSIZE or $self->{'_REQUEST_'}{'RFC2347'}{$option} < TFTP_MIN_BLKSIZE) 542 { 543 $self->{'_REQUEST_'}{'RFC2347'}{$option} = $self->{'BlkSize'}; 544 } 545 else 546 { 547 $self->{'_RESPONSE_'}{'RFC2347'}{$option} = $self->{'_REQUEST_'}{'RFC2347'}{$option}; 548 $self->{'BlkSize'} = $self->{'_RESPONSE_'}{'RFC2347'}{$option}; 549 } 550 } 551 elsif(uc($option) eq 'TSIZE') 552 { 553 # Negotiate the transfer size 554 if($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_RRQ) 555 { 556 $self->{'_RESPONSE_'}{'RFC2347'}{$option} = $self->{'FileSize'}; 557 } 558 else 559 { 560 $self->{'FileSize'} = $self->{'_REQUEST_'}{'RFC2347'}{$option}; 561 } 562 } 563 elsif(uc($option) eq 'TIMEOUT') 564 { 565 # Negotiate the transfer timeout 566 if($self->{'_REQUEST_'}{'RFC2347'}{$option} > TFTP_MAX_TIMEOUT or $self->{'_REQUEST_'}{'RFC2347'}{$option} < TFTP_MIN_TIMEOUT) 567 { 568 $self->{'_RESPONSE_'}{'RFC2347'}{$option} = $self->{'ACKtimeout'}; 569 } 570 else 571 { 572 $self->{'_RESPONSE_'}{'RFC2347'}{$option} = $self->{'_REQUEST_'}{'RFC2347'}{$option}; 573 $self->{'ACKtimeout'} = $self->{'_REQUEST_'}{'RFC2347'}{$option}; 574 } 575 } 576 else 577 { 578 # Negotiate other options... 579 } 580 } 581 582 # post processing 583 if($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_WRQ) 584 { 585 if($self->{'FileSize'} and $self->{'BlkSize'}) 586 { 587 $self->{'_REQUEST_'}{'LASTACK'} = int($self->{'FileSize'} / $self->{'BlkSize'}) + 1; 588 } 589 } 590 591 # send OACK for RFC 2347 options 592 return($self->sendOACK()); 593 } 594 else 595 { 596 if($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_WRQ) 597 { 598 # opcode is WRQ: send ACK for datablock 0 599 if($self->{'_UDPSERVER_'}->send(pack("nn", TFTP_OPCODE_ACK, 0))) 600 { 601 return(1); 602 } 603 else 604 { 605 $! = $self->{'_UDPSERVER_'}->sockopt(SO_ERROR); 606 $LASTERROR = sprintf "Socket SEND error: %s\n", $!; 607 return(undef); 608 } 609 } 610 else 611 { 612 return(1); 613 } 614 } 615 } 616 617 618 # 619 # Usage: $requestOBJ->readFILE(\$data); 620 # return number of bytes read from file if success or undef if error 621 # 622 sub readFILE 623 { 624 my $self = shift; 625 my $datablk = shift; 626 627 if($self->{'_REQUEST_'}{'PREVACK'} < $self->{'_REQUEST_'}{'LASTACK'}) 628 { 629 # if requested block is next block, read next block and return bytes read 630 my $fh = $self->{'_REQUEST_'}{'_FH_'}; 631 # modified for supporting NETASCII transfers on 25/05/2009 632 # my $bytes = read($fh, $$datablk, $self->{'BlkSize'}); 633 my $bytes = sysread($fh, $$datablk, $self->{'BlkSize'}); 634 if(defined($bytes)) 635 { 636 return($bytes); 637 } 638 else 639 { 640 $LASTERROR = sprintf "Error $! reading file '%s'", $self->{'_REQUEST_'}{'FileName'}; 641 return(undef); 642 } 643 } 644 else 645 { 646 # if requested block is last block, return length of last block 647 return(length($$datablk)); 648 } 649 } 650 651 652 # 653 # Usage: $requestOBJ->writeFILE(\$data); 654 # return number of bytes written to file if success or undef if error 655 # 656 sub writeFILE 657 { 658 my $self = shift; 659 my $datablk = shift; 660 661 if($self->{'_REQUEST_'}{'PREVBLK'} > $self->{'_REQUEST_'}{'LASTBLK'}) 662 { 663 # if last block is < than previous block, return length of last block 664 return(length($$datablk)); 665 } 666 elsif($self->{'_REQUEST_'}{'LASTBLK'} eq ($self->{'_REQUEST_'}{'PREVBLK'} + 1)) 667 { 668 # if block is next block, write next block and return bytes written 669 my $fh = $self->{'_REQUEST_'}{'_FH_'}; 670 my $bytes = syswrite($fh, $$datablk); 671 return($bytes); 672 } 673 else 674 { 675 $LASTERROR = sprintf "TFTP Error DATA block %d is out of sequence, expected block was %d", $self->{'_REQUEST_'}{'LASTBLK'}, $self->{'_REQUEST_'}{'PREVBLK'} + 1; 676 $self->sendERR(5); 677 return(undef); 678 } 679 } 680 681 682 # 683 # Usage: $requestOBJ->sendFILE(); 684 # return 1 if success or undef if error 685 # 686 sub sendFILE 687 { 688 my $self = shift; 689 690 while(1) 691 { 692 if($self->{'_REQUEST_'}{'LASTACK'} < $self->{'_REQUEST_'}{'LASTBLK'}) 693 { 694 my $datablk = 0; 695 if(defined($self->readFILE(\$datablk))) 696 { 697 # read from file successful 698 # increment the transferred bytes counter 699 $self->{'_REQUEST_'}{'TotalBytes'} += length($datablk); 700 if($self->sendDATA(\$datablk)) 701 { 702 # send to socket successful 703 if($self->{'CallBack'}) 704 { 705 &{$self->{'CallBack'}}($self); 706 } 707 } 708 else 709 { 710 # error sending to socket 711 return(undef); 712 } 713 } 714 else 715 { 716 # error reading from file 717 return(undef); 718 } 719 } 720 else 721 { 722 # transfer completed 723 return(1); 724 } 725 } 726 } 727 728 729 # 730 # Usage: $requestOBJ->recvFILE(); 731 # return 1 if success or undef if error 732 # 733 sub recvFILE 734 { 735 my $self = shift; 736 737 $self->{'_REQUEST_'}{'LASTBLK'} = 0; 738 $self->{'_REQUEST_'}{'PREVBLK'} = 0; 739 740 while(1) 741 { 742 my $datablk = 0; 743 if($self->recvDATA(\$datablk)) 744 { 745 # DATA received 746 if(defined($self->writeFILE(\$datablk))) 747 { 748 # DATA written to file 749 my $udpserver = $self->{'_UDPSERVER_'}; 750 751 if(defined($udpserver->send(pack("nn", TFTP_OPCODE_ACK, $self->{'_REQUEST_'}{'LASTBLK'})))) 752 { 753 # sent ACK 754 # increment the transferred bytes counter 755 $self->{'_REQUEST_'}{'TotalBytes'} += length($datablk); 756 if(length($datablk) < $self->{'BlkSize'}) 757 { 758 return(1); 759 } 760 else 761 { 762 next; 763 } 764 } 765 else 766 { 767 $! = $udpserver->sockopt(SO_ERROR); 768 $LASTERROR = sprintf "Socket SEND error: %s\n", $!; 769 return(undef); 770 } 771 } 772 else 773 { 774 # error writing data 775 return(undef); 776 } 777 } 778 else 779 { 780 # timeout waiting for data 781 return(undef); 782 } 783 } 784 } 785 786 # 787 # Usage: $requestOBJ->recvDATA(\$data); 788 # return 1 if success or undef if error 789 # 790 sub recvDATA 791 { 792 my $self = shift; 793 my $datablk = shift; 794 795 my ($datagram, $opcode, $datain); 796 797 my $udpserver = $self->{'_UDPSERVER_'}; 798 799 # vars for IO select 800 my ($rin, $rout, $ein, $eout) = ('', '', '', ''); 801 vec($rin, fileno($udpserver), 1) = 1; 802 803 # wait for data 804 if(select($rout=$rin, undef, $eout=$ein, $self->{'ACKtimeout'})) 805 { 806 # read the message 807 if($udpserver->recv($datagram, $self->{'BlkSize'} + 4)) 808 { 809 # decode the message 810 ($opcode, $datain) = unpack("na*", $datagram); 811 if($opcode eq TFTP_OPCODE_DATA) 812 { 813 # message is DATA 814 $self->{'_REQUEST_'}{'PREVBLK'} = $self->{'_REQUEST_'}{'LASTBLK'}; 815 ($self->{'_REQUEST_'}{'LASTBLK'}, $$datablk) = unpack("na*", $datain); 816 817 if($self->{'CallBack'}) 818 { 819 &{$self->{'CallBack'}}($self); 820 } 821 822 return(1); 823 } 824 elsif($opcode eq TFTP_OPCODE_ERROR) 825 { 826 # message is ERR 827 $LASTERROR = sprintf "TFTP error message: %s", $datain; 828 return(undef); 829 } 830 else 831 { 832 # other messages... 833 $LASTERROR = sprintf "Opcode %d not supported waiting for DATA\n", $opcode; 834 return(undef); 835 } 836 } 837 else 838 { 839 $! = $udpserver->sockopt(SO_ERROR); 840 $LASTERROR = sprintf "Socket RECV error: %s\n", $!; 841 return(undef); 842 } 843 } 844 else 845 { 846 $LASTERROR = sprintf "Timeout occurred on DATA packet %d\n", $self->{'_REQUEST_'}{'LASTBLK'} + 1; 847 return(undef); 848 } 849 } 850 851 852 # 853 # Usage: $requestOBJ->sendDATA(\$data); 854 # return 1 if success or undef if error 855 # 856 sub sendDATA 857 { 858 my $self = shift; 859 my $datablk = shift; 860 861 my $udpserver = $self->{'_UDPSERVER_'}; 862 my $retry = 0; 863 864 my ($datagram, $opcode, $datain); 865 866 while($retry < $self->{'ACKretries'}) 867 { 868 if($udpserver->send(pack("nna*", TFTP_OPCODE_DATA, $self->{'_REQUEST_'}{'LASTACK'} + 1, $$datablk))) 869 { 870 # vars for IO select 871 my ($rin, $rout, $ein, $eout) = ('', '', '', ''); 872 vec($rin, fileno($udpserver), 1) = 1; 873 874 # wait for acknowledge 875 if(select($rout=$rin, undef, $eout=$ein, $self->{'ACKtimeout'})) 876 { 877 # read the message 878 if($udpserver->recv($datagram, TFTP_MAX_BLKSIZE + 4)) 879 { 880 # decode the message 881 ($opcode, $datain) = unpack("na*", $datagram); 882 if($opcode eq TFTP_OPCODE_ACK) 883 { 884 # message is ACK 885 # modified for supporting more blocks count than 65535, O.Z. 15.08.2007 886 $self->{'_REQUEST_'}{'PREVACK'} = $self->{'_REQUEST_'}{'LASTACK'}; 887 if(int(($self->{'_REQUEST_'}{'LASTACK'}+1) % 65536) == unpack("n", $datain)){ 888 $self->{'_REQUEST_'}{'LASTACK'}++; 889 }; 890 return(1); 891 } 892 elsif($opcode eq TFTP_OPCODE_ERROR) 893 { 894 # message is ERR 895 $LASTERROR = sprintf "TFTP error message: %s", $datain; 896 return(undef); 897 } 898 else 899 { 900 # other messages... 901 $LASTERROR = sprintf "Opcode %d not supported as a reply to DATA\n", $opcode; 902 return(undef); 903 } 904 } 905 else 906 { 907 $! = $udpserver->sockopt(SO_ERROR); 908 $LASTERROR = sprintf "Socket RECV error: %s\n", $!; 909 return(undef); 910 } 911 } 912 else 913 { 914 $LASTERROR = sprintf "Retry %d - timeout occurred on ACK packet %d\n", $retry, $self->{'_REQUEST_'}{'LASTACK'} + 1; 915 $debug and carp($LASTERROR); 916 $retry++; 917 } 918 } 919 else 920 { 921 $! = $udpserver->sockopt(SO_ERROR); 922 $LASTERROR = sprintf "Socket SEND error: %s\n", $!; 923 return(undef); 924 } 925 } 926 } 927 928 # 929 # Usage: $requestOBJ->openFILE() 930 # returns 1 if file is opened, undef if error 931 # 932 sub openFILE 933 { 934 # the request object 935 my $self = shift; 936 937 if($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_RRQ) 938 { 939 ######################################## 940 # opcode is RRQ, open file for reading # 941 ######################################## 942 if(open(RFH, "<".$self->{'_REQUEST_'}{'FileName'})) 943 { 944 # if OCTET mode, set FileHandle to binary mode... 945 if($self->{'_REQUEST_'}{'Mode'} eq 'OCTET') 946 { 947 binmode(RFH); 948 } 949 950 my $size = -s($self->{'_REQUEST_'}{'FileName'}); 951 $self->{'_REQUEST_'}{'LASTBLK'} = 1 + int($size / $self->{'BlkSize'}); 952 953 # save the filehandle reference... 954 $self->{'_REQUEST_'}{'_FH_'} = *RFH; 955 956 return(1); 957 } 958 else 959 { 960 $LASTERROR = sprintf "Error opening file \'%s\' for reading\n", $self->{'_REQUEST_'}{'FileName'}; 961 return(undef); 962 } 963 } 964 elsif($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_WRQ) 965 { 966 ######################################## 967 # opcode is WRQ, open file for writing # 968 ######################################## 969 if(open(WFH, ">".$self->{'_REQUEST_'}{'FileName'})) 970 { 971 # if OCTET mode, set FileHandle to binary mode... 972 if($self->{'_REQUEST_'}{'Mode'} eq 'OCTET') 973 { 974 binmode(WFH); 975 } 976 977 # save the filehandle reference... 978 $self->{'_REQUEST_'}{'_FH_'} = *WFH; 979 980 return(1); 981 } 982 else 983 { 984 $LASTERROR = sprintf "Error opening file \'%s\' for writing\n", $self->{'_REQUEST_'}{'FileName'}; 985 return(undef); 986 } 987 } 988 else 989 { 990 ############################ 991 # other opcodes are errors # 992 ############################ 993 $LASTERROR = sprintf "OPCODE %d is not supported\n", $self->{'_REQUEST_'}{'OPCODE'}; 994 return(undef); 995 } 996 } 997 998 # 999 # Usage: $requestOBJ->closeFILE() 1000 # returns 1 if file is success, undef if error 1001 # 1002 sub closeFILE 1003 { 1004 my $self = shift; 1005 1006 if($self->{'_REQUEST_'}{'_FH_'}) 1007 { 1008 if(close($self->{'_REQUEST_'}{'_FH_'})) 1009 { 1010 return(1); 1011 } 1012 else 1013 { 1014 $LASTERROR = "Error closing filehandle\n"; 1015 return(undef); 1016 } 1017 } 1018 else 1019 { 1020 return(1); 1021 } 1022 } 1023 1024 # 1025 # Usage: $requestOBJ->checkFILE() 1026 # returns 1 if file is found, undef if file is not found 1027 # 1028 sub checkFILE 1029 { 1030 # the request object 1031 my $self = shift; 1032 1033 # requested file 1034 my $reqfile = $self->{'_REQUEST_'}{'FileName'}; 1035 1036 if($self->{'FileName'}) 1037 { 1038 # filename is fixed 1039 $self->{'_REQUEST_'}{'FileName'} = $self->{'FileName'}; 1040 1041 if(($self->{'FileName'} =~ /$reqfile/) and -e($self->{'FileName'})) 1042 { 1043 # fixed name contains requested file and file exists 1044 $self->{'FileSize'} = -s($self->{'FileName'}); 1045 return(1); 1046 } 1047 } 1048 elsif($self->{'RootDir'}) 1049 { 1050 # rootdir is fixed 1051 $reqfile = $self->{'RootDir'}.'/'.$reqfile; 1052 $self->{'_REQUEST_'}{'FileName'} = $reqfile; 1053 1054 if(-e($reqfile)) 1055 { 1056 # file exists in rootdir 1057 $self->{'FileSize'} = -s($reqfile); 1058 return(1); 1059 } 1060 } 1061 1062 return(undef); 1063 } 1064 1065 # 1066 # Usage: $requestOBJ->sendOACK(); 1067 # return 1 for success and undef for error (see $Net::TFTPd::LASTERROR for cause) 1068 # 1069 sub sendOACK 1070 { 1071 # the request object 1072 my $self = shift; 1073 my $udpserver = $self->{'_UDPSERVER_'}; 1074 my $retry = 0; 1075 1076 my ($datagram, $opcode, $datain); 1077 1078 while($retry < $self->{'ACKretries'}) 1079 { 1080 # send oack 1081 my $data = join("\0", %{ $self->{'_RESPONSE_'}{'RFC2347'} })."\0"; 1082 if($udpserver->send(pack("na*", TFTP_OPCODE_OACK, $data))) 1083 { 1084 # opcode is RRQ 1085 if($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_RRQ) 1086 { 1087 # vars for IO select 1088 my ($rin, $rout, $ein, $eout) = ('', '', '', ''); 1089 vec($rin, fileno($udpserver), 1) = 1; 1090 1091 # wait for acknowledge 1092 if(select($rout=$rin, undef, $eout=$ein, $self->{'ACKtimeout'})) 1093 { 1094 # read the message 1095 if($udpserver->recv($datagram, TFTP_MAX_BLKSIZE + 4)) 1096 { 1097 # decode the message 1098 ($opcode, $datain) = unpack("na*", $datagram); 1099 if($opcode == TFTP_OPCODE_ACK) 1100 { 1101 # message is ACK 1102 my $lastack = unpack("n", $datain); 1103 if($lastack) 1104 { 1105 # ack is not for block 0... ERROR 1106 $LASTERROR = sprintf "Received ACK for block %d instead of 0", $lastack; 1107 return(undef); 1108 } 1109 return 1; 1110 } 1111 elsif($opcode == TFTP_OPCODE_ERROR) 1112 { 1113 # message is ERR 1114 $LASTERROR = sprintf "TFTP error message: %s", $datain; 1115 return(undef); 1116 } 1117 else 1118 { 1119 # other messages... 1120 $LASTERROR = sprintf "Opcode %d not supported as a reply to OACK\n", $opcode; 1121 return(undef); 1122 } 1123 } 1124 else 1125 { 1126 $! = $udpserver->sockopt(SO_ERROR); 1127 $LASTERROR = sprintf "Socket RECV error: %s\n", $!; 1128 return (undef); 1129 } 1130 } 1131 else 1132 { 1133 $LASTERROR = sprintf "Retry %d - timeout occurred waiting reply for OACK packet\n", $retry; 1134 $debug and carp($LASTERROR); 1135 $retry++; 1136 } 1137 } 1138 elsif($self->{'_REQUEST_'}{'OPCODE'} eq TFTP_OPCODE_WRQ) 1139 { 1140 # opcode is WRQ 1141 return(1); 1142 } 1143 } 1144 else 1145 { 1146 $! = $udpserver->sockopt(SO_ERROR); 1147 $LASTERROR = sprintf "Socket SEND error: %s\n", $!; 1148 return(undef); 1149 } 1150 } 1151 } 1152 1153 # 1154 # Usage: $requestOBJ->sendERR($code, $message); 1155 # returns 1 if success, undef if error 1156 # 1157 sub sendERR 1158 { 1159 my $self = shift; 1160 my($errcode, $errmsg) = @_; 1161 # modified for supporting NETASCII transfers on 25/05/2009 1162 #$errmsg or $errmsg = ''; 1163 $errmsg or $errmsg = $ERRORS{$errcode}; 1164 1165 my $udpserver = $self->{'_UDPSERVER_'}; 1166 1167 if($udpserver->send(pack("nnZ*", 5, $errcode, $errmsg))) 1168 { 1169 return(1); 1170 } 1171 else 1172 { 1173 $! = $udpserver->sockopt(SO_ERROR); 1174 $LASTERROR = sprintf "Socket SEND error: %s\n", $!; 1175 return(undef); 1176 } 1177 } 1178 1179 sub error 1180 { 1181 return($LASTERROR); 1182 } 1183 1184 # Preloaded methods go here. 1185 1186 1; 1187 __END__ 1188 1189 # Below is stub documentation for your module. You better edit it! 1190 1191 =head1 NAME 1192 1193 Net::TFTPd - Perl extension for Trivial File Transfer Protocol Server 1194 1195 =head1 SYNOPSIS 1196 1197 use strict; 1198 use Net::TFTPd; 1199 1200 my $tftpdOBJ = Net::TFTPd->new('RootDir' => 'path/to/files') 1201 or die "Error creating TFTPd listener: %s", Net::TFTPd->error; 1202 1203 my $tftpRQ = $tftpdOBJ->waitRQ(10) 1204 or die "Error waiting for TFTP request: %s", Net::TFTPd->error; 1205 1206 $tftpRQ->processRQ() 1207 or die "Error processing TFTP request: %s", Net::TFTPd->error; 1208 1209 printf "%u bytes has been transferred", $tftpRQ->getTotalBytes() || 0; 1210 1211 =head1 DESCRIPTION 1212 1213 C<Net::TFTPd> is a class implementing a simple I<Trivial File Transfer Protocol> server in Perl as described in RFC1350. 1214 1215 C<Net::TFTPd> also supports the TFTP Option Extension (as described in RFC2347), with the following options: 1216 1217 RFC2348 TFTP Blocksize Option 1218 RFC2349 TFTP Timeout Interval and Transfer Size Options 1219 1220 =head1 EXPORT 1221 1222 None by default. 1223 1224 =head2 %OPCODES 1225 1226 The %OPCODES tag exports the I<%OPCODES> hash: 1227 1228 %OPCODES = ( 1229 1 => 'RRQ', 1230 2 => 'WRQ', 1231 3 => 'DATA', 1232 4 => 'ACK', 1233 5 => 'ERROR', 1234 6 => 'OACK', 1235 'RRQ' => 1, 1236 'WRQ' => 2, 1237 'DATA' => 3, 1238 'ACK' => 4, 1239 'ERROR' => 5, 1240 'OACK' => 6 1241 ); 1242 1243 =head1 Listener constructor 1244 1245 =head2 new() 1246 1247 $listener = new Net::TFTPd( ['RootDir' => 'path/to/files' | 'FileName' => 'path/to/file'] [, OPTIONS ] ); 1248 1249 or 1250 1251 $listener = Net::TFTPd->new( ['RootDir' => 'path/to/files' | 'FileName' => 'path/to/file'] [, OPTIONS ] ); 1252 1253 Create a new Net::TFTPd object where 'path/to/files' is the default path to file repository 1254 or 'path/to/file' is the single file allowed for download, and OPTIONS are the default server 1255 options. 1256 1257 Valid options are: 1258 1259 Option Description Default 1260 ------ ----------- ------- 1261 LocalAddr Interface to bind to (for multi-homed server) any 1262 LocalPort Port to bind server to 69 1263 Timeout Timeout in seconds to wait for a request 10 1264 ACKtimeout Timeout in seconds to wait for an ACK packet 4 1265 ACKretries Maximum number of retries waiting for ACK 4 1266 Readable Clients are allowed to read files 1 1267 Writable Clients are allowed to write files 0 1268 BlkSize Minimum blocksize to negotiate for transfers 512 1269 CallBack Reference to code executed for each transferred block - 1270 Debug Activates debug mode (verbose) 0 1271 1272 =head2 CallBack 1273 1274 The CallBack code is called by processRQ method for each tranferred block. 1275 1276 The code receives (into @_ array) a reference to internal I<$request> object. 1277 1278 Example: 1279 1280 sub callback 1281 { 1282 my $req = shift; 1283 printf "block: %u\/%u\n", $req->{'_REQUEST_'}{'LASTACK'}, $req->{'_REQUEST_'}{'LASTBLK'}; 1284 } 1285 1286 my $tftpdOBJ = Net::TFTPd->new('RootDir' => 'c:/temp', 'Timeout' => 60, 'CallBack' => \&callback) or die Net::TFTPd->error; 1287 1288 =head1 Listener methods 1289 1290 =head2 waitRQ() 1291 1292 $request = $listener->waitRQ([Timeout]); 1293 1294 Waits for a client request (RRQ or WRQ) and returns a I<$request> object or I<undef> if timed out. 1295 1296 If I<Timeout> is missing, the timeout defined for I<$listener> object is used instead. 1297 1298 When the method returns, the program should fork() and process the request invoking processRQ() while the parent process should re-start waiting for another request. 1299 1300 =head1 Request methods 1301 1302 =head2 processRQ() 1303 1304 $ret = $request->processRQ(); 1305 1306 Processes a request and returns 1 if success, undef if error. 1307 1308 =head2 getFileName() 1309 1310 $ret = $request->getFileName(); 1311 1312 Returns the requested file name. 1313 1314 =head2 getMode() 1315 1316 $ret = $request->getMode(); 1317 1318 Returns the transfer mode for the request. 1319 1320 =head2 getBlkSize() 1321 1322 $ret = $request->getBlkSize(); 1323 1324 Returns the block size used for the transfer. 1325 1326 =head2 getPeerAddr() 1327 1328 $ret = $request->getPeerAddr(); 1329 1330 Returns the address of the requesting client. 1331 1332 =head2 getPeerPort() 1333 1334 $ret = $request->getPeerMode(); 1335 1336 Returns the port of the requesting client. 1337 1338 =head2 getTotalBytes() 1339 1340 $ret = $request->getTotalBytes(); 1341 1342 Returns the number of bytes transferred for the request. 1343 1344 =head1 CREDITS 1345 1346 Thanks to E<lt>VinceE<gt> for the NETASCII support and transferred bytes patch. 1347 1348 =head1 AUTHOR 1349 1350 Luigino Masarati, E<lt>lmasarati@hotmail.comE<gt> 1351 1352 =head1 SEE ALSO 1353 1354 L<Net::TFTP>. 1355 1356 =cut 1357