--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jndi/ldap/lib/LdapMessage.java Wed Aug 14 11:14:54 2019 +0100
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+/**
+ * An LDAP message.
+ */
+public class LdapMessage {
+
+ private final byte[] message;
+ private int messageID;
+ private Operation operation;
+
+ public enum Operation {
+ BIND_REQUEST(0x60, "BindRequest"), // [APPLICATION 0]
+ BIND_RESPONSE(0x61, "BindResponse"), // [APPLICATION 1]
+ UNBIND_REQUEST(0x42, "UnbindRequest"), // [APPLICATION 2]
+ SEARCH_REQUEST(0x63, "SearchRequest"), // [APPLICATION 3]
+ SEARCH_RESULT_ENTRY(0x64, "SearchResultEntry"), // [APPLICATION 4]
+ SEARCH_RESULT_DONE(0x65, "SearchResultDone"), // [APPLICATION 5]
+ MODIFY_REQUEST(0x66, "ModifyRequest"), // [APPLICATION 6]
+ MODIFY_RESPONSE(0x67, "ModifyResponse"), // [APPLICATION 7]
+ ADD_REQUEST(0x68, "AddRequest"), // [APPLICATION 8]
+ ADD_RESPONSE(0x69, "AddResponse"), // [APPLICATION 9]
+ DELETE_REQUEST(0x4A, "DeleteRequest"), // [APPLICATION 10]
+ DELETE_RESPONSE(0x6B, "DeleteResponse"), // [APPLICATION 11]
+ MODIFY_DN_REQUEST(0x6C, "ModifyDNRequest"), // [APPLICATION 12]
+ MODIFY_DN_RESPONSE(0x6D, "ModifyDNResponse"), // [APPLICATION 13]
+ COMPARE_REQUEST(0x6E, "CompareRequest"), // [APPLICATION 14]
+ COMPARE_RESPONSE(0x6F, "CompareResponse"), // [APPLICATION 15]
+ ABANDON_REQUEST(0x50, "AbandonRequest"), // [APPLICATION 16]
+ SEARCH_RESULT_REFERENCE(0x73, "SearchResultReference"), // [APPLICATION 19]
+ EXTENDED_REQUEST(0x77, "ExtendedRequest"), // [APPLICATION 23]
+ EXTENDED_RESPONSE(0x78, "ExtendedResponse"), // [APPLICATION 24]
+ INTERMEDIATE_RESPONSE(0x79, "IntermediateResponse"); // [APPLICATION 25]
+
+ private final int id;
+ private final String name;
+
+ Operation(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ private static Operation fromId(int id) {
+ Optional<Operation> optional = Stream.of(Operation.values())
+ .filter(o -> o.id == id).findFirst();
+ if (optional.isPresent()) {
+ return optional.get();
+ } else {
+ throw new RuntimeException(
+ "Unknown id " + id + " for enum Operation.");
+ }
+ }
+ }
+
+ public LdapMessage(byte[] message) {
+ this.message = message;
+ parse();
+ }
+
+ public LdapMessage(String hexString) {
+ this(parseHexBinary(hexString));
+ }
+
+ // Extracts the message ID and operation ID from an LDAP protocol encoding
+ private void parse() {
+ if (message == null || message.length < 2) {
+ throw new RuntimeException(
+ "Invalid ldap message: " + Arrays.toString(message));
+ }
+
+ if (message[0] != 0x30) {
+ throw new RuntimeException("Bad LDAP encoding in message, "
+ + "expected ASN.1 SEQUENCE tag (0x30), encountered "
+ + message[0]);
+ }
+
+ int index = 2;
+ if ((message[1] & 0x80) == 0x80) {
+ index += (message[1] & 0x0F);
+ }
+
+ if (message[index] != 0x02) {
+ throw new RuntimeException("Bad LDAP encoding in message, "
+ + "expected ASN.1 INTEGER tag (0x02), encountered "
+ + message[index]);
+ }
+ int length = message[index + 1];
+ index += 2;
+ messageID = new BigInteger(1,
+ Arrays.copyOfRange(message, index, index + length)).intValue();
+ index += length;
+ int operationID = message[index];
+ operation = Operation.fromId(operationID);
+ }
+
+ /**
+ * Return original ldap message in byte array.
+ *
+ * @return original ldap message
+ */
+ public byte[] getMessage() {
+ return Arrays.copyOf(message, message.length);
+ }
+
+ /**
+ * Return ldap message id.
+ *
+ * @return ldap message id.
+ */
+ public int getMessageID() {
+ return messageID;
+ }
+
+ /**
+ * Return ldap message's operation.
+ *
+ * @return ldap message's operation.
+ */
+ public Operation getOperation() {
+ return operation;
+ }
+
+ private static byte[] parseHexBinary(String s) {
+
+ final int len = s.length();
+
+ // "111" is not a valid hex encoding.
+ if (len % 2 != 0) {
+ throw new IllegalArgumentException(
+ "hexBinary needs to be even-length: " + s);
+ }
+
+ byte[] out = new byte[len / 2];
+
+ for (int i = 0; i < len; i += 2) {
+ int h = Character.digit(s.charAt(i), 16);
+ int l = Character.digit(s.charAt(i + 1), 16);
+ if (h == -1 || l == -1) {
+ throw new IllegalArgumentException(
+ "contains illegal character for hexBinary: " + s);
+ }
+
+ out[i / 2] = (byte) (h * 16 + l);
+ }
+
+ return out;
+ }
+
+ public static int getMessageLength(byte[] encoding) {
+ if (encoding.length < 2) {
+ // not enough data to extract msg len, just return -1
+ return -1;
+ }
+
+ if (encoding[0] != 0x30) {
+ throw new RuntimeException("Error: bad LDAP encoding message: "
+ + "expected ASN.1 SEQUENCE tag (0x30), encountered "
+ + encoding[0]);
+ }
+
+ int len;
+ int index = 1;
+ int payloadLen = 0;
+
+ if ((encoding[1] & 0x80) == 0x80) {
+ len = (encoding[1] & 0x0F);
+ index++;
+ } else {
+ len = 1;
+ }
+
+ if (len > 4) {
+ throw new RuntimeException(
+ "Error: LDAP encoding message payload too large");
+ }
+
+ if (encoding.length < index + len) {
+ // additional data required to extract payload len, return -1
+ return -1;
+ }
+
+ for (byte b : Arrays.copyOfRange(encoding, index, index + len)) {
+ payloadLen = payloadLen << 8 | (b & 0xFF);
+ }
+
+ if (payloadLen <= 0) {
+ throw new RuntimeException(
+ "Error: invalid LDAP encoding message length or payload too large");
+ }
+
+ return index + len + payloadLen;
+ }
+}