All LDAPv3 requests and responses are encapsulated in a BER sequence called an LDAP message. This is defined as follows in RFC 4511 section 4.1.1:

LDAPMessage ::= SEQUENCE {
     messageID       MessageID,
     protocolOp      CHOICE {
          bindRequest           BindRequest,
          bindResponse          BindResponse,
          unbindRequest         UnbindRequest,
          searchRequest         SearchRequest,
          searchResEntry        SearchResultEntry,
          searchResDone         SearchResultDone,
          searchResRef          SearchResultReference,
          modifyRequest         ModifyRequest,
          modifyResponse        ModifyResponse,
          addRequest            AddRequest,
          addResponse           AddResponse,
          delRequest            DelRequest,
          delResponse           DelResponse,
          modDNRequest          ModifyDNRequest,
          modDNResponse         ModifyDNResponse,
          compareRequest        CompareRequest,
          compareResponse       CompareResponse,
          abandonRequest        AbandonRequest,
          extendedReq           ExtendedRequest,
          extendedResp          ExtendedResponse,
          intermediateResponse  IntermediateResponse },
     controls       [0] Controls OPTIONAL }

As you can see, an LDAP message contains three components: a message ID, a protocol operation, and an optional set of controls. We will look at each of these in more detail in the following sections.

The Message ID

The messageID element of an LDAPMessage sequence is used to correlate requests and responses.

Whenever a client sends a request to the directory server, it chooses the message ID for that request. All of the messages that the server returns in response to that request will have the same message ID as the request. This allows LDAP to operate as an asynchronous protocol, where a single client connection can have multiple outstanding requests at a time. This can allow multiple application threads to share the same connection. It can also allow a single thread to submit multiple requests at the same time, as long as the content of one request doesn’t depend on the response from another of the requests being submitted.

The messageID type is defined alongside the LDAPMessage sequence in RFC 4511 section 4.1.1. That definition is:

MessageID ::= INTEGER (0 ..  maxInt)

maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --

That is, the messageID component of an LDAPMessage sequence is an integer between 0 and 2,147,483,647 (although message ID zero is reserved for a special type of message called an unsolicited notification, which is a message that the server sends to the client without any corresponding request). When sending a request, the client must choose a message ID between 1 and 2,147,483,647 that is not already in use by any outstanding request on that connection (and ideally, that has not also been used by any recently-abandoned request). A client will typically maintain a counter that is incremented for each request so that there will be over two billion requests before a message ID needs to be reused.

The Protocol Operation

The protocolOp element of an LDAPMessage sequence provides the main content of the request or response. It encapsulates all of the details about the request or response, and therefore its format varies based on the content of that request or response. For example, a DeleteRequest protocol operation includes the DN of the entry to delete, while a SearchRequest protocol operation includes elements that specify the base DN, scope, filter, and other components of the search request.

We’ll look at each type of protocolOp element in detail in subsequent sections.

The Controls

An LDAPMessage sequence may optionally include a set of controls that provide additional information about the operation.

If a control is included in a request, then it may provide additional information about how the server should process that request. For example, an LDAP message that contains a search request protocol operation may also include a server-side sort request control to indicate that the client wants the server to return the results to the client in a particular order.

If a control is included in a response, then it may provide additional information to the client about the way that the operation was processed. Response controls are often (but not always) triggered by a corresponding request control. For example, if a search request message includes a server-side sort request control, then the corresponding search result done message may include a server-side sort response control with information about whether it was able to successfully sort the results.

The Controls type is defined in RFC 4511 section 4.1.11, as:

Controls ::= SEQUENCE OF control Control

Control ::= SEQUENCE {
     controlType             LDAPOID,
     criticality             BOOLEAN DEFAULT FALSE,
     controlValue            OCTET STRING OPTIONAL }

The LDAPOID type is defined in RFC 4511 section 4.1.2 as:

LDAPOID ::= OCTET STRING -- Constrained to <numericoid>
           -- [RFC4512]

That is, if the optional controls element is present in an LDAPMessage, then it will be a sequence of sequences, each of which contains up to three elements. Those elements are:

  • controlType — An object identifier (OID) that identifies the type of control. An OID is formatted as groups of digits separated by periods such that the OID cannot start or end with a period and cannot contain consecutive periods. For example, the OID that identifies the server-side sort request control is “1.2.840.113556.1.4.473”.
  • criticality — A Boolean value that indicates whether the control should be considered critical to the processing of the operation. The criticality is really only applicable to request messages, and it indicates how the server should behave if it can’t perform the processing indicated by the control (for example, because the server doesn’t recognize the control provided by the client, because the control isn’t appropriate for the type of request, because the client doesn’t have permission to use the requested control, or because the server just can’t do what the client was asking). If the criticality is true and the server can’t perform the processing indicated by the control, then the server should reject the operation with an UNAVAILABLE_CRITICAL_EXTENSION result. If the criticality is false and the server can’t perform the processing indicated by the control, then the server should behave as if the request message simply did not include that request control.
  • controlValue — An optional value that contains additional information for use in processing the control. In some cases, the OID is all that is needed to convey all of the meaning of the control, but it’s often necessary to provide additional information, and in those cases that additional information is encoded in the controlValue element. For example, in the server-side sort request control, the OID is enough to indicate that the client wants the server to sort the results, but the value is needed to specify the sort order. If a control has a value, then the way that value is encoded varies from one control to another.

An Example Encoded LDAP Message

Even though we haven’t yet gone into any detail on any of the different types of protocol operations or controls, it may still be helpful to provide an example of an encoded LDAP message. We’ll provide many more examples in later sections, but we’ll start here with a simple one: an LDAP message with a message ID of five that requests deleting the dc=example,dc=com entry and includes a subtree delete request control to indicate that the server should also remove any entries that exist below the target entry.

The delete request protocol operation is defined as follows in RFC 4511 section 4.8:

DelRequest ::= [APPLICATION 10] LDAPDN

where LDAPDN is defined in RFC 4511 section 4.1.3 as:

           -- Constrained to <distinguishedName> [RFC4514]

and LDAPString is defined in RFC 4511 section 4.1.2 as:

LDAPString ::= OCTET STRING -- UTF-8 encoded,
              -- [ISO10646] characters

So basically, a delete request protocol operation is simply encoded as an octet string with a BER type of application-specific ten (0x4a) whose value is the string representation of the DN of the entry to delete.

A subtree delete request control is defined in draft-armijo-ldap-treedelete. In this specification, the control has an OID of “1.2.840.113556.1.4.805” and no value. The draft states that the criticality may be either true or false, so in this case we’ll make it true so that the operation will fail if the server can’t process the subtree delete, which will allow us to differentiate between a problem in the server’s support for the subtree delete control and some other problem in processing.

With all of this in mind, the encoded representation of the LDAP message containing the delete request described above is:

30 35 02 01 05 4a 11 64 63 3d 65 78 61 6d 70 6c
65 2c 64 63 3d 63 6f 6d a0 1d 30 1b 04 16 31 2e
32 2e 38 34 30 2e 31 31 33 35 35 36 2e 31 2e 34
2e 38 30 35 01 01 ff

And here’s a formatted version with comments to make it easier to interpret. For subsequent sections, only the formatted representation will be provided.

30 35 -- Begin the LDAPMessage sequence
   02 01 05 -- The message ID (integer value 5)
   4a 11 64 63 3d 65 78 61 6d 70 -- The delete request protocol op
         6c 65 2c 64 63 3d 63 6f -- (octet string
         6d                      -- dc=example,dc=com)
   a0 1d -- Begin the sequence for the set of controls
      30 1b -- Begin the sequence for the first control
         04 16 31 2e 32 2e 38 34 30 2e -- The control OID
 31 31 33 35 35 36 2e 31 -- (octet string
 2e 34 2e 38 30 35       -- 1.2.840.113556.1.4.805)
         01 01 ff -- The control criticality (Boolean true)

Previous: The ASN.1 Basic Encoding Rules Next: The LDAPResult Sequence