    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 *      https://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;
    022import java.nio.charset.Charset;
    023
    024/**
    025 * A final class derived from TFTPPacket defining the TFTP Error packet type.
    026 * <p>
    027 * 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
    028 * 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
    029 * should only be concerned with the {@link org.apache.commons.net.tftp.TFTPClient} class {@link org.apache.commons.net.tftp.TFTPClient#receiveFile
    030 * receiveFile()} and {@link org.apache.commons.net.tftp.TFTPClient#sendFile sendFile()} methods.
    031 * </p>
    032 *
    033 * @see TFTPPacket
    034 * @see TFTPPacketException
    035 * @see TFTP
    036 */
    037
    038public final class TFTPErrorPacket extends TFTPPacket {
    039
    040    /** The undefined error code according to RFC 783, value 0. */
    041    public static final int UNDEFINED = 0;
    042
    043    /** The file not found error code according to RFC 783, value 1. */
    044    public static final int FILE_NOT_FOUND = 1;
    045
    046    /** The access violation error code according to RFC 783, value 2. */
    047    public static final int ACCESS_VIOLATION = 2;
    048
    049    /** The disk full error code according to RFC 783, value 3. */
    050    public static final int OUT_OF_SPACE = 3;
    051
    052    /**
    053     * The illegal TFTP operation error code according to RFC 783, value 4.
    054     */
    055    public static final int ILLEGAL_OPERATION = 4;
    056
    057    /** The unknown transfer id error code according to RFC 783, value 5. */
    058    public static final int UNKNOWN_TID = 5;
    059
    060    /** The file already exists error code according to RFC 783, value 6. */
    061    public static final int FILE_EXISTS = 6;
    062
    063    /** The no such user error code according to RFC 783, value 7. */
    064    public static final int NO_SUCH_USER = 7;
    065
    066    /**
    067     * The invalid options error code according to RFC 2347, value 8.
    068     *
    069     * @since 3.12.0
    070     */
    071    public static final int INVALID_OPTIONS_VALUE = 8;
    072
    073    /** The error code of this packet. */
    074    private final int error;
    075
    076    /** The error message of this packet. */
    077    private final String message;
    078
    079    /**
    080     * Creates an error packet based from a received datagram. Assumes the datagram is at least length 4, else an ArrayIndexOutOfBoundsException may be thrown.
    081     *
    082     * @param datagram The datagram containing the received error.
    083     * @throws TFTPPacketException If the datagram isn't a valid TFTP error packet.
    084     */
    085    TFTPErrorPacket(final DatagramPacket datagram) throws TFTPPacketException {
    086        super(ERROR, datagram.getAddress(), datagram.getPort());
    087        int index;
    088        final int length;
    089        final byte[] data;
    090        final StringBuilder buffer;
    091
    092        data = datagram.getData();
    093        length = datagram.getLength();
    094
    095        if (getType() != data[1]) {
    096            throw new TFTPPacketException("TFTP operator code does not match type.");
    097        }
    098
    099        error = (data[2] & 0xff) << 8 | data[3] & 0xff;
    100
    101        if (length < 5) {
    102            throw new TFTPPacketException("Bad error packet. No message.");
    103        }
    104
    105        index = 4;
    106        buffer = new StringBuilder();
    107
    108        while (index < length && data[index] != 0) {
    109            buffer.append((char) data[index]);
    110            ++index;
    111        }
    112
    113        message = buffer.toString();
    114    }
    115
    116    /**
    117     * Creates an error packet to be sent to a host at a given port with an error code and error message.
    118     *
    119     * @param destination The host to which the packet is going to be sent.
    120     * @param port        The port to which the packet is going to be sent.
    121     * @param error       The error code of the packet.
    122     * @param message     The error message of the packet.
    123     */
    124    public TFTPErrorPacket(final InetAddress destination, final int port, final int error, final String message) {
    125        super(ERROR, destination, port);
    126
    127        this.error = error;
    128        this.message = message;
    129    }
    130
    131    /**
    132     * Gets the error code of the packet.
    133     *
    134     * @return The error code of the packet.
    135     */
    136    public int getError() {
    137        return error;
    138    }
    139
    140    /**
    141     * Gets the error message of the packet.
    142     *
    143     * @return The error message of the packet.
    144     */
    145    public String getMessage() {
    146        return message;
    147    }
    148
    149    /**
    150     * Creates a UDP datagram containing all the TFTP error packet data in the proper format. This is a method exposed to the programmer in case he wants to
    151     * implement his own TFTP client instead of using the {@link org.apache.commons.net.tftp.TFTPClient} class. Under normal circumstances, you should not have
    152     * a need to call this method.
    153     *
    154     * @return A UDP datagram containing the TFTP error packet.
    155     */
    156    @Override
    157    public DatagramPacket newDatagram() {
    158        final byte[] data;
    159        final int length;
    160
    161        length = message.length();
    162
    163        data = new byte[length + 5];
    164        data[0] = 0;
    165        data[1] = (byte) type;
    166        data[2] = (byte) ((error & 0xffff) >> 8);
    167        data[3] = (byte) (error & 0xff);
    168
    169        System.arraycopy(message.getBytes(Charset.defaultCharset()), 0, data, 4, length);
    170
    171        data[length + 4] = 0;
    172
    173        return new DatagramPacket(data, data.length, address, port);
    174    }
    175
    176    /**
    177     * This is a method only available within the package for implementing efficient datagram transport by eliminating buffering. It takes a datagram as an
    178     * argument, and a byte buffer in which to store the raw datagram data. Inside the method, the data is set as the datagram's data and the datagram returned.
    179     *
    180     * @param datagram The datagram to create.
    181     * @param data     The buffer to store the packet and to use in the datagram.
    182     * @return The datagram argument.
    183     */
    184    @Override
    185    DatagramPacket newDatagram(final DatagramPacket datagram, final byte[] data) {
    186        final int length;
    187
    188        length = message.length();
    189
    190        data[0] = 0;
    191        data[1] = (byte) type;
    192        data[2] = (byte) ((error & 0xffff) >> 8);
    193        data[3] = (byte) (error & 0xff);
    194
    195        System.arraycopy(message.getBytes(Charset.defaultCharset()), 0, data, 4, length);
    196
    197        data[length + 4] = 0;
    198
    199        datagram.setAddress(address);
    200        datagram.setPort(port);
    201        datagram.setData(data);
    202        datagram.setLength(length + 4);
    203
    204        return datagram;
    205    }
    206
    207    /**
    208     * For debugging
    209     *
    210     * @since 3.6
    211     */
    212    @Override
    213    public String toString() {
    214        return super.toString() + " ERR " + error + " " + message;
    215    }
    216}