    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        final int to;
    144        final 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 | InterruptedIOException e)
    158        {
    159            // Do nothing.  We timed out so we hope we're caught up.
    160        }
    161
    162        setSoTimeout(to);
    163    }
    164
    165
    166    /**
    167     * This is a special method to perform a more efficient packet receive.
    168     * It should only be used after calling
    169     * {@link #beginBufferedOps  beginBufferedOps() }.  beginBufferedOps()
    170     * initializes a set of buffers used internally that prevent the new
    171     * allocation of a DatagramPacket and byte array for each send and receive.
    172     * To use these buffers you must call the bufferedReceive() and
    173     * bufferedSend() methods instead of send() and receive().  You must
    174     * also be certain that you don't manipulate the resulting packet in
    175     * such a way that it interferes with future buffered operations.
    176     * For example, a TFTPDataPacket received with bufferedReceive() will
    177     * have a reference to the internal byte buffer.  You must finish using
    178     * this data before calling bufferedReceive() again, or else the data
    179     * will be overwritten by the the call.
    180     *
    181     * @return The TFTPPacket received.
    182     * @throws InterruptedIOException  If a socket timeout occurs.  The
    183     *       Java documentation claims an InterruptedIOException is thrown
    184     *       on a DatagramSocket timeout, but in practice we find a
    185     *       SocketException is thrown.  You should catch both to be safe.
    186     * @throws SocketException  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 IOException  If some other I/O error occurs.
    191     * @throws TFTPPacketException If an invalid TFTP packet is received.
    192     */
    193    public final TFTPPacket bufferedReceive() throws IOException,
    194                InterruptedIOException, SocketException, TFTPPacketException
    195    {
    196        receiveDatagram.setData(receiveBuffer);
    197        receiveDatagram.setLength(receiveBuffer.length);
    198        _socket_.receive(receiveDatagram);
    199
    200        final TFTPPacket newTFTPPacket = TFTPPacket.newTFTPPacket(receiveDatagram);
    201        trace("<", newTFTPPacket);
    202        return newTFTPPacket;
    203    }
    204
    205    /**
    206     * This is a special method to perform a more efficient packet send.
    207     * It should only be used after calling
    208     * {@link #beginBufferedOps  beginBufferedOps() }.  beginBufferedOps()
    209     * initializes a set of buffers used internally that prevent the new
    210     * allocation of a DatagramPacket and byte array for each send and receive.
    211     * To use these buffers you must call the bufferedReceive() and
    212     * bufferedSend() methods instead of send() and receive().  You must
    213     * also be certain that you don't manipulate the resulting packet in
    214     * such a way that it interferes with future buffered operations.
    215     * For example, a TFTPDataPacket received with bufferedReceive() will
    216     * have a reference to the internal byte buffer.  You must finish using
    217     * this data before calling bufferedReceive() again, or else the data
    218     * will be overwritten by the the call.
    219     *
    220     * @param packet  The TFTP packet to send.
    221     * @throws IOException  If some  I/O error occurs.
    222     */
    223    public final void bufferedSend(final TFTPPacket packet) throws IOException
    224    {
    225        trace(">", packet);
    226        _socket_.send(packet.newDatagram(sendDatagram, sendBuffer));
    227    }
    228
    229
    230    /**
    231     * Initializes the internal buffers. Buffers are used by
    232     * {@link #bufferedSend  bufferedSend() } and
    233     * {@link #bufferedReceive  bufferedReceive() }.  This
    234     * method must be called before calling either one of those two
    235     * methods.  When you finish using buffered operations, you must
    236     * call {@link #endBufferedOps  endBufferedOps() }.
    237     */
    238    public final void beginBufferedOps()
    239    {
    240        receiveBuffer = new byte[PACKET_SIZE];
    241        receiveDatagram =
    242            new DatagramPacket(receiveBuffer, receiveBuffer.length);
    243        sendBuffer = new byte[PACKET_SIZE];
    244        sendDatagram =
    245            new DatagramPacket(sendBuffer, sendBuffer.length);
    246    }
    247
    248    /**
    249     * Releases the resources used to perform buffered sends and receives.
    250     */
    251    public final void endBufferedOps()
    252    {
    253        receiveBuffer = null;
    254        receiveDatagram = null;
    255        sendBuffer = null;
    256        sendDatagram = null;
    257    }
    258
    259
    260    /**
    261     * Sends a TFTP packet to its destination.
    262     *
    263     * @param packet  The TFTP packet to send.
    264     * @throws IOException  If some  I/O error occurs.
    265     */
    266    public final void send(final TFTPPacket packet) throws IOException
    267    {
    268        trace(">", packet);
    269        _socket_.send(packet.newDatagram());
    270    }
    271
    272
    273    /**
    274     * Receives a TFTPPacket.
    275     *
    276     * @return The TFTPPacket received.
    277     * @throws InterruptedIOException  If a socket timeout occurs.  The
    278     *       Java documentation claims an InterruptedIOException is thrown
    279     *       on a DatagramSocket timeout, but in practice we find a
    280     *       SocketException is thrown.  You should catch both to be safe.
    281     * @throws SocketException  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 IOException  If some other I/O error occurs.
    286     * @throws TFTPPacketException If an invalid TFTP packet is received.
    287     */
    288    public final TFTPPacket receive() throws IOException, InterruptedIOException,
    289                SocketException, TFTPPacketException
    290    {
    291        final DatagramPacket packet;
    292
    293        packet = new DatagramPacket(new byte[PACKET_SIZE], PACKET_SIZE);
    294
    295        _socket_.receive(packet);
    296
    297        final TFTPPacket newTFTPPacket = TFTPPacket.newTFTPPacket(packet);
    298        trace("<", newTFTPPacket);
    299        return newTFTPPacket;
    300    }
    301
    302    /**
    303     * Trace facility; this implementation does nothing.
    304     * <p>
    305     * Override it to trace the data, for example:<br>
    306     * {@code System.out.println(direction + " " + packet.toString());}
    307     * @param direction {@code >} or  {@code <}
    308     * @param packet the packet to be sent or that has been received respectively
    309     * @since 3.6
    310     */
    311    protected void trace(final String direction, final TFTPPacket packet) {
    312    }
    313
    314}