    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            StringBuilder buffer = new StringBuilder();
    118
    119            int index = 2;
    120            int length = datagram.getLength();
    121
    122            while (index < length && data[index] != 0)
    123            {
    124                buffer.append((char)data[index]);
    125                ++index;
    126            }
    127
    128            _filename = buffer.toString();
    129
    130            if (index >= length)
    131                throw new TFTPPacketException("Bad filename and mode format.");
    132
    133            buffer.setLength(0);
    134            ++index; // need to advance beyond the end of string marker
    135            while (index < length && data[index] != 0)
    136            {
    137                buffer.append((char)data[index]);
    138                ++index;
    139            }
    140
    141            String modeString = buffer.toString().toLowerCase(java.util.Locale.ENGLISH);
    142            length = _modeStrings.length;
    143
    144            int mode = 0;
    145            for (index = 0; index < length; index++)
    146            {
    147                if (modeString.equals(_modeStrings[index]))
    148                {
    149                    mode = index;
    150                    break;
    151                }
    152            }
    153
    154            _mode = mode;
    155
    156            if (index >= length)
    157            {
    158                throw new TFTPPacketException("Unrecognized TFTP transfer mode: " + modeString);
    159                // May just want to default to binary mode instead of throwing
    160                // exception.
    161                //_mode = TFTP.OCTET_MODE;
    162            }
    163        }
    164
    165
    166        /***
    167         * This is a method only available within the package for
    168         * implementing efficient datagram transport by elminating buffering.
    169         * It takes a datagram as an argument, and a byte buffer in which
    170         * to store the raw datagram data.  Inside the method, the data
    171         * is set as the datagram's data and the datagram returned.
    172         * <p>
    173         * @param datagram  The datagram to create.
    174         * @param data The buffer to store the packet and to use in the datagram.
    175         * @return The datagram argument.
    176         ***/
    177        @Override
    178        final DatagramPacket _newDatagram(DatagramPacket datagram, byte[] data)
    179        {
    180            int fileLength, modeLength;
    181
    182            fileLength = _filename.length();
    183            modeLength = _modeBytes[_mode].length;
    184
    185            data[0] = 0;
    186            data[1] = (byte)_type;
    187            System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength);
    188            data[fileLength + 2] = 0;
    189            System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3,
    190                             modeLength);
    191
    192            datagram.setAddress(_address);
    193            datagram.setPort(_port);
    194            datagram.setData(data);
    195            datagram.setLength(fileLength + modeLength + 3);
    196
    197            return datagram;
    198        }
    199
    200        /***
    201         * Creates a UDP datagram containing all the TFTP
    202         * request packet data in the proper format.
    203         * This is a method exposed to the programmer in case he
    204         * wants to implement his own TFTP client instead of using
    205         * the {@link org.apache.commons.net.tftp.TFTPClient}
    206         * class.  Under normal circumstances, you should not have a need to call
    207         * this method.
    208         * <p>
    209         * @return A UDP datagram containing the TFTP request packet.
    210         ***/
    211        @Override
    212        public final DatagramPacket newDatagram()
    213        {
    214            int fileLength, modeLength;
    215            byte[] data;
    216
    217            fileLength = _filename.length();
    218            modeLength = _modeBytes[_mode].length;
    219
    220            data = new byte[fileLength + modeLength + 4];
    221            data[0] = 0;
    222            data[1] = (byte)_type;
    223            System.arraycopy(_filename.getBytes(), 0, data, 2, fileLength);
    224            data[fileLength + 2] = 0;
    225            System.arraycopy(_modeBytes[_mode], 0, data, fileLength + 3,
    226                             modeLength);
    227
    228            return new DatagramPacket(data, data.length, _address, _port);
    229        }
    230
    231        /***
    232         * Returns the transfer mode of the request.
    233         * <p>
    234         * @return The transfer mode of the request.
    235         ***/
    236        public final int getMode()
    237        {
    238            return _mode;
    239        }
    240
    241        /***
    242         * Returns the requested filename.
    243         * <p>
    244         * @return The requested filename.
    245         ***/
    246        public final String getFilename()
    247        {
    248            return _filename;
    249        }
    250    }