test/jdk/com/sun/jndi/ldap/lib/LdapMessage.java
branchJDK-8210696-branch
changeset 57345 ff884a2f247b
equal deleted inserted replaced
57343:9a11a7e1c035 57345:ff884a2f247b
       
     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 }