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}