    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 * TFTPPacket is an abstract class encapsulating the functionality common to the 5 types of TFTP packets. It also provides a static factory method that will
    025 * create the correct TFTP packet instance from a datagram. This relieves the programmer from having to figure out what kind of TFTP packet is contained in a
    026 * datagram and create it himself.
    027 * <p>
    028 * Details regarding the TFTP protocol and the format of TFTP packets can be found in RFC 783. But the point of these classes is to keep you from having to
    029 * worry about the internals. Additionally, only very few people should have to care about any of the TFTPPacket classes or derived classes. Almost all users
    030 * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile
    031 * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods.
    032 *
    033 *
    034 * @see TFTPPacketException
    035 * @see TFTP
    036 */
    037
    038public abstract class TFTPPacket {
    039    /**
    040     * The minimum size of a packet. This is 4 bytes. It is enough to store the opcode and blocknumber or other required data depending on the packet type.
    041     */
    042    static final int MIN_PACKET_SIZE = 4;
    043
    044    /**
    045     * This is the actual TFTP spec identifier and is equal to 1. Identifier returned by {@link #getType getType()} indicating a read request packet.
    046     */
    047    public static final int READ_REQUEST = 1;
    048
    049    /**
    050     * This is the actual TFTP spec identifier and is equal to 2. Identifier returned by {@link #getType getType()} indicating a write request packet.
    051     */
    052    public static final int WRITE_REQUEST = 2;
    053
    054    /**
    055     * This is the actual TFTP spec identifier and is equal to 3. Identifier returned by {@link #getType getType()} indicating a data packet.
    056     */
    057    public static final int DATA = 3;
    058
    059    /**
    060     * This is the actual TFTP spec identifier and is equal to 4. Identifier returned by {@link #getType getType()} indicating an acknowledgement packet.
    061     */
    062    public static final int ACKNOWLEDGEMENT = 4;
    063
    064    /**
    065     * This is the actual TFTP spec identifier and is equal to 5. Identifier returned by {@link #getType getType()} indicating an error packet.
    066     */
    067    public static final int ERROR = 5;
    068
    069    /**
    070     * The TFTP data packet maximum segment size in bytes. This is 512 and is useful for those familiar with the TFTP protocol who want to use the
    071     * {@link org.apache.commons.net.tftp.TFTP} class methods to implement their own TFTP servers or clients.
    072     */
    073    public static final int SEGMENT_SIZE = 512;
    074
    075    /**
    076     * When you receive a datagram that you expect to be a TFTP packet, you use this factory method to create the proper TFTPPacket object encapsulating the
    077     * data contained in that datagram. This method is the only way you can instantiate a TFTPPacket derived class from a datagram.
    078     *
    079     * @param datagram The datagram containing a TFTP packet.
    080     * @return The TFTPPacket object corresponding to the datagram.
    081     * @throws TFTPPacketException If the datagram does not contain a valid TFTP packet.
    082     */
    083    public static final TFTPPacket newTFTPPacket(final DatagramPacket datagram) throws TFTPPacketException {
    084        final byte[] data;
    085        TFTPPacket packet;
    086
    087        if (datagram.getLength() < MIN_PACKET_SIZE) {
    088            throw new TFTPPacketException("Bad packet. Datagram data length is too short.");
    089        }
    090
    091        data = datagram.getData();
    092
    093        switch (data[1]) {
    094        case READ_REQUEST:
    095            packet = new TFTPReadRequestPacket(datagram);
    096            break;
    097        case WRITE_REQUEST:
    098            packet = new TFTPWriteRequestPacket(datagram);
    099            break;
    100        case DATA:
    101            packet = new TFTPDataPacket(datagram);
    102            break;
    103        case ACKNOWLEDGEMENT:
    104            packet = new TFTPAckPacket(datagram);
    105            break;
    106        case ERROR:
    107            packet = new TFTPErrorPacket(datagram);
    108            break;
    109        default:
    110            throw new TFTPPacketException("Bad packet.  Invalid TFTP operator code.");
    111        }
    112
    113        return packet;
    114    }
    115
    116    /** The type of packet. */
    117    int type;
    118
    119    /** The port the packet came from or is going to. */
    120    int port;
    121
    122    /** The host the packet is going to be sent or where it came from. */
    123    InetAddress address;
    124
    125    /**
    126     * This constructor is not visible outside the package. It is used by subclasses within the package to initialize base data.
    127     *
    128     * @param type    The type of the packet.
    129     * @param address The host the packet came from or is going to be sent.
    130     * @param port    The port the packet came from or is going to be sent.
    131     **/
    132    TFTPPacket(final int type, final InetAddress address, final int port) {
    133        this.type = type;
    134        this.address = address;
    135        this.port = port;
    136    }
    137
    138    /**
    139     * Returns the address of the host where the packet is going to be sent or where it came from.
    140     *
    141     * @return The type of the packet.
    142     */
    143    public final InetAddress getAddress() {
    144        return address;
    145    }
    146
    147    /**
    148     * Returns the port where the packet is going to be sent or where it came from.
    149     *
    150     * @return The port where the packet came from or where it is going.
    151     */
    152    public final int getPort() {
    153        return port;
    154    }
    155
    156    /**
    157     * Returns the type of the packet.
    158     *
    159     * @return The type of the packet.
    160     */
    161    public final int getType() {
    162        return type;
    163    }
    164
    165    /**
    166     * Creates a UDP datagram containing all the TFTP packet data in the proper format. This is an abstract method, exposed to the programmer in case he wants
    167     * to implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should not
    168     * have a need to call this method.
    169     *
    170     * @return A UDP datagram containing the TFTP packet.
    171     */
    172    public abstract DatagramPacket newDatagram();
    173
    174    /**
    175     * This is an abstract method only available within the package for implementing efficient datagram transport by eliminating buffering. It takes a datagram
    176     * as an argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data should be set as the datagram's data and the
    177     * datagram returned.
    178     *
    179     * @param datagram The datagram to create.
    180     * @param data     The buffer to store the packet and to use in the datagram.
    181     * @return The datagram argument.
    182     */
    183    abstract DatagramPacket newDatagram(DatagramPacket datagram, byte[] data);
    184
    185    /**
    186     * Sets the host address where the packet is going to be sent.
    187     *
    188     * @param address the address to set
    189     */
    190    public final void setAddress(final InetAddress address) {
    191        this.address = address;
    192    }
    193
    194    /**
    195     * Sets the port where the packet is going to be sent.
    196     *
    197     * @param port the port to set
    198     */
    199    public final void setPort(final int port) {
    200        this.port = port;
    201    }
    202
    203    /**
    204     * For debugging
    205     *
    206     * @since 3.6
    207     */
    208    @Override
    209    public String toString() {
    210        return address + " " + port + " " + type;
    211    }
    212}