    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
    018    package org.apache.commons.net.tftp;
    019
    020    import java.net.DatagramPacket;
    021    import 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
    050    public 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    }