/* * 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. * <p> * 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. * <p> * <p> * @author Daniel F. Savarese * @see org.apache.commons.net.DatagramSocketClient * @see TFTPPacket * @see TFTPPacketException * @see TFTPClient ***/ public class ExtendedTFTP 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 = ExtendedTFTPPacket.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. * <p> * @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 ExtendedTFTP() { 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. * <p> * @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. * <p> * @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 ExtendedTFTPPacket.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. * <p> * @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); } public final void restartBufferedOps(int packetSize) { endBufferedOps(); packetSize = packetSize + 4; receiveBuffer = new byte[packetSize]; receiveDatagram = new DatagramPacket(receiveBuffer, receiveBuffer.length); sendBuffer = new byte[packetSize]; 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. * <p> * @param packet The TFTP packet to send. * @exception IOException If some I/O error occurs. ***/ public final void send(ExtendedTFTPPacket packet) throws IOException { _socket_.send(packet.newDatagram()); } /*** * Receives a TFTPPacket. * <p> * @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 ExtendedTFTPPacket.newTFTPPacket(packet); } }