File Coverage

blib/lib/Net/TFTPd.pm
Criterion Covered Total %
statement 53 428 12.3
branch 0 192 0.0
condition 0 35 0.0
subroutine 18 42 42.8
pod 10 24 41.6
total 81 721 11.2


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