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