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