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.commons.net;
020
021import java.io.IOException;
022import java.io.Serializable;
023import java.net.Inet6Address;
024import java.net.InetAddress;
025import java.net.UnknownHostException;
026
027import lucee.commons.lang.StringUtil;
028import lucee.runtime.type.util.ListUtil;
029
030public class IPRange implements Serializable {
031        
032        /**
033         * 
034         */
035        private static final long serialVersionUID = 4427999443422764L;
036        private static final short N256 = 256;
037        private static final int SIZE = 4;
038        
039        private Range[] ranges=new Range[SIZE];
040        int max=0;
041        
042        
043        private static class Range {
044                private final short[] from;
045                private final short[] to;
046                private final boolean equality;
047
048                private Range(short[] from, short[] to) throws IOException{
049                        if(from.length!=to.length)
050                                throw new IOException("both ip address must be from same type, IPv4 or IPv6");
051                        
052                        this.from=from;
053                        this.to=to;
054                        this.equality=equal(from,to);
055                }
056                private Range(short[] from){
057                        this.from=from;
058                        this.to=from;
059                        this.equality=true;
060                }
061                private boolean inRange(short[] sarr) {
062                        if(from.length!=sarr.length) return false;
063                        
064                        if(equality) return equal(from, sarr);
065                        
066                        for(int i=0;i<from.length;i++){
067                                if(from[i]>sarr[i] ||  to[i]<sarr[i])
068                                        return false;
069                        }
070                        return true;
071                }
072                
073                public String toString(){
074                        if(equality) return toString(from);
075                        return toString(from)+"-"+toString(to);
076                }
077                
078                private String toString(short[] sarr) {
079                        if(sarr.length==4)
080                                return new StringBuilder().append(sarr[0]).append(".").append(sarr[1]).append(".").append(sarr[2]).append(".").append(sarr[3]).toString();
081                        
082                        return new StringBuilder()
083                        .append(toHex(sarr[0],sarr[1],false)).append(":")
084                        .append(toHex(sarr[2],sarr[3],true)).append(":")
085                        .append(toHex(sarr[4],sarr[5],true)).append(":")
086                        .append(toHex(sarr[6],sarr[7],true)).append(":")
087                        .append(toHex(sarr[8],sarr[9],true)).append(":")
088                        .append(toHex(sarr[10],sarr[11],true)).append(":")
089                        .append(toHex(sarr[12],sarr[13],true)).append(":")
090                        .append(toHex(sarr[14],sarr[15],false)).toString();
091                        
092                        
093                }
094                
095                
096                
097                private String toHex(int first, int second, boolean allowEmpty) {
098                        String str1=Integer.toString(first,16);
099                        while(str1.length()<2)str1="0"+str1;
100                        String str2=Integer.toString(second,16);
101                        while(str2.length()<2)str2="0"+str2;
102                        str1+=str2;
103                        if(allowEmpty && str1.equals("0000")) return "";
104                        
105                        while(str1.length()>1 && str1.charAt(0)=='0')str1=str1.substring(1);
106                        
107                        
108                        
109                        return str1;
110                }
111                private boolean equal(short[] left, short[] right) {
112                        for(int i=0;i<left.length;i++){
113                                if(left[i]!=right[i]) return false;
114                        }
115                        return true;
116                }
117                
118                
119        }
120        
121
122        private void add(String ip) throws IOException {
123                ip=ip.trim();
124                // no wildcard defined
125                if(ip.indexOf('*')==-1) {
126                        add(new Range(toShortArray(toInetAddress(ip))));
127                        return;
128                }
129                
130                if("*".equals(ip)) {
131                        add("*.*.*.*");
132                        add("*:*:*:*:*:*:*:*");
133                        return;
134                }
135                
136                String from = ip.replace('*', '0');
137                String to;
138                InetAddress addr1 = toInetAddress(from);
139                if(addr1 instanceof Inet6Address) 
140                        to=StringUtil.replace(ip, "*","ffff",false);
141                else 
142                        to=StringUtil.replace(ip, "*","255",false);
143                add(new Range(toShortArray(addr1),toShortArray(toInetAddress(to))));
144        }
145
146        private void add(String ip1,String ip2) throws IOException {
147                add(new Range(toShortArray(toInetAddress(ip1)),toShortArray(toInetAddress(ip2))));
148        }
149        public static IPRange getInstance(String raw) throws IOException {
150                return getInstance(ListUtil.listToStringArray(raw, ','));
151        }
152
153        public static IPRange getInstance(String[] raw) throws IOException {
154                IPRange range=new IPRange();
155                String[] arr = ListUtil.trimItems(ListUtil.trim(raw));
156                String str;
157                int index;
158                for(int i=0;i<arr.length;i++){
159                        str=arr[i];
160                        if(str.length()>0) {
161                                index=str.indexOf('-');
162                                if(index!=-1) range.add(str.substring(0,index), str.substring(index+1));
163                                else range.add(str);
164                        }
165                }
166                return range;
167                
168        }
169        
170        
171        private synchronized void add(Range range) {
172                if(max>=ranges.length) {
173                        Range[] tmp=new Range[ranges.length+SIZE];
174                        for(int i=0;i<ranges.length;i++){
175                                tmp[i]=ranges[i];
176                        }
177                        ranges=tmp;
178                }
179                ranges[max++]=range;
180        }
181
182        public boolean inRange(String ip) throws IOException {
183                return inRange(toShortArray(ip));
184        }
185
186        public boolean inRange(short[] ip) {
187                for(int i=0;i<max;i++){
188                        if(ranges[i].inRange(ip)) return true;
189                }
190                return false;
191        }
192        
193        
194        public String toString(){
195                StringBuilder sb=new StringBuilder();
196                for(int i=0;i<max;i++){
197                        if(i>0)sb.append(",");
198                        sb.append(ranges[i].toString());
199                }
200                return sb.toString();
201        }
202
203        public static short[] toShortArray(String ip) throws IOException {
204                return toShortArray(toInetAddress(ip));
205        }
206        
207        private static InetAddress toInetAddress(String ip) throws IOException {
208                // TODO Auto-generated method stub
209                try {
210                        return InetAddress.getByName(ip);
211                } catch (UnknownHostException e) {
212                        throw new IOException("cannot parse the ip ["+ip+"]");
213                }
214        }
215
216        private static short[] toShortArray(InetAddress ia){
217                byte[] addr = ia.getAddress();
218                short[] sarr=new short[addr.length];
219                for(int i=0;i<addr.length;i++){
220                        sarr[i]=byte2short(addr[i]);
221                }
222                return sarr;
223        }
224        
225        private static short byte2short(byte b){
226                if(b<0) return (short)(b+N256);
227                return b;
228        }
229}