/servers/jain-slee/resources/tftp-server/ra/src/main/java/org/mobicents/slee/resource/tftp/TFTPTransfer.java
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
- /*
- * JBoss, Home of Professional Open Source
- * Copyright 2011, Red Hat, Inc. and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
- package org.mobicents.slee.resource.tftp;
-
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.PipedInputStream;
- import java.io.PipedOutputStream;
- import java.net.SocketTimeoutException;
-
- import javax.slee.facilities.Tracer;
-
- import net.java.slee.resource.tftp.TransferActivity;
- import net.java.slee.resource.tftp.events.RequestEvent;
-
- import org.apache.commons.net.io.FromNetASCIIOutputStream;
- import org.apache.commons.net.io.ToNetASCIIInputStream;
- import org.apache.commons.net.tftp.TFTP;
- import org.apache.commons.net.tftp.TFTPAckPacket;
- import org.apache.commons.net.tftp.TFTPDataPacket;
- import org.apache.commons.net.tftp.TFTPErrorPacket;
- import org.apache.commons.net.tftp.TFTPPacket;
- import org.apache.commons.net.tftp.TFTPReadRequestPacket;
- import org.apache.commons.net.tftp.TFTPWriteRequestPacket;
- import org.mobicents.slee.resource.tftp.TftpServerResourceAdaptor.ServerMode;
- import org.mobicents.slee.resource.tftp.events.RequestEventImpl;
-
- /**
- * Handle an actual tftp transfer (server side).
- * Copied and modified from Apache Commons Net (http://commons.apache.org/net).
- * <P>
- * Original code by Dan Ambrust.
- *
- * @author tuijldert
- */
- public class TFTPTransfer implements Runnable {
- private transient Tracer trc;
-
- private volatile boolean threadSuspended = true;
-
- protected void suspend(long timeout) {
- if (threadSuspended) {
- try {
- synchronized(this) {
- while (threadSuspended)
- wait(timeout);
- }
- } catch (InterruptedException e) {}
- }
- }
-
- protected synchronized void resume() {
- threadSuspended = !threadSuspended;
- if (!threadSuspended)
- notify();
- }
-
- private TftpServerResourceAdaptor ra_;
- private ServerMode mode_;
- private int maxTimeoutRetries_;
- private int socketTimeout_;
- private boolean shutdownTransfer = false;
-
- private TFTPPacket tftpPacket_;
- private TransferActivity activity_;
- private TFTP transferTftp_ = null;
- private InputStream sbbIs, is_;
- private OutputStream sbbOs, os_;
-
- public TFTPTransfer(TFTPPacket tftpPacket, TransferActivity activity,
- ServerMode mode, int maxTimeoutRetries, int socketTimeout,
- TftpServerResourceAdaptor ra, Tracer trc) {
- tftpPacket_ = tftpPacket;
- activity_ = activity;
- mode_ = mode;
- maxTimeoutRetries_ = maxTimeoutRetries;
- socketTimeout_ = socketTimeout;
- ra_ = ra;
- this.trc = trc;
- }
-
- public void shutdown() {
- if (trc.isFineEnabled())
- trc.fine("Transfer ended for thread: " + Thread.currentThread().getName());
-
- shutdownTransfer = true;
- try {
- transferTftp_.close();
- } catch (RuntimeException e) {
- // noop
- }
- resume();
- }
-
- public boolean isRunning() {
- return !shutdownTransfer;
- }
-
- public void run() {
- try {
- if (trc.isFineEnabled())
- trc.fine("Transfer started in thread: " + Thread.currentThread().getName());
-
- transferTftp_ = new TFTP();
-
- transferTftp_.beginBufferedOps();
- transferTftp_.setDefaultTimeout(socketTimeout_);
-
- transferTftp_.open();
-
- if (isRead()) {
- handleRead(((TFTPReadRequestPacket) tftpPacket_));
- } else if (isWrite()) {
- handleWrite((TFTPWriteRequestPacket) tftpPacket_);
- } else {
- trc.warning("Unsupported TFTP request (" + tftpPacket_ + ") - ignored.");
- }
- } catch (Exception e) {
- if (!shutdownTransfer) {
- trc.severe("Unexpected Error in during TFTP file transfer. Transfer aborted. "
- + e);
- }
- } finally {
- try {
- if (transferTftp_ != null && transferTftp_.isOpen()) {
- transferTftp_.endBufferedOps();
- transferTftp_.close();
- }
- } catch (Exception e) {
- // noop
- }
- ra_.endTransferRequestActivity(activity_);
- }
- }
-
- protected boolean isRead() {
- return tftpPacket_ instanceof TFTPReadRequestPacket;
- }
-
- protected boolean isWrite() {
- return tftpPacket_ instanceof TFTPWriteRequestPacket;
- }
-
- public void sendError(int errorCode, String reason) {
- try {
- if (transferTftp_ != null && transferTftp_.isOpen()) {
- if (trc.isFineEnabled())
- trc.fine(String.format("Send error - code[%d] reason[%s]", errorCode, reason));
-
- transferTftp_.bufferedSend(new TFTPErrorPacket(tftpPacket_.getAddress(),
- tftpPacket_.getPort(), errorCode, reason));
- transferTftp_.endBufferedOps();
- }
- } catch (Exception e) { }
- shutdown();
- }
-
- /*
- * Handle a tftp read request.
- */
- private void handleRead(TFTPReadRequestPacket trrp) throws Exception {
- long totalBytesSent = 0;
- try {
- if (mode_ == ServerMode.PUT_ONLY) {
- transferTftp_.bufferedSend(new TFTPErrorPacket(trrp.getAddress(),
- trrp.getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
- "Read not allowed by server."));
- return;
- }
- if (trc.isFineEnabled())
- trc.fine("READ request received, get cracking");
- fireEvent(trrp);
- suspend(0); // TODO: do we really need to wait forever?
-
- if (trrp.getMode() == TFTP.NETASCII_MODE) {
- is_ = new ToNetASCIIInputStream(is_);
- }
- byte[] temp = new byte[TFTPDataPacket.MAX_DATA_LENGTH];
- TFTPPacket answer;
- int block = 1;
- boolean sendNext = true;
- int readLength = TFTPDataPacket.MAX_DATA_LENGTH;
- TFTPDataPacket lastSentData = null;
-
- // We are reading a file, so when we read less than the
- // requested bytes, we know that we are at the end of the file.
- while (readLength == TFTPDataPacket.MAX_DATA_LENGTH && !shutdownTransfer) {
- if (sendNext) {
- readLength = is_.read(temp);
- if (readLength == -1) {
- readLength = 0;
- }
- lastSentData = new TFTPDataPacket(trrp.getAddress(), trrp.getPort(), block,
- temp, 0, readLength);
- transferTftp_.bufferedSend(lastSentData);
- totalBytesSent += readLength;
- }
- answer = null;
- int timeoutCount = 0;
- while (!shutdownTransfer
- && (answer == null || !answer.getAddress().equals(trrp.getAddress()) || answer
- .getPort() != trrp.getPort())) {
- // listen for an answer.
- if (answer != null) {
- // The answer that we got didn't come from the
- // expected source, fire back an error, and continue
- // listening.
- trc.warning("TFTP Server ignoring message from unexpected source.");
- transferTftp_.bufferedSend(new TFTPErrorPacket(answer.getAddress(),
- answer.getPort(), TFTPErrorPacket.UNKNOWN_TID,
- "Unexpected Host or Port"));
- }
- try {
- answer = transferTftp_.bufferedReceive();
- } catch (SocketTimeoutException e) {
- if (timeoutCount >= maxTimeoutRetries_) {
- throw e;
- }
- // didn't get an ack for this data. need to resend
- // it.
- timeoutCount++;
- transferTftp_.bufferedSend(lastSentData);
- continue;
- }
- }
- if (answer == null || !(answer instanceof TFTPAckPacket)) {
- if (!shutdownTransfer) {
- trc.severe("Unexpected response from tftp client during transfer ("
- + answer + "). Transfer aborted.");
- }
- break;
- } else {
- // once we get here, we know we have an answer packet
- // from the correct host.
- TFTPAckPacket ack = (TFTPAckPacket) answer;
- if (ack.getBlockNumber() != block) {
- /*
- * The origional tftp spec would have called on us to resend the
- * previous data here, however, that causes the SAS Syndrome.
- * http://www.faqs.org/rfcs/rfc1123.html section 4.2.3.1 The modified
- * spec says that we ignore a duplicate ack. If the packet was really
- * lost, we will time out on receive, and resend the previous data at
- * that point.
- */
- sendNext = false;
- } else {
- // send the next block
- block++;
- if (block > 65535) {
- // wrap the block number
- block = 0;
- }
- sendNext = true;
- }
- }
- }
- } finally {
- if (trc.isFineEnabled())
- trc.fine("Bytes sent = " + totalBytesSent);
- try {
- if (is_ != null) is_.close();
- if (sbbOs != null) sbbOs.close();
- } catch (IOException e) {
- // noop
- }
- }
- }
-
- private void fireEvent(TFTPPacket packet) throws Exception {
- RequestEvent event = new RequestEventImpl(packet, this);
- try {
- ra_.fireEvent(event, activity_, packet.getAddress().toString());
- } catch (Exception e) {
- transferTftp_.bufferedSend(new TFTPErrorPacket(packet.getAddress(), packet
- .getPort(), TFTPErrorPacket.UNDEFINED, e.getMessage()));
- throw e;
- }
- }
-
- /*
- * handle a tftp write request.
- */
- private void handleWrite(TFTPWriteRequestPacket twrp) throws Exception {
- long totalBytesReceived = 0;
- try {
- if (mode_ == ServerMode.GET_ONLY) {
- transferTftp_.bufferedSend(new TFTPErrorPacket(twrp.getAddress(), twrp
- .getPort(), TFTPErrorPacket.ILLEGAL_OPERATION,
- "Write not allowed by server."));
- return;
- }
- if (trc.isFineEnabled())
- trc.fine("WRITE request received, get cracking");
- fireEvent(twrp);
- suspend(0);
-
- if (twrp.getMode() == TFTP.NETASCII_MODE) {
- os_ = new FromNetASCIIOutputStream(os_);
- }
- TFTPAckPacket lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
- transferTftp_.bufferedSend(lastSentAck);
-
- int lastBlock = 0;
- while (true) {
- // get the response - ensure it is from the right place.
- TFTPPacket dataPacket = null;
- int timeoutCount = 0;
-
- while (!shutdownTransfer &&
- (dataPacket == null ||
- !dataPacket.getAddress().equals(twrp.getAddress()) ||
- dataPacket.getPort() != twrp.getPort())) {
- // listen for an answer.
- if (dataPacket != null) {
- // The data that we got didn't come from the
- // expected source, fire back an error, and continue
- // listening.
- trc.warning("TFTP Server ignoring message from unexpected source.");
- transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket.getAddress(),
- dataPacket.getPort(), TFTPErrorPacket.UNKNOWN_TID,
- "Unexpected Host or Port"));
- }
- try {
- dataPacket = transferTftp_.bufferedReceive();
- } catch (SocketTimeoutException e) {
- if (timeoutCount >= maxTimeoutRetries_) {
- throw e;
- }
- // It didn't get our ack. Resend it.
- transferTftp_.bufferedSend(lastSentAck);
- timeoutCount++;
- continue;
- }
- }
- if (dataPacket != null && dataPacket instanceof TFTPWriteRequestPacket) {
- // it must have missed our initial ack. Send another.
- lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), 0);
- transferTftp_.bufferedSend(lastSentAck);
- } else if (dataPacket == null || !(dataPacket instanceof TFTPDataPacket)) {
- if (!shutdownTransfer) {
- trc.severe("Unexpected response from tftp client during transfer ("
- + dataPacket + "). Transfer aborted.");
- }
- break;
- } else {
- int block = ((TFTPDataPacket) dataPacket).getBlockNumber();
- byte[] data = ((TFTPDataPacket) dataPacket).getData();
- int dataLength = ((TFTPDataPacket) dataPacket).getDataLength();
- int dataOffset = ((TFTPDataPacket) dataPacket).getDataOffset();
-
- if (block > lastBlock || (lastBlock == 65535 && block == 0)) {
- // it might resend a data block if it missed our ack
- // - don't rewrite the block.
- os_.write(data, dataOffset, dataLength);
- lastBlock = block;
- fireEvent(dataPacket);
- totalBytesReceived += dataLength;
- }
- lastSentAck = new TFTPAckPacket(twrp.getAddress(), twrp.getPort(), block);
- transferTftp_.bufferedSend(lastSentAck);
- if (dataLength < TFTPDataPacket.MAX_DATA_LENGTH) {
- // end of stream signal - The tranfer is complete.
- if (trc.isFineEnabled())
- trc.fine("Bytes received = " + totalBytesReceived);
- os_.close();
-
- // But my ack may be lost - so listen to see if I
- // need to resend the ack.
- for (int i = 0; i < maxTimeoutRetries_; i++) {
- try {
- dataPacket = transferTftp_.bufferedReceive();
- } catch (SocketTimeoutException e) {
- // this is the expected route - the client
- // shouldn't be sending any more packets.
- break;
- }
- if (dataPacket != null &&
- (!dataPacket.getAddress().equals(twrp.getAddress()) ||
- dataPacket.getPort() != twrp.getPort())) {
- // make sure it was from the right client...
- transferTftp_.bufferedSend(new TFTPErrorPacket(dataPacket
- .getAddress(), dataPacket.getPort(),
- TFTPErrorPacket.UNKNOWN_TID,
- "Unexpected Host or Port"));
- } else {
- // This means they sent us the last
- // datapacket again, must have missed our
- // ack. resend it.
- transferTftp_.bufferedSend(lastSentAck);
- }
- }
- // all done.
- break;
- }
- }
- }
- } finally {
- if (sbbIs != null) sbbIs.close();
- if (os_ != null) os_.close();
- }
- }
-
- /* Routines implementing the transfer activity. */
-
- protected void receiveFile(String filename) throws FileNotFoundException, IOException {
- receiveFile(new File(filename));
- }
-
- protected void receiveFile(File file) throws FileNotFoundException, IOException {
- if (!isWrite())
- throw new IOException("No write request pending");
- if (os_ == null) {
- os_ = new BufferedOutputStream(new FileOutputStream(file));
- resume();
- }
- }
-
- protected InputStream getInputStream() throws IOException {
- if (!isWrite())
- throw new IOException("No write request pending");
- if (sbbIs == null && os_ == null) {
- os_ = new PipedOutputStream();
- sbbIs = new PipedInputStream((PipedOutputStream) os_);
- resume();
- }
- return sbbIs;
- }
-
- protected void sendFile(String filename) throws FileNotFoundException, IOException {
- sendFile(new File(filename));
- }
-
- protected void sendFile(File file) throws FileNotFoundException, IOException {
- if (!isRead())
- throw new IOException("No read request pending");
- if (is_ == null) {
- is_ = new BufferedInputStream(new FileInputStream(file));
- resume();
- }
- }
-
- protected OutputStream getOutputStream() throws IOException {
- if (!isRead())
- throw new IOException("No read request pending");
- if (sbbOs == null && is_ == null) {
- sbbOs = new PipedOutputStream();
- is_ = new PipedInputStream((PipedOutputStream) sbbOs);
- resume();
- }
- return sbbOs;
- }
- }