1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.net.tftp;
  17. import java.io.IOException;
  18. import java.io.InputStream;
  19. import java.io.InterruptedIOException;
  20. import java.io.OutputStream;
  21. import java.net.InetAddress;
  22. import java.net.SocketException;
  23. import java.net.UnknownHostException;
  24. import org.apache.commons.net.io.FromNetASCIIOutputStream;
  25. import org.apache.commons.net.io.ToNetASCIIInputStream;
  26. /***
  27. * The TFTPClient class encapsulates all the aspects of the TFTP protocol
  28. * necessary to receive and send files through TFTP. It is derived from
  29. * the <a href="org.apache.commons.net.tftp.TFTP.html"> TFTP class </a> because
  30. * it is more convenient than using aggregation, and as a result exposes
  31. * the same set of methods to allow you to deal with the TFTP protocol
  32. * directly. However, almost every user should only be concerend with the
  33. * the <a href="org.apache.commons.net.DatagramSocketClient.html#open"> open() </a>,
  34. * <a href="org.apache.commons.net.DatagramSocketClient.html#close"> close() </a>,
  35. * <a href="#sendFile"> sendFile() </a>, and
  36. * <a href="#receiveFile"> receiveFile() </a> methods. Additionally, the
  37. * <a href="#setMaxTimeouts"> setMaxTimeouts() </a> and
  38. * <a href="org.apache.commons.net.DatagramSocketClient.html#setDefaultTimeout">
  39. * setDefaultTimeout() </a> methods may be of importance for performance
  40. * tuning.
  41. * <p>
  42. * Details regarding the TFTP protocol and the format of TFTP packets can
  43. * be found in RFC 783. But the point of these classes is to keep you
  44. * from having to worry about the internals.
  45. * <p>
  46. * <p>
  47. * @author Daniel F. Savarese
  48. * @see TFTP
  49. * @see TFTPPacket
  50. * @see TFTPPacketException
  51. ***/
  52. public class TFTPClient extends TFTP
  53. {
  54. /***
  55. * The default number of times a receive attempt is allowed to timeout
  56. * before ending attempts to retry the receive and failing. The default
  57. * is 5 timeouts.
  58. ***/
  59. public static final int DEFAULT_MAX_TIMEOUTS = 5;
  60. /*** The maximum number of timeouts allowed before failing. ***/
  61. private int __maxTimeouts;
  62. /***
  63. * Creates a TFTPClient instance with a default timeout of DEFAULT_TIMEOUT,
  64. * maximum timeouts value of DEFAULT_MAX_TIMEOUTS, a null socket,
  65. * and buffered operations disabled.
  66. ***/
  67. public TFTPClient()
  68. {
  69. __maxTimeouts = DEFAULT_MAX_TIMEOUTS;
  70. }
  71. /***
  72. * Sets the maximum number of times a receive attempt is allowed to
  73. * timeout during a receiveFile() or sendFile() operation before ending
  74. * attempts to retry the receive and failing.
  75. * The default is DEFAULT_MAX_TIMEOUTS.
  76. * <p>
  77. * @param numTimeouts The maximum number of timeouts to allow. Values
  78. * less than 1 should not be used, but if they are, they are
  79. * treated as 1.
  80. ***/
  81. public void setMaxTimeouts(int numTimeouts)
  82. {
  83. if (__maxTimeouts < 1)
  84. __maxTimeouts = 1;
  85. else
  86. __maxTimeouts = numTimeouts;
  87. }
  88. /***
  89. * Returns the maximum number of times a receive attempt is allowed to
  90. * timeout before ending attempts to retry the receive and failing.
  91. * <p>
  92. * @return The maximum number of timeouts allowed.
  93. ***/
  94. public int getMaxTimeouts()
  95. {
  96. return __maxTimeouts;
  97. }
  98. /***
  99. * Requests a named file from a remote host, writes the
  100. * file to an OutputStream, closes the connection, and returns the number
  101. * of bytes read. A local UDP socket must first be created by
  102. * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
  103. * invoking this method. This method will not close the OutputStream
  104. * containing the file; you must close it after the method invocation.
  105. * <p>
  106. * @param filename The name of the file to receive.
  107. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  108. * @param output The OutputStream to which the file should be written.
  109. * @param host The remote host serving the file.
  110. * @param port The port number of the remote TFTP server.
  111. * @exception IOException If an I/O error occurs. The nature of the
  112. * error will be reported in the message.
  113. ***/
  114. public int receiveFile(String filename, int mode, OutputStream output,
  115. InetAddress host, int port) throws IOException
  116. {
  117. int bytesRead, timeouts, lastBlock, block, hostPort, dataLength;
  118. TFTPPacket sent, received = null;
  119. TFTPErrorPacket error;
  120. TFTPDataPacket data;
  121. TFTPAckPacket ack = new TFTPAckPacket(host, port, 0);
  122. beginBufferedOps();
  123. dataLength = lastBlock = hostPort = bytesRead = 0;
  124. block = 1;
  125. if (mode == TFTP.ASCII_MODE)
  126. output = new FromNetASCIIOutputStream(output);
  127. sent =
  128. new TFTPReadRequestPacket(host, port, filename, mode);
  129. _sendPacket:
  130. do
  131. {
  132. bufferedSend(sent);
  133. _receivePacket:
  134. while (true)
  135. {
  136. timeouts = 0;
  137. while (timeouts < __maxTimeouts)
  138. {
  139. try
  140. {
  141. received = bufferedReceive();
  142. break;
  143. }
  144. catch (SocketException e)
  145. {
  146. if (++timeouts >= __maxTimeouts)
  147. {
  148. endBufferedOps();
  149. throw new IOException("Connection timed out.");
  150. }
  151. continue;
  152. }
  153. catch (InterruptedIOException e)
  154. {
  155. if (++timeouts >= __maxTimeouts)
  156. {
  157. endBufferedOps();
  158. throw new IOException("Connection timed out.");
  159. }
  160. continue;
  161. }
  162. catch (TFTPPacketException e)
  163. {
  164. endBufferedOps();
  165. throw new IOException("Bad packet: " + e.getMessage());
  166. }
  167. }
  168. // The first time we receive we get the port number and
  169. // answering host address (for hosts with multiple IPs)
  170. if (lastBlock == 0)
  171. {
  172. hostPort = received.getPort();
  173. ack.setPort(hostPort);
  174. if(!host.equals(received.getAddress()))
  175. {
  176. host = received.getAddress();
  177. ack.setAddress(host);
  178. sent.setAddress(host);
  179. }
  180. }
  181. // Comply with RFC 783 indication that an error acknowledgement
  182. // should be sent to originator if unexpected TID or host.
  183. if (host.equals(received.getAddress()) &&
  184. received.getPort() == hostPort)
  185. {
  186. switch (received.getType())
  187. {
  188. case TFTPPacket.ERROR:
  189. error = (TFTPErrorPacket)received;
  190. endBufferedOps();
  191. throw new IOException("Error code " + error.getError() +
  192. " received: " + error.getMessage());
  193. case TFTPPacket.DATA:
  194. data = (TFTPDataPacket)received;
  195. dataLength = data.getDataLength();
  196. lastBlock = data.getBlockNumber();
  197. if (lastBlock == block)
  198. {
  199. try
  200. {
  201. output.write(data.getData(), data.getDataOffset(),
  202. dataLength);
  203. }
  204. catch (IOException e)
  205. {
  206. error = new TFTPErrorPacket(host, hostPort,
  207. TFTPErrorPacket.OUT_OF_SPACE,
  208. "File write failed.");
  209. bufferedSend(error);
  210. endBufferedOps();
  211. throw e;
  212. }
  213. ++block;
  214. break _receivePacket;
  215. }
  216. else
  217. {
  218. discardPackets();
  219. if (lastBlock == (block - 1))
  220. continue _sendPacket; // Resend last acknowledgement.
  221. continue _receivePacket; // Start fetching packets again.
  222. }
  223. //break;
  224. default:
  225. endBufferedOps();
  226. throw new IOException("Received unexpected packet type.");
  227. }
  228. }
  229. else
  230. {
  231. error = new TFTPErrorPacket(received.getAddress(),
  232. received.getPort(),
  233. TFTPErrorPacket.UNKNOWN_TID,
  234. "Unexpected host or port.");
  235. bufferedSend(error);
  236. continue _sendPacket;
  237. }
  238. // We should never get here, but this is a safety to avoid
  239. // infinite loop. If only Java had the goto statement.
  240. //break;
  241. }
  242. ack.setBlockNumber(lastBlock);
  243. sent = ack;
  244. bytesRead += dataLength;
  245. } // First data packet less than 512 bytes signals end of stream.
  246. while (dataLength == TFTPPacket.SEGMENT_SIZE);
  247. bufferedSend(sent);
  248. endBufferedOps();
  249. return bytesRead;
  250. }
  251. /***
  252. * Requests a named file from a remote host, writes the
  253. * file to an OutputStream, closes the connection, and returns the number
  254. * of bytes read. A local UDP socket must first be created by
  255. * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
  256. * invoking this method. This method will not close the OutputStream
  257. * containing the file; you must close it after the method invocation.
  258. * <p>
  259. * @param filename The name of the file to receive.
  260. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  261. * @param output The OutputStream to which the file should be written.
  262. * @param hostname The name of the remote host serving the file.
  263. * @param port The port number of the remote TFTP server.
  264. * @exception IOException If an I/O error occurs. The nature of the
  265. * error will be reported in the message.
  266. * @exception UnknownHostException If the hostname cannot be resolved.
  267. ***/
  268. public int receiveFile(String filename, int mode, OutputStream output,
  269. String hostname, int port)
  270. throws UnknownHostException, IOException
  271. {
  272. return receiveFile(filename, mode, output, InetAddress.getByName(hostname),
  273. port);
  274. }
  275. /***
  276. * Same as calling receiveFile(filename, mode, output, host, TFTP.DEFAULT_PORT).
  277. *
  278. * @param filename The name of the file to receive.
  279. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  280. * @param output The OutputStream to which the file should be written.
  281. * @param host The remote host serving the file.
  282. * @exception IOException If an I/O error occurs. The nature of the
  283. * error will be reported in the message.
  284. ***/
  285. public int receiveFile(String filename, int mode, OutputStream output,
  286. InetAddress host)
  287. throws IOException
  288. {
  289. return receiveFile(filename, mode, output, host, DEFAULT_PORT);
  290. }
  291. /***
  292. * Same as calling receiveFile(filename, mode, output, hostname, TFTP.DEFAULT_PORT).
  293. *
  294. * @param filename The name of the file to receive.
  295. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  296. * @param output The OutputStream to which the file should be written.
  297. * @param hostname The name of the remote host serving the file.
  298. * @exception IOException If an I/O error occurs. The nature of the
  299. * error will be reported in the message.
  300. * @exception UnknownHostException If the hostname cannot be resolved.
  301. ***/
  302. public int receiveFile(String filename, int mode, OutputStream output,
  303. String hostname)
  304. throws UnknownHostException, IOException
  305. {
  306. return receiveFile(filename, mode, output, InetAddress.getByName(hostname),
  307. DEFAULT_PORT);
  308. }
  309. /***
  310. * Requests to send a file to a remote host, reads the file from an
  311. * InputStream, sends the file to the remote host, and closes the
  312. * connection. A local UDP socket must first be created by
  313. * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
  314. * invoking this method. This method will not close the InputStream
  315. * containing the file; you must close it after the method invocation.
  316. * <p>
  317. * @param filename The name the remote server should use when creating
  318. * the file on its file system.
  319. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  320. * @param host The remote host receiving the file.
  321. * @param port The port number of the remote TFTP server.
  322. * @exception IOException If an I/O error occurs. The nature of the
  323. * error will be reported in the message.
  324. ***/
  325. public void sendFile(String filename, int mode, InputStream input,
  326. InetAddress host, int port) throws IOException
  327. {
  328. int bytesRead, timeouts, lastBlock, block, hostPort, dataLength, offset;
  329. TFTPPacket sent, received = null;
  330. TFTPErrorPacket error;
  331. TFTPDataPacket data =
  332. new TFTPDataPacket(host, port, 0, _sendBuffer, 4, 0);
  333. ;
  334. TFTPAckPacket ack;
  335. beginBufferedOps();
  336. dataLength = lastBlock = hostPort = bytesRead = 0;
  337. block = 0;
  338. if (mode == TFTP.ASCII_MODE)
  339. input = new ToNetASCIIInputStream(input);
  340. sent =
  341. new TFTPWriteRequestPacket(host, port, filename, mode);
  342. _sendPacket:
  343. do
  344. {
  345. bufferedSend(sent);
  346. _receivePacket:
  347. while (true)
  348. {
  349. timeouts = 0;
  350. while (timeouts < __maxTimeouts)
  351. {
  352. try
  353. {
  354. received = bufferedReceive();
  355. break;
  356. }
  357. catch (SocketException e)
  358. {
  359. if (++timeouts >= __maxTimeouts)
  360. {
  361. endBufferedOps();
  362. throw new IOException("Connection timed out.");
  363. }
  364. continue;
  365. }
  366. catch (InterruptedIOException e)
  367. {
  368. if (++timeouts >= __maxTimeouts)
  369. {
  370. endBufferedOps();
  371. throw new IOException("Connection timed out.");
  372. }
  373. continue;
  374. }
  375. catch (TFTPPacketException e)
  376. {
  377. endBufferedOps();
  378. throw new IOException("Bad packet: " + e.getMessage());
  379. }
  380. }
  381. // The first time we receive we get the port number and
  382. // answering host address (for hosts with multiple IPs)
  383. if (lastBlock == 0)
  384. {
  385. hostPort = received.getPort();
  386. data.setPort(hostPort);
  387. if(!host.equals(received.getAddress()))
  388. {
  389. host = received.getAddress();
  390. data.setAddress(host);
  391. sent.setAddress(host);
  392. }
  393. }
  394. // Comply with RFC 783 indication that an error acknowledgement
  395. // should be sent to originator if unexpected TID or host.
  396. if (host.equals(received.getAddress()) &&
  397. received.getPort() == hostPort)
  398. {
  399. switch (received.getType())
  400. {
  401. case TFTPPacket.ERROR:
  402. error = (TFTPErrorPacket)received;
  403. endBufferedOps();
  404. throw new IOException("Error code " + error.getError() +
  405. " received: " + error.getMessage());
  406. case TFTPPacket.ACKNOWLEDGEMENT:
  407. ack = (TFTPAckPacket)received;
  408. lastBlock = ack.getBlockNumber();
  409. if (lastBlock == block)
  410. {
  411. ++block;
  412. break _receivePacket;
  413. }
  414. else
  415. {
  416. discardPackets();
  417. if (lastBlock == (block - 1))
  418. continue _sendPacket; // Resend last acknowledgement.
  419. continue _receivePacket; // Start fetching packets again.
  420. }
  421. //break;
  422. default:
  423. endBufferedOps();
  424. throw new IOException("Received unexpected packet type.");
  425. }
  426. }
  427. else
  428. {
  429. error = new TFTPErrorPacket(received.getAddress(),
  430. received.getPort(),
  431. TFTPErrorPacket.UNKNOWN_TID,
  432. "Unexpected host or port.");
  433. bufferedSend(error);
  434. continue _sendPacket;
  435. }
  436. // We should never get here, but this is a safety to avoid
  437. // infinite loop. If only Java had the goto statement.
  438. //break;
  439. }
  440. dataLength = TFTPPacket.SEGMENT_SIZE;
  441. offset = 4;
  442. while (dataLength > 0 &&
  443. (bytesRead = input.read(_sendBuffer, offset, dataLength)) > 0)
  444. {
  445. offset += bytesRead;
  446. dataLength -= bytesRead;
  447. }
  448. data.setBlockNumber(block);
  449. data.setData(_sendBuffer, 4, offset - 4);
  450. sent = data;
  451. }
  452. while (dataLength == 0);
  453. bufferedSend(sent);
  454. endBufferedOps();
  455. }
  456. /***
  457. * Requests to send a file to a remote host, reads the file from an
  458. * InputStream, sends the file to the remote host, and closes the
  459. * connection. A local UDP socket must first be created by
  460. * <a href="org.apache.commons.net.DatagramSocketClient.html#open">open()</a> before
  461. * invoking this method. This method will not close the InputStream
  462. * containing the file; you must close it after the method invocation.
  463. * <p>
  464. * @param filename The name the remote server should use when creating
  465. * the file on its file system.
  466. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  467. * @param hostname The name of the remote host receiving the file.
  468. * @param port The port number of the remote TFTP server.
  469. * @exception IOException If an I/O error occurs. The nature of the
  470. * error will be reported in the message.
  471. * @exception UnknownHostException If the hostname cannot be resolved.
  472. ***/
  473. public void sendFile(String filename, int mode, InputStream input,
  474. String hostname, int port)
  475. throws UnknownHostException, IOException
  476. {
  477. sendFile(filename, mode, input, InetAddress.getByName(hostname), port);
  478. }
  479. /***
  480. * Same as calling sendFile(filename, mode, input, host, TFTP.DEFAULT_PORT).
  481. *
  482. * @param filename The name the remote server should use when creating
  483. * the file on its file system.
  484. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  485. * @param host The name of the remote host receiving the file.
  486. * @exception IOException If an I/O error occurs. The nature of the
  487. * error will be reported in the message.
  488. * @exception UnknownHostException If the hostname cannot be resolved.
  489. ***/
  490. public void sendFile(String filename, int mode, InputStream input,
  491. InetAddress host)
  492. throws IOException
  493. {
  494. sendFile(filename, mode, input, host, DEFAULT_PORT);
  495. }
  496. /***
  497. * Same as calling sendFile(filename, mode, input, hostname, TFTP.DEFAULT_PORT).
  498. *
  499. * @param filename The name the remote server should use when creating
  500. * the file on its file system.
  501. * @param mode The TFTP mode of the transfer (one of the MODE constants).
  502. * @param hostname The name of the remote host receiving the file.
  503. * @exception IOException If an I/O error occurs. The nature of the
  504. * error will be reported in the message.
  505. * @exception UnknownHostException If the hostname cannot be resolved.
  506. ***/
  507. public void sendFile(String filename, int mode, InputStream input,
  508. String hostname)
  509. throws UnknownHostException, IOException
  510. {
  511. sendFile(filename, mode, input, InetAddress.getByName(hostname),
  512. DEFAULT_PORT);
  513. }
  514. }