From 063284837c8c366e5502b1b0264b8eb807b61732 Mon Sep 17 00:00:00 2001 From: Joe Robinson Date: Wed, 27 Oct 2010 14:21:09 +0100 Subject: Basic upload functionality to predifined location, with basic file browser --- org/apache/commons/net/tftp/TFTP.java | 301 ++++++++++ org/apache/commons/net/tftp/TFTPAckPacket.java | 158 ++++++ org/apache/commons/net/tftp/TFTPClient.java | 610 +++++++++++++++++++++ org/apache/commons/net/tftp/TFTPDataPacket.java | 254 +++++++++ org/apache/commons/net/tftp/TFTPErrorPacket.java | 226 ++++++++ org/apache/commons/net/tftp/TFTPPacket.java | 247 +++++++++ .../commons/net/tftp/TFTPPacketException.java | 57 ++ .../commons/net/tftp/TFTPReadRequestPacket.java | 80 +++ org/apache/commons/net/tftp/TFTPRequestPacket.java | 253 +++++++++ .../commons/net/tftp/TFTPWriteRequestPacket.java | 79 +++ 10 files changed, 2265 insertions(+) create mode 100644 org/apache/commons/net/tftp/TFTP.java create mode 100644 org/apache/commons/net/tftp/TFTPAckPacket.java create mode 100644 org/apache/commons/net/tftp/TFTPClient.java create mode 100644 org/apache/commons/net/tftp/TFTPDataPacket.java create mode 100644 org/apache/commons/net/tftp/TFTPErrorPacket.java create mode 100644 org/apache/commons/net/tftp/TFTPPacket.java create mode 100644 org/apache/commons/net/tftp/TFTPPacketException.java create mode 100644 org/apache/commons/net/tftp/TFTPReadRequestPacket.java create mode 100644 org/apache/commons/net/tftp/TFTPRequestPacket.java create mode 100644 org/apache/commons/net/tftp/TFTPWriteRequestPacket.java (limited to 'org/apache/commons/net/tftp') diff --git a/org/apache/commons/net/tftp/TFTP.java b/org/apache/commons/net/tftp/TFTP.java new file mode 100644 index 0000000..662da11 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTP.java @@ -0,0 +1,301 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.io.IOException; +import java.io.InterruptedIOException; +import java.net.DatagramPacket; +import java.net.SocketException; + +import org.apache.commons.net.DatagramSocketClient; + +/*** + * The TFTP class exposes a set of methods to allow you to deal with the TFTP + * protocol directly, in case you want to write your own TFTP client or + * server. However, almost every user should only be concerend with + * the {@link org.apache.commons.net.DatagramSocketClient#open open() }, + * and {@link org.apache.commons.net.DatagramSocketClient#close close() }, + * methods. Additionally,the a + * {@link org.apache.commons.net.DatagramSocketClient#setDefaultTimeout setDefaultTimeout() } + * method may be of importance for performance tuning. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. + *

+ *

+ * @author Daniel F. Savarese + * @see org.apache.commons.net.DatagramSocketClient + * @see TFTPPacket + * @see TFTPPacketException + * @see TFTPClient + ***/ + +public class TFTP extends DatagramSocketClient +{ + /*** + * The ascii transfer mode. Its value is 0 and equivalent to NETASCII_MODE + ***/ + public static final int ASCII_MODE = 0; + + /*** + * The netascii transfer mode. Its value is 0. + ***/ + public static final int NETASCII_MODE = 0; + + /*** + * The binary transfer mode. Its value is 1 and equivalent to OCTET_MODE. + ***/ + public static final int BINARY_MODE = 1; + + /*** + * The image transfer mode. Its value is 1 and equivalent to OCTET_MODE. + ***/ + public static final int IMAGE_MODE = 1; + + /*** + * The octet transfer mode. Its value is 1. + ***/ + public static final int OCTET_MODE = 1; + + /*** + * The default number of milliseconds to wait to receive a datagram + * before timing out. The default is 5000 milliseconds (5 seconds). + ***/ + public static final int DEFAULT_TIMEOUT = 5000; + + /*** + * The default TFTP port according to RFC 783 is 69. + ***/ + public static final int DEFAULT_PORT = 69; + + /*** + * The size to use for TFTP packet buffers. Its 4 plus the + * TFTPPacket.SEGMENT_SIZE, i.e. 516. + ***/ + static final int PACKET_SIZE = TFTPPacket.SEGMENT_SIZE + 4; + + /*** A buffer used to accelerate receives in bufferedReceive() ***/ + private byte[] __receiveBuffer; + + /*** A datagram used to minimize memory allocation in bufferedReceive() ***/ + private DatagramPacket __receiveDatagram; + + /*** A datagram used to minimize memory allocation in bufferedSend() ***/ + private DatagramPacket __sendDatagram; + + /*** + * A buffer used to accelerate sends in bufferedSend(). + * It is left package visible so that TFTPClient may be slightly more + * efficient during file sends. It saves the creation of an + * additional buffer and prevents a buffer copy in _newDataPcket(). + ***/ + byte[] _sendBuffer; + + + /*** + * Returns the TFTP string representation of a TFTP transfer mode. + * Will throw an ArrayIndexOutOfBoundsException if an invalid transfer + * mode is specified. + *

+ * @param mode The TFTP transfer mode. One of the MODE constants. + * @return The TFTP string representation of the TFTP transfer mode. + ***/ + public static final String getModeName(int mode) + { + return TFTPRequestPacket._modeStrings[mode]; + } + + /*** + * Creates a TFTP instance with a default timeout of DEFAULT_TIMEOUT, + * a null socket, and buffered operations disabled. + ***/ + public TFTP() + { + setDefaultTimeout(DEFAULT_TIMEOUT); + __receiveBuffer = null; + __receiveDatagram = null; + } + + /*** + * This method synchronizes a connection by discarding all packets that + * may be in the local socket buffer. This method need only be called + * when you implement your own TFTP client or server. + *

+ * @exception IOException if an I/O error occurs. + ***/ + public final void discardPackets() throws IOException + { + int to; + DatagramPacket datagram; + + datagram = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE); + + to = getSoTimeout(); + setSoTimeout(1); + + try + { + while (true) + _socket_.receive(datagram); + } + catch (SocketException e) + { + // Do nothing. We timed out so we hope we're caught up. + } + catch (InterruptedIOException e) + { + // Do nothing. We timed out so we hope we're caught up. + } + + setSoTimeout(to); + } + + + /*** + * This is a special method to perform a more efficient packet receive. + * It should only be used after calling + * {@link #beginBufferedOps beginBufferedOps() }. beginBufferedOps() + * initializes a set of buffers used internally that prevent the new + * allocation of a DatagramPacket and byte array for each send and receive. + * To use these buffers you must call the bufferedReceive() and + * bufferedSend() methods instead of send() and receive(). You must + * also be certain that you don't manipulate the resulting packet in + * such a way that it interferes with future buffered operations. + * For example, a TFTPDataPacket received with bufferedReceive() will + * have a reference to the internal byte buffer. You must finish using + * this data before calling bufferedReceive() again, or else the data + * will be overwritten by the the call. + *

+ * @return The TFTPPacket received. + * @exception InterruptedIOException If a socket timeout occurs. The + * Java documentation claims an InterruptedIOException is thrown + * on a DatagramSocket timeout, but in practice we find a + * SocketException is thrown. You should catch both to be safe. + * @exception SocketException If a socket timeout occurs. The + * Java documentation claims an InterruptedIOException is thrown + * on a DatagramSocket timeout, but in practice we find a + * SocketException is thrown. You should catch both to be safe. + * @exception IOException If some other I/O error occurs. + * @exception TFTPPacketException If an invalid TFTP packet is received. + ***/ + public final TFTPPacket bufferedReceive() throws IOException, + InterruptedIOException, SocketException, TFTPPacketException + { + __receiveDatagram.setData(__receiveBuffer); + __receiveDatagram.setLength(__receiveBuffer.length); + _socket_.receive(__receiveDatagram); + + return TFTPPacket.newTFTPPacket(__receiveDatagram); + } + + /*** + * This is a special method to perform a more efficient packet send. + * It should only be used after calling + * {@link #beginBufferedOps beginBufferedOps() }. beginBufferedOps() + * initializes a set of buffers used internally that prevent the new + * allocation of a DatagramPacket and byte array for each send and receive. + * To use these buffers you must call the bufferedReceive() and + * bufferedSend() methods instead of send() and receive(). You must + * also be certain that you don't manipulate the resulting packet in + * such a way that it interferes with future buffered operations. + * For example, a TFTPDataPacket received with bufferedReceive() will + * have a reference to the internal byte buffer. You must finish using + * this data before calling bufferedReceive() again, or else the data + * will be overwritten by the the call. + *

+ * @param packet The TFTP packet to send. + * @exception IOException If some I/O error occurs. + ***/ + public final void bufferedSend(TFTPPacket packet) throws IOException + { + _socket_.send(packet._newDatagram(__sendDatagram, _sendBuffer)); + } + + + /*** + * Initializes the internal buffers. Buffers are used by + * {@link #bufferedSend bufferedSend() } and + * {@link #bufferedReceive bufferedReceive() }. This + * method must be called before calling either one of those two + * methods. When you finish using buffered operations, you must + * call {@link #endBufferedOps endBufferedOps() }. + ***/ + public final void beginBufferedOps() + { + __receiveBuffer = new byte[PACKET_SIZE]; + __receiveDatagram = + new DatagramPacket(__receiveBuffer, __receiveBuffer.length); + _sendBuffer = new byte[PACKET_SIZE]; + __sendDatagram = + new DatagramPacket(_sendBuffer, _sendBuffer.length); + } + + /*** + * Releases the resources used to perform buffered sends and receives. + ***/ + public final void endBufferedOps() + { + __receiveBuffer = null; + __receiveDatagram = null; + _sendBuffer = null; + __sendDatagram = null; + } + + + /*** + * Sends a TFTP packet to its destination. + *

+ * @param packet The TFTP packet to send. + * @exception IOException If some I/O error occurs. + ***/ + public final void send(TFTPPacket packet) throws IOException + { + _socket_.send(packet.newDatagram()); + } + + + /*** + * Receives a TFTPPacket. + *

+ * @return The TFTPPacket received. + * @exception InterruptedIOException If a socket timeout occurs. The + * Java documentation claims an InterruptedIOException is thrown + * on a DatagramSocket timeout, but in practice we find a + * SocketException is thrown. You should catch both to be safe. + * @exception SocketException If a socket timeout occurs. The + * Java documentation claims an InterruptedIOException is thrown + * on a DatagramSocket timeout, but in practice we find a + * SocketException is thrown. You should catch both to be safe. + * @exception IOException If some other I/O error occurs. + * @exception TFTPPacketException If an invalid TFTP packet is received. + ***/ + public final TFTPPacket receive() throws IOException, InterruptedIOException, + SocketException, TFTPPacketException + { + DatagramPacket packet; + + packet = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE); + + _socket_.receive(packet); + + return TFTPPacket.newTFTPPacket(packet); + } + + +} diff --git a/org/apache/commons/net/tftp/TFTPAckPacket.java b/org/apache/commons/net/tftp/TFTPAckPacket.java new file mode 100644 index 0000000..a0fe7f9 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPAckPacket.java @@ -0,0 +1,158 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * A final class derived from TFTPPacket definiing the TFTP Acknowledgement + * packet type. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTPPacketException + * @see TFTP + ***/ + +public final class TFTPAckPacket extends TFTPPacket +{ + /*** The block number being acknowledged by the packet. ***/ + int _blockNumber; + + /*** + * Creates an acknowledgment packet to be sent to a host at a given port + * acknowledging receipt of a block. + *

+ * @param destination The host to which the packet is going to be sent. + * @param port The port to which the packet is going to be sent. + * @param blockNumber The block number being acknowledged. + ***/ + public TFTPAckPacket(InetAddress destination, int port, int blockNumber) + { + super(TFTPPacket.ACKNOWLEDGEMENT, destination, port); + _blockNumber = blockNumber; + } + + /*** + * Creates an acknowledgement packet based from a received + * datagram. Assumes the datagram is at least length 4, else an + * ArrayIndexOutOfBoundsException may be thrown. + *

+ * @param datagram The datagram containing the received acknowledgement. + * @throws TFTPPacketException If the datagram isn't a valid TFTP + * acknowledgement packet. + ***/ + TFTPAckPacket(DatagramPacket datagram) throws TFTPPacketException + { + super(TFTPPacket.ACKNOWLEDGEMENT, datagram.getAddress(), + datagram.getPort()); + byte[] data; + + data = datagram.getData(); + + if (getType() != data[1]) + throw new TFTPPacketException("TFTP operator code does not match type."); + + _blockNumber = (((data[2] & 0xff) << 8) | (data[3] & 0xff)); + } + + /*** + * This is a method only available within the package for + * implementing efficient datagram transport by elminating buffering. + * It takes a datagram as an argument, and a byte buffer in which + * to store the raw datagram data. Inside the method, the data + * is set as the datagram's data and the datagram returned. + *

+ * @param datagram The datagram to create. + * @param data The buffer to store the packet and to use in the datagram. + * @return The datagram argument. + ***/ + @Override + DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data) + { + data[0] = 0; + data[1] = (byte)_type; + data[2] = (byte)((_blockNumber & 0xffff) >> 8); + data[3] = (byte)(_blockNumber & 0xff); + + datagram.setAddress(_address); + datagram.setPort(_port); + datagram.setData(data); + datagram.setLength(4); + + return datagram; + } + + + /*** + * Creates a UDP datagram containing all the TFTP + * acknowledgement packet data in the proper format. + * This is a method exposed to the programmer in case he + * wants to implement his own TFTP client instead of using + * the {@link org.apache.commons.net.tftp.TFTPClient} + * class. Under normal circumstances, you should not have a need to call this + * method. + *

+ * @return A UDP datagram containing the TFTP acknowledgement packet. + ***/ + @Override + public DatagramPacket newDatagram() + { + byte[] data; + + data = new byte[4]; + data[0] = 0; + data[1] = (byte)_type; + data[2] = (byte)((_blockNumber & 0xffff) >> 8); + data[3] = (byte)(_blockNumber & 0xff); + + return new DatagramPacket(data, data.length, _address, _port); + } + + + /*** + * Returns the block number of the acknowledgement. + *

+ * @return The block number of the acknowledgement. + ***/ + public int getBlockNumber() + { + return _blockNumber; + } + + + /*** Sets the block number of the acknowledgement. ***/ + public void setBlockNumber(int blockNumber) + { + _blockNumber = blockNumber; + } +} + diff --git a/org/apache/commons/net/tftp/TFTPClient.java b/org/apache/commons/net/tftp/TFTPClient.java new file mode 100644 index 0000000..71d4ec6 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPClient.java @@ -0,0 +1,610 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InterruptedIOException; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.UnknownHostException; +import org.apache.commons.net.io.FromNetASCIIOutputStream; +import org.apache.commons.net.io.ToNetASCIIInputStream; + +/*** + * The TFTPClient class encapsulates all the aspects of the TFTP protocol + * necessary to receive and send files through TFTP. It is derived from + * the {@link org.apache.commons.net.tftp.TFTP} because + * it is more convenient than using aggregation, and as a result exposes + * the same set of methods to allow you to deal with the TFTP protocol + * directly. However, almost every user should only be concerend with the + * the {@link org.apache.commons.net.DatagramSocketClient#open open() }, + * {@link org.apache.commons.net.DatagramSocketClient#close close() }, + * {@link #sendFile sendFile() }, and + * {@link #receiveFile receiveFile() } methods. Additionally, the + * {@link #setMaxTimeouts setMaxTimeouts() } and + * {@link org.apache.commons.net.DatagramSocketClient#setDefaultTimeout setDefaultTimeout() } + * methods may be of importance for performance + * tuning. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTP + * @see TFTPPacket + * @see TFTPPacketException + ***/ + +public class TFTPClient extends TFTP +{ + /*** + * The default number of times a receive attempt is allowed to timeout + * before ending attempts to retry the receive and failing. The default + * is 5 timeouts. + ***/ + public static final int DEFAULT_MAX_TIMEOUTS = 5; + + /*** The maximum number of timeouts allowed before failing. ***/ + private int __maxTimeouts; + + /*** + * Creates a TFTPClient instance with a default timeout of DEFAULT_TIMEOUT, + * maximum timeouts value of DEFAULT_MAX_TIMEOUTS, a null socket, + * and buffered operations disabled. + ***/ + public TFTPClient() + { + __maxTimeouts = DEFAULT_MAX_TIMEOUTS; + } + + /*** + * Sets the maximum number of times a receive attempt is allowed to + * timeout during a receiveFile() or sendFile() operation before ending + * attempts to retry the receive and failing. + * The default is DEFAULT_MAX_TIMEOUTS. + *

+ * @param numTimeouts The maximum number of timeouts to allow. Values + * less than 1 should not be used, but if they are, they are + * treated as 1. + ***/ + public void setMaxTimeouts(int numTimeouts) + { + if (numTimeouts < 1) + __maxTimeouts = 1; + else + __maxTimeouts = numTimeouts; + } + + /*** + * Returns the maximum number of times a receive attempt is allowed to + * timeout before ending attempts to retry the receive and failing. + *

+ * @return The maximum number of timeouts allowed. + ***/ + public int getMaxTimeouts() + { + return __maxTimeouts; + } + + + /*** + * Requests a named file from a remote host, writes the + * file to an OutputStream, closes the connection, and returns the number + * of bytes read. A local UDP socket must first be created by + * {@link org.apache.commons.net.DatagramSocketClient#open open()} before + * invoking this method. This method will not close the OutputStream + * containing the file; you must close it after the method invocation. + *

+ * @param filename The name of the file to receive. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param output The OutputStream to which the file should be written. + * @param host The remote host serving the file. + * @param port The port number of the remote TFTP server. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + ***/ + public int receiveFile(String filename, int mode, OutputStream output, + InetAddress host, int port) throws IOException + { + int bytesRead, timeouts, lastBlock, block, hostPort, dataLength; + TFTPPacket sent, received = null; + TFTPErrorPacket error; + TFTPDataPacket data; + TFTPAckPacket ack = new TFTPAckPacket(host, port, 0); + + beginBufferedOps(); + + dataLength = lastBlock = hostPort = bytesRead = 0; + block = 1; + + if (mode == TFTP.ASCII_MODE) + output = new FromNetASCIIOutputStream(output); + + sent = + new TFTPReadRequestPacket(host, port, filename, mode); + +_sendPacket: + do + { + bufferedSend(sent); + +_receivePacket: + while (true) + { + timeouts = 0; + while (timeouts < __maxTimeouts) + { + try + { + received = bufferedReceive(); + break; + } + catch (SocketException e) + { + if (++timeouts >= __maxTimeouts) + { + endBufferedOps(); + throw new IOException("Connection timed out."); + } + continue; + } + catch (InterruptedIOException e) + { + if (++timeouts >= __maxTimeouts) + { + endBufferedOps(); + throw new IOException("Connection timed out."); + } + continue; + } + catch (TFTPPacketException e) + { + endBufferedOps(); + throw new IOException("Bad packet: " + e.getMessage()); + } + } + + // The first time we receive we get the port number and + // answering host address (for hosts with multiple IPs) + if (lastBlock == 0) + { + hostPort = received.getPort(); + ack.setPort(hostPort); + if(!host.equals(received.getAddress())) + { + host = received.getAddress(); + ack.setAddress(host); + sent.setAddress(host); + } + } + + // Comply with RFC 783 indication that an error acknowledgement + // should be sent to originator if unexpected TID or host. + if (host.equals(received.getAddress()) && + received.getPort() == hostPort) + { + + switch (received.getType()) + { + case TFTPPacket.ERROR: + error = (TFTPErrorPacket)received; + endBufferedOps(); + throw new IOException("Error code " + error.getError() + + " received: " + error.getMessage()); + case TFTPPacket.DATA: + data = (TFTPDataPacket)received; + dataLength = data.getDataLength(); + + lastBlock = data.getBlockNumber(); + + if (lastBlock == block) + { + try + { + output.write(data.getData(), data.getDataOffset(), + dataLength); + } + catch (IOException e) + { + error = new TFTPErrorPacket(host, hostPort, + TFTPErrorPacket.OUT_OF_SPACE, + "File write failed."); + bufferedSend(error); + endBufferedOps(); + throw e; + } + ++block; + if (block > 65535) + { + // wrap the block number + block = 0; + } + + break _receivePacket; + } + else + { + discardPackets(); + + if (lastBlock == (block == 0 ? 65535 : (block - 1))) + continue _sendPacket; // Resend last acknowledgement. + + continue _receivePacket; // Start fetching packets again. + } + //break; + + default: + endBufferedOps(); + throw new IOException("Received unexpected packet type."); + } + } + else + { + error = new TFTPErrorPacket(received.getAddress(), + received.getPort(), + TFTPErrorPacket.UNKNOWN_TID, + "Unexpected host or port."); + bufferedSend(error); + continue _sendPacket; + } + + // We should never get here, but this is a safety to avoid + // infinite loop. If only Java had the goto statement. + //break; + } + + ack.setBlockNumber(lastBlock); + sent = ack; + bytesRead += dataLength; + } // First data packet less than 512 bytes signals end of stream. + + while (dataLength == TFTPPacket.SEGMENT_SIZE); + + bufferedSend(sent); + endBufferedOps(); + + return bytesRead; + } + + + /*** + * Requests a named file from a remote host, writes the + * file to an OutputStream, closes the connection, and returns the number + * of bytes read. A local UDP socket must first be created by + * {@link org.apache.commons.net.DatagramSocketClient#open open()} before + * invoking this method. This method will not close the OutputStream + * containing the file; you must close it after the method invocation. + *

+ * @param filename The name of the file to receive. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param output The OutputStream to which the file should be written. + * @param hostname The name of the remote host serving the file. + * @param port The port number of the remote TFTP server. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + * @exception UnknownHostException If the hostname cannot be resolved. + ***/ + public int receiveFile(String filename, int mode, OutputStream output, + String hostname, int port) + throws UnknownHostException, IOException + { + return receiveFile(filename, mode, output, InetAddress.getByName(hostname), + port); + } + + + /*** + * Same as calling receiveFile(filename, mode, output, host, TFTP.DEFAULT_PORT). + * + * @param filename The name of the file to receive. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param output The OutputStream to which the file should be written. + * @param host The remote host serving the file. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + ***/ + public int receiveFile(String filename, int mode, OutputStream output, + InetAddress host) + throws IOException + { + return receiveFile(filename, mode, output, host, DEFAULT_PORT); + } + + /*** + * Same as calling receiveFile(filename, mode, output, hostname, TFTP.DEFAULT_PORT). + * + * @param filename The name of the file to receive. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param output The OutputStream to which the file should be written. + * @param hostname The name of the remote host serving the file. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + * @exception UnknownHostException If the hostname cannot be resolved. + ***/ + public int receiveFile(String filename, int mode, OutputStream output, + String hostname) + throws UnknownHostException, IOException + { + return receiveFile(filename, mode, output, InetAddress.getByName(hostname), + DEFAULT_PORT); + } + + + /*** + * Requests to send a file to a remote host, reads the file from an + * InputStream, sends the file to the remote host, and closes the + * connection. A local UDP socket must first be created by + * {@link org.apache.commons.net.DatagramSocketClient#open open()} before + * invoking this method. This method will not close the InputStream + * containing the file; you must close it after the method invocation. + *

+ * @param filename The name the remote server should use when creating + * the file on its file system. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param host The remote host receiving the file. + * @param port The port number of the remote TFTP server. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + ***/ + public void sendFile(String filename, int mode, InputStream input, + InetAddress host, int port) throws IOException + { + int bytesRead, timeouts, lastBlock, block, hostPort, dataLength, offset, totalThisPacket; + TFTPPacket sent, received = null; + TFTPErrorPacket error; + TFTPDataPacket data = + new TFTPDataPacket(host, port, 0, _sendBuffer, 4, 0); + TFTPAckPacket ack; + + boolean justStarted = true; + + beginBufferedOps(); + + dataLength = lastBlock = hostPort = bytesRead = totalThisPacket = 0; + block = 0; + boolean lastAckWait = false; + + if (mode == TFTP.ASCII_MODE) + input = new ToNetASCIIInputStream(input); + + sent = + new TFTPWriteRequestPacket(host, port, filename, mode); + +_sendPacket: + do + { + // first time: block is 0, lastBlock is 0, send a request packet. + // subsequent: block is integer starting at 1, send data packet. + bufferedSend(sent); + + // this is trying to receive an ACK +_receivePacket: + while (true) + { + + + timeouts = 0; + while (timeouts < __maxTimeouts) + { + try + { + received = bufferedReceive(); + break; + } + catch (SocketException e) + { + if (++timeouts >= __maxTimeouts) + { + endBufferedOps(); + throw new IOException("Connection timed out."); + } + continue; + } + catch (InterruptedIOException e) + { + if (++timeouts >= __maxTimeouts) + { + endBufferedOps(); + throw new IOException("Connection timed out."); + } + continue; + } + catch (TFTPPacketException e) + { + endBufferedOps(); + throw new IOException("Bad packet: " + e.getMessage()); + } + } // end of while loop over tries to receive + + // The first time we receive we get the port number and + // answering host address (for hosts with multiple IPs) + if (justStarted) + { + justStarted = false; + hostPort = received.getPort(); + data.setPort(hostPort); + if(!host.equals(received.getAddress())) + { + host = received.getAddress(); + data.setAddress(host); + sent.setAddress(host); + } + } + + // Comply with RFC 783 indication that an error acknowledgement + // should be sent to originator if unexpected TID or host. + if (host.equals(received.getAddress()) && + received.getPort() == hostPort) + { + + switch (received.getType()) + { + case TFTPPacket.ERROR: + error = (TFTPErrorPacket)received; + endBufferedOps(); + throw new IOException("Error code " + error.getError() + + " received: " + error.getMessage()); + case TFTPPacket.ACKNOWLEDGEMENT: + ack = (TFTPAckPacket)received; + + lastBlock = ack.getBlockNumber(); + + if (lastBlock == block) + { + ++block; + if (block > 65535) + { + // wrap the block number + block = 0; + } + if (lastAckWait) { + + break _sendPacket; + } + else { + break _receivePacket; + } + } + else + { + discardPackets(); + + if (lastBlock == (block == 0 ? 65535 : (block - 1))) + continue _sendPacket; // Resend last acknowledgement. + + continue _receivePacket; // Start fetching packets again. + } + //break; + + default: + endBufferedOps(); + throw new IOException("Received unexpected packet type."); + } + } + else + { + error = new TFTPErrorPacket(received.getAddress(), + received.getPort(), + TFTPErrorPacket.UNKNOWN_TID, + "Unexpected host or port."); + bufferedSend(error); + continue _sendPacket; + } + + // We should never get here, but this is a safety to avoid + // infinite loop. If only Java had the goto statement. + //break; + } + + // OK, we have just gotten ACK about the last data we sent. Make another + // and send it + + dataLength = TFTPPacket.SEGMENT_SIZE; + offset = 4; + totalThisPacket = 0; + while (dataLength > 0 && + (bytesRead = input.read(_sendBuffer, offset, dataLength)) > 0) + { + offset += bytesRead; + dataLength -= bytesRead; + totalThisPacket += bytesRead; + } + + if( totalThisPacket < TFTPPacket.SEGMENT_SIZE ) { + /* this will be our last packet -- send, wait for ack, stop */ + lastAckWait = true; + } + data.setBlockNumber(block); + data.setData(_sendBuffer, 4, totalThisPacket); + sent = data; + } + while ( totalThisPacket > 0 || lastAckWait ); + // Note: this was looping while dataLength == 0 || lastAckWait, + // which was discarding the last packet if it was not full size + // Should send the packet. + + endBufferedOps(); + } + + + /*** + * Requests to send a file to a remote host, reads the file from an + * InputStream, sends the file to the remote host, and closes the + * connection. A local UDP socket must first be created by + * {@link org.apache.commons.net.DatagramSocketClient#open open()} before + * invoking this method. This method will not close the InputStream + * containing the file; you must close it after the method invocation. + *

+ * @param filename The name the remote server should use when creating + * the file on its file system. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param hostname The name of the remote host receiving the file. + * @param port The port number of the remote TFTP server. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + * @exception UnknownHostException If the hostname cannot be resolved. + ***/ + public void sendFile(String filename, int mode, InputStream input, + String hostname, int port) + throws UnknownHostException, IOException + { + sendFile(filename, mode, input, InetAddress.getByName(hostname), port); + } + + + /*** + * Same as calling sendFile(filename, mode, input, host, TFTP.DEFAULT_PORT). + * + * @param filename The name the remote server should use when creating + * the file on its file system. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param host The name of the remote host receiving the file. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + * @exception UnknownHostException If the hostname cannot be resolved. + ***/ + public void sendFile(String filename, int mode, InputStream input, + InetAddress host) + throws IOException + { + sendFile(filename, mode, input, host, DEFAULT_PORT); + } + + /*** + * Same as calling sendFile(filename, mode, input, hostname, TFTP.DEFAULT_PORT). + * + * @param filename The name the remote server should use when creating + * the file on its file system. + * @param mode The TFTP mode of the transfer (one of the MODE constants). + * @param hostname The name of the remote host receiving the file. + * @exception IOException If an I/O error occurs. The nature of the + * error will be reported in the message. + * @exception UnknownHostException If the hostname cannot be resolved. + ***/ + public void sendFile(String filename, int mode, InputStream input, + String hostname) + throws UnknownHostException, IOException + { + sendFile(filename, mode, input, InetAddress.getByName(hostname), + DEFAULT_PORT); + } +} diff --git a/org/apache/commons/net/tftp/TFTPDataPacket.java b/org/apache/commons/net/tftp/TFTPDataPacket.java new file mode 100644 index 0000000..24c0dd3 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPDataPacket.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * A final class derived from TFTPPacket definiing the TFTP Data + * packet type. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTPPacketException + * @see TFTP + ***/ + +public final class TFTPDataPacket extends TFTPPacket +{ + /*** The maximum number of bytes in a TFTP data packet (512) ***/ + public static final int MAX_DATA_LENGTH = 512; + + /*** The minimum number of bytes in a TFTP data packet (0) ***/ + public static final int MIN_DATA_LENGTH = 0; + + /*** The block number of the packet. ***/ + int _blockNumber; + + /*** The length of the data. ***/ + int _length; + + /*** The offset into the _data array at which the data begins. ***/ + int _offset; + + /*** The data stored in the packet. ***/ + byte[] _data; + + /*** + * Creates a data packet to be sent to a host at a given port + * with a given block number. The actual data to be sent is passed as + * an array, an offset, and a length. The offset is the offset into + * the byte array where the data starts. The length is the length of + * the data. If the length is greater than MAX_DATA_LENGTH, it is + * truncated. + *

+ * @param destination The host to which the packet is going to be sent. + * @param port The port to which the packet is going to be sent. + * @param blockNumber The block number of the data. + * @param data The byte array containing the data. + * @param offset The offset into the array where the data starts. + * @param length The length of the data. + ***/ + public TFTPDataPacket(InetAddress destination, int port, int blockNumber, + byte[] data, int offset, int length) + { + super(TFTPPacket.DATA, destination, port); + + _blockNumber = blockNumber; + _data = data; + _offset = offset; + + if (length > MAX_DATA_LENGTH) + _length = MAX_DATA_LENGTH; + else + _length = length; + } + + public TFTPDataPacket(InetAddress destination, int port, int blockNumber, + byte[] data) + { + this(destination, port, blockNumber, data, 0, data.length); + } + + + /*** + * Creates a data packet based from a received + * datagram. Assumes the datagram is at least length 4, else an + * ArrayIndexOutOfBoundsException may be thrown. + *

+ * @param datagram The datagram containing the received data. + * @throws TFTPPacketException If the datagram isn't a valid TFTP + * data packet. + ***/ + TFTPDataPacket(DatagramPacket datagram) throws TFTPPacketException + { + super(TFTPPacket.DATA, datagram.getAddress(), datagram.getPort()); + + _data = datagram.getData(); + _offset = 4; + + if (getType() != _data[1]) + throw new TFTPPacketException("TFTP operator code does not match type."); + + _blockNumber = (((_data[2] & 0xff) << 8) | (_data[3] & 0xff)); + + _length = datagram.getLength() - 4; + + if (_length > MAX_DATA_LENGTH) + _length = MAX_DATA_LENGTH; + } + + /*** + * This is a method only available within the package for + * implementing efficient datagram transport by elminating buffering. + * It takes a datagram as an argument, and a byte buffer in which + * to store the raw datagram data. Inside the method, the data + * is set as the datagram's data and the datagram returned. + *

+ * @param datagram The datagram to create. + * @param data The buffer to store the packet and to use in the datagram. + * @return The datagram argument. + ***/ + @Override + DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data) + { + data[0] = 0; + data[1] = (byte)_type; + data[2] = (byte)((_blockNumber & 0xffff) >> 8); + data[3] = (byte)(_blockNumber & 0xff); + + // Doublecheck we're not the same + if (data != _data) + System.arraycopy(_data, _offset, data, 4, _length); + + datagram.setAddress(_address); + datagram.setPort(_port); + datagram.setData(data); + datagram.setLength(_length + 4); + + return datagram; + } + + /*** + * Creates a UDP datagram containing all the TFTP + * data packet data in the proper format. + * This is a method exposed to the programmer in case he + * wants to implement his own TFTP client instead of using + * the {@link org.apache.commons.net.tftp.TFTPClient} + * class. + * Under normal circumstances, you should not have a need to call this + * method. + *

+ * @return A UDP datagram containing the TFTP data packet. + ***/ + @Override + public DatagramPacket newDatagram() + { + byte[] data; + + data = new byte[_length + 4]; + data[0] = 0; + data[1] = (byte)_type; + data[2] = (byte)((_blockNumber & 0xffff) >> 8); + data[3] = (byte)(_blockNumber & 0xff); + + System.arraycopy(_data, _offset, data, 4, _length); + + return new DatagramPacket(data, _length + 4, _address, _port); + } + + /*** + * Returns the block number of the data packet. + *

+ * @return The block number of the data packet. + ***/ + public int getBlockNumber() + { + return _blockNumber; + } + + /*** Sets the block number of the data packet. ***/ + public void setBlockNumber(int blockNumber) + { + _blockNumber = blockNumber; + } + + /*** + * Sets the data for the data packet. + *

+ * @param data The byte array containing the data. + * @param offset The offset into the array where the data starts. + * @param length The length of the data. + ***/ + public void setData(byte[] data, int offset, int length) + { + _data = data; + _offset = offset; + _length = length; + + if (length > MAX_DATA_LENGTH) + _length = MAX_DATA_LENGTH; + else + _length = length; + } + + /*** + * Returns the length of the data part of the data packet. + *

+ * @return The length of the data part of the data packet. + ***/ + public int getDataLength() + { + return _length; + } + + /*** + * Returns the offset into the byte array where the packet data actually + * starts. + *

+ * @return The offset into the byte array where the packet data actually + * starts. + ***/ + public int getDataOffset() + { + return _offset; + } + + /*** + * Returns the byte array containing the packet data. + *

+ * @return The byte array containing the packet data. + ***/ + public byte[] getData() + { + return _data; + } +} diff --git a/org/apache/commons/net/tftp/TFTPErrorPacket.java b/org/apache/commons/net/tftp/TFTPErrorPacket.java new file mode 100644 index 0000000..2e7aec2 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPErrorPacket.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * A final class derived from TFTPPacket definiing the TFTP Error + * packet type. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTPPacketException + * @see TFTP + ***/ + +public final class TFTPErrorPacket extends TFTPPacket +{ + /*** The undefined error code according to RFC 783, value 0. ***/ + public static final int UNDEFINED = 0; + + /*** The file not found error code according to RFC 783, value 1. ***/ + public static final int FILE_NOT_FOUND = 1; + + /*** The access violation error code according to RFC 783, value 2. ***/ + public static final int ACCESS_VIOLATION = 2; + + /*** The disk full error code according to RFC 783, value 3. ***/ + public static final int OUT_OF_SPACE = 3; + + /*** + * The illegal TFTP operation error code according to RFC 783, value 4. + ***/ + public static final int ILLEGAL_OPERATION = 4; + + /*** The unknown transfer id error code according to RFC 783, value 5. ***/ + public static final int UNKNOWN_TID = 5; + + /*** The file already exists error code according to RFC 783, value 6. ***/ + public static final int FILE_EXISTS = 6; + + /*** The no such user error code according to RFC 783, value 7. ***/ + public static final int NO_SUCH_USER = 7; + + /*** The error code of this packet. ***/ + int _error; + + /*** The error message of this packet. ***/ + String _message; + + /*** + * Creates an error packet to be sent to a host at a given port + * with an error code and error message. + *

+ * @param destination The host to which the packet is going to be sent. + * @param port The port to which the packet is going to be sent. + * @param error The error code of the packet. + * @param message The error message of the packet. + ***/ + public TFTPErrorPacket(InetAddress destination, int port, + int error, String message) + { + super(TFTPPacket.ERROR, destination, port); + + _error = error; + _message = message; + } + + /*** + * Creates an error packet based from a received + * datagram. Assumes the datagram is at least length 4, else an + * ArrayIndexOutOfBoundsException may be thrown. + *

+ * @param datagram The datagram containing the received error. + * @throws TFTPPacketException If the datagram isn't a valid TFTP + * error packet. + ***/ + TFTPErrorPacket(DatagramPacket datagram) throws TFTPPacketException + { + super(TFTPPacket.ERROR, datagram.getAddress(), datagram.getPort()); + int index, length; + byte[] data; + StringBuffer buffer; + + data = datagram.getData(); + length = datagram.getLength(); + + if (getType() != data[1]) + throw new TFTPPacketException("TFTP operator code does not match type."); + + _error = (((data[2] & 0xff) << 8) | (data[3] & 0xff)); + + if (length < 5) + throw new TFTPPacketException("Bad error packet. No message."); + + index = 4; + buffer = new StringBuffer(); + + while (index < length && data[index] != 0) + { + buffer.append((char)data[index]); + ++index; + } + + _message = buffer.toString(); + } + + /*** + * This is a method only available within the package for + * implementing efficient datagram transport by elminating buffering. + * It takes a datagram as an argument, and a byte buffer in which + * to store the raw datagram data. Inside the method, the data + * is set as the datagram's data and the datagram returned. + *

+ * @param datagram The datagram to create. + * @param data The buffer to store the packet and to use in the datagram. + * @return The datagram argument. + ***/ + @Override + DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data) + { + int length; + + length = _message.length(); + + data[0] = 0; + data[1] = (byte)_type; + data[2] = (byte)((_error & 0xffff) >> 8); + data[3] = (byte)(_error & 0xff); + + System.arraycopy(_message.getBytes(), 0, data, 4, length); + + data[length + 4] = 0; + + datagram.setAddress(_address); + datagram.setPort(_port); + datagram.setData(data); + datagram.setLength(length + 4); + + return datagram; + } + + + /*** + * Creates a UDP datagram containing all the TFTP + * error packet data in the proper format. + * This is a method exposed to the programmer in case he + * wants to implement his own TFTP client instead of using + * the {@link org.apache.commons.net.tftp.TFTPClient} + * class. + * Under normal circumstances, you should not have a need to call this + * method. + *

+ * @return A UDP datagram containing the TFTP error packet. + ***/ + @Override + public DatagramPacket newDatagram() + { + byte[] data; + int length; + + length = _message.length(); + + data = new byte[length + 5]; + data[0] = 0; + data[1] = (byte)_type; + data[2] = (byte)((_error & 0xffff) >> 8); + data[3] = (byte)(_error & 0xff); + + System.arraycopy(_message.getBytes(), 0, data, 4, length); + + data[length + 4] = 0; + + return new DatagramPacket(data, data.length, _address, _port); + } + + + /*** + * Returns the error code of the packet. + *

+ * @return The error code of the packet. + ***/ + public int getError() + { + return _error; + } + + + /*** + * Returns the error message of the packet. + *

+ * @return The error message of the packet. + ***/ + public String getMessage() + { + return _message; + } +} diff --git a/org/apache/commons/net/tftp/TFTPPacket.java b/org/apache/commons/net/tftp/TFTPPacket.java new file mode 100644 index 0000000..e1dc5f1 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPPacket.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * TFTPPacket is an abstract class encapsulating the functionality common + * to the 5 types of TFTP packets. It also provides a static factory + * method that will create the correct TFTP packet instance from a + * datagram. This relieves the programmer from having to figure out what + * kind of TFTP packet is contained in a datagram and create it himself. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacketException + * @see TFTP + ***/ + +public abstract class TFTPPacket +{ + /*** + * The minimum size of a packet. This is 4 bytes. It is enough + * to store the opcode and blocknumber or other required data + * depending on the packet type. + ***/ + static final int MIN_PACKET_SIZE = 4; + + /*** + * This is the actual TFTP spec + * identifier and is equal to 1. + * Identifier returned by {@link #getType getType()} + * indicating a read request packet. + ***/ + public static final int READ_REQUEST = 1; + + /*** + * This is the actual TFTP spec + * identifier and is equal to 2. + * Identifier returned by {@link #getType getType()} + * indicating a write request packet. + ***/ + public static final int WRITE_REQUEST = 2; + + /*** + * This is the actual TFTP spec + * identifier and is equal to 3. + * Identifier returned by {@link #getType getType()} + * indicating a data packet. + ***/ + public static final int DATA = 3; + + /*** + * This is the actual TFTP spec + * identifier and is equal to 4. + * Identifier returned by {@link #getType getType()} + * indicating an acknowledgement packet. + ***/ + public static final int ACKNOWLEDGEMENT = 4; + + /*** + * This is the actual TFTP spec + * identifier and is equal to 5. + * Identifier returned by {@link #getType getType()} + * indicating an error packet. + ***/ + public static final int ERROR = 5; + + /*** + * The TFTP data packet maximum segment size in bytes. This is 512 + * and is useful for those familiar with the TFTP protocol who want + * to use the {@link org.apache.commons.net.tftp.TFTP} + * class methods to implement their own TFTP servers or clients. + ***/ + public static final int SEGMENT_SIZE = 512; + + /*** The type of packet. ***/ + int _type; + + /*** The port the packet came from or is going to. ***/ + int _port; + + /*** The host the packet is going to be sent or where it came from. ***/ + InetAddress _address; + + /*** + * When you receive a datagram that you expect to be a TFTP packet, you use + * this factory method to create the proper TFTPPacket object + * encapsulating the data contained in that datagram. This method is the + * only way you can instantiate a TFTPPacket derived class from a + * datagram. + *

+ * @param datagram The datagram containing a TFTP packet. + * @return The TFTPPacket object corresponding to the datagram. + * @exception TFTPPacketException If the datagram does not contain a valid + * TFTP packet. + ***/ + public final static TFTPPacket newTFTPPacket(DatagramPacket datagram) + throws TFTPPacketException + { + byte[] data; + TFTPPacket packet = null; + + if (datagram.getLength() < MIN_PACKET_SIZE) + throw new TFTPPacketException( + "Bad packet. Datagram data length is too short."); + + data = datagram.getData(); + + switch (data[1]) + { + case READ_REQUEST: + packet = new TFTPReadRequestPacket(datagram); + break; + case WRITE_REQUEST: + packet = new TFTPWriteRequestPacket(datagram); + break; + case DATA: + packet = new TFTPDataPacket(datagram); + break; + case ACKNOWLEDGEMENT: + packet = new TFTPAckPacket(datagram); + break; + case ERROR: + packet = new TFTPErrorPacket(datagram); + break; + default: + throw new TFTPPacketException( + "Bad packet. Invalid TFTP operator code."); + } + + return packet; + } + + /*** + * This constructor is not visible outside of the package. It is used + * by subclasses within the package to initialize base data. + *

+ * @param type The type of the packet. + * @param address The host the packet came from or is going to be sent. + * @param port The port the packet came from or is going to be sent. + **/ + TFTPPacket(int type, InetAddress address, int port) + { + _type = type; + _address = address; + _port = port; + } + + /*** + * This is an abstract method only available within the package for + * implementing efficient datagram transport by elminating buffering. + * It takes a datagram as an argument, and a byte buffer in which + * to store the raw datagram data. Inside the method, the data + * should be set as the datagram's data and the datagram returned. + *

+ * @param datagram The datagram to create. + * @param data The buffer to store the packet and to use in the datagram. + * @return The datagram argument. + ***/ + abstract DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data); + + /*** + * Creates a UDP datagram containing all the TFTP packet + * data in the proper format. + * This is an abstract method, exposed to the programmer in case he + * wants to implement his own TFTP client instead of using + * the {@link org.apache.commons.net.tftp.TFTPClient} + * class. + * Under normal circumstances, you should not have a need to call this + * method. + *

+ * @return A UDP datagram containing the TFTP packet. + ***/ + public abstract DatagramPacket newDatagram(); + + /*** + * Returns the type of the packet. + *

+ * @return The type of the packet. + ***/ + public final int getType() + { + return _type; + } + + /*** + * Returns the address of the host where the packet is going to be sent + * or where it came from. + *

+ * @return The type of the packet. + ***/ + public final InetAddress getAddress() + { + return _address; + } + + /*** + * Returns the port where the packet is going to be sent + * or where it came from. + *

+ * @return The port where the packet came from or where it is going. + ***/ + public final int getPort() + { + return _port; + } + + /*** Sets the port where the packet is going to be sent. ***/ + public final void setPort(int port) + { + _port = port; + } + + /*** Sets the host address where the packet is going to be sent. ***/ + public final void setAddress(InetAddress address) + { + _address = address; + } +} diff --git a/org/apache/commons/net/tftp/TFTPPacketException.java b/org/apache/commons/net/tftp/TFTPPacketException.java new file mode 100644 index 0000000..286fa3a --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPPacketException.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +/*** + * A class used to signify the occurrence of an error in the creation of + * a TFTP packet. It is not declared final so that it may be subclassed + * to identify more specific errors. You would only want to do this if + * you were building your own TFTP client or server on top of the + * {@link org.apache.commons.net.tftp.TFTP} + * class if you + * wanted more functionality than the + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods provide. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTP + ***/ + +public class TFTPPacketException extends Exception +{ + + /*** + * Simply calls the corresponding constructor of its superclass. + ***/ + public TFTPPacketException() + { + super(); + } + + /*** + * Simply calls the corresponding constructor of its superclass. + ***/ + public TFTPPacketException(String message) + { + super(message); + } +} diff --git a/org/apache/commons/net/tftp/TFTPReadRequestPacket.java b/org/apache/commons/net/tftp/TFTPReadRequestPacket.java new file mode 100644 index 0000000..2669df6 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPReadRequestPacket.java @@ -0,0 +1,80 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * A class derived from TFTPRequestPacket definiing a TFTP read request + * packet type. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTPRequestPacket + * @see TFTPPacketException + * @see TFTP + ***/ + +public final class TFTPReadRequestPacket extends TFTPRequestPacket +{ + + /*** + * Creates a read request packet to be sent to a host at a + * given port with a filename and transfer mode request. + *

+ * @param destination The host to which the packet is going to be sent. + * @param port The port to which the packet is going to be sent. + * @param filename The requested filename. + * @param mode The requested transfer mode. This should be on of the TFTP + * class MODE constants (e.g., TFTP.NETASCII_MODE). + ***/ + public TFTPReadRequestPacket(InetAddress destination, int port, + String filename, int mode) + { + super(destination, port, TFTPPacket.READ_REQUEST, filename, mode); + } + + /*** + * Creates a read request packet of based on a received + * datagram and assumes the datagram has already been identified as a + * read request. Assumes the datagram is at least length 4, else an + * ArrayIndexOutOfBoundsException may be thrown. + *

+ * @param datagram The datagram containing the received request. + * @throws TFTPPacketException If the datagram isn't a valid TFTP + * request packet. + ***/ + TFTPReadRequestPacket(DatagramPacket datagram) throws TFTPPacketException + { + super(TFTPPacket.READ_REQUEST, datagram); + } + +} diff --git a/org/apache/commons/net/tftp/TFTPRequestPacket.java b/org/apache/commons/net/tftp/TFTPRequestPacket.java new file mode 100644 index 0000000..a0f4407 --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPRequestPacket.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * An abstract class derived from TFTPPacket definiing a TFTP Request + * packet type. It is subclassed by the + * {@link org.apache.commons.net.tftp.TFTPReadRequestPacket} + * and + * {@link org.apache.commons.net.tftp.TFTPWriteRequestPacket} + * classes. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTPReadRequestPacket + * @see TFTPWriteRequestPacket + * @see TFTPPacketException + * @see TFTP + ***/ + +public abstract class TFTPRequestPacket extends TFTPPacket +{ + /*** + * An array containing the string names of the transfer modes and indexed + * by the transfer mode constants. + ***/ + static final String[] _modeStrings = { "netascii", "octet" }; + + /*** + * A null terminated byte array representation of the ascii names of the + * transfer mode constants. This is convenient for creating the TFTP + * request packets. + ***/ + static final byte[] _modeBytes[] = { + { (byte)'n', (byte)'e', (byte)'t', (byte)'a', (byte)'s', (byte)'c', + (byte)'i', (byte)'i', 0 }, + { (byte)'o', (byte)'c', (byte)'t', (byte)'e', (byte)'t', 0 } + }; + + /*** The transfer mode of the request. ***/ + int _mode; + + /*** The filename of the request. ***/ + String _filename; + + /*** + * Creates a request packet of a given type to be sent to a host at a + * given port with a filename and transfer mode request. + *

+ * @param destination The host to which the packet is going to be sent. + * @param port The port to which the packet is going to be sent. + * @param type The type of the request (either TFTPPacket.READ_REQUEST or + * TFTPPacket.WRITE_REQUEST). + * @param filename The requested filename. + * @param mode The requested transfer mode. This should be on of the TFTP + * class MODE constants (e.g., TFTP.NETASCII_MODE). + ***/ + TFTPRequestPacket(InetAddress destination, int port, + int type, String filename, int mode) + { + super(type, destination, port); + + _filename = filename; + _mode = mode; + } + + /*** + * Creates a request packet of a given type based on a received + * datagram. Assumes the datagram is at least length 4, else an + * ArrayIndexOutOfBoundsException may be thrown. + *

+ * @param type The type of the request (either TFTPPacket.READ_REQUEST or + * TFTPPacket.WRITE_REQUEST). + * @param datagram The datagram containing the received request. + * @throws TFTPPacketException If the datagram isn't a valid TFTP + * request packet of the appropriate type. + ***/ + TFTPRequestPacket(int type, DatagramPacket datagram) + throws TFTPPacketException + { + super(type, datagram.getAddress(), datagram.getPort()); + + byte[] data; + int index, length; + String mode; + StringBuffer buffer; + + data = datagram.getData(); + + if (getType() != data[1]) + throw new TFTPPacketException("TFTP operator code does not match type."); + + buffer = new StringBuffer(); + + index = 2; + length = datagram.getLength(); + + while (index < length && data[index] != 0) + { + buffer.append((char)data[index]); + ++index; + } + + _filename = buffer.toString(); + + if (index >= length) + throw new TFTPPacketException("Bad filename and mode format."); + + buffer.setLength(0); + ++index; // need to advance beyond the end of string marker + while (index < length && data[index] != 0) + { + buffer.append((char)data[index]); + ++index; + } + + mode = buffer.toString().toLowerCase(java.util.Locale.ENGLISH); + length = _modeStrings.length; + + for (index = 0; index < length; index++) + { + if (mode.equals(_modeStrings[index])) + { + _mode = index; + break; + } + } + + if (index >= length) + { + throw new TFTPPacketException("Unrecognized TFTP transfer mode: " + mode); + // May just want to default to binary mode instead of throwing + // exception. + //_mode = TFTP.OCTET_MODE; + } + } + + + /*** + * This is a method only available within the package for + * implementing efficient datagram transport by elminating buffering. + * It takes a datagram as an argument, and a byte buffer in which + * to store the raw datagram data. Inside the method, the data + * is set as the datagram's data and the datagram returned. + *

+ * @param datagram The datagram to create. + * @param data The buffer to store the packet and to use in the datagram. + * @return The datagram argument. + ***/ + @Override + final DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data) + { + int fileLength, modeLength; + + fileLength = _filename.length(); + modeLength = _modeBytes[_mode].length; + + data[0] = 0; + data[1] = (byte)_type; + System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength); + data[fileLength + 2] = 0; + System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3, + modeLength); + + datagram.setAddress(_address); + datagram.setPort(_port); + datagram.setData(data); + datagram.setLength(fileLength + modeLength + 3); + + return datagram; + } + + /*** + * Creates a UDP datagram containing all the TFTP + * request packet data in the proper format. + * This is a method exposed to the programmer in case he + * wants to implement his own TFTP client instead of using + * the {@link org.apache.commons.net.tftp.TFTPClient} + * class. Under normal circumstances, you should not have a need to call + * this method. + *

+ * @return A UDP datagram containing the TFTP request packet. + ***/ + @Override + public final DatagramPacket newDatagram() + { + int fileLength, modeLength; + byte[] data; + + fileLength = _filename.length(); + modeLength = _modeBytes[_mode].length; + + data = new byte[fileLength + modeLength + 4]; + data[0] = 0; + data[1] = (byte)_type; + System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength); + data[fileLength + 2] = 0; + System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3, + modeLength); + + return new DatagramPacket(data, data.length, _address, _port); + } + + /*** + * Returns the transfer mode of the request. + *

+ * @return The transfer mode of the request. + ***/ + public final int getMode() + { + return _mode; + } + + /*** + * Returns the requested filename. + *

+ * @return The requested filename. + ***/ + public final String getFilename() + { + return _filename; + } +} diff --git a/org/apache/commons/net/tftp/TFTPWriteRequestPacket.java b/org/apache/commons/net/tftp/TFTPWriteRequestPacket.java new file mode 100644 index 0000000..b545b2d --- /dev/null +++ b/org/apache/commons/net/tftp/TFTPWriteRequestPacket.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.net.tftp; + +import java.net.DatagramPacket; +import java.net.InetAddress; + +/*** + * A class derived from TFTPRequestPacket definiing a TFTP write request + * packet type. + *

+ * Details regarding the TFTP protocol and the format of TFTP packets can + * be found in RFC 783. But the point of these classes is to keep you + * from having to worry about the internals. Additionally, only very + * few people should have to care about any of the TFTPPacket classes + * or derived classes. Almost all users should only be concerned with the + * {@link org.apache.commons.net.tftp.TFTPClient} class + * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()} + * and + * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} + * methods. + *

+ *

+ * @author Daniel F. Savarese + * @see TFTPPacket + * @see TFTPRequestPacket + * @see TFTPPacketException + * @see TFTP + ***/ + +public final class TFTPWriteRequestPacket extends TFTPRequestPacket +{ + + /*** + * Creates a write request packet to be sent to a host at a + * given port with a filename and transfer mode request. + *

+ * @param destination The host to which the packet is going to be sent. + * @param port The port to which the packet is going to be sent. + * @param filename The requested filename. + * @param mode The requested transfer mode. This should be on of the TFTP + * class MODE constants (e.g., TFTP.NETASCII_MODE). + ***/ + public TFTPWriteRequestPacket(InetAddress destination, int port, + String filename, int mode) + { + super(destination, port, TFTPPacket.WRITE_REQUEST, filename, mode); + } + + /*** + * Creates a write request packet of based on a received + * datagram and assumes the datagram has already been identified as a + * write request. Assumes the datagram is at least length 4, else an + * ArrayIndexOutOfBoundsException may be thrown. + *

+ * @param datagram The datagram containing the received request. + * @throws TFTPPacketException If the datagram isn't a valid TFTP + * request packet. + ***/ + TFTPWriteRequestPacket(DatagramPacket datagram) throws TFTPPacketException + { + super(TFTPPacket.WRITE_REQUEST, datagram); + } +} -- cgit v1.2.3