1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.forgerock.opendj.examples;
29
30 import static org.forgerock.opendj.ldap.ErrorResultException.newErrorResult;
31
32 import java.io.IOException;
33 import java.util.LinkedList;
34 import java.util.List;
35
36 import org.forgerock.opendj.ldap.Connection;
37 import org.forgerock.opendj.ldap.ConnectionFactory;
38 import org.forgerock.opendj.ldap.Connections;
39 import org.forgerock.opendj.ldap.ErrorResultException;
40 import org.forgerock.opendj.ldap.IntermediateResponseHandler;
41 import org.forgerock.opendj.ldap.LDAPClientContext;
42 import org.forgerock.opendj.ldap.LDAPConnectionFactory;
43 import org.forgerock.opendj.ldap.LDAPListener;
44 import org.forgerock.opendj.ldap.LDAPListenerOptions;
45 import org.forgerock.opendj.ldap.RequestContext;
46 import org.forgerock.opendj.ldap.RequestHandler;
47 import org.forgerock.opendj.ldap.ResultCode;
48 import org.forgerock.opendj.ldap.ResultHandler;
49 import org.forgerock.opendj.ldap.RoundRobinLoadBalancingAlgorithm;
50 import org.forgerock.opendj.ldap.SearchResultHandler;
51 import org.forgerock.opendj.ldap.ServerConnectionFactory;
52 import org.forgerock.opendj.ldap.controls.ProxiedAuthV2RequestControl;
53 import org.forgerock.opendj.ldap.requests.AddRequest;
54 import org.forgerock.opendj.ldap.requests.BindRequest;
55 import org.forgerock.opendj.ldap.requests.CancelExtendedRequest;
56 import org.forgerock.opendj.ldap.requests.CompareRequest;
57 import org.forgerock.opendj.ldap.requests.DeleteRequest;
58 import org.forgerock.opendj.ldap.requests.ExtendedRequest;
59 import org.forgerock.opendj.ldap.requests.ModifyDNRequest;
60 import org.forgerock.opendj.ldap.requests.ModifyRequest;
61 import org.forgerock.opendj.ldap.requests.Request;
62 import org.forgerock.opendj.ldap.requests.Requests;
63 import org.forgerock.opendj.ldap.requests.SearchRequest;
64 import org.forgerock.opendj.ldap.requests.StartTLSExtendedRequest;
65 import org.forgerock.opendj.ldap.responses.BindResult;
66 import org.forgerock.opendj.ldap.responses.CompareResult;
67 import org.forgerock.opendj.ldap.responses.ExtendedResult;
68 import org.forgerock.opendj.ldap.responses.Result;
69 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
70 import org.forgerock.opendj.ldap.responses.SearchResultReference;
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89 public final class Proxy {
90 private static final class ProxyBackend implements RequestHandler<RequestContext> {
91 private final ConnectionFactory factory;
92 private final ConnectionFactory bindFactory;
93
94 private ProxyBackend(final ConnectionFactory factory, final ConnectionFactory bindFactory) {
95 this.factory = factory;
96 this.bindFactory = bindFactory;
97 }
98
99 private abstract class AbstractRequestCompletionHandler
100 <R extends Result, H extends ResultHandler<? super R>>
101 implements ResultHandler<R> {
102 final H resultHandler;
103 final Connection connection;
104
105 AbstractRequestCompletionHandler(final Connection connection, final H resultHandler) {
106 this.connection = connection;
107 this.resultHandler = resultHandler;
108 }
109
110 @Override
111 public final void handleErrorResult(final ErrorResultException error) {
112 connection.close();
113 resultHandler.handleErrorResult(error);
114 }
115
116 @Override
117 public final void handleResult(final R result) {
118 connection.close();
119 resultHandler.handleResult(result);
120 }
121
122 }
123
124 private abstract class ConnectionCompletionHandler<R extends Result> implements
125 ResultHandler<Connection> {
126 private final ResultHandler<? super R> resultHandler;
127
128 ConnectionCompletionHandler(final ResultHandler<? super R> resultHandler) {
129 this.resultHandler = resultHandler;
130 }
131
132 @Override
133 public final void handleErrorResult(final ErrorResultException error) {
134 resultHandler.handleErrorResult(error);
135 }
136
137 @Override
138 public abstract void handleResult(Connection connection);
139
140 }
141
142 private final class RequestCompletionHandler<R extends Result> extends
143 AbstractRequestCompletionHandler<R, ResultHandler<? super R>> {
144 RequestCompletionHandler(final Connection connection,
145 final ResultHandler<? super R> resultHandler) {
146 super(connection, resultHandler);
147 }
148 }
149
150 private final class SearchRequestCompletionHandler extends
151 AbstractRequestCompletionHandler<Result, SearchResultHandler> implements
152 SearchResultHandler {
153
154 SearchRequestCompletionHandler(final Connection connection,
155 final SearchResultHandler resultHandler) {
156 super(connection, resultHandler);
157 }
158
159
160
161
162 @Override
163 public final boolean handleEntry(final SearchResultEntry entry) {
164 return resultHandler.handleEntry(entry);
165 }
166
167
168
169
170 @Override
171 public final boolean handleReference(final SearchResultReference reference) {
172 return resultHandler.handleReference(reference);
173 }
174
175 }
176
177 private volatile ProxiedAuthV2RequestControl proxiedAuthControl = null;
178
179
180
181
182 @Override
183 public void handleAdd(final RequestContext requestContext, final AddRequest request,
184 final IntermediateResponseHandler intermediateResponseHandler,
185 final ResultHandler<? super Result> resultHandler) {
186 addProxiedAuthControl(request);
187 final ConnectionCompletionHandler<Result> outerHandler =
188 new ConnectionCompletionHandler<Result>(resultHandler) {
189
190 @Override
191 public void handleResult(final Connection connection) {
192 final RequestCompletionHandler<Result> innerHandler =
193 new RequestCompletionHandler<Result>(connection, resultHandler);
194 connection.addAsync(request, intermediateResponseHandler, innerHandler);
195 }
196
197 };
198
199 factory.getConnectionAsync(outerHandler);
200 }
201
202
203
204
205 @Override
206 public void handleBind(final RequestContext requestContext, final int version,
207 final BindRequest request,
208 final IntermediateResponseHandler intermediateResponseHandler,
209 final ResultHandler<? super BindResult> resultHandler) {
210
211 if (request.getAuthenticationType() != ((byte) 0x80)) {
212
213 resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
214 "non-SIMPLE authentication not supported: "
215 + request.getAuthenticationType()));
216 } else {
217
218
219 final ConnectionCompletionHandler<BindResult> outerHandler =
220 new ConnectionCompletionHandler<BindResult>(resultHandler) {
221
222 @Override
223 public void handleResult(final Connection connection) {
224 final ResultHandler<BindResult> innerHandler =
225 new ResultHandler<BindResult>() {
226
227 @Override
228 public final void handleErrorResult(
229 final ErrorResultException error) {
230 connection.close();
231 resultHandler.handleErrorResult(error);
232 }
233
234 @Override
235 public final void handleResult(final BindResult result) {
236 connection.close();
237 proxiedAuthControl =
238 ProxiedAuthV2RequestControl
239 .newControl("dn:"
240 + request.getName());
241 resultHandler.handleResult(result);
242 }
243 };
244 connection.bindAsync(request, intermediateResponseHandler,
245 innerHandler);
246 }
247
248 };
249
250 proxiedAuthControl = null;
251 bindFactory.getConnectionAsync(outerHandler);
252 }
253 }
254
255
256
257
258 @Override
259 public void handleCompare(final RequestContext requestContext,
260 final CompareRequest request,
261 final IntermediateResponseHandler intermediateResponseHandler,
262 final ResultHandler<? super CompareResult> resultHandler) {
263 addProxiedAuthControl(request);
264 final ConnectionCompletionHandler<CompareResult> outerHandler =
265 new ConnectionCompletionHandler<CompareResult>(resultHandler) {
266
267 @Override
268 public void handleResult(final Connection connection) {
269 final RequestCompletionHandler<CompareResult> innerHandler =
270 new RequestCompletionHandler<CompareResult>(connection,
271 resultHandler);
272 connection.compareAsync(request, intermediateResponseHandler,
273 innerHandler);
274 }
275
276 };
277
278 factory.getConnectionAsync(outerHandler);
279 }
280
281
282
283
284 @Override
285 public void handleDelete(final RequestContext requestContext, final DeleteRequest request,
286 final IntermediateResponseHandler intermediateResponseHandler,
287 final ResultHandler<? super Result> resultHandler) {
288 addProxiedAuthControl(request);
289 final ConnectionCompletionHandler<Result> outerHandler =
290 new ConnectionCompletionHandler<Result>(resultHandler) {
291
292 @Override
293 public void handleResult(final Connection connection) {
294 final RequestCompletionHandler<Result> innerHandler =
295 new RequestCompletionHandler<Result>(connection, resultHandler);
296 connection.deleteAsync(request, intermediateResponseHandler,
297 innerHandler);
298 }
299
300 };
301
302 factory.getConnectionAsync(outerHandler);
303 }
304
305
306
307
308 @Override
309 public <R extends ExtendedResult> void handleExtendedRequest(
310 final RequestContext requestContext, final ExtendedRequest<R> request,
311 final IntermediateResponseHandler intermediateResponseHandler,
312 final ResultHandler<? super R> resultHandler) {
313 if (request.getOID().equals(CancelExtendedRequest.OID)) {
314
315 resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
316 "Cancel extended request operation not supported"));
317 } else if (request.getOID().equals(StartTLSExtendedRequest.OID)) {
318
319 resultHandler.handleErrorResult(newErrorResult(ResultCode.PROTOCOL_ERROR,
320 "StartTLS extended request operation not supported"));
321 } else {
322
323 addProxiedAuthControl(request);
324
325 final ConnectionCompletionHandler<R> outerHandler =
326 new ConnectionCompletionHandler<R>(resultHandler) {
327
328 @Override
329 public void handleResult(final Connection connection) {
330 final RequestCompletionHandler<R> innerHandler =
331 new RequestCompletionHandler<R>(connection, resultHandler);
332 connection.extendedRequestAsync(request,
333 intermediateResponseHandler, innerHandler);
334 }
335
336 };
337
338 factory.getConnectionAsync(outerHandler);
339 }
340 }
341
342
343
344
345 @Override
346 public void handleModify(final RequestContext requestContext, final ModifyRequest request,
347 final IntermediateResponseHandler intermediateResponseHandler,
348 final ResultHandler<? super Result> resultHandler) {
349 addProxiedAuthControl(request);
350 final ConnectionCompletionHandler<Result> outerHandler =
351 new ConnectionCompletionHandler<Result>(resultHandler) {
352
353 @Override
354 public void handleResult(final Connection connection) {
355 final RequestCompletionHandler<Result> innerHandler =
356 new RequestCompletionHandler<Result>(connection, resultHandler);
357 connection.modifyAsync(request, intermediateResponseHandler,
358 innerHandler);
359 }
360
361 };
362
363 factory.getConnectionAsync(outerHandler);
364 }
365
366
367
368
369 @Override
370 public void handleModifyDN(final RequestContext requestContext,
371 final ModifyDNRequest request,
372 final IntermediateResponseHandler intermediateResponseHandler,
373 final ResultHandler<? super Result> resultHandler) {
374 addProxiedAuthControl(request);
375 final ConnectionCompletionHandler<Result> outerHandler =
376 new ConnectionCompletionHandler<Result>(resultHandler) {
377
378 @Override
379 public void handleResult(final Connection connection) {
380 final RequestCompletionHandler<Result> innerHandler =
381 new RequestCompletionHandler<Result>(connection, resultHandler);
382 connection.modifyDNAsync(request, intermediateResponseHandler,
383 innerHandler);
384 }
385
386 };
387
388 factory.getConnectionAsync(outerHandler);
389 }
390
391
392
393
394 @Override
395 public void handleSearch(final RequestContext requestContext, final SearchRequest request,
396 final IntermediateResponseHandler intermediateResponseHandler,
397 final SearchResultHandler resultHandler) {
398 addProxiedAuthControl(request);
399 final ConnectionCompletionHandler<Result> outerHandler =
400 new ConnectionCompletionHandler<Result>(resultHandler) {
401
402 @Override
403 public void handleResult(final Connection connection) {
404 final SearchRequestCompletionHandler innerHandler =
405 new SearchRequestCompletionHandler(connection, resultHandler);
406 connection.searchAsync(request, intermediateResponseHandler,
407 innerHandler);
408 }
409
410 };
411
412 factory.getConnectionAsync(outerHandler);
413 }
414
415 private void addProxiedAuthControl(final Request request) {
416 final ProxiedAuthV2RequestControl control = proxiedAuthControl;
417 if (control != null) {
418 request.addControl(control);
419 }
420 }
421
422 }
423
424
425
426
427
428
429
430
431
432 public static void main(final String[] args) {
433 if (args.length < 6 || args.length % 2 != 0) {
434 System.err.println("Usage: listenAddress listenPort "
435 + "proxyDN proxyPassword remoteAddress1 remotePort1 "
436 + "remoteAddress2 remotePort2 ...");
437 System.exit(1);
438 }
439
440
441 final String localAddress = args[0];
442 final int localPort = Integer.parseInt(args[1]);
443
444 final String proxyDN = args[2];
445 final String proxyPassword = args[3];
446
447
448 final List<ConnectionFactory> factories = new LinkedList<ConnectionFactory>();
449 final List<ConnectionFactory> bindFactories = new LinkedList<ConnectionFactory>();
450 for (int i = 4; i < args.length; i += 2) {
451 final String remoteAddress = args[i];
452 final int remotePort = Integer.parseInt(args[i + 1]);
453
454 factories.add(Connections.newFixedConnectionPool(Connections
455 .newAuthenticatedConnectionFactory(new LDAPConnectionFactory(remoteAddress,
456 remotePort), Requests.newSimpleBindRequest(proxyDN, proxyPassword
457 .toCharArray())), Integer.MAX_VALUE));
458 bindFactories.add(Connections.newFixedConnectionPool(new LDAPConnectionFactory(
459 remoteAddress, remotePort), Integer.MAX_VALUE));
460 }
461 final RoundRobinLoadBalancingAlgorithm algorithm =
462 new RoundRobinLoadBalancingAlgorithm(factories);
463 final RoundRobinLoadBalancingAlgorithm bindAlgorithm =
464 new RoundRobinLoadBalancingAlgorithm(bindFactories);
465 final ConnectionFactory factory = Connections.newLoadBalancer(algorithm);
466 final ConnectionFactory bindFactory = Connections.newLoadBalancer(bindAlgorithm);
467
468
469 final ProxyBackend backend = new ProxyBackend(factory, bindFactory);
470 final ServerConnectionFactory<LDAPClientContext, Integer> connectionHandler =
471 Connections.newServerConnectionFactory(backend);
472
473
474 final LDAPListenerOptions options = new LDAPListenerOptions().setBacklog(4096);
475 LDAPListener listener = null;
476 try {
477 listener = new LDAPListener(localAddress, localPort, connectionHandler, options);
478 System.out.println("Press any key to stop the server...");
479 System.in.read();
480 } catch (final IOException e) {
481 System.out.println("Error listening on " + localAddress + ":" + localPort);
482 e.printStackTrace();
483 } finally {
484 if (listener != null) {
485 listener.close();
486 }
487 }
488 }
489
490 private Proxy() {
491
492 }
493 }