1:
38:
39: package ;
40:
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59:
60:
68: public class LDAPConnection
69: {
70:
71:
74: public static final int DEFAULT_PORT = 389;
75:
76: public static final int SCOPE_BASE_OBJECT = 0;
77: public static final int SCOPE_SINGLE_LEVEL = 1;
78: public static final int SCOPE_WHOLE_SUBTREE = 2;
79:
80:
84: public static final int DEREF_NEVER = 0;
85:
86:
90: public static final int DEREF_IN_SEARCHING = 1;
91:
92:
96: public static final int DEREF_FINDING_BASE_OBJ = 2;
97:
98:
102: public static final int DEREF_ALWAYS = 3;
103:
104: private static final int SUCCESS = 0;
105: private static final int SASL_BIND_IN_PROGRESS = 14;
106:
107: private static final int MESSAGE = 0x30;
108: private static final int BIND_REQUEST = 0x60;
109: private static final int BIND_RESPONSE = 0x61;
110: private static final int UNBIND_REQUEST = 0x62;
111: private static final int SEARCH_REQUEST = 0x63;
112: private static final int SEARCH_RESULT = 0x64;
113: private static final int SEARCH_RESULT_DONE = 0x65;
114: private static final int MODIFY_REQUEST = 0x66;
115: private static final int MODIFY_RESPONSE = 0x67;
116: private static final int ADD_REQUEST = 0x68;
117: private static final int ADD_RESPONSE = 0x69;
118: private static final int DELETE_REQUEST = 0x6a;
119: private static final int DELETE_RESPONSE = 0x6b;
120: private static final int MODIFY_DN_REQUEST = 0x6c;
121: private static final int MODIFY_DN_RESPONSE = 0x6d;
122: private static final int SEARCH_REFERENCE = 0x73;
123:
124: protected String host;
125: protected int port;
126: protected int version;
127:
128: private Socket socket;
129: private InputStream in;
130: private OutputStream out;
131:
132: private int messageId;
133: private Map asyncResponses;
134:
135:
140: public LDAPConnection(String host)
141: throws IOException
142: {
143: this(host, DEFAULT_PORT, 0, 0);
144: }
145:
146:
151: public LDAPConnection(String host, int port)
152: throws IOException
153: {
154: this(host, port, 0, 0);
155: }
156:
157:
164: public LDAPConnection(String host, int port,
165: int connectionTimeout, int timeout)
166: throws IOException
167: {
168: this.host = host;
169: if (port < 0)
170: {
171: port = DEFAULT_PORT;
172: }
173: this.port = port;
174: messageId = 0;
175: asyncResponses = new HashMap();
176: version = 3;
177:
178:
179: socket = new Socket();
180: SocketAddress address = new InetSocketAddress(host, port);
181: if (connectionTimeout > 0)
182: {
183: socket.connect(address, connectionTimeout);
184: }
185: else
186: {
187: socket.connect(address);
188: }
189: in = new BufferedInputStream(socket.getInputStream());
190: out = new BufferedOutputStream(socket.getOutputStream());
191: }
192:
193:
198: public void setVersion(int version)
199: {
200: if (version < 2 || version > 3)
201: {
202: throw new IllegalArgumentException(Integer.toString(version));
203: }
204: this.version = version;
205: }
206:
207:
216: public LDAPResult bind(String name, String mechanism,
217: byte[] credentials, Control[] controls)
218: throws IOException
219: {
220: int id = messageId++;
221: boolean utf8 = (version == 3);
222: BEREncoder bind = new BEREncoder(utf8);
223: if (mechanism == null)
224: {
225: bind.append(version);
226: bind.append(name);
227: if (credentials != null)
228: {
229: bind.append(credentials);
230: }
231: }
232: else
233: {
234: bind.append(version);
235: bind.append(name);
236:
237: BEREncoder saslCredentials = new BEREncoder(utf8);
238: saslCredentials.append(mechanism);
239: if (credentials != null)
240: {
241: saslCredentials.append(credentials);
242: }
243: bind.append(saslCredentials.toByteArray(), BERConstants.SEQUENCE);
244: }
245:
246: BEREncoder ctls = new BEREncoder(utf8);
247: if (controls != null)
248: {
249: for (int i = 0; i < controls.length; i++)
250: {
251: ctls.append(controlSequence(controls[i], utf8),
252: BERConstants.SEQUENCE);
253: }
254: }
255: bind.append(ctls.toByteArray(), BERConstants.CONTEXT);
256:
257: write(id, BIND_REQUEST, bind.toByteArray());
258:
259: BERDecoder response = read(id);
260: BERDecoder resultSequence = response.parseSequence(BIND_RESPONSE);
261: LDAPResult result = parseResult(resultSequence);
262: if (resultSequence.available())
263: {
264: byte[] serverCreds = resultSequence.parseOctetString();
265:
266: }
267:
268: return result;
269: }
270:
271:
276: public void unbind()
277: throws IOException
278: {
279: int id = messageId++;
280: boolean utf8 = (version == 3);
281: BEREncoder unbind = new BEREncoder(utf8);
282: unbind.appendNull();
283: write(id, UNBIND_REQUEST, unbind.toByteArray());
284:
285: socket.close();
286: }
287:
288:
307: public LDAPResult search(String name, int scope, int derefAliases,
308: int sizeLimit, int timeLimit,
309: boolean typesOnly, String filter,
310: String[] attributes, Control[] controls,
311: ResultHandler handler)
312: throws IOException
313: {
314: if (filter == null || filter.length() == 0)
315: {
316: filter = "(objectClass=*)";
317: }
318: int id = messageId++;
319: boolean utf8 = (version == 3);
320: BEREncoder search = new BEREncoder(utf8);
321: search.append(name);
322: search.append(scope, BERConstants.ENUMERATED);
323: search.append(derefAliases, BERConstants.ENUMERATED);
324: search.append(sizeLimit);
325: search.append(timeLimit);
326: search.append(typesOnly);
327: search.appendFilter(filter);
328: BEREncoder attributeSequence = new BEREncoder(utf8);
329: if (attributes != null)
330: {
331: for (int i = 0; i < attributes.length; i++)
332: {
333: attributeSequence.append(attributes[i]);
334: }
335: }
336: search.append(attributeSequence.toByteArray(), BERConstants.SEQUENCE);
337:
338: BEREncoder ctls = new BEREncoder(utf8);
339: if (controls != null)
340: {
341: for (int i = 0; i < controls.length; i++)
342: {
343: ctls.append(controlSequence(controls[i], utf8),
344: BERConstants.SEQUENCE);
345: }
346: }
347: search.append(ctls.toByteArray(), BERConstants.SEQUENCE);
348:
349: write(id, SEARCH_REQUEST, search.toByteArray());
350: do
351: {
352: BERDecoder response = read(id);
353: int code = response.parseType();
354: switch (code)
355: {
356: case SEARCH_RESULT:
357: BERDecoder entry = response.parseSequence(code);
358: String objectName = entry.parseString();
359: BERDecoder attributeSeq = entry.parseSequence(0x30);
360: Map attrs = new TreeMap();
361: while (attributeSeq.available())
362: {
363: BERDecoder attribute = attributeSeq.parseSequence(0x30);
364: String type = attribute.parseString();
365: BERDecoder values = attribute.parseSet(0x31);
366: List acc = new ArrayList();
367: while (values.available())
368: {
369: int valueType = values.parseType();
370: switch (valueType)
371: {
372: case BERConstants.BOOLEAN:
373: acc.add(Boolean.valueOf(values.parseBoolean()));
374: break;
375: case BERConstants.INTEGER:
376: case BERConstants.ENUMERATED:
377: acc.add(new Integer(values.parseInt()));
378: break;
379:
380: case BERConstants.UTF8_STRING:
381: acc.add(values.parseString());
382: break;
383: case BERConstants.OCTET_STRING:
384: acc.add(values.parseOctetString());
385: break;
386: }
387: }
388: attrs.put(type, acc);
389: }
390: handler.searchResultEntry(objectName, attrs);
391: break;
392: case SEARCH_REFERENCE:
393: List acc = new ArrayList();
394: BERDecoder urls = response.parseSequence(code);
395: while (urls.available())
396: {
397: acc.add(urls.parseString());
398: }
399: handler.searchResultReference(acc);
400: break;
401: case SEARCH_RESULT_DONE:
402: return parseResult(response.parseSequence(code));
403: default:
404: throw new ProtocolException("Unexpected response: " + code);
405: }
406: }
407: while (true);
408: }
409:
410:
418: public LDAPResult modify(String name, final Modification[] modifications)
419: throws IOException
420: {
421: int id = messageId++;
422: boolean utf8 = (version == 3);
423: BEREncoder modify = new BEREncoder(utf8);
424: modify.append(name);
425: BEREncoder modSeq = new BEREncoder(utf8);
426: for (int i = 0; i < modifications.length; i++)
427: {
428: BEREncoder mod = new BEREncoder(utf8);
429: mod.append(modifications[i].operation);
430: BEREncoder typeAndValues = new BEREncoder(utf8);
431: typeAndValues.append(modifications[i].type);
432: BEREncoder values = new BEREncoder(utf8);
433: appendValues(values, modifications[i].values);
434: typeAndValues.append(values.toByteArray(), BERConstants.SET);
435: mod.append(typeAndValues.toByteArray(), BERConstants.SEQUENCE);
436: modSeq.append(mod.toByteArray(), BERConstants.SEQUENCE);
437: }
438: modify.append(modSeq.toByteArray(), BERConstants.SEQUENCE);
439:
440: write(id, MODIFY_REQUEST, modify.toByteArray());
441:
442: BERDecoder response = read(id);
443: BERDecoder resultSequence = response.parseSequence(MODIFY_RESPONSE);
444: LDAPResult result = parseResult(resultSequence);
445: return result;
446: }
447:
448:
453: public LDAPResult add(String name, AttributeValues[] attributes)
454: throws IOException
455: {
456: int id = messageId++;
457: boolean utf8 = (version == 3);
458: BEREncoder add = new BEREncoder(utf8);
459: add.append(name);
460: BEREncoder attrSeq = new BEREncoder(utf8);
461: for (int i = 0; i < attributes.length; i++)
462: {
463: BEREncoder attr = new BEREncoder(utf8);
464: attr.append(attributes[i].type);
465: BEREncoder values = new BEREncoder(utf8);
466: appendValues(values, attributes[i].values);
467: attr.append(values.toByteArray(), BERConstants.SET);
468: attrSeq.append(attr.toByteArray(), BERConstants.SEQUENCE);
469: }
470: add.append(attrSeq.toByteArray(), BERConstants.SEQUENCE);
471:
472: write(id, ADD_REQUEST, add.toByteArray());
473:
474: BERDecoder response = read(id);
475: BERDecoder resultSequence = response.parseSequence(ADD_RESPONSE);
476: LDAPResult result = parseResult(resultSequence);
477: return result;
478: }
479:
480:
484: public LDAPResult delete(String name)
485: throws IOException
486: {
487: int id = messageId++;
488: boolean utf8 = (version == 3);
489: BEREncoder del = new BEREncoder(utf8);
490: del.append(name);
491:
492: write(id, DELETE_REQUEST, del.toByteArray());
493:
494: BERDecoder response = read(id);
495: int code = response.parseType();
496: if (code != DELETE_RESPONSE)
497: {
498: throw new ProtocolException("Unexpected response type: " +
499: code);
500: }
501: BERDecoder resultSequence = response.parseSequence();
502: LDAPResult result = parseResult(resultSequence);
503: return result;
504: }
505:
506:
518: public LDAPResult modifyDN(String name, String newRDN,
519: boolean deleteOldRDN, String newSuperior)
520: throws IOException
521: {
522: int id = messageId++;
523: boolean utf8 = (version == 3);
524: BEREncoder modifyDN = new BEREncoder(utf8);
525: modifyDN.append(name);
526: modifyDN.append(newRDN);
527: modifyDN.append(deleteOldRDN);
528: if (newSuperior != null)
529: {
530: modifyDN.append(newSuperior);
531: }
532:
533: write(id, MODIFY_DN_REQUEST, modifyDN.toByteArray());
534:
535: BERDecoder response = read(id);
536: BERDecoder resultSequence = response.parseSequence(MODIFY_DN_RESPONSE);
537: LDAPResult result = parseResult(resultSequence);
538: return result;
539: }
540:
541:
542:
543:
544:
545:
546:
547:
548:
549:
552: void appendValues(BEREncoder encoder, Set values)
553: throws BERException
554: {
555: if (values != null)
556: {
557: for (Iterator i = values.iterator(); i.hasNext(); )
558: {
559: Object value = i.next();
560: if (value == null)
561: {
562: encoder.appendNull();
563: }
564: else if (value instanceof String)
565: {
566: encoder.append((String) value);
567: }
568: else if (value instanceof Integer)
569: {
570: encoder.append(((Integer) value).intValue());
571: }
572: else if (value instanceof Boolean)
573: {
574: encoder.append(((Boolean) value).booleanValue());
575: }
576: else if (value instanceof byte[])
577: {
578: encoder.append((byte[]) value);
579: }
580:
581: else
582: {
583: throw new ClassCastException(value.getClass().getName());
584: }
585: }
586: }
587: }
588:
589:
592: byte[] controlSequence(final Control control, boolean utf8)
593: throws IOException
594: {
595: BEREncoder encoder = new BEREncoder(utf8);
596: encoder.append(control.getID());
597: if (control.isCritical())
598: {
599: encoder.append(true);
600: }
601: return encoder.toByteArray();
602: }
603:
604:
607: LDAPResult parseResult(BERDecoder response)
608: throws IOException
609: {
610: int status = response.parseInt();
611: String matchingDN = response.parseString();
612: String errorMessage = response.parseString();
613: String[] referrals = null;
614: if (response.available())
615: {
616: int type = response.parseType();
617: if (type == BERConstants.SEQUENCE)
618: {
619: ArrayList list = new ArrayList();
620: BERDecoder sequence = response.parseSequence();
621: type = sequence.parseType();
622: while (type != -1)
623: {
624: list.add(sequence.parseString());
625: }
626: referrals = new String[list.size()];
627: list.toArray(referrals);
628: }
629: }
630: return new LDAPResult(status, matchingDN, errorMessage, referrals);
631: }
632:
633:
639: void write(int id, int code, byte[] request)
640: throws IOException
641: {
642: boolean utf8 = (version == 3);
643: BEREncoder envelope = new BEREncoder(utf8);
644: envelope.append(id);
645: envelope.append(request, code);
646: BEREncoder message = new BEREncoder(utf8);
647: message.append(envelope.toByteArray(), MESSAGE);
648: byte[] toSend = message.toByteArray();
649:
650: out.write(toSend);
651: out.flush();
652: }
653:
654:
659: BERDecoder read(int id)
660: throws IOException
661: {
662:
663: Integer key = new Integer(id);
664: List responses = (List) asyncResponses.get(key);
665: if (responses != null)
666: {
667: BERDecoder response = (BERDecoder) responses.remove(0);
668: if (responses.size() == 0)
669: {
670: asyncResponses.remove(key);
671: }
672: return response;
673: }
674: do
675: {
676:
677: byte[] bytes = readMessage();
678: boolean utf8 = (version == 3);
679: BERDecoder message = new BERDecoder(bytes, utf8);
680: message = message.parseSequence(MESSAGE);
681:
682: int msgId = message.parseInt();
683: if (msgId == id)
684: {
685: return message;
686: }
687: else
688: {
689:
690: key = new Integer(msgId);
691: responses = (List) asyncResponses.get(key);
692: if (responses == null)
693: {
694: responses = new ArrayList();
695: asyncResponses.put(key, responses);
696: }
697: responses.add(message);
698: }
699: }
700: while (true);
701: }
702:
703:
706: byte[] readMessage()
707: throws IOException
708: {
709:
710:
711:
712: byte[] header = new byte[6];
713: int offset = 0;
714: header[offset++] = (byte) readByte();
715: int len = readByte();
716: header[offset++] = (byte) len;
717: if ((len & 0x80) != 0)
718: {
719: int lsize = len - 0x80;
720: if (lsize > 4)
721: {
722: throw new BERException("Data too long: " + lsize);
723: }
724: len = 0;
725: for (int i = 0; i < lsize; i++)
726: {
727: int c = readByte();
728: header[offset++] = (byte) c;
729: len = (len << 8) + c;
730: }
731: }
732:
733: byte[] message = new byte[offset + len];
734: System.arraycopy(header, 0, message, 0, offset);
735: if (len == 0)
736: {
737: return message;
738: }
739: header = null;
740:
741: do
742: {
743: int l = in.read(message, offset, len);
744: if (l == -1)
745: {
746: throw new IOException("EOF");
747: }
748: offset += l;
749: len -= l;
750: }
751: while (len > 0);
752: return message;
753: }
754:
755:
758: int readByte()
759: throws IOException
760: {
761: int ret = in.read();
762: if (ret == -1)
763: {
764: throw new IOException("EOF");
765: }
766: return ret & 0xff;
767: }
768:
769: }