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}