Fix DNSLocator when Network Disconnect
[aaf/authz.git] / cadi / client / src / main / java / org / onap / aaf / cadi / locator / DNSLocator.java
1 /**
2  * ============LICENSE_START====================================================
3  * org.onap.aaf
4  * ===========================================================================
5  * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
6  * ===========================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END====================================================
19  *
20  */
21
22 package org.onap.aaf.cadi.locator;
23
24 import java.io.IOException;
25 import java.net.InetAddress;
26 //import java.net.InetAddress;
27 import java.net.URI;
28 import java.net.URISyntaxException;
29 import java.net.UnknownHostException;
30
31 import org.onap.aaf.cadi.Access;
32 import org.onap.aaf.cadi.Access.Level;
33 import org.onap.aaf.cadi.LocatorException;
34
35 public class DNSLocator implements SizedLocator<URI> {
36     private static enum Status {UNTRIED, OK, INVALID, SLOW};
37     private static final int CHECK_TIME = 3000;
38
39     private String host, protocol;
40     private Access access;
41     private Host[] hosts;
42     private int startPort, endPort;
43     private String suffix;
44     // Added for JUnit Purposes.  Force correct responses in odd situations on tests
45     private final DNSLookup dnsLookup;
46
47     private int size = 1; // initial, until refreshed.
48
49     public DNSLocator(Access access, String protocol, String host, String range) {
50         this(access, protocol, host, range, DNSLookup.dflt);
51     }
52     
53     public DNSLocator(Access access, String protocol, String host, String range, DNSLookup dnsLookup) {
54         this.host = host;
55         this.protocol = protocol;
56         this.access = access;
57         this.dnsLookup = dnsLookup;
58          
59         int dash = range.indexOf('-');
60         if (dash<0) {
61             startPort = endPort = Integer.parseInt(range);
62         } else {
63             startPort = Integer.parseInt(range.substring(0,dash));
64             endPort = Integer.parseInt(range.substring(dash + 1));
65         }
66         refresh();
67     }
68
69     public DNSLocator(Access access, String aaf_locate) throws LocatorException {
70         this(access, aaf_locate, DNSLookup.dflt);
71     }
72     
73     public DNSLocator(Access access, String aaf_locate, DNSLookup dnsLookup) throws LocatorException {
74         this.access = access;
75         this.dnsLookup = dnsLookup;
76         if (aaf_locate==null) {
77             throw new LocatorException("Null passed into DNSLocator constructor");
78         }
79         int start, defPort;
80         if (aaf_locate.startsWith("https://")) {
81             protocol = "https";
82             start = 8; // https://
83             defPort = 443;
84         } else if (aaf_locate.startsWith("http://")) {
85             protocol = "http";
86             start = 7; // http://
87             defPort = 80;
88         } else {
89             throw new LocatorException("DNSLocator accepts only https or http protocols.  (requested URL " + aaf_locate + ')');
90         }
91         host = parseHostAndPorts(aaf_locate, start, defPort);
92         refresh();
93     }
94
95     public static DNSLocator create(Access access, String url) throws LocatorException {
96         return new DNSLocator(access, url);
97     }
98
99     @Override
100     public URI get(Item item) throws LocatorException {
101         return hosts[((DLItem)item).cnt].uri;
102     }
103
104     @Override
105     public boolean hasItems() {
106         if(hosts!=null) {
107           for (Host h : hosts) {
108             if (h.status==Status.OK) {
109                 return true;
110             }
111           }
112         }
113         return false;
114     }
115
116     @Override
117     public void invalidate(Item item) {
118         DLItem di = (DLItem)item;
119         hosts[di.cnt].status = Status.INVALID;
120     }
121
122     @Override
123     public Item best() throws LocatorException {
124         // not a good "best"
125         for (int i=0;i<hosts.length;++i) {
126             switch(hosts[i].status) {
127                 case OK:
128                     return new DLItem(i);
129                 case INVALID:
130                     break;
131                 case SLOW:
132                     break;
133                 case UNTRIED:
134                     try {
135                         if (hosts[i].ia.isReachable(CHECK_TIME)) {
136                             hosts[i].status = Status.OK;
137                             return new DLItem(i);
138                         }
139                     } catch (IOException e) {
140                         throw new LocatorException(e);
141                     }
142                     break;
143                 default:
144                     break;
145             }
146         }
147         throw new LocatorException("No Available URIs for " + host);
148     }
149
150     @Override
151     public Item first() throws LocatorException {
152         return new DLItem(0);
153     }
154
155     @Override
156     public Item next(Item item) throws LocatorException {
157         DLItem di = (DLItem)item;
158         if (++di.cnt<hosts.length) {
159             return di;
160         } else {
161             return null;
162         }
163     }
164
165     @Override
166     public boolean refresh() {
167         try {
168             InetAddress[] ias = dnsLookup.getAllByName(host);
169             Host[] temp = new Host[ias.length * (1 + endPort - startPort)];
170             int cnt = -1;
171             for (int j=startPort; j<=endPort; ++j) {
172                 for (int i=0;i<ias.length;++i) {
173                     temp[++cnt] = new Host(ias[i], j, suffix);
174                 }
175             }
176             hosts = temp;
177             size = temp.length * (endPort-startPort+1);
178             return true;
179         } catch (Exception e) {
180             access.log(Level.ERROR, e);
181         }
182         return false;
183     }
184
185     private String parseHostAndPorts(String aaf_locate, int _start, int defaultPort) throws LocatorException {
186         int slash, start;
187         int colon = aaf_locate.indexOf(':',_start);
188         if (colon > 0) {
189             host = aaf_locate.substring(_start,colon);
190             start = colon + 1;
191             int left = aaf_locate.indexOf('[', start);
192             if (left > 0) {
193                 int right = aaf_locate.indexOf(']', left + 1);
194                 if (right < 0) {
195                     throw new LocatorException("Missing closing bracket in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
196                 } else if (right == (left + 1)) {
197                     throw new LocatorException("Missing ports in brackets in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
198                 }
199                 int dash = aaf_locate.indexOf('-', left + 1);
200                 if (dash == (right - 1) || dash == (left + 1)) {
201                     throw new LocatorException("Missing ports in brackets in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
202                 }
203                 if (dash < 0) {
204                     startPort = endPort = Integer.parseInt(aaf_locate.substring(left + 1, right));
205                 } else {
206                     startPort = Integer.parseInt(aaf_locate.substring(left + 1, dash));
207                     endPort = Integer.parseInt(aaf_locate.substring(dash + 1, right));
208                 }
209                 slash = aaf_locate.indexOf('/', start);
210                 if(slash>=0) {
211                     suffix = aaf_locate.substring(slash);
212                 }
213
214             } else {
215                 slash = aaf_locate.indexOf('/', start);
216                 if (slash == start) {
217                     throw new LocatorException("Missing port before '/' in DNSLocator constructor.  (requested URL " + aaf_locate + ')');
218                 }
219                 if (slash < 0) {
220                     startPort = endPort = Integer.parseInt(aaf_locate.substring(start));
221                 } else {
222                     startPort = endPort = Integer.parseInt(aaf_locate.substring(start, slash));
223                     suffix = aaf_locate.substring(slash);
224                 }
225             }
226         } else {
227             slash = aaf_locate.indexOf('/', _start);
228             host = slash<_start?aaf_locate.substring(_start):aaf_locate.substring(_start,slash);
229             startPort = endPort = defaultPort;
230         }
231
232         return host;
233     }
234     
235     /**
236      * 
237      * Some ISPs return InetEntries for bogus entries, making JUnit Testing difficult.
238      * We add this interface to force validly empty responses.
239      * We don't use Lambda at this point, so we can continue to support JKD 1.7 
240      * a while longer.
241      *
242      */
243     public interface DNSLookup {
244         InetAddress[] getAllByName(String host) throws UnknownHostException;
245         public static final DNSLookup dflt = new DNSLookup() {
246         public InetAddress[] getAllByName(String host) throws UnknownHostException {
247         return InetAddress.getAllByName(host);
248         }
249         };
250     }
251     
252     private class Host {
253         private URI uri;
254         private InetAddress ia;
255         private Status status;
256
257         public Host(InetAddress inetAddress, int port, String suffix) throws URISyntaxException {
258             ia = inetAddress;
259             uri = new URI(protocol,null,inetAddress.getCanonicalHostName(),port,suffix,null,null);
260             status = Status.UNTRIED;
261         }
262
263         public String toString() {
264             return uri.toString() + " - " + status.name();
265         }
266     }
267
268     private class DLItem implements Item {
269         public DLItem(int i) {
270             cnt = i;
271         }
272
273         private int cnt;
274     }
275
276     public void destroy() {}
277
278     public int size() {
279         return size;
280     }
281 }