    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.tftp;
    019
    020import java.io.IOException;
    021import java.io.InterruptedIOException;
    022import java.net.DatagramPacket;
    023import java.net.SocketException;
    024
    025import org.apache.commons.net.DatagramSocketClient;
    026
    027/***
    028 * The TFTP class exposes a set of methods to allow you to deal with the TFTP
    029 * protocol directly, in case you want to write your own TFTP client or
    030 * server.  However, almost every user should only be concerend with
    031 * the {@link org.apache.commons.net.DatagramSocketClient#open  open() },
    032 * and {@link org.apache.commons.net.DatagramSocketClient#close  close() },
    033 * methods. Additionally,the a
    034 * {@link org.apache.commons.net.DatagramSocketClient#setDefaultTimeout setDefaultTimeout() }
    035 *  method may be of importance for performance tuning.
    036 * <p>
    037 * Details regarding the TFTP protocol and the format of TFTP packets can
    038 * be found in RFC 783.  But the point of these classes is to keep you
    039 * from having to worry about the internals.
    040 *
    041 *
    042 * @see org.apache.commons.net.DatagramSocketClient
    043 * @see TFTPPacket
    044 * @see TFTPPacketException
    045 * @see TFTPClient
    046 ***/
    047
    048public class TFTP extends DatagramSocketClient
    049{
    050    /***
    051     * The ascii transfer mode.  Its value is 0 and equivalent to NETASCII_MODE
    052     ***/
    053    public static final int ASCII_MODE = 0;
    054
    055    /***
    056     * The netascii transfer mode.  Its value is 0.
    057     ***/
    058    public static final int NETASCII_MODE = 0;
    059
    060    /***
    061     * The binary transfer mode.  Its value is 1 and equivalent to OCTET_MODE.
    062     ***/
    063    public static final int BINARY_MODE = 1;
    064
    065    /***
    066     * The image transfer mode.  Its value is 1 and equivalent to OCTET_MODE.
    067     ***/
    068    public static final int IMAGE_MODE = 1;
    069
    070    /***
    071     * The octet transfer mode.  Its value is 1.
    072     ***/
    073    public static final int OCTET_MODE = 1;
    074
    075    /***
    076     * The default number of milliseconds to wait to receive a datagram
    077     * before timing out.  The default is 5000 milliseconds (5 seconds).
    078     ***/
    079    public static final int DEFAULT_TIMEOUT = 5000;
    080
    081    /***
    082     * The default TFTP port according to RFC 783 is 69.
    083     ***/
    084    public static final int DEFAULT_PORT = 69;
    085
    086    /***
    087     * The size to use for TFTP packet buffers.  Its 4 plus the
    088     * TFTPPacket.SEGMENT_SIZE, i.e. 516.
    089     ***/
    090    static final int PACKET_SIZE = TFTPPacket.SEGMENT_SIZE + 4;
    091
    092    /*** A buffer used to accelerate receives in bufferedReceive() ***/
    093    private byte[] receiveBuffer;
    094
    095    /*** A datagram used to minimize memory allocation in bufferedReceive() ***/
    096    private DatagramPacket receiveDatagram;
    097
    098    /*** A datagram used to minimize memory allocation in bufferedSend() ***/
    099    private DatagramPacket sendDatagram;
    100
    101    /***
    102     * A buffer used to accelerate sends in bufferedSend().
    103     * It is left package visible so that TFTPClient may be slightly more
    104     * efficient during file sends.  It saves the creation of an
    105     * additional buffer and prevents a buffer copy in _newDataPcket().
    106     ***/
    107    byte[] sendBuffer;
    108
    109
    110    /***
    111     * Returns the TFTP string representation of a TFTP transfer mode.
    112     * Will throw an ArrayIndexOutOfBoundsException if an invalid transfer
    113     * mode is specified.
    114     *
    115     * @param mode  The TFTP transfer mode.  One of the MODE constants.
    116     * @return  The TFTP string representation of the TFTP transfer mode.
    117     ***/
    118    public static final String getModeName(final int mode)
    119    {
    120        return TFTPRequestPacket._modeStrings[mode];
    121    }
    122
    123    /***
    124     * Creates a TFTP instance with a default timeout of DEFAULT_TIMEOUT,
    125     * a null socket, and buffered operations disabled.
    126     ***/
    127    public TFTP()
    128    {
    129        setDefaultTimeout(DEFAULT_TIMEOUT);
    130        receiveBuffer = null;
    131        receiveDatagram = null;
    132    }
    133
    134    /***
    135     * This method synchronizes a connection by discarding all packets that
    136     * may be in the local socket buffer.  This method need only be called
    137     * when you implement your own TFTP client or server.
    138     *
    139     * @throws IOException if an I/O error occurs.
    140     ***/
    141    public final void discardPackets() throws IOException
    142    {
    143        int to;
    144        DatagramPacket datagram;
    145
    146        datagram = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE);
    147
    148        to = getSoTimeout();
    149        setSoTimeout(1);
    150
    151        try
    152        {
    153            while (true) {
    154                _socket_.receive(datagram);
    155            }
    156        }
    157        catch (final SocketException e)
    158        {
    159            // Do nothing.  We timed out so we hope we're caught up.
    160        }
    161        catch (final InterruptedIOException e)
    162        {
    163            // Do nothing.  We timed out so we hope we're caught up.
    164        }
    165
    166        setSoTimeout(to);
    167    }
    168
    169
    170    /***
    171     * This is a special method to perform a more efficient packet receive.
    172     * It should only be used after calling
    173     * {@link #beginBufferedOps  beginBufferedOps() }.  beginBufferedOps()
    174     * initializes a set of buffers used internally that prevent the new
    175     * allocation of a DatagramPacket and byte array for each send and receive.
    176     * To use these buffers you must call the bufferedReceive() and
    177     * bufferedSend() methods instead of send() and receive().  You must
    178     * also be certain that you don't manipulate the resulting packet in
    179     * such a way that it interferes with future buffered operations.
    180     * For example, a TFTPDataPacket received with bufferedReceive() will
    181     * have a reference to the internal byte buffer.  You must finish using
    182     * this data before calling bufferedReceive() again, or else the data
    183     * will be overwritten by the the call.
    184     *
    185     * @return The TFTPPacket received.
    186     * @throws InterruptedIOException  If a socket timeout occurs.  The
    187     *       Java documentation claims an InterruptedIOException is thrown
    188     *       on a DatagramSocket timeout, but in practice we find a
    189     *       SocketException is thrown.  You should catch both to be safe.
    190     * @throws SocketException  If a socket timeout occurs.  The
    191     *       Java documentation claims an InterruptedIOException is thrown
    192     *       on a DatagramSocket timeout, but in practice we find a
    193     *       SocketException is thrown.  You should catch both to be safe.
    194     * @throws IOException  If some other I/O error occurs.
    195     * @throws TFTPPacketException If an invalid TFTP packet is received.
    196     ***/
    197    public final TFTPPacket bufferedReceive() throws IOException,
    198                InterruptedIOException, SocketException, TFTPPacketException
    199    {
    200        receiveDatagram.setData(receiveBuffer);
    201        receiveDatagram.setLength(receiveBuffer.length);
    202        _socket_.receive(receiveDatagram);
    203
    204        final TFTPPacket newTFTPPacket = TFTPPacket.newTFTPPacket(receiveDatagram);
    205        trace("<", newTFTPPacket);
    206        return newTFTPPacket;
    207    }
    208
    209    /***
    210     * This is a special method to perform a more efficient packet send.
    211     * It should only be used after calling
    212     * {@link #beginBufferedOps  beginBufferedOps() }.  beginBufferedOps()
    213     * initializes a set of buffers used internally that prevent the new
    214     * allocation of a DatagramPacket and byte array for each send and receive.
    215     * To use these buffers you must call the bufferedReceive() and
    216     * bufferedSend() methods instead of send() and receive().  You must
    217     * also be certain that you don't manipulate the resulting packet in
    218     * such a way that it interferes with future buffered operations.
    219     * For example, a TFTPDataPacket received with bufferedReceive() will
    220     * have a reference to the internal byte buffer.  You must finish using
    221     * this data before calling bufferedReceive() again, or else the data
    222     * will be overwritten by the the call.
    223     *
    224     * @param packet  The TFTP packet to send.
    225     * @throws IOException  If some  I/O error occurs.
    226     ***/
    227    public final void bufferedSend(final TFTPPacket packet) throws IOException
    228    {
    229        trace(">", packet);
    230        _socket_.send(packet._newDatagram(sendDatagram, sendBuffer));
    231    }
    232
    233
    234    /***
    235     * Initializes the internal buffers. Buffers are used by
    236     * {@link #bufferedSend  bufferedSend() } and
    237     * {@link #bufferedReceive  bufferedReceive() }.  This
    238     * method must be called before calling either one of those two
    239     * methods.  When you finish using buffered operations, you must
    240     * call {@link #endBufferedOps  endBufferedOps() }.
    241     ***/
    242    public final void beginBufferedOps()
    243    {
    244        receiveBuffer = new byte[PACKET_SIZE];
    245        receiveDatagram =
    246            new DatagramPacket(receiveBuffer, receiveBuffer.length);
    247        sendBuffer = new byte[PACKET_SIZE];
    248        sendDatagram =
    249            new DatagramPacket(sendBuffer, sendBuffer.length);
    250    }
    251
    252    /***
    253     * Releases the resources used to perform buffered sends and receives.
    254     ***/
    255    public final void endBufferedOps()
    256    {
    257        receiveBuffer = null;
    258        receiveDatagram = null;
    259        sendBuffer = null;
    260        sendDatagram = null;
    261    }
    262
    263
    264    /***
    265     * Sends a TFTP packet to its destination.
    266     *
    267     * @param packet  The TFTP packet to send.
    268     * @throws IOException  If some  I/O error occurs.
    269     ***/
    270    public final void send(final TFTPPacket packet) throws IOException
    271    {
    272        trace(">", packet);
    273        _socket_.send(packet.newDatagram());
    274    }
    275
    276
    277    /***
    278     * Receives a TFTPPacket.
    279     *
    280     * @return The TFTPPacket received.
    281     * @throws InterruptedIOException  If a socket timeout occurs.  The
    282     *       Java documentation claims an InterruptedIOException is thrown
    283     *       on a DatagramSocket timeout, but in practice we find a
    284     *       SocketException is thrown.  You should catch both to be safe.
    285     * @throws SocketException  If a socket timeout occurs.  The
    286     *       Java documentation claims an InterruptedIOException is thrown
    287     *       on a DatagramSocket timeout, but in practice we find a
    288     *       SocketException is thrown.  You should catch both to be safe.
    289     * @throws IOException  If some other I/O error occurs.
    290     * @throws TFTPPacketException If an invalid TFTP packet is received.
    291     ***/
    292    public final TFTPPacket receive() throws IOException, InterruptedIOException,
    293                SocketException, TFTPPacketException
    294    {
    295        DatagramPacket packet;
    296
    297        packet = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE);
    298
    299        _socket_.receive(packet);
    300
    301        final TFTPPacket newTFTPPacket = TFTPPacket.newTFTPPacket(packet);
    302        trace("<", newTFTPPacket);
    303        return newTFTPPacket;
    304    }
    305
    306    /**
    307     * Trace facility; this implementation does nothing.
    308     * <p>
    309     * Override it to trace the data, for example:<br>
    310     * {@code System.out.println(direction + " " + packet.toString());}
    311     * @param direction {@code >} or  {@code <}
    312     * @param packet the packet to be sent or that has been received respectively
    313     * @since 3.6
    314     */
    315    protected void trace(final String direction, final TFTPPacket packet) {
    316    }
    317
    318}