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