001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.net.ipsettings; 020 021 022import java.net.Inet4Address; 023import java.net.Inet6Address; 024import java.net.InetAddress; 025import java.net.UnknownHostException; 026import java.util.ArrayList; 027import java.util.Collections; 028import java.util.List; 029import java.util.Map; 030import java.util.TreeMap; 031 032import lucee.commons.lang.ExceptionUtil; 033 034 035/** 036 * an efficient data structure for IP-range based settings 037 */ 038public class IPSettings { 039 040 041 public static final Map EMPTY = Collections.EMPTY_MAP; 042 043 044 private IPRangeNode<Map> root, ipv4, ipv6; 045 private boolean isSorted; 046 private int version; 047 048 049 public IPSettings() { 050 051 try { 052 053 root = new IPRangeNodeRoot(); 054 055 root.addChild( ipv4 = new IPRangeNode( "0.0.0.0", "255.255.255.255" ) ); 056 root.addChild( ipv6 = new IPRangeNode( "0:0:0:0:0:0:0:0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ) ); 057 } 058 catch (Throwable t) { 059 ExceptionUtil.rethrowIfNecessary(t); 060 } // all valid addresses, should never happen 061 } 062 063 064 /** 065 * all added data should go through this method 066 * 067 * @param ipr 068 * @param doCheck 069 */ 070 public synchronized void put( IPRangeNode<Map> ipr, boolean doCheck ) { 071 072 IPRangeNode parent = ipr.isV4() ? ipv4 : ipv6; 073 074 parent.addChild( ipr, doCheck ); 075 076 version++; 077 isSorted = false; 078 } 079 080 081 /** calls put( IPRangeNode ipr ) */ 082 public void put( IPRangeNode<Map> ipr ) { 083 084 this.put( ipr, true ); 085 } 086 087 088 /** 089 * puts all the children at the IPv4 or IPv6 nodes for fast insertion. 090 * this method does not look for a more accurate insertion point and is useful 091 * when adding many items at once, e.g. for Country Codes of all known IP ranges 092 * 093 * @param children 094 */ 095 public void putAll( List<IPRangeNode<Map>> children ) { 096 097 for ( IPRangeNode child : children ) { 098 099 this.put( child, false ); // pass false for optimized insertion performance 100 } 101 } 102 103 104 public void putSettings( String lower, String upper, Map settings ) throws UnknownHostException { 105 106 IPRangeNode<Map> ipr = new IPRangeNode(lower, upper); 107 ipr.setData( settings ); 108 109 this.put( ipr ); 110 } 111 112 113 public void putSettings( String addr, Map settings ) throws UnknownHostException { 114 115 if ( addr.equals( "*" ) ) { 116 117 root.setData( settings ); 118 return; 119 } 120 121 IPRangeNode<Map> ipr = new IPRangeNode(addr); 122 ipr.setData( settings ); 123 124 this.put( ipr ); 125 } 126 127 128 /** 129 * returns a single, best matching node for the given address 130 * 131 * @param addr 132 * @return 133 */ 134 public IPRangeNode get( InetAddress addr ) { 135 136 if ( version == 0 ) // no data was added 137 return null; 138 139 IPRangeNode node = isV4( addr ) ? ipv4 : ipv6; 140 141 if ( !this.isSorted ) 142 this.optimize(); 143 144 return node.findFast( addr ); 145 } 146 147 148 /** 149 * returns a List of all the nodes (from root to best matching) for the given address 150 * 151 * @param iaddr 152 * @return 153 */ 154 public List<IPRangeNode> getChain( InetAddress iaddr ) { 155 156 List<IPRangeNode> result = new ArrayList(); 157 158 result.add( root ); 159 160 IPRangeNode node = isV4( iaddr ) ? ipv4 : ipv6; 161 node.findFast( iaddr, result ); 162 163 return result; 164 } 165 166 167 /** 168 * returns the cumulative settings for a given address 169 * 170 * @param iaddr 171 * @return 172 */ 173 public Map getSettings( InetAddress iaddr ) { 174 175 Map result = new TreeMap(String.CASE_INSENSITIVE_ORDER); 176 177 List<IPRangeNode> chain = getChain( iaddr ); 178 179 for ( IPRangeNode<Map> ipr : chain ) { 180 181 Map m = ipr.getData(); 182 if ( m != null ) 183 result.putAll( m ); 184 } 185 186 return result; 187 } 188 189 190 /** 191 * returns the cumulative settings for a given address 192 * 193 * @param addr 194 * @return 195 */ 196 public Map getSettings( String addr ) { 197 198 try { 199 200 return this.getSettings( InetAddress.getByName(addr) ); 201 } 202 catch (Throwable t) { 203 ExceptionUtil.rethrowIfNecessary(t); 204 } 205 206 return EMPTY; 207 } 208 209 210 /** 211 * returns the settings for a single (non-cumulative) node that best matches the given address 212 * 213 * @param addr 214 * @return 215 */ 216 public Map getNodeSettings( InetAddress addr ) { 217 218 IPRangeNode<Map> ipr = this.get( addr ); 219 220 if ( ipr != null ) { 221 222 Map result = ipr.getData(); 223 224 if ( result != null ) 225 return result; 226 } 227 228 return EMPTY; 229 } 230 231 232 /** 233 * returns the settings for a single (non-cumulative) node that best matches the given address 234 * 235 * @param addr 236 * @return 237 */ 238 public Map getNodeSettings( String addr ) { 239 240 try { 241 242 return this.getNodeSettings( InetAddress.getByName(addr) ); 243 } 244 catch (Throwable t) { 245 ExceptionUtil.rethrowIfNecessary(t); 246 } 247 248 return EMPTY; 249 } 250 251 252 public int getVersion() { 253 254 return version; 255 } 256 257 258 /** sorts the data for fast binary search */ 259 private void optimize() { 260 261 root.getChildren().sortChildren(); 262 263 isSorted = true; 264 } 265 266 267 /** returns true if the value is an IPv4 address */ 268 public static boolean isV4(InetAddress addr) { 269 270 return addr instanceof Inet4Address; 271 } 272 273 /** returns true if the value is an IPv6 address */ 274 public static boolean isV6(InetAddress addr) { 275 276 return addr instanceof Inet6Address; 277 } 278 279}