001/* 002 * $HeadURL: http://juliusdavies.ca/svn/not-yet-commons-ssl/trunk/src/java/org/apache/commons/ssl/util/IPAddressParser.java $ 003 * $Revision: 121 $ 004 * $Date: 2007-11-13 21:26:57 -0800 (Tue, 13 Nov 2007) $ 005 * 006 * ==================================================================== 007 * Licensed to the Apache Software Foundation (ASF) under one 008 * or more contributor license agreements. See the NOTICE file 009 * distributed with this work for additional information 010 * regarding copyright ownership. The ASF licenses this file 011 * to you under the Apache License, Version 2.0 (the 012 * "License"); you may not use this file except in compliance 013 * with the License. You may obtain a copy of the License at 014 * 015 * http://www.apache.org/licenses/LICENSE-2.0 016 * 017 * Unless required by applicable law or agreed to in writing, 018 * software distributed under the License is distributed on an 019 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 020 * KIND, either express or implied. See the License for the 021 * specific language governing permissions and limitations 022 * under the License. 023 * ==================================================================== 024 * 025 * This software consists of voluntary contributions made by many 026 * individuals on behalf of the Apache Software Foundation. For more 027 * information on the Apache Software Foundation, please see 028 * <http://www.apache.org/>. 029 * 030 */ 031package org.apache.commons.ssl.util; 032 033/** 034 * Parses String representations of IPv4 and IPv6 addresses, and converts 035 * them to byte[]. Returns null if the supplied String is not a valid IP 036 * address. 037 * <p/> 038 * IPv6 addresses are allowed to include square brackets (e.g., "[::a:b:c:d]"), 039 * but IPv4 addresses are not. This is to help in situation where an IPv6 040 * literal address is encoded directly inside a URL (the square brackets allow 041 * the web client to separate the IPv6 address from its port, since the colon 042 * character is overloaded in that context). 043 */ 044public class IPAddressParser { 045 046 /** 047 * Converts the supplied IPv4 literal to byte[], or null if the 048 * IPv4 address was invalid. 049 * 050 * @param s Literal IPv4 address. 051 * @return byte[] array or null if the supplied IPv4 address was invalid. 052 */ 053 public static byte[] parseIPv4Literal(String s) { 054 s = s != null ? s.trim() : ""; 055 String[] toks = s.split("\\."); 056 byte[] ip = new byte[4]; 057 if (toks.length == 4) { 058 for (int i = 0; i < ip.length; i++) { 059 try { 060 int val = Integer.parseInt(toks[i]); 061 if (val < 0 || val > 255) { 062 return null; 063 } 064 ip[i] = (byte) val; 065 } catch (NumberFormatException nfe) { 066 return null; 067 } 068 } 069 return ip; 070 } 071 return null; 072 } 073 074 /** 075 * Converts the supplied IPv6 literal to byte[], or null if the 076 * IPv6 address was invalid. 077 * 078 * @param s Literal IPv6 address. 079 * @return byte[] array or null if the supplied IPv6 address was invalid. 080 */ 081 public static byte[] parseIPv6Literal(String s) { 082 s = s != null ? s.trim() : ""; 083 if (s.length() > 0 && s.charAt(0) == '[' && s.charAt(s.length() - 1) == ']') { 084 s = s.substring(1, s.length() - 1).trim(); 085 } 086 int x = s.lastIndexOf(':'); 087 int y = s.indexOf('.'); 088 // Contains a dot! Look for IPv4 literal suffix. 089 if (x >= 0 && y > x) { 090 byte[] ip4Suffix = parseIPv4Literal(s.substring(x + 1)); 091 if (ip4Suffix == null) { 092 return null; 093 } 094 s = s.substring(0, x) + ":" + ip4ToHex(ip4Suffix); 095 } 096 097 // Check that we only have a single occurence of "::". 098 x = s.indexOf("::"); 099 if (x >= 0) { 100 if (s.indexOf("::", x + 1) >= 0) { 101 return null; 102 } 103 } 104 105 // This array helps us expand the "::" into the zeroes it represents. 106 String[] raw = new String[]{"0000", "0000", "0000", "0000", "0000", "0000", "0000", "0000"}; 107 if (s.indexOf("::") >= 0) { 108 String[] split = s.split("::", -1); 109 String[] prefix = splitOnColon(split[0]); 110 String[] suffix = splitOnColon(split[1]); 111 112 // Make sure the "::" zero-expander has some room to expand! 113 if (prefix.length + suffix.length > 7) { 114 return null; 115 } 116 for (int i = 0; i < prefix.length; i++) { 117 raw[i] = prependZeroes(prefix[i]); 118 } 119 int startPos = raw.length - suffix.length; 120 for (int i = 0; i < suffix.length; i++) { 121 raw[startPos + i] = prependZeroes(suffix[i]); 122 } 123 } else { 124 // Okay, whew, no "::" zero-expander, but we still have to make sure 125 // each element contains 4 hex characters. 126 raw = splitOnColon(s); 127 if (raw.length != 8) { 128 return null; 129 } 130 for (int i = 0; i < raw.length; i++) { 131 raw[i] = prependZeroes(raw[i]); 132 } 133 } 134 135 byte[] ip6 = new byte[16]; 136 int i = 0; 137 for (int j = 0; j < raw.length; j++) { 138 String tok = raw[j]; 139 if (tok.length() > 4) { 140 return null; 141 } 142 String prefix = tok.substring(0, 2); 143 String suffix = tok.substring(2, 4); 144 try { 145 ip6[i++] = (byte) Integer.parseInt(prefix, 16); 146 ip6[i++] = (byte) Integer.parseInt(suffix, 16); 147 } catch (NumberFormatException nfe) { 148 return null; 149 } 150 } 151 return ip6; 152 } 153 154 private static String prependZeroes(String s) { 155 switch (s.length()) { 156 case 0: return "0000"; 157 case 1: return "000" + s; 158 case 2: return "00" + s; 159 case 3: return "0" + s; 160 default: return s; 161 } 162 } 163 164 private static String[] splitOnColon(String s) { 165 if ("".equals(s)) { 166 return new String[]{}; 167 } else { 168 return s.split(":"); 169 } 170 } 171 172 private static String ip4ToHex(byte[] b) { 173 return b2s(b[0]) + b2s(b[1]) + ":" + b2s(b[2]) + b2s(b[3]); 174 } 175 176 private static String b2s(byte b) { 177 String s = Integer.toHexString(b >= 0 ? b : 256 + b); 178 if (s.length() < 2) { 179 s = "0" + s; 180 } 181 return s; 182 } 183}