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(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 (SocketException e)
158        {
159            // Do nothing.  We timed out so we hope we're caught up.
160        }
161        catch (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        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(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(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        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 ">" or "<"
312     * @param packet the packet to be sent or that has been received
313     * @since 3.6
314     */
315    protected void trace(String direction, TFTPPacket packet) {
316    }
317
318}