|
1 /* |
|
2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 import java.math.BigInteger; |
|
25 import java.util.Arrays; |
|
26 import java.util.Optional; |
|
27 import java.util.stream.Stream; |
|
28 |
|
29 /** |
|
30 * Class to present one Ldap message. |
|
31 */ |
|
32 public class LdapMessage { |
|
33 private final byte[] messages; |
|
34 private int messageID; |
|
35 private Operation operation; |
|
36 |
|
37 public enum Operation { |
|
38 BIND_REQUEST(0x60, "BindRequest"), // [APPLICATION 0] |
|
39 BIND_RESPONSE(0x61, "BindResponse"), // [APPLICATION 1] |
|
40 UNBIND_REQUEST(0x42, "UnbindRequest"), // [APPLICATION 2] |
|
41 SEARCH_REQUEST(0x63, "SearchRequest"), // [APPLICATION 3] |
|
42 SEARCH_RESULT_ENTRY(0x64, "SearchResultEntry"), // [APPLICATION 4] |
|
43 SEARCH_RESULT_DONE(0x65, "SearchResultDone"), // [APPLICATION 5] |
|
44 MODIFY_REQUEST(0x66, "ModifyRequest"), // [APPLICATION 6] |
|
45 MODIFY_RESPONSE(0x67, "ModifyResponse"), // [APPLICATION 7] |
|
46 ADD_REQUEST(0x68, "AddRequest"), // [APPLICATION 8] |
|
47 ADD_RESPONSE(0x69, "AddResponse"), // [APPLICATION 9] |
|
48 DELETE_REQUEST(0x4A, "DeleteRequest"), // [APPLICATION 10] |
|
49 DELETE_RESPONSE(0x6B, "DeleteResponse"), // [APPLICATION 11] |
|
50 MODIFY_DN_REQUEST(0x6C, "ModifyDNRequest"), // [APPLICATION 12] |
|
51 MODIFY_DN_RESPONSE(0x6D, "ModifyDNResponse"), // [APPLICATION 13] |
|
52 COMPARE_REQUEST(0x6E, "CompareRequest"), // [APPLICATION 14] |
|
53 COMPARE_RESPONSE(0x6F, "CompareResponse"), // [APPLICATION 15] |
|
54 ABANDON_REQUEST(0x50, "AbandonRequest"), // [APPLICATION 16] |
|
55 SEARCH_RESULT_REFERENCE(0x73, |
|
56 "SearchResultReference"), // [APPLICATION 19] |
|
57 EXTENDED_REQUEST(0x77, "ExtendedRequest"), // [APPLICATION 23] |
|
58 EXTENDED_RESPONSE(0x78, "ExtendedResponse"), // [APPLICATION 24] |
|
59 INTERMEDIATE_RESPONSE(0x79, "IntermediateResponse"); // [APPLICATION 25] |
|
60 |
|
61 private final int id; |
|
62 private final String name; |
|
63 |
|
64 Operation(int id, String name) { |
|
65 this.id = id; |
|
66 this.name = name; |
|
67 } |
|
68 |
|
69 public int getId() { |
|
70 return id; |
|
71 } |
|
72 |
|
73 @Override |
|
74 public String toString() { |
|
75 return name; |
|
76 } |
|
77 |
|
78 public static Operation fromId(int id) { |
|
79 Optional<Operation> optional = Stream.of(Operation.values()) |
|
80 .filter(o -> o.id == id).findFirst(); |
|
81 if (optional.isPresent()) { |
|
82 return optional.get(); |
|
83 } else { |
|
84 throw new RuntimeException( |
|
85 "Unknown id " + id + " for enum Operation."); |
|
86 } |
|
87 } |
|
88 } |
|
89 |
|
90 public LdapMessage(byte[] messages) { |
|
91 this.messages = messages; |
|
92 parse(); |
|
93 } |
|
94 |
|
95 public LdapMessage(String hexString) { |
|
96 this(parseHexBinary(hexString)); |
|
97 } |
|
98 |
|
99 // Extracts the message ID and operation ID from an LDAP protocol encoding |
|
100 private void parse() { |
|
101 if (messages == null || messages.length < 2) { |
|
102 throw new RuntimeException( |
|
103 "Invalid ldap messages: " + Arrays.toString(messages)); |
|
104 } |
|
105 |
|
106 if (messages[0] != 0x30) { |
|
107 throw new RuntimeException("Bad LDAP encoding in messages, " |
|
108 + "expected ASN.1 SEQUENCE tag (0x30), encountered " |
|
109 + messages[0]); |
|
110 } |
|
111 |
|
112 int index = 2; |
|
113 if ((messages[1] & 0x80) == 0x80) { |
|
114 index += (messages[1] & 0x0F); |
|
115 } |
|
116 |
|
117 if (messages[index] != 0x02) { |
|
118 throw new RuntimeException("Bad LDAP encoding in messages, " |
|
119 + "expected ASN.1 INTEGER tag (0x02), encountered " |
|
120 + messages[index]); |
|
121 } |
|
122 int length = messages[index + 1]; |
|
123 index += 2; |
|
124 messageID = new BigInteger(1, |
|
125 Arrays.copyOfRange(messages, index, index + length)).intValue(); |
|
126 index += length; |
|
127 int operationID = messages[index]; |
|
128 operation = Operation.fromId(operationID); |
|
129 } |
|
130 |
|
131 /** |
|
132 * Return original ldap message in byte array. |
|
133 * |
|
134 * @return original ldap message |
|
135 */ |
|
136 public byte[] getMessages() { |
|
137 return Arrays.copyOf(messages, messages.length); |
|
138 } |
|
139 |
|
140 /** |
|
141 * Return ldap message id. |
|
142 * |
|
143 * @return ldap message id. |
|
144 */ |
|
145 public int getMessageID() { |
|
146 return messageID; |
|
147 } |
|
148 |
|
149 /** |
|
150 * Return ldap message's operation. |
|
151 * |
|
152 * @return ldap message's operation. |
|
153 */ |
|
154 public Operation getOperation() { |
|
155 return operation; |
|
156 } |
|
157 |
|
158 private static byte[] parseHexBinary(String s) { |
|
159 |
|
160 final int len = s.length(); |
|
161 |
|
162 // "111" is not a valid hex encoding. |
|
163 if (len % 2 != 0) { |
|
164 throw new IllegalArgumentException( |
|
165 "hexBinary needs to be even-length: " + s); |
|
166 } |
|
167 |
|
168 byte[] out = new byte[len / 2]; |
|
169 |
|
170 for (int i = 0; i < len; i += 2) { |
|
171 int h = hexToBin(s.charAt(i)); |
|
172 int l = hexToBin(s.charAt(i + 1)); |
|
173 if (h == -1 || l == -1) { |
|
174 throw new IllegalArgumentException( |
|
175 "contains illegal character for hexBinary: " + s); |
|
176 } |
|
177 |
|
178 out[i / 2] = (byte) (h * 16 + l); |
|
179 } |
|
180 |
|
181 return out; |
|
182 } |
|
183 |
|
184 private static int hexToBin(char ch) { |
|
185 if ('0' <= ch && ch <= '9') { |
|
186 return ch - '0'; |
|
187 } |
|
188 if ('A' <= ch && ch <= 'F') { |
|
189 return ch - 'A' + 10; |
|
190 } |
|
191 if ('a' <= ch && ch <= 'f') { |
|
192 return ch - 'a' + 10; |
|
193 } |
|
194 return -1; |
|
195 } |
|
196 } |