PortParser.java revision 8d76c14c2b733a4cb61da029a527fc36ca4ac51c
1/*
2 * Copyright 2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.mockftpserver.core.util;
17
18import org.mockftpserver.core.CommandSyntaxException;
19
20import java.net.InetAddress;
21import java.net.UnknownHostException;
22import java.util.Arrays;
23import java.util.List;
24
25/**
26 * Utility class for parsing port values from command arguments.
27 *
28 * @author Chris Mair
29 * @version $Revision$ - $Date$
30 */
31public final class PortParser {
32
33    /**
34     * Private constructor. All methods are static.
35     */
36    private PortParser() {
37    }
38
39    /**
40     * Parse a 32-bit IP address from the String[] of FTP command parameters.
41     *
42     * @param parameters - the String[] of command parameters. It is the concatenation
43     *                   of a 32-bit internet host address and a 16-bit TCP port address. This address
44     *                   information is broken into 8-bit fields and the value of each field is encoded
45     *                   as a separate parameter whose value is a decimal number (in character string
46     *                   representation).  Thus, the six parameters for the port command would be:
47     *                   h1,h2,h3,h4,p1,p2
48     *                   where h1 is the high order 8 bits of the internet host address, and p1 is the
49     *                   high order 8 bits of the port number.
50     * @return the InetAddres representing the host parsed from the parameters
51     * @throws org.mockftpserver.core.util.AssertFailedException
52     *                                       - if parameters is null or contains an insufficient number of elements
53     * @throws NumberFormatException         - if one of the parameters does not contain a parsable integer
54     * @throws java.net.UnknownHostException
55     */
56    public static InetAddress parseHost(String[] parameters) throws UnknownHostException {
57        verifySufficientParameters(parameters);
58
59        byte host1 = parseByte(parameters[0]);
60        byte host2 = parseByte(parameters[1]);
61        byte host3 = parseByte(parameters[2]);
62        byte host4 = parseByte(parameters[3]);
63
64        byte[] address = {host1, host2, host3, host4};
65        InetAddress inetAddress = InetAddress.getByAddress(address);
66
67        return inetAddress;
68    }
69
70    /**
71     * Parse a 16-bit port number from the String[] of FTP command parameters.
72     *
73     * @param parameters - the String[] of command parameters. It is the concatenation
74     *                   of a 32-bit internet host address and a 16-bit TCP port address. This address
75     *                   information is broken into 8-bit fields and the value of each field is encoded
76     *                   as a separate parameter whose value is a decimal number (in character string
77     *                   representation).  Thus, the six parameters for the port command would be:
78     *                   h1,h2,h3,h4,p1,p2
79     *                   where h1 is the high order 8 bits of the internet host address, and p1 is the
80     *                   high order 8 bits of the port number.
81     * @return the port number parsed from the parameters
82     * @throws org.mockftpserver.core.util.AssertFailedException
83     *                               - if parameters is null or contains an insufficient number of elements
84     * @throws NumberFormatException - if one of the parameters does not contain a parsable integer
85     */
86    public static int parsePortNumber(String[] parameters) {
87        verifySufficientParameters(parameters);
88
89        int port1 = Integer.parseInt(parameters[4]);
90        int port2 = Integer.parseInt(parameters[5]);
91        int port = (port1 << 8) + port2;
92
93        return port;
94    }
95
96    /**
97     * Verify that the parameters is not null and contains the required number of elements
98     *
99     * @param parameters - the String[] of command parameters
100     * @throws CommandSyntaxException - if parameters is null or contains an insufficient number of elements
101     */
102    private static void verifySufficientParameters(String[] parameters) {
103        if (parameters == null || parameters.length < 6) {
104            List parms = parameters == null ? null : Arrays.asList(parameters);
105            throw new CommandSyntaxException("The PORT command must contain least be 6 parameters: " + parms);
106        }
107    }
108
109    /**
110     * Convert the InetAddess and port number to a comma-delimited list of byte values,
111     * suitable for the response String from the PASV command.
112     *
113     * @param host - the InetAddress
114     * @param port - the port number
115     * @return the comma-delimited list of byte values, e.g., "196,168,44,55,23,77"
116     */
117    public static String convertHostAndPortToCommaDelimitedBytes(InetAddress host, int port) {
118        StringBuffer buffer = new StringBuffer();
119        byte[] address = host.getAddress();
120        for (int i = 0; i < address.length; i++) {
121            int positiveValue = (address[i] >= 0) ? address[i] : 256 + address[i];
122            buffer.append(positiveValue);
123            buffer.append(",");
124        }
125        int p1 = port >> 8;
126        int p2 = port % 256;
127        buffer.append(String.valueOf(p1));
128        buffer.append(",");
129        buffer.append(String.valueOf(p2));
130        return buffer.toString();
131    }
132
133    /**
134     * Parse the specified String as an unsigned decimal byte value (i.e., 0..255). We can't just use
135     * Byte.parseByte(string) because that parses the string as a signed byte.
136     *
137     * @param string - the String containing the decimal byte representation to be parsed
138     * @return the byte value
139     */
140    private static byte parseByte(String string) {
141        return (byte) (0xFF & Short.parseShort(string));
142    }
143
144}