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 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 int port_;
94 private Exception serverException = null;
95 private 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 public void run()
273 {
274 try
275 {
276 while (!shutdownServer)
277 {
278 TFTPPacket tftpPacket;
279
280 tftpPacket = serverTftp_.receive();
281
282 TFTPTransfer tt = new TFTPTransfer(tftpPacket);
283 synchronized(transfers_)
284 {
285 transfers_.add(tt);
286 }
287
288 Thread thread = new Thread(tt);
289 thread.setDaemon(true);
290 thread.start();
291 }
292 }
293 catch (Exception e)
294 {
295 if (!shutdownServer)
296 {
297 serverException = e;
298 logError_.println("Unexpected Error in TFTP Server - Server shut down! + " + e);
299 }
300 }
301 finally
302 {
303 shutdownServer = true;
304 if (serverTftp_ != null && serverTftp_.isOpen())
305 {
306 serverTftp_.close();
307 }
308 }
309 }
310
311
312
313
314
315 public void shutdown()
316 {
317 shutdownServer = true;
318
319 synchronized(transfers_)
320 {
321 Iterator<TFTPTransfer> it = transfers_.iterator();
322 while (it.hasNext())
323 {
324 it.next().shutdown();
325 }
326 }
327
328 try
329 {
330 serverTftp_.close();
331 }
332 catch (RuntimeException e)
333 {
334
335 }
336
337 try {
338 serverThread.join();
339 } catch (InterruptedException e) {
340
341 }
342 }
343
344
345
346
347 private class TFTPTransfer implements Runnable
348 {
349 private TFTPPacket tftpPacket_;
350
351 private boolean shutdownTransfer = false;
352
353 TFTP transferTftp_ = null;
354
355 public TFTPTransfer(TFTPPacket tftpPacket)
356 {
357 tftpPacket_ = tftpPacket;
358 }
359
360 public void shutdown()
361 {
362 shutdownTransfer = true;
363 try
364 {
365 transferTftp_.close();
366 }
367 catch (RuntimeException e)
368 {
369
370 }
371 }
372
373 public void run()
374 {
375 try
376 {
377 transferTftp_ = new TFTP();
378
379 transferTftp_.beginBufferedOps();
380 transferTftp_.setDefaultTimeout(socketTimeout_);
381
382 transferTftp_.open();
383
384 if (tftpPacket_ instanceof TFTPReadRequestPacket)
385 {
386 handleRead(((TFTPReadRequestPacket) tftpPacket_));
387 }
388 else if (tftpPacket_ instanceof TFTPWriteRequestPacket)
389 {
390 handleWrite((TFTPWriteRequestPacket) tftpPacket_);
391 }
392 else
393 {
394 log_.println("Unsupported TFTP request (" + tftpPacket_ + ") - ignored.");
395 }
396 }
397 catch (Exception e)
398 {
399 if (!shutdownTransfer)
400 {
401 logError_
402 .println("Unexpected Error in during TFTP file transfer. Transfer aborted. "
403 + e);
404 }
405 }
406 finally
407 {
408 try
409 {
410 if (transferTftp_ != null && transferTftp_.isOpen())
411 {
412 transferTftp_.endBufferedOps();
413 transferTftp_.close();
414 }
415 }
416 catch (Exception e)
417 {
418
419 }
420 synchronized(transfers_)
421 {
422 transfers_.remove(this);
423 }
424 }
425 }
426
427
428
429
430 private void handleRead(TFTPReadRequestPacket trrp) throws IOException, TFTPPacketException
431 {
432 InputStream is = null;
433 try
434 {
435 if (mode_ == ServerMode.PUT_ONLY)
436 {
437 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
438 .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
439 "Read not allowed by server."));
440 return;
441 }
442
443 try
444 {
445 is = new BufferedInputStream(new FileInputStream(buildSafeFile(
446 serverReadDirectory_, trrp.getFilename(), false)));
447 }
448 catch (FileNotFoundException e)
449 {
450 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
451 .getPort(), TFTPErrorPacket.FILE_NOT_FOUND, e.getMessage()));
452 return;
453 }
454 catch (Exception e)
455 {
456 transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(), trrp
457 .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
458 return;
459 }
460
461 if (trrp.getMode() == TFTP.NETASCII_MODE)
462 {
463 is = new ToNetASCIIInputStream(is);
464 }
465
466 byte[] temp = new byte[TFTPDataPacket.MAX_DATA_LENGTH];
467
468 TFTPPacket answer;
469
470 int block = 1;
471 boolean sendNext = true;
472
473 int readLength = TFTPDataPacket.MAX_DATA_LENGTH;
474
475 TFTPDataPacket lastSentData = null;
476
477
478
479 while (readLength == TFTPDataPacket.MAX_DATA_LENGTH && !shutdownTransfer)
480 {
481 if (sendNext)
482 {
483 readLength = is.read(temp);
484 if (readLength == -1)
485 {
486 readLength = 0;
487 }
488
489 lastSentData = new TFTPDataPacket(trrp.getAddress(), trrp.getPort(), block,
490 temp, 0, readLength);
491 transferTftp_.bufferedSend(lastSentData);
492 }
493
494 answer = null;
495
496 int timeoutCount = 0;
497
498 while (!shutdownTransfer
499 && (answer == null || !answer.getAddress().equals(trrp.getAddress()) || answer
500 .getPort() != trrp.getPort()))
501 {
502
503 if (answer != null)
504 {
505
506
507
508 log_.println("TFTP Server ignoring message from unexpected source.");
509 transferTftp_.bufferedSend(new TFTPErrorPacket(answer.getAddress(),
510 answer.getPort(), TFTPErrorPacket.UNKNOWN_TID,
511 "Unexpected Host or Port"));
512 }
513 try
514 {
515 answer = transferTftp_.bufferedReceive();
516 }
517 catch (SocketTimeoutException e)
518 {
519 if (timeoutCount >= maxTimeoutRetries_)
520 {
521 throw e;
522 }
523
524
525 timeoutCount++;
526 transferTftp_.bufferedSend(lastSentData);
527 continue;
528 }
529 }
530
531 if (answer == null || !(answer instanceof TFTPAckPacket))
532 {
533 if (!shutdownTransfer)
534 {
535 logError_
536 .println("Unexpected response from tftp client during transfer ("
537 + answer + "). Transfer aborted.");
538 }
539 break;
540 }
541 else
542 {
543
544
545 TFTPAckPacket ack = (TFTPAckPacket) answer;
546 if (ack.getBlockNumber() != block)
547 {
548
549
550
551
552
553
554
555
556 sendNext = false;
557 }
558 else
559 {
560
561 block++;
562 if (block > 65535)
563 {
564
565 block = 0;
566 }
567 sendNext = true;
568 }
569 }
570 }
571 }
572 finally
573 {
574 try
575 {
576 if (is != null)
577 {
578 is.close();
579 }
580 }
581 catch (IOException e)
582 {
583
584 }
585 }
586 }
587
588
589
590
591 private void handleWrite(TFTPWriteRequestPacket twrp) throws IOException,
592 TFTPPacketException
593 {
594 OutputStream bos = null;
595 try
596 {
597 if (mode_ == ServerMode.GET_ONLY)
598 {
599 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
600 .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
601 "Write not allowed by server."));
602 return;
603 }
604
605 int lastBlock = 0;
606 String fileName = twrp.getFilename();
607
608 try
609 {
610 File temp = buildSafeFile(serverWriteDirectory_, fileName, true);
611 if (temp.exists())
612 {
613 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
614 .getPort(), TFTPErrorPacket.FILE_EXISTS, "File already exists"));
615 return;
616 }
617 bos = new BufferedOutputStream(new FileOutputStream(temp));
618
619 if (twrp.getMode() == TFTP.NETASCII_MODE)
620 {
621 bos = new FromNetASCIIOutputStream(bos);
622 }
623 }
624 catch (Exception e)
625 {
626 transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
627 .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
628 return;
629 }
630
631 TFTPAckPacket lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
632 transferTftp_.bufferedSend(lastSentAck);
633
634 while (true)
635 {
636
637 TFTPPacket dataPacket = null;
638
639 int timeoutCount = 0;
640
641 while (!shutdownTransfer
642 && (dataPacket == null
643 || !dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket
644 .getPort() != twrp.getPort()))
645 {
646
647 if (dataPacket != null)
648 {
649
650
651
652 log_.println("TFTP Server ignoring message from unexpected source.");
653 transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket.getAddress(),
654 dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID,
655 "Unexpected Host or Port"));
656 }
657
658 try
659 {
660 dataPacket = transferTftp_.bufferedReceive();
661 }
662 catch (SocketTimeoutException e)
663 {
664 if (timeoutCount >= maxTimeoutRetries_)
665 {
666 throw e;
667 }
668
669 transferTftp_.bufferedSend(lastSentAck);
670 timeoutCount++;
671 continue;
672 }
673 }
674
675 if (dataPacket != null && dataPacket instanceof TFTPWriteRequestPacket)
676 {
677
678 lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
679 transferTftp_.bufferedSend(lastSentAck);
680 }
681 else if (dataPacket == null || !(dataPacket instanceof TFTPDataPacket))
682 {
683 if (!shutdownTransfer)
684 {
685 logError_
686 .println("Unexpected response from tftp client during transfer ("
687 + dataPacket + "). Transfer aborted.");
688 }
689 break;
690 }
691 else
692 {
693 int block = ((TFTPDataPacket) dataPacket).getBlockNumber();
694 byte[] data = ((TFTPDataPacket) dataPacket).getData();
695 int dataLength = ((TFTPDataPacket) dataPacket).getDataLength();
696 int dataOffset = ((TFTPDataPacket) dataPacket).getDataOffset();
697
698 if (block > lastBlock || (lastBlock == 65535 && block == 0))
699 {
700
701
702 bos.write(data, dataOffset, dataLength);
703 lastBlock = block;
704 }
705
706 lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), block);
707 transferTftp_.bufferedSend(lastSentAck);
708 if (dataLength < TFTPDataPacket.MAX_DATA_LENGTH)
709 {
710
711 bos.close();
712
713
714
715 for (int i = 0; i < maxTimeoutRetries_; i++)
716 {
717 try
718 {
719 dataPacket = transferTftp_.bufferedReceive();
720 }
721 catch (SocketTimeoutException e)
722 {
723
724
725 break;
726 }
727
728 if (dataPacket != null
729 && (!dataPacket.getAddress().equals(twrp.getAddress()) || dataPacket
730 .getPort() != twrp.getPort()))
731 {
732
733 transferTftp_
734 .bufferedSend(new TFTPErrorPacket(dataPacket
735 .getAddress(), dataPacket.getPort(),
736 TFTPErrorPacket.UNKNOWN_TID,
737 "Unexpected Host or Port"));
738 }
739 else
740 {
741
742
743
744 transferTftp_.bufferedSend(lastSentAck);
745 }
746 }
747
748
749 break;
750 }
751 }
752 }
753 }
754 finally
755 {
756 if (bos != null)
757 {
758 bos.close();
759 }
760 }
761 }
762
763
764
765
766
767 private File buildSafeFile(File serverDirectory, String fileName, boolean createSubDirs)
768 throws IOException
769 {
770 File temp = new File(serverDirectory, fileName);
771 temp = temp.getCanonicalFile();
772
773 if (!isSubdirectoryOf(serverDirectory, temp))
774 {
775 throw new IOException("Cannot access files outside of tftp server root.");
776 }
777
778
779 if (createSubDirs)
780 {
781 createDirectory(temp.getParentFile());
782 }
783
784 return temp;
785 }
786
787
788
789
790 private void createDirectory(File file) throws IOException
791 {
792 File parent = file.getParentFile();
793 if (parent == null)
794 {
795 throw new IOException("Unexpected error creating requested directory");
796 }
797 if (!parent.exists())
798 {
799
800 createDirectory(parent);
801 }
802
803 if (parent.isDirectory())
804 {
805 if (file.isDirectory())
806 {
807 return;
808 }
809 boolean result = file.mkdir();
810 if (!result)
811 {
812 throw new IOException("Couldn't create requested directory");
813 }
814 }
815 else
816 {
817 throw new IOException(
818 "Invalid directory path - file in the way of requested folder");
819 }
820 }
821
822
823
824
825 private boolean isSubdirectoryOf(File parent, File child)
826 {
827 File childsParent = child.getParentFile();
828 if (childsParent == null)
829 {
830 return false;
831 }
832 if (childsParent.equals(parent))
833 {
834 return true;
835 }
836 else
837 {
838 return isSubdirectoryOf(parent, childsParent);
839 }
840 }
841 }
842
843
844
845
846
847
848 public void setLog(PrintStream log)
849 {
850 this.log_ = log;
851 }
852
853
854
855
856
857
858 public void setLogError(PrintStream logError)
859 {
860 this.logError_ = logError;
861 }
862 }