    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.net.DatagramPacket;
    021import java.net.InetAddress;
    022
    023/***
    024 * An abstract class derived from TFTPPacket definiing a TFTP Request
    025 * packet type.  It is subclassed by the
    026 * {@link org.apache.commons.net.tftp.TFTPReadRequestPacket}
    027 *   and
    028 * {@link org.apache.commons.net.tftp.TFTPWriteRequestPacket}
    029 *  classes.
    030 * <p>
    031 * Details regarding the TFTP protocol and the format of TFTP packets can
    032 * be found in RFC 783.  But the point of these classes is to keep you
    033 * from having to worry about the internals.  Additionally, only very
    034 * few people should have to care about any of the TFTPPacket classes
    035 * or derived classes.  Almost all users should only be concerned with the
    036 * {@link org.apache.commons.net.tftp.TFTPClient} class
    037 * {@link org.apache.commons.net.tftp.TFTPClient#receiveFile receiveFile()}
    038 * and
    039 * {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()}
    040 * methods.
    041 * <p>
    042 * <p>
    043 * @see TFTPPacket
    044 * @see TFTPReadRequestPacket
    045 * @see TFTPWriteRequestPacket
    046 * @see TFTPPacketException
    047 * @see TFTP
    048 ***/
    049
    050public abstract class TFTPRequestPacket extends TFTPPacket
    051{
    052    /***
    053     * An array containing the string names of the transfer modes and indexed
    054     * by the transfer mode constants.
    055     ***/
    056    static final String[] _modeStrings = { "netascii", "octet" };
    057
    058    /***
    059     * A null terminated byte array representation of the ascii names of the
    060     * transfer mode constants.  This is convenient for creating the TFTP
    061     * request packets.
    062     ***/
    063    private static final byte[] _modeBytes[] = {
    064                                           { (byte)'n', (byte)'e', (byte)'t', (byte)'a', (byte)'s', (byte)'c',
    065                                             (byte)'i', (byte)'i', 0 },
    066                                           { (byte)'o', (byte)'c', (byte)'t', (byte)'e', (byte)'t', 0 }
    067                                       };
    068
    069    /*** The transfer mode of the request. ***/
    070    private final int _mode;
    071
    072    /*** The filename of the request. ***/
    073    private final String _filename;
    074
    075    /***
    076     * Creates a request packet of a given type to be sent to a host at a
    077     * given port with a filename and transfer mode request.
    078     * <p>
    079     * @param destination  The host to which the packet is going to be sent.
    080     * @param port  The port to which the packet is going to be sent.
    081     * @param type The type of the request (either TFTPPacket.READ_REQUEST or
    082     *             TFTPPacket.WRITE_REQUEST).
    083     * @param filename The requested filename.
    084     * @param mode The requested transfer mode.  This should be on of the TFTP
    085     *        class MODE constants (e.g., TFTP.NETASCII_MODE).
    086     ***/
    087    TFTPRequestPacket(InetAddress destination, int port,
    088                      int type, String filename, int mode)
    089    {
    090        super(type, destination, port);
    091
    092        _filename = filename;
    093        _mode = mode;
    094    }
    095
    096    /***
    097     * Creates a request packet of a given type based on a received
    098     * datagram.  Assumes the datagram is at least length 4, else an
    099     * ArrayIndexOutOfBoundsException may be thrown.
    100     * <p>
    101     * @param type The type of the request (either TFTPPacket.READ_REQUEST or
    102     *             TFTPPacket.WRITE_REQUEST).
    103     * @param datagram  The datagram containing the received request.
    104     * @throws TFTPPacketException  If the datagram isn't a valid TFTP
    105     *         request packet of the appropriate type.
    106     ***/
    107    TFTPRequestPacket(int type, DatagramPacket datagram)
    108    throws TFTPPacketException
    109    {
    110        super(type, datagram.getAddress(), datagram.getPort());
    111
    112        byte[] data = datagram.getData();
    113
    114        if (getType() != data[1]) {
    115            throw new TFTPPacketException("TFTP operator code does not match type.");
    116        }
    117
    118        StringBuilder buffer = new StringBuilder();
    119
    120        int index = 2;
    121        int length = datagram.getLength();
    122
    123        while (index < length && data[index] != 0)
    124        {
    125            buffer.append((char)data[index]);
    126            ++index;
    127        }
    128
    129        _filename = buffer.toString();
    130
    131        if (index >= length) {
    132            throw new TFTPPacketException("Bad filename and mode format.");
    133        }
    134
    135        buffer.setLength(0);
    136        ++index; // need to advance beyond the end of string marker
    137        while (index < length && data[index] != 0)
    138        {
    139            buffer.append((char)data[index]);
    140            ++index;
    141        }
    142
    143        String modeString = buffer.toString().toLowerCase(java.util.Locale.ENGLISH);
    144        length = _modeStrings.length;
    145
    146        int mode = 0;
    147        for (index = 0; index < length; index++)
    148        {
    149            if (modeString.equals(_modeStrings[index]))
    150            {
    151                mode = index;
    152                break;
    153            }
    154        }
    155
    156        _mode = mode;
    157
    158        if (index >= length)
    159        {
    160            throw new TFTPPacketException("Unrecognized TFTP transfer mode: " + modeString);
    161            // May just want to default to binary mode instead of throwing
    162            // exception.
    163            //_mode = TFTP.OCTET_MODE;
    164        }
    165    }
    166
    167
    168    /***
    169     * This is a method only available within the package for
    170     * implementing efficient datagram transport by elminating buffering.
    171     * It takes a datagram as an argument, and a byte buffer in which
    172     * to store the raw datagram data.  Inside the method, the data
    173     * is set as the datagram's data and the datagram returned.
    174     * <p>
    175     * @param datagram  The datagram to create.
    176     * @param data The buffer to store the packet and to use in the datagram.
    177     * @return The datagram argument.
    178     ***/
    179    @Override
    180    final DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data)
    181    {
    182        int fileLength, modeLength;
    183
    184        fileLength = _filename.length();
    185        modeLength = _modeBytes[_mode].length;
    186
    187        data[0] = 0;
    188        data[1] = (byte)_type;
    189        System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength);
    190        data[fileLength + 2] = 0;
    191        System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3,
    192                         modeLength);
    193
    194        datagram.setAddress(_address);
    195        datagram.setPort(_port);
    196        datagram.setData(data);
    197        datagram.setLength(fileLength + modeLength + 3);
    198
    199        return datagram;
    200    }
    201
    202    /***
    203     * Creates a UDP datagram containing all the TFTP
    204     * request packet data in the proper format.
    205     * This is a method exposed to the programmer in case he
    206     * wants to implement his own TFTP client instead of using
    207     * the {@link org.apache.commons.net.tftp.TFTPClient}
    208     * class.  Under normal circumstances, you should not have a need to call
    209     * this method.
    210     * <p>
    211     * @return A UDP datagram containing the TFTP request packet.
    212     ***/
    213    @Override
    214    public final DatagramPacket newDatagram()
    215    {
    216        int fileLength, modeLength;
    217        byte[] data;
    218
    219        fileLength = _filename.length();
    220        modeLength = _modeBytes[_mode].length;
    221
    222        data = new byte[fileLength + modeLength + 4];
    223        data[0] = 0;
    224        data[1] = (byte)_type;
    225        System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength);
    226        data[fileLength + 2] = 0;
    227        System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3,
    228                         modeLength);
    229
    230        return new DatagramPacket(data, data.length, _address, _port);
    231    }
    232
    233    /***
    234     * Returns the transfer mode of the request.
    235     * <p>
    236     * @return The transfer mode of the request.
    237     ***/
    238    public final int getMode()
    239    {
    240        return _mode;
    241    }
    242
    243    /***
    244     * Returns the requested filename.
    245     * <p>
    246     * @return The requested filename.
    247     ***/
    248    public final String getFilename()
    249    {
    250        return _filename;
    251    }
    252}