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.InetAddress;
024import java.net.UnknownHostException;
025import java.util.Comparator;
026import java.util.List;
027
028import lucee.commons.lang.ExceptionUtil;
029
030
031public class IPRangeNode<T> implements Comparable<IPRangeNode>, Comparator<IPRangeNode> {
032
033        private InetAddress lower;
034        private InetAddress upper;
035        private boolean isSingle;
036
037        private T data;
038        private IPRangeCollection children;
039
040
041        public IPRangeNode( InetAddress lower, InetAddress upper) {
042
043                int c = comparerIAddr.compare(lower, upper);
044
045                if ( c <= 0 ) {
046                        this.lower = lower;
047                        this.upper = upper;
048                }
049                else {
050                        this.lower = upper;
051                        this.upper = lower;
052                }
053
054                this.isSingle = ( c == 0 );
055                this.children = new IPRangeCollection();
056        }
057
058
059        public IPRangeNode(String lower, String upper) throws UnknownHostException {
060
061                this( InetAddress.getByName( lower ), InetAddress.getByName( upper ) );
062        }
063
064
065        public IPRangeNode(String addr) throws UnknownHostException {
066
067                this( addr, addr );
068        }
069
070
071        public boolean isSingleAddress() {
072
073                return isSingle;
074        }
075
076
077        public boolean isInRange(InetAddress addr) {
078
079                if ( this.isV4() != IPSettings.isV4(addr) )
080                        return false;
081
082                return comparerIAddr.compare(lower, addr) <= 0
083                                && comparerIAddr.compare(upper, addr) >= 0;
084        }
085
086
087        public boolean containsRange(IPRangeNode other) {
088
089                if ( this.isV4() != other.isV4() )
090                        return false;
091
092                return this.isInRange(other.lower) && this.isInRange(other.upper);
093        }
094
095
096        /**
097         *
098         * @param child
099         * @param doCheck - passing false will avoid searching for a "better" parent, for a more efficient insert in large data sets (e.g. Country Codes of all known ranges)
100         * @return - true if the child was added
101         */
102        synchronized boolean addChild( IPRangeNode child, boolean doCheck ) {
103
104                if ( !this.containsRange( child ) )
105                        return false;
106
107                IPRangeNode parent = this;
108
109                if ( doCheck )
110                        parent = findRange( child );
111
112                // TODO: check for eqaulity of new child and found parent
113
114                parent.children.add( child, doCheck );
115
116                return true;
117        }
118
119
120        /** calls addChild( child, true ) */
121        public boolean addChild( IPRangeNode child ) {
122
123                return addChild(child, true);
124        }
125
126
127        public T getData() {
128
129                return data;
130        }
131
132        public void setData(T data) {
133
134                this.data = data;
135        }
136
137
138        public IPRangeNode findRange(IPRangeNode child) {
139
140                IPRangeNode result = null;
141
142                if ( this.containsRange(child) ) {
143
144                        result = this;
145
146                        IPRangeNode temp = this.children.findRange( child );
147                        if ( temp != null )
148                                result = temp;
149                }
150
151                return result;
152        }
153
154
155        public IPRangeNode findAddr(InetAddress iaddr) {
156
157                IPRangeNode result = null;
158
159                if ( this.isInRange(iaddr) ) {
160
161                        result = this;
162
163                        if ( this.hasChildren() ) {
164
165                                IPRangeNode temp = children.findAddr( iaddr );
166                                if ( temp != null )
167                                        result = temp;
168                        }
169                }
170
171                return result;
172        }
173
174
175        public IPRangeNode findAddr(String addr) {
176
177                try {
178
179                        return findAddr( InetAddress.getByName(addr) );
180                }
181                catch (Throwable t) {
182                        ExceptionUtil.rethrowIfNecessary(t);
183                }
184
185                return null;
186        }
187
188
189        public IPRangeNode findFast(InetAddress iaddr, List<IPRangeNode> parents) {
190
191                IPRangeNode result = null;
192
193                if ( this.isInRange(iaddr) ) {
194
195                        result = this;
196                        if ( parents != null )  parents.add( result );
197
198                        if ( this.hasChildren() ) {
199
200                                IPRangeNode temp = children.findFast( iaddr, parents );
201
202                                if ( temp != null )
203                                        result = temp;
204                        }
205                }
206
207                return result;
208        }
209
210
211        public IPRangeNode findFast(InetAddress iaddr) {
212
213                return findFast(iaddr, null);
214        }
215    
216    
217    /*/ works
218    public IPRangeNode findFast(InetAddress iaddr) {
219        
220        IPRangeNode result = null;
221        
222        if ( this.isInRange(iaddr) ) {
223            
224            result = this;
225            
226            if ( this.hasChildren() ) {
227            
228                IPRangeNode temp = children.findFast( iaddr );
229                if ( temp != null )                    
230                    result = temp;
231            }
232        }
233        
234        return result;
235    }   //*/
236
237
238        public IPRangeNode findFast(String addr) {
239
240                try {
241
242                        return findFast( InetAddress.getByName(addr) );
243                }
244                catch (Throwable t) {
245                        ExceptionUtil.rethrowIfNecessary(t);
246                }
247
248                return null;
249        }
250
251
252        IPRangeCollection getChildren() {
253
254                return children;
255        }
256
257
258        boolean hasChildren() {
259
260                return children.size() > 0;
261        }
262
263
264        @Override
265        public int compareTo(IPRangeNode other) {
266
267                int c = comparerIAddr.compare(this.lower, other.lower);
268
269                if ( c != 0 )
270                        return c;
271
272                c = comparerIAddr.compare(this.upper, other.upper);
273
274                return c;
275        }
276
277
278        @Override
279        public int compare(IPRangeNode lhs, IPRangeNode rhs) {
280
281                return lhs.compareTo(rhs);
282        }
283
284
285        @Override
286        public boolean equals( Object o ) {
287
288                if ( o instanceof IPRangeNode ) {
289
290                        return this.compareTo((IPRangeNode)o) == 0;
291                }
292
293                return false;
294        }
295
296        @Override
297        public int hashCode() {
298
299                return this.lower.hashCode();
300        }
301
302        @Override
303        public String toString() {
304
305                if ( isSingle )
306                        return this.lower.toString().substring(1) + String.format(" (%d)", this.children.size());
307
308                return this.lower.toString().substring(1) + " - " + this.upper.toString().substring(1) + String.format(" (%d)", this.children.size());
309        }
310
311
312        public boolean isV4() {
313
314                return IPSettings.isV4( this.lower );
315        }
316
317        public boolean isV6() {
318
319                return IPSettings.isV6( this.lower );
320        }
321
322
323        public static final Comparator<IPRangeNode> comparerRange = new Comparator<IPRangeNode>() {
324
325                @Override
326                public int compare(IPRangeNode lhs, IPRangeNode rhs) {
327
328                        return lhs.compareTo(rhs);
329                }
330        };
331
332
333        public static final Comparator<InetAddress> comparerIAddr = new Comparator<InetAddress>() {
334
335                @Override
336                public int compare(InetAddress lhs, InetAddress rhs) {
337
338                        if ( (lhs instanceof Inet4Address) != (rhs instanceof Inet4Address) )
339                                throw new IllegalArgumentException("Both arguments must be of the same IP Version");
340
341                        byte[] barrLhs = lhs.getAddress();
342                        byte[] barrRhs = rhs.getAddress();
343
344                        for (int i=0; i < barrLhs.length; i++) {
345
346                                int l = barrLhs[i] & 0xff;                                      // fix signed bit in byte
347                                int r = barrRhs[i] & 0xff;
348
349                                if (l < r)
350                                        return -1;
351                                else if (l > r)
352                                        return 1;
353                        }
354
355                        return 0;                                                           // equal
356                }
357        };
358
359
360}