1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.net.tftp;
19
20 import java.io.BufferedInputStream;
21 import java.io.BufferedOutputStream;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.OutputStream;
29 import java.io.PrintStream;
30 import java.net.SocketTimeoutException;
31 import java.util.HashSet;
32 import java.util.Iterator;
33
34 import org.apache.commons.net.io.FromNetASCIIOutputStream;
35 import org.apache.commons.net.io.ToNetASCIIInputStream;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83 public class TFTPServer implements Runnable
84 {
85 private static final int DEFAULT_TFTP_PORT = 69;
86 public static enum ServerMode { GET_ONLY, PUT_ONLY, GET_AND_PUT; }
87
88 private final HashSet<TFTPTransfer> transfers_ = new HashSet<TFTPTransfer>();
89 private volatile boolean shutdownServer = false;
90 private TFTP serverTftp_;
91 private File serverReadDirectory_;
92 private File serverWriteDirectory_;
93 private final int port_;
94 private Exception serverException = null;
95 private final ServerMode mode_;
96
97
98 private static final PrintStream nullStream = new PrintStream(
99 new OutputStream() {
100 @Override
101 public void write(int b){}
102 @Override
103 public void write(byte[] b) throws IOException {}
104 }
105 );
106
107
108
109 private PrintStream log_;
110 private PrintStream logError_;
111
112 private int maxTimeoutRetries_ = 3;
113 private int socketTimeout_;
114 private Thread serverThread;
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134 public TFTPServer(File serverReadDirectory, File serverWriteDirectory, ServerMode mode)
135 throws IOException
136 {
137 this(serverReadDirectory, serverWriteDirectory, DEFAULT_TFTP_PORT, mode, null, null);
138 }
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158 public TFTPServer(File serverReadDirectory, File serverWriteDirectory, int port, ServerMode mode,
159 PrintStream log, PrintStream errorLog) throws IOException
160 {
161 port_ = port;
162 mode_ = mode;
163 log_ = (log == null ? nullStream: log);
164 logError_ = (errorLog == null ? nullStream : errorLog);
165 launch(serverReadDirectory, serverWriteDirectory);
166 }
167
168
169
170
171
172
173 public void setMaxTimeoutRetries(int retries)
174 {
175 if (retries < 0)
176 {
177 throw new RuntimeException("Invalid Value");
178 }
179 maxTimeoutRetries_ = retries;
180 }
181
182
183
184
185 public int getMaxTimeoutRetries()
186 {
187 return maxTimeoutRetries_;
188 }
189
190
191
192
193
194
195 public void setSocketTimeout(int timeout)
196 {
197 if (timeout < 10)
198 {
199 throw new RuntimeException("Invalid Value");
200 }
201 socketTimeout_ = timeout;
202 }
203
204
205
206
207 public int getSocketTimeout()
208 {
209 return socketTimeout_;
210 }
211
212
213
214
215 private void launch(File serverReadDirectory, File serverWriteDirectory) throws IOException
216 {
217 log_.println("Starting TFTP Server on port " + port_ + ". Read directory: "
218 + serverReadDirectory + " Write directory: " + serverWriteDirectory
219 + " Server Mode is " + mode_);
220
221 serverReadDirectory_ = serverReadDirectory.getCanonicalFile();
222 if (!serverReadDirectory_.exists() || !serverReadDirectory.isDirectory())
223 {
224 throw new IOException("The server read directory " + serverReadDirectory_
225 + " does not exist");
226 }
227
228 serverWriteDirectory_ = serverWriteDirectory.getCanonicalFile();
229 if (!serverWriteDirectory_.exists() || !serverWriteDirectory.isDirectory())
230 {
231 throw new IOException("The server write directory " + serverWriteDirectory_
232 + " does not exist");
233 }
234
235 serverTftp_ = new TFTP();
236
237
238 socketTimeout_ = serverTftp_.getDefaultTimeout();
239
240
241 serverTftp_.setDefaultTimeout(0);
242
243 serverTftp_.open(port_);
244
245 serverThread = new Thread(this);
246 serverThread.setDaemon(true);
247 serverThread.start();
248 }
249
250 @Override
251 protected void finalize() throws Throwable
252 {
253 shutdown();
254 }
255
256
257
258
259
260
261
262
263 public boolean isRunning() throws Exception
264 {
265 if (shutdownServer && serverException != null)
266 {
267 throw serverException;
268 }
269 return !shutdownServer;
270 }
271
272
273 public void run()
274 {
275 try
276 {
277 while (!shutdownServer)
278 {
279 TFTPPacket tftpPacket;
280
281 tftpPacket = serverTftp_.receive();
282
283 TFTPTransfer tt = new TFTPTransfer(tftpPacket);
284 synchronized(transfers_)
285 {
286 transfers_.add(tt);
287 }
288
289 Thread thread = new Thread(tt);
290 thread.setDaemon(true);
291 thread.start();
292 }
293 }
294 catch (Exception e)
295 {
296 if (!shutdownServer)
297 {
298 serverException = e;
299 logError_.println("Unexpected Error in TFTP Server - Server shut down! + " + e);
300 }
301 }
302 finally
303 {
304 shutdownServer = true;
305 if (serverTftp_ != null && serverTftp_.isOpen())
306 {
307 serverTftp_.close();
308 }
309 }
310 }
311
312
313
314
315
316 public void shutdown()
317 {
318 shutdownServer = true;
319
320 synchronized(transfers_)
321 {
322 Iterator<TFTPTransfer> it = transfers_.iterator();
323 while (it.hasNext())
324 {
325 it.next().shutdown();
326 }
327 }
328
329 try
330 {
331 serverTftp_.close();
332 }
333 catch (RuntimeException e)
334 {
335
336 }
337
338 try {
339 serverThread.join();
340 } catch (InterruptedException e) {
341
342 }
343 }
344
345
346
347
348 private class TFTPTransfer implements Runnable
349 {
350 private final TFTPPacket tftpPacket_;
351
352 private boolean shutdownTransfer = false;
353
354 TFTP transferTftp_ = null;
355
356 public TFTPTransfer(TFTPPacket tftpPacket)
357 {
358 tftpPacket_ = tftpPacket;
359 }
360
361 public void shutdown()
362 {
363 shutdownTransfer = true;
364 try
365 {
366 transferTftp_.close();
367 }
368 catch (RuntimeException e)
369 {
370
371 }
372 }
373
374
375 public void run()
376 {
377 try
378 {
379 transferTftp_ = new TFTP();
380
381 transferTftp_.beginBufferedOps();
382 transferTftp_.setDefaultTimeout(socketTimeout_);
383
384 transferTftp_.open();
385
386 if (tftpPacket_ instanceof TFTPReadRequestPacket)
387 {
388 handleRead(((TFTPReadRequestPacket) tftpPacket_));
389 }
390 else if (tftpPacket_ instanceof TFTPWriteRequestPacket)
391 {
392 handleWrite((TFTPWriteRequestPacket) tftpPacket_);
393 }
394 else
395 {
396 log_.println("Unsupported TFTP request (" + tftpPacket_ + ") - ignored.");
397 }
398 }
399 catch (Exception e)
400 {
401 if (!shutdownTransfer)
402 {
403 logError_
404 .println("Unexpected Error in during TFTP file transfer. Transfer aborted. "
405 + e);
406 }
407 }
408 finally
409 {
410 try
411 {
412 if (transferTftp_ != null && transferTftp_.isOpen())
413 {
414 transferTftp_.endBufferedOps();
415 transferTftp_.close();
416 }
417 }
418 catch (Exception e)
419 {
420
421 }
422 synchronized(transfers_)
423 {
424 transfers_.remove(this);
425 }
426 }
427 }
428
429
430
431
432 private void handleRead(TFTPReadRequestPacket trrp) throws IOException, TFTPPacketException
433 {
434 InputStream is = null;
435 try
436 {
437 if (mode_ == ServerMode.PUT_ONLY)
438 {
439 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
440 .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
441 "Read not allowed by server."));
442 return;
443 }
444
445 try
446 {
447 is = new BufferedInputStream(new FileInputStream(buildSafeFile(
448 serverReadDirectory_, trrp.getFilename(), false)));
449 }
450 catch (FileNotFoundException e)
451 {
452 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
453 .getPort(), TFTPErrorPacket.FILE_NOT_FOUND, e.getMessage()));
454 return;
455 }
456 catch (Exception e)
457 {
458 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
459 .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
460 return;
461 }
462
463 if (trrp.getMode() == TFTP.NETASCII_MODE)
464 {
465 is = new ToNetASCIIInputStream(is);
466 }
467
468 byte[] temp = new byte[TFTPDataPacket.MAX_DATA_LENGTH];
469
470 TFTPPacket answer;
471
472 int block = 1;
473 boolean sendNext = true;
474
475 int readLength = TFTPDataPacket.MAX_DATA_LENGTH;
476
477 TFTPDataPacket lastSentData = null;
478
479
480
481 while (readLength == TFTPDataPacket.MAX_DATA_LENGTH && !shutdownTransfer)
482 {
483 if (sendNext)
484 {
485 readLength = is.read(temp);
486 if (readLength == -1)
487 {
488 readLength = 0;
489 }
490
491 lastSentData = new TFTPDataPacket(trrp.getAddress(), trrp.getPort(), block,
492 temp, 0, readLength);
493 transferTftp_.bufferedSend(lastSentData);
494 }
495
496 answer = null;
497
498 int timeoutCount = 0;
499
500 while (!shutdownTransfer
501 && (answer == null || !answer.getAddress().equals(trrp.getAddress()) || answer
502 .getPort() != trrp.getPort()))
503 {
504
505 if (answer != null)
506 {
507
508
509
510 log_.println("TFTP Server ignoring message from unexpected source.");
511 transferTftp_.bufferedSend(new TFTPErrorPacket(answer.getAddress(),
512 answer.getPort(), TFTPErrorPacket.UNKNOWN_TID,
513 "Unexpected Host or Port"));
514 }
515 try
516 {
517 answer = transferTftp_.bufferedReceive();
518 }
519 catch (SocketTimeoutException e)
520 {
521 if (timeoutCount >= maxTimeoutRetries_)
522 {
523 throw e;
524 }
525
526
527 timeoutCount++;
528 transferTftp_.bufferedSend(lastSentData);
529 continue;
530 }
531 }
532
533 if (answer == null || !(answer instanceof TFTPAckPacket))
534 {
535 if (!shutdownTransfer)
536 {
537 logError_
538 .println("Unexpected response from tftp client during transfer ("
539 + answer + "). Transfer aborted.");
540 }
541 break;
542 }
543 else
544 {
545
546
547 TFTPAckPacket ack = (TFTPAckPacket) answer;
548 if (ack.getBlockNumber() != block)
549 {
550
551
552
553
554
555
556
557
558 sendNext = false;
559 }
560 else
561 {
562
563 block++;
564 if (block > 65535)
565 {
566
567 block = 0;
568 }
569 sendNext = true;
570 }
571 }
572 }
573 }
574 finally
575 {
576 try
577 {
578 if (is != null)
579 {
580 is.close();
581 }
582 }
583 catch (IOException e)
584 {
585
586 }
587 }
588 }
589
590
591
592
593 private void handleWrite(TFTPWriteRequestPacket twrp) throws IOException,
594 TFTPPacketException
595 {
596 OutputStream bos = null;
597 try
598 {
599 if (mode_ == ServerMode.GET_ONLY)
600 {
601 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
602 .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
603 "Write not allowed by server."));
604 return;
605 }
606
607 int lastBlock = 0;
608 String fileName = twrp.getFilename();
609
610 try
611 {
612 File temp = buildSafeFile(serverWriteDirectory_, fileName, true);
613 if (temp.exists())
614 {
615 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
616 .getPort(), TFTPErrorPacket.FILE_EXISTS, "File already exists"));
617 return;
618 }
619 bos = new BufferedOutputStream(new FileOutputStream(temp));
620
621 if (twrp.getMode() == TFTP.NETASCII_MODE)
622 {
623 bos = new FromNetASCIIOutputStream(bos);
624 }
625 }
626 catch (Exception e)
627 {
628 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
629 .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
630 return;
631 }
632
633 TFTPAckPacket lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
634 transferTftp_.bufferedSend(lastSentAck);
635
636 while (true)
637 {
638
639 TFTPPacket dataPacket = null;
640
641 int timeoutCount = 0;
642
643 while (!shutdownTransfer
644 && (dataPacket == null
645 || !dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket
646 .getPort() != twrp.getPort()))
647 {
648
649 if (dataPacket != null)
650 {
651
652
653
654 log_.println("TFTP Server ignoring message from unexpected source.");
655 transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket.getAddress(),
656 dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID,
657 "Unexpected Host or Port"));
658 }
659
660 try
661 {
662 dataPacket = transferTftp_.bufferedReceive();
663 }
664 catch (SocketTimeoutException e)
665 {
666 if (timeoutCount >= maxTimeoutRetries_)
667 {
668 throw e;
669 }
670
671 transferTftp_.bufferedSend(lastSentAck);
672 timeoutCount++;
673 continue;
674 }
675 }
676
677 if (dataPacket != null && dataPacket instanceof TFTPWriteRequestPacket)
678 {
679
680 lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
681 transferTftp_.bufferedSend(lastSentAck);
682 }
683 else if (dataPacket == null || !(dataPacket instanceof TFTPDataPacket))
684 {
685 if (!shutdownTransfer)
686 {
687 logError_
688 .println("Unexpected response from tftp client during transfer ("
689 + dataPacket + "). Transfer aborted.");
690 }
691 break;
692 }
693 else
694 {
695 int block = ((TFTPDataPacket) dataPacket).getBlockNumber();
696 byte[] data = ((TFTPDataPacket) dataPacket).getData();
697 int dataLength = ((TFTPDataPacket) dataPacket).getDataLength();
698 int dataOffset = ((TFTPDataPacket) dataPacket).getDataOffset();
699
700 if (block > lastBlock || (lastBlock == 65535 && block == 0))
701 {
702
703
704 bos.write(data, dataOffset, dataLength);
705 lastBlock = block;
706 }
707
708 lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), block);
709 transferTftp_.bufferedSend(lastSentAck);
710 if (dataLength < TFTPDataPacket.MAX_DATA_LENGTH)
711 {
712
713 bos.close();
714
715
716
717 for (int i = 0; i < maxTimeoutRetries_; i++)
718 {
719 try
720 {
721 dataPacket = transferTftp_.bufferedReceive();
722 }
723 catch (SocketTimeoutException e)
724 {
725
726
727 break;
728 }
729
730 if (dataPacket != null
731 && (!dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket
732 .getPort() != twrp.getPort()))
733 {
734
735 transferTftp_
736 .bufferedSend(new TFTPErrorPacket(dataPacket
737 .getAddress(), dataPacket.getPort(),
738 TFTPErrorPacket.UNKNOWN_TID,
739 "Unexpected Host or Port"));
740 }
741 else
742 {
743
744
745
746 transferTftp_.bufferedSend(lastSentAck);
747 }
748 }
749
750
751 break;
752 }
753 }
754 }
755 }
756 finally
757 {
758 if (bos != null)
759 {
760 bos.close();
761 }
762 }
763 }
764
765
766
767
768
769 private File buildSafeFile(File serverDirectory, String fileName, boolean createSubDirs)
770 throws IOException
771 {
772 File temp = new File(serverDirectory, fileName);
773 temp = temp.getCanonicalFile();
774
775 if (!isSubdirectoryOf(serverDirectory, temp))
776 {
777 throw new IOException("Cannot access files outside of tftp server root.");
778 }
779
780
781 if (createSubDirs)
782 {
783 createDirectory(temp.getParentFile());
784 }
785
786 return temp;
787 }
788
789
790
791
792 private void createDirectory(File file) throws IOException
793 {
794 File parent = file.getParentFile();
795 if (parent == null)
796 {
797 throw new IOException("Unexpected error creating requested directory");
798 }
799 if (!parent.exists())
800 {
801
802 createDirectory(parent);
803 }
804
805 if (parent.isDirectory())
806 {
807 if (file.isDirectory())
808 {
809 return;
810 }
811 boolean result = file.mkdir();
812 if (!result)
813 {
814 throw new IOException("Couldn't create requested directory");
815 }
816 }
817 else
818 {
819 throw new IOException(
820 "Invalid directory path - file in the way of requested folder");
821 }
822 }
823
824
825
826
827 private boolean isSubdirectoryOf(File parent, File child)
828 {
829 File childsParent = child.getParentFile();
830 if (childsParent == null)
831 {
832 return false;
833 }
834 if (childsParent.equals(parent))
835 {
836 return true;
837 }
838 else
839 {
840 return isSubdirectoryOf(parent, childsParent);
841 }
842 }
843 }
844
845
846
847
848
849
850 public void setLog(PrintStream log)
851 {
852 this.log_ = log;
853 }
854
855
856
857
858
859
860 public void setLogError(PrintStream logError)
861 {
862 this.logError_ = logError;
863 }
864 }