22
22
*/
23
23
package opengrok .auth .plugin .ldap ;
24
24
25
+ import java .io .IOException ;
25
26
import java .io .Serializable ;
27
+ import java .net .InetAddress ;
28
+ import java .net .InetSocketAddress ;
29
+ import java .net .Socket ;
30
+ import java .net .URI ;
31
+ import java .net .URISyntaxException ;
32
+ import java .net .UnknownHostException ;
26
33
import java .util .Hashtable ;
27
34
import java .util .logging .Level ;
28
35
import java .util .logging .Logger ;
@@ -64,12 +71,12 @@ public LdapServer() {
64
71
65
72
public LdapServer (String server ) {
66
73
this (prepareEnv ());
67
- this . url = server ;
74
+ setName ( server ) ;
68
75
}
69
76
70
77
public LdapServer (String server , String username , String password ) {
71
78
this (prepareEnv ());
72
- this . url = server ;
79
+ setName ( server ) ;
73
80
this .username = username ;
74
81
this .password = password ;
75
82
}
@@ -122,13 +129,95 @@ public void setInterval(int interval) {
122
129
this .interval = interval ;
123
130
}
124
131
132
+ private String urlToHostname (String urlStr ) throws URISyntaxException {
133
+ URI uri = new URI (urlStr );
134
+ return uri .getHost ();
135
+ }
136
+
137
+ /**
138
+ * This method converts the scheme from URI to port number.
139
+ * It is limited to the ldap/ldaps schemes.
140
+ * The method could be static however then it cannot be easily mocked in testing.
141
+ * @return port number or -1 if the scheme in given URI is not known
142
+ * @throws URISyntaxException if the URI is not valid
143
+ */
144
+ public int getPort () throws URISyntaxException {
145
+ URI uri = new URI (getUrl ());
146
+ switch (uri .getScheme ()) {
147
+ case "ldaps" :
148
+ return 636 ;
149
+ case "ldap" :
150
+ return 389 ;
151
+ }
152
+
153
+ return -1 ;
154
+ }
155
+
156
+ private boolean isReachable (InetAddress addr , int port , int timeOutMillis ) {
157
+ try {
158
+ try (Socket soc = new Socket ()) {
159
+ soc .connect (new InetSocketAddress (addr , port ), timeOutMillis );
160
+ }
161
+ return true ;
162
+ } catch (IOException e ) {
163
+ return false ;
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Wraps InetAddress.getAllByName() so that it can be mocked in testing.
169
+ * (mocking static methods is not really possible with Mockito)
170
+ * @param hostname hostname string
171
+ * @return array of InetAddress objects
172
+ * @throws UnknownHostException if the host cannot be resolved to any IP address
173
+ */
174
+ public InetAddress [] getAddresses (String hostname ) throws UnknownHostException {
175
+ return InetAddress .getAllByName (hostname );
176
+ }
177
+
125
178
/**
126
- * The LDAP server is working only when its connection is not null. This
127
- * tries to establish the connection if it is not established already.
179
+ * Go through all IP addresses and find out if they are reachable.
180
+ * @return true if all IP addresses are reachable, false otherwise
181
+ */
182
+ public boolean isReachable () {
183
+ try {
184
+ InetAddress [] addresses = getAddresses (urlToHostname (getUrl ()));
185
+ if (addresses .length == 0 ) {
186
+ LOGGER .log (Level .WARNING , "LDAP server {0} does not resolve to any IP address" , this );
187
+ return false ;
188
+ }
189
+
190
+ for (InetAddress addr : addresses ) {
191
+ // InetAddr.isReachable() is not sufficient as it can only check ICMP and TCP echo.
192
+ int port = getPort ();
193
+ if (!isReachable (addr , port , getConnectTimeout ())) {
194
+ LOGGER .log (Level .WARNING , "LDAP server {0} is not reachable on {1}:{2}" ,
195
+ new Object []{this , addr , Integer .toString (port )});
196
+ return false ;
197
+ }
198
+ }
199
+ } catch (UnknownHostException e ) {
200
+ LOGGER .log (Level .SEVERE , String .format ("cannot get IP addresses for LDAP server %s" , this ), e );
201
+ return false ;
202
+ } catch (URISyntaxException e ) {
203
+ LOGGER .log (Level .SEVERE , String .format ("not a valid URI: %s" , getUrl ()), e );
204
+ return false ;
205
+ }
206
+
207
+ return true ;
208
+ }
209
+
210
+ /**
211
+ * The LDAP server is working only when it is reachable and its connection is not null.
212
+ * This method tries to establish the connection if it is not established already.
128
213
*
129
214
* @return true if it is working
130
215
*/
131
216
public synchronized boolean isWorking () {
217
+ if (!isReachable ()) {
218
+ return false ;
219
+ }
220
+
132
221
if (ctx == null ) {
133
222
ctx = connect ();
134
223
}
@@ -144,7 +233,7 @@ private synchronized LdapContext connect() {
144
233
LOGGER .log (Level .INFO , "Connecting to LDAP server {0} " , this .toString ());
145
234
146
235
if (errorTimestamp > 0 && errorTimestamp + interval > System .currentTimeMillis ()) {
147
- LOGGER .log (Level .INFO , "LDAP server {0} is down" , this .url );
236
+ LOGGER .log (Level .WARNING , "LDAP server {0} is down" , this .url );
148
237
close ();
149
238
return null ;
150
239
}
@@ -169,7 +258,7 @@ private synchronized LdapContext connect() {
169
258
LOGGER .log (Level .INFO , "Connected to LDAP server {0}" , this .toString ());
170
259
errorTimestamp = 0 ;
171
260
} catch (NamingException ex ) {
172
- LOGGER .log (Level .INFO , "LDAP server {0} is not responding" , env .get (Context .PROVIDER_URL ));
261
+ LOGGER .log (Level .WARNING , "LDAP server {0} is not responding" , env .get (Context .PROVIDER_URL ));
173
262
errorTimestamp = System .currentTimeMillis ();
174
263
close ();
175
264
return ctx = null ;
@@ -194,7 +283,7 @@ public NamingEnumeration<SearchResult> search(String name, String filter, Search
194
283
}
195
284
196
285
/**
197
- * Lookups the LDAP server .
286
+ * Perform LDAP search .
198
287
*
199
288
* @param name base dn for the search
200
289
* @param filter LDAP filter
0 commit comments