    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 *
    042 *
    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 file name 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 file name and transfer mode request.
    078     *
    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 file name.
    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(final InetAddress destination, final int port,
    088                      final int type, final String fileName, final int mode)
    089    {
    090        super(type, destination, port);
    091
    092        this.fileName = fileName;
    093        this.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     *
    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(final int type, final DatagramPacket datagram)
    108    throws TFTPPacketException
    109    {
    110        super(type, datagram.getAddress(), datagram.getPort());
    111
    112        final byte[] data = datagram.getData();
    113
    114        if (getType() != data[1]) {
    115            throw new TFTPPacketException("TFTP operator code does not match type.");
    116        }
    117
    118        final 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        this.fileName = buffer.toString();
    130
    131        if (index >= length) {
    132            throw new TFTPPacketException("Bad file name 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        final 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        this.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     *
    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(final DatagramPacket datagram, final byte[] data)
    181    {
    182        final int fileLength;
    183        final int modeLength;
    184
    185        fileLength = fileName.length();
    186        modeLength = modeBytes[mode].length;
    187
    188        data[0] = 0;
    189        data[1] = (byte)type;
    190        System.arraycopy(fileName.getBytes(), 0, data, 2, fileLength);
    191        data[fileLength + 2] = 0;
    192        System.arraycopy(modeBytes[mode], 0, data, fileLength + 3,
    193                         modeLength);
    194
    195        datagram.setAddress(address);
    196        datagram.setPort(port);
    197        datagram.setData(data);
    198        datagram.setLength(fileLength + modeLength + 3);
    199
    200        return datagram;
    201    }
    202
    203    /**
    204     * Creates a UDP datagram containing all the TFTP
    205     * request packet data in the proper format.
    206     * This is a method exposed to the programmer in case he
    207     * wants to implement his own TFTP client instead of using
    208     * the {@link org.apache.commons.net.tftp.TFTPClient}
    209     * class.  Under normal circumstances, you should not have a need to call
    210     * this method.
    211     *
    212     * @return A UDP datagram containing the TFTP request packet.
    213     */
    214    @Override
    215    public final DatagramPacket newDatagram()
    216    {
    217        final int fileLength;
    218        final int modeLength;
    219        final byte[] data;
    220
    221        fileLength = fileName.length();
    222        modeLength = modeBytes[mode].length;
    223
    224        data = new byte[fileLength + modeLength + 4];
    225        data[0] = 0;
    226        data[1] = (byte)type;
    227        System.arraycopy(fileName.getBytes(), 0, data, 2, fileLength);
    228        data[fileLength + 2] = 0;
    229        System.arraycopy(modeBytes[mode], 0, data, fileLength + 3,
    230                         modeLength);
    231
    232        return new DatagramPacket(data, data.length, address, port);
    233    }
    234
    235    /**
    236     * Returns the transfer mode of the request.
    237     *
    238     * @return The transfer mode of the request.
    239     */
    240    public final int getMode()
    241    {
    242        return mode;
    243    }
    244
    245    /**
    246     * Returns the requested file name.
    247     *
    248     * @return The requested file name.
    249     */
    250    public final String getFilename()
    251    {
    252        return fileName;
    253    }
    254}