PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/jain-slee/resources/tftp-server/ra/src/main/java/org/mobicents/slee/resource/tftp/TFTPTransfer.java

http://mobicents.googlecode.com/
Java | 479 lines | 360 code | 41 blank | 78 comment | 104 complexity | 2815d102412bb868bb90c6d77c7ddc38 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.slee.resource.tftp;
  23. import java.io.BufferedInputStream;
  24. import java.io.BufferedOutputStream;
  25. import java.io.File;
  26. import java.io.FileInputStream;
  27. import java.io.FileNotFoundException;
  28. import java.io.FileOutputStream;
  29. import java.io.IOException;
  30. import java.io.InputStream;
  31. import java.io.OutputStream;
  32. import java.io.PipedInputStream;
  33. import java.io.PipedOutputStream;
  34. import java.net.SocketTimeoutException;
  35. import javax.slee.facilities.Tracer;
  36. import net.java.slee.resource.tftp.TransferActivity;
  37. import net.java.slee.resource.tftp.events.RequestEvent;
  38. import org.apache.commons.net.io.FromNetASCIIOutputStream;
  39. import org.apache.commons.net.io.ToNetASCIIInputStream;
  40. import org.apache.commons.net.tftp.TFTP;
  41. import org.apache.commons.net.tftp.TFTPAckPacket;
  42. import org.apache.commons.net.tftp.TFTPDataPacket;
  43. import org.apache.commons.net.tftp.TFTPErrorPacket;
  44. import org.apache.commons.net.tftp.TFTPPacket;
  45. import org.apache.commons.net.tftp.TFTPReadRequestPacket;
  46. import org.apache.commons.net.tftp.TFTPWriteRequestPacket;
  47. import org.mobicents.slee.resource.tftp.TftpServerResourceAdaptor.ServerMode;
  48. import org.mobicents.slee.resource.tftp.events.RequestEventImpl;
  49. /**
  50. * Handle an actual tftp transfer (server side).
  51. * Copied and modified from Apache Commons Net (http://commons.apache.org/net).
  52. * <P>
  53. * Original code by Dan Ambrust.
  54. *
  55. * @author tuijldert
  56. */
  57. public class TFTPTransfer implements Runnable {
  58. private transient Tracer trc;
  59. private volatile boolean threadSuspended = true;
  60. protected void suspend(long timeout) {
  61. if (threadSuspended) {
  62. try {
  63. synchronized(this) {
  64. while (threadSuspended)
  65. wait(timeout);
  66. }
  67. } catch (InterruptedException e) {}
  68. }
  69. }
  70. protected synchronized void resume() {
  71. threadSuspended = !threadSuspended;
  72. if (!threadSuspended)
  73. notify();
  74. }
  75. private TftpServerResourceAdaptor ra_;
  76. private ServerMode mode_;
  77. private int maxTimeoutRetries_;
  78. private int socketTimeout_;
  79. private boolean shutdownTransfer = false;
  80. private TFTPPacket tftpPacket_;
  81. private TransferActivity activity_;
  82. private TFTP transferTftp_ = null;
  83. private InputStream sbbIs, is_;
  84. private OutputStream sbbOs, os_;
  85. public TFTPTransfer(TFTPPacket tftpPacket, TransferActivity activity,
  86. ServerMode mode, int maxTimeoutRetries, int socketTimeout,
  87. TftpServerResourceAdaptor ra, Tracer trc) {
  88. tftpPacket_ = tftpPacket;
  89. activity_ = activity;
  90. mode_ = mode;
  91. maxTimeoutRetries_ = maxTimeoutRetries;
  92. socketTimeout_ = socketTimeout;
  93. ra_ = ra;
  94. this.trc = trc;
  95. }
  96. public void shutdown() {
  97. if (trc.isFineEnabled())
  98. trc.fine("Transfer ended for thread: " + Thread.currentThread().getName());
  99. shutdownTransfer = true;
  100. try {
  101. transferTftp_.close();
  102. } catch (RuntimeException e) {
  103. // noop
  104. }
  105. resume();
  106. }
  107. public boolean isRunning() {
  108. return !shutdownTransfer;
  109. }
  110. public void run() {
  111. try {
  112. if (trc.isFineEnabled())
  113. trc.fine("Transfer started in thread: " + Thread.currentThread().getName());
  114. transferTftp_ = new TFTP();
  115. transferTftp_.beginBufferedOps();
  116. transferTftp_.setDefaultTimeout(socketTimeout_);
  117. transferTftp_.open();
  118. if (isRead()) {
  119. handleRead(((TFTPReadRequestPacket) tftpPacket_));
  120. } else if (isWrite()) {
  121. handleWrite((TFTPWriteRequestPacket) tftpPacket_);
  122. } else {
  123. trc.warning("Unsupported TFTP request (" + tftpPacket_ + ") - ignored.");
  124. }
  125. } catch (Exception e) {
  126. if (!shutdownTransfer) {
  127. trc.severe("Unexpected Error in during TFTP file transfer. Transfer aborted. "
  128. + e);
  129. }
  130. } finally {
  131. try {
  132. if (transferTftp_ != null && transferTftp_.isOpen()) {
  133. transferTftp_.endBufferedOps();
  134. transferTftp_.close();
  135. }
  136. } catch (Exception e) {
  137. // noop
  138. }
  139. ra_.endTransferRequestActivity(activity_);
  140. }
  141. }
  142. protected boolean isRead() {
  143. return tftpPacket_ instanceof TFTPReadRequestPacket;
  144. }
  145. protected boolean isWrite() {
  146. return tftpPacket_ instanceof TFTPWriteRequestPacket;
  147. }
  148. public void sendError(int errorCode, String reason) {
  149. try {
  150. if (transferTftp_ != null && transferTftp_.isOpen()) {
  151. if (trc.isFineEnabled())
  152. trc.fine(String.format("Send error - code[%d] reason[%s]", errorCode, reason));
  153. transferTftp_.bufferedSend(new TFTPErrorPacket(tftpPacket_.getAddress(),
  154. tftpPacket_.getPort(), errorCode, reason));
  155. transferTftp_.endBufferedOps();
  156. }
  157. } catch (Exception e) { }
  158. shutdown();
  159. }
  160. /*
  161. * Handle a tftp read request.
  162. */
  163. private void handleRead(TFTPReadRequestPacket trrp) throws Exception {
  164. long totalBytesSent = 0;
  165. try {
  166. if (mode_ == ServerMode.PUT_ONLY) {
  167. transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(),
  168. trrp.getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
  169. "Read not allowed by server."));
  170. return;
  171. }
  172. if (trc.isFineEnabled())
  173. trc.fine("READ request received, get cracking");
  174. fireEvent(trrp);
  175. suspend(0); // TODO: do we really need to wait forever?
  176. if (trrp.getMode() == TFTP.NETASCII_MODE) {
  177. is_ = new ToNetASCIIInputStream(is_);
  178. }
  179. byte[] temp = new byte[TFTPDataPacket.MAX_DATA_LENGTH];
  180. TFTPPacket answer;
  181. int block = 1;
  182. boolean sendNext = true;
  183. int readLength = TFTPDataPacket.MAX_DATA_LENGTH;
  184. TFTPDataPacket lastSentData = null;
  185. // We are reading a file, so when we read less than the
  186. // requested bytes, we know that we are at the end of the file.
  187. while (readLength == TFTPDataPacket.MAX_DATA_LENGTH && !shutdownTransfer) {
  188. if (sendNext) {
  189. readLength = is_.read(temp);
  190. if (readLength == -1) {
  191. readLength = 0;
  192. }
  193. lastSentData = new TFTPDataPacket(trrp.getAddress(), trrp.getPort(), block,
  194. temp, 0, readLength);
  195. transferTftp_.bufferedSend(lastSentData);
  196. totalBytesSent += readLength;
  197. }
  198. answer = null;
  199. int timeoutCount = 0;
  200. while (!shutdownTransfer
  201. && (answer == null || !answer.getAddress().equals(trrp.getAddress()) || answer
  202. .getPort() != trrp.getPort())) {
  203. // listen for an answer.
  204. if (answer != null) {
  205. // The answer that we got didn't come from the
  206. // expected source, fire back an error, and continue
  207. // listening.
  208. trc.warning("TFTP Server ignoring message from unexpected source.");
  209. transferTftp_.bufferedSend(new TFTPErrorPacket(answer.getAddress(),
  210. answer.getPort(), TFTPErrorPacket.UNKNOWN_TID,
  211. "Unexpected Host or Port"));
  212. }
  213. try {
  214. answer = transferTftp_.bufferedReceive();
  215. } catch (SocketTimeoutException e) {
  216. if (timeoutCount >= maxTimeoutRetries_) {
  217. throw e;
  218. }
  219. // didn't get an ack for this data. need to resend
  220. // it.
  221. timeoutCount++;
  222. transferTftp_.bufferedSend(lastSentData);
  223. continue;
  224. }
  225. }
  226. if (answer == null || !(answer instanceof TFTPAckPacket)) {
  227. if (!shutdownTransfer) {
  228. trc.severe("Unexpected response from tftp client during transfer ("
  229. + answer + "). Transfer aborted.");
  230. }
  231. break;
  232. } else {
  233. // once we get here, we know we have an answer packet
  234. // from the correct host.
  235. TFTPAckPacket ack = (TFTPAckPacket) answer;
  236. if (ack.getBlockNumber() != block) {
  237. /*
  238. * The origional tftp spec would have called on us to resend the
  239. * previous data here, however, that causes the SAS Syndrome.
  240. * http://www.faqs.org/rfcs/rfc1123.html section 4.2.3.1 The modified
  241. * spec says that we ignore a duplicate ack. If the packet was really
  242. * lost, we will time out on receive, and resend the previous data at
  243. * that point.
  244. */
  245. sendNext = false;
  246. } else {
  247. // send the next block
  248. block++;
  249. if (block > 65535) {
  250. // wrap the block number
  251. block = 0;
  252. }
  253. sendNext = true;
  254. }
  255. }
  256. }
  257. } finally {
  258. if (trc.isFineEnabled())
  259. trc.fine("Bytes sent = " + totalBytesSent);
  260. try {
  261. if (is_ != null) is_.close();
  262. if (sbbOs != null) sbbOs.close();
  263. } catch (IOException e) {
  264. // noop
  265. }
  266. }
  267. }
  268. private void fireEvent(TFTPPacket packet) throws Exception {
  269. RequestEvent event = new RequestEventImpl(packet, this);
  270. try {
  271. ra_.fireEvent(event, activity_, packet.getAddress().toString());
  272. } catch (Exception e) {
  273. transferTftp_.bufferedSend(new TFTPErrorPacket(packet.getAddress(), packet
  274. .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
  275. throw e;
  276. }
  277. }
  278. /*
  279. * handle a tftp write request.
  280. */
  281. private void handleWrite(TFTPWriteRequestPacket twrp) throws Exception {
  282. long totalBytesReceived = 0;
  283. try {
  284. if (mode_ == ServerMode.GET_ONLY) {
  285. transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
  286. .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
  287. "Write not allowed by server."));
  288. return;
  289. }
  290. if (trc.isFineEnabled())
  291. trc.fine("WRITE request received, get cracking");
  292. fireEvent(twrp);
  293. suspend(0);
  294. if (twrp.getMode() == TFTP.NETASCII_MODE) {
  295. os_ = new FromNetASCIIOutputStream(os_);
  296. }
  297. TFTPAckPacket lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
  298. transferTftp_.bufferedSend(lastSentAck);
  299. int lastBlock = 0;
  300. while (true) {
  301. // get the response - ensure it is from the right place.
  302. TFTPPacket dataPacket = null;
  303. int timeoutCount = 0;
  304. while (!shutdownTransfer &&
  305. (dataPacket == null ||
  306. !dataPacket.getAddress().equals(twrp.getAddress()) ||
  307. dataPacket.getPort() != twrp.getPort())) {
  308. // listen for an answer.
  309. if (dataPacket != null) {
  310. // The data that we got didn't come from the
  311. // expected source, fire back an error, and continue
  312. // listening.
  313. trc.warning("TFTP Server ignoring message from unexpected source.");
  314. transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket.getAddress(),
  315. dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID,
  316. "Unexpected Host or Port"));
  317. }
  318. try {
  319. dataPacket = transferTftp_.bufferedReceive();
  320. } catch (SocketTimeoutException e) {
  321. if (timeoutCount >= maxTimeoutRetries_) {
  322. throw e;
  323. }
  324. // It didn't get our ack. Resend it.
  325. transferTftp_.bufferedSend(lastSentAck);
  326. timeoutCount++;
  327. continue;
  328. }
  329. }
  330. if (dataPacket != null && dataPacket instanceof TFTPWriteRequestPacket) {
  331. // it must have missed our initial ack. Send another.
  332. lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
  333. transferTftp_.bufferedSend(lastSentAck);
  334. } else if (dataPacket == null || !(dataPacket instanceof TFTPDataPacket)) {
  335. if (!shutdownTransfer) {
  336. trc.severe("Unexpected response from tftp client during transfer ("
  337. + dataPacket + "). Transfer aborted.");
  338. }
  339. break;
  340. } else {
  341. int block = ((TFTPDataPacket) dataPacket).getBlockNumber();
  342. byte[] data = ((TFTPDataPacket) dataPacket).getData();
  343. int dataLength = ((TFTPDataPacket) dataPacket).getDataLength();
  344. int dataOffset = ((TFTPDataPacket) dataPacket).getDataOffset();
  345. if (block > lastBlock || (lastBlock == 65535 && block == 0)) {
  346. // it might resend a data block if it missed our ack
  347. // - don't rewrite the block.
  348. os_.write(data, dataOffset, dataLength);
  349. lastBlock = block;
  350. fireEvent(dataPacket);
  351. totalBytesReceived += dataLength;
  352. }
  353. lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), block);
  354. transferTftp_.bufferedSend(lastSentAck);
  355. if (dataLength < TFTPDataPacket.MAX_DATA_LENGTH) {
  356. // end of stream signal - The tranfer is complete.
  357. if (trc.isFineEnabled())
  358. trc.fine("Bytes received = " + totalBytesReceived);
  359. os_.close();
  360. // But my ack may be lost - so listen to see if I
  361. // need to resend the ack.
  362. for (int i = 0; i < maxTimeoutRetries_; i++) {
  363. try {
  364. dataPacket = transferTftp_.bufferedReceive();
  365. } catch (SocketTimeoutException e) {
  366. // this is the expected route - the client
  367. // shouldn't be sending any more packets.
  368. break;
  369. }
  370. if (dataPacket != null &&
  371. (!dataPacket.getAddress().equals(twrp.getAddress()) ||
  372. dataPacket.getPort() != twrp.getPort())) {
  373. // make sure it was from the right client...
  374. transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket
  375. .getAddress(), dataPacket.getPort(),
  376. TFTPErrorPacket.UNKNOWN_TID,
  377. "Unexpected Host or Port"));
  378. } else {
  379. // This means they sent us the last
  380. // datapacket again, must have missed our
  381. // ack. resend it.
  382. transferTftp_.bufferedSend(lastSentAck);
  383. }
  384. }
  385. // all done.
  386. break;
  387. }
  388. }
  389. }
  390. } finally {
  391. if (sbbIs != null) sbbIs.close();
  392. if (os_ != null) os_.close();
  393. }
  394. }
  395. /* Routines implementing the transfer activity. */
  396. protected void receiveFile(String filename) throws FileNotFoundException, IOException {
  397. receiveFile(new File(filename));
  398. }
  399. protected void receiveFile(File file) throws FileNotFoundException, IOException {
  400. if (!isWrite())
  401. throw new IOException("No write request pending");
  402. if (os_ == null) {
  403. os_ = new BufferedOutputStream(new FileOutputStream(file));
  404. resume();
  405. }
  406. }
  407. protected InputStream getInputStream() throws IOException {
  408. if (!isWrite())
  409. throw new IOException("No write request pending");
  410. if (sbbIs == null && os_ == null) {
  411. os_ = new PipedOutputStream();
  412. sbbIs = new PipedInputStream((PipedOutputStream) os_);
  413. resume();
  414. }
  415. return sbbIs;
  416. }
  417. protected void sendFile(String filename) throws FileNotFoundException, IOException {
  418. sendFile(new File(filename));
  419. }
  420. protected void sendFile(File file) throws FileNotFoundException, IOException {
  421. if (!isRead())
  422. throw new IOException("No read request pending");
  423. if (is_ == null) {
  424. is_ = new BufferedInputStream(new FileInputStream(file));
  425. resume();
  426. }
  427. }
  428. protected OutputStream getOutputStream() throws IOException {
  429. if (!isRead())
  430. throw new IOException("No read request pending");
  431. if (sbbOs == null && is_ == null) {
  432. sbbOs = new PipedOutputStream();
  433. is_ = new PipedInputStream((PipedOutputStream) sbbOs);
  434. resume();
  435. }
  436. return sbbOs;
  437. }
  438. }