From 563fc272a36c8be317fbe95c8308ca2dfa29c3aa Mon Sep 17 00:00:00 2001 From: Quentin Dufour Date: Wed, 7 Jul 2021 01:49:33 +0200 Subject: Vendor goldap, fix ASN.1 BER integer and length encoding - Add tests for goldap to prevent regressions - Disable reconnection for our functional tests --- .drone.yml | 26 +- .gitignore | 1 + go.mod | 2 +- go.sum | 12 +- goldap/abandon_request.go | 25 + goldap/add_request.go | 53 + goldap/add_response.go | 28 + goldap/asn1.go | 761 +++++++++ goldap/asn1_test.go | 54 + goldap/assertion_value.go | 44 + goldap/attribute.go | 40 + goldap/attribute_description.go | 50 + goldap/attribute_list.go | 43 + goldap/attribute_selection.go | 46 + goldap/attribute_value.go | 24 + goldap/attribute_value_assertion.go | 77 + goldap/authentication_choice.go | 37 + goldap/bind_request.go | 93 ++ goldap/bind_response.go | 56 + goldap/boolean.go | 62 + goldap/bytes.go | 199 +++ goldap/bytes_test.go | 172 ++ goldap/compare_request.go | 53 + goldap/compare_response.go | 29 + goldap/control.go | 94 ++ goldap/controls.go | 44 + goldap/del_request.go | 24 + goldap/del_response.go | 29 + goldap/dn.go | 60 + goldap/enumerated.go | 34 + goldap/error.go | 9 + goldap/extended_request.go | 69 + goldap/extended_response.go | 85 + goldap/filter.go | 70 + goldap/filter_and.go | 54 + goldap/filter_approx_match.go | 34 + goldap/filter_equality_match.go | 34 + goldap/filter_extensible_match.go | 28 + goldap/filter_greater_or_equal.go | 34 + goldap/filter_less_or_equal.go | 34 + goldap/filter_not.go | 40 + goldap/filter_or.go | 52 + goldap/filter_present.go | 28 + goldap/filter_substring.go | 187 +++ goldap/integer.go | 53 + goldap/intermediate_response.go | 89 + goldap/matching_rule_assertion.go | 117 ++ goldap/matching_rule_id.go | 29 + goldap/message.go | 139 ++ goldap/message_id.go | 46 + goldap/message_test.go | 76 + goldap/modify_dn_request.go | 87 + goldap/modify_dn_response.go | 28 + goldap/modify_request.go | 89 + goldap/modify_request_change.go | 43 + goldap/modify_response.go | 36 + goldap/octetstring.go | 44 + goldap/oid.go | 50 + goldap/partial_attribute.go | 76 + goldap/partial_attribute_list.go | 53 + goldap/protocol_op.go | 63 + goldap/read.go | 18 + goldap/read_error_test.go | 3146 +++++++++++++++++++++++++++++++++++ goldap/read_test.go | 2936 ++++++++++++++++++++++++++++++++ goldap/referral.go | 50 + goldap/relative_ldap_dn.go | 15 + goldap/result.go | 282 ++++ goldap/sasl_credentials.go | 63 + goldap/search_request.go | 246 +++ goldap/search_result_done.go | 31 + goldap/search_result_entry.go | 58 + goldap/search_result_reference.go | 53 + goldap/size_test.go | 85 + goldap/string.go | 38 + goldap/struct.go | 739 ++++++++ goldap/struct_methods.go | 7 + goldap/unbind_request.go | 38 + goldap/uri.go | 32 + goldap/write.go | 6 + goldap/write_test.go | 19 + ldapserver/client.go | 8 +- ldapserver/constants.go | 2 +- ldapserver/message.go | 2 +- ldapserver/packet.go | 3 +- ldapserver/responsemessage.go | 2 +- ldapserver/route.go | 2 +- main.go | 19 +- read.go | 2 +- test/bottin_test.go | 160 ++ test/config.json.test | 13 + test/create.go | 281 ++++ test/functionTest.go | 173 ++ test/go.mod | 8 + test/go.sum | 26 + test/handler.go | 141 ++ test/request.go | 59 + test/runner.sh | 16 + write.go | 2 +- 98 files changed, 12902 insertions(+), 27 deletions(-) create mode 100644 goldap/abandon_request.go create mode 100644 goldap/add_request.go create mode 100644 goldap/add_response.go create mode 100644 goldap/asn1.go create mode 100644 goldap/asn1_test.go create mode 100644 goldap/assertion_value.go create mode 100644 goldap/attribute.go create mode 100644 goldap/attribute_description.go create mode 100644 goldap/attribute_list.go create mode 100644 goldap/attribute_selection.go create mode 100644 goldap/attribute_value.go create mode 100644 goldap/attribute_value_assertion.go create mode 100644 goldap/authentication_choice.go create mode 100644 goldap/bind_request.go create mode 100644 goldap/bind_response.go create mode 100644 goldap/boolean.go create mode 100644 goldap/bytes.go create mode 100644 goldap/bytes_test.go create mode 100644 goldap/compare_request.go create mode 100644 goldap/compare_response.go create mode 100644 goldap/control.go create mode 100644 goldap/controls.go create mode 100644 goldap/del_request.go create mode 100644 goldap/del_response.go create mode 100644 goldap/dn.go create mode 100644 goldap/enumerated.go create mode 100644 goldap/error.go create mode 100644 goldap/extended_request.go create mode 100644 goldap/extended_response.go create mode 100644 goldap/filter.go create mode 100644 goldap/filter_and.go create mode 100644 goldap/filter_approx_match.go create mode 100644 goldap/filter_equality_match.go create mode 100644 goldap/filter_extensible_match.go create mode 100644 goldap/filter_greater_or_equal.go create mode 100644 goldap/filter_less_or_equal.go create mode 100644 goldap/filter_not.go create mode 100644 goldap/filter_or.go create mode 100644 goldap/filter_present.go create mode 100644 goldap/filter_substring.go create mode 100644 goldap/integer.go create mode 100644 goldap/intermediate_response.go create mode 100644 goldap/matching_rule_assertion.go create mode 100644 goldap/matching_rule_id.go create mode 100644 goldap/message.go create mode 100644 goldap/message_id.go create mode 100644 goldap/message_test.go create mode 100644 goldap/modify_dn_request.go create mode 100644 goldap/modify_dn_response.go create mode 100644 goldap/modify_request.go create mode 100644 goldap/modify_request_change.go create mode 100644 goldap/modify_response.go create mode 100644 goldap/octetstring.go create mode 100644 goldap/oid.go create mode 100644 goldap/partial_attribute.go create mode 100644 goldap/partial_attribute_list.go create mode 100644 goldap/protocol_op.go create mode 100644 goldap/read.go create mode 100644 goldap/read_error_test.go create mode 100644 goldap/read_test.go create mode 100644 goldap/referral.go create mode 100644 goldap/relative_ldap_dn.go create mode 100644 goldap/result.go create mode 100644 goldap/sasl_credentials.go create mode 100644 goldap/search_request.go create mode 100644 goldap/search_result_done.go create mode 100644 goldap/search_result_entry.go create mode 100644 goldap/search_result_reference.go create mode 100644 goldap/size_test.go create mode 100644 goldap/string.go create mode 100644 goldap/struct.go create mode 100644 goldap/struct_methods.go create mode 100644 goldap/unbind_request.go create mode 100644 goldap/uri.go create mode 100644 goldap/write.go create mode 100644 goldap/write_test.go create mode 100644 test/bottin_test.go create mode 100644 test/config.json.test create mode 100644 test/create.go create mode 100644 test/functionTest.go create mode 100644 test/go.mod create mode 100644 test/go.sum create mode 100644 test/handler.go create mode 100644 test/request.go create mode 100755 test/runner.sh diff --git a/.drone.yml b/.drone.yml index 9eea880..c7fb744 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,13 +1,25 @@ --- -pipeline: - build: - image: golang:stretch - commands: - - go get -d -v - - go build -v +kind: pipeline +name: bottin + +steps: +- name: build + image: golang:stretch + commands: + - go get -d -v + - go build -v + - cd test + - go test -i -c -o test + +- name: test_bottin + image: consul:latest + environment: + BOTTIN_DEFAULT_ADMIN_PW: priZ4Cg0x5NkSyiIN/MpvWw4ZEy8f8s1 + commands: + - ash test/runner.sh --- kind: signature -hmac: 8f49fdf0e4abb0790827eed7cac8eedd5e11705d1fa01954a84929933eb7b254 +hmac: ff246a04c3df8a2f39c8b446dea920622d61950e6caaac886931bdb05d0706ed ... diff --git a/.gitignore b/.gitignore index d739b6f..2a2f53c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ bottin bottin.static config.json +test/test diff --git a/go.mod b/go.mod index 75156e0..11a9d2c 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,6 @@ go 1.13 require ( github.com/google/uuid v1.1.1 github.com/hashicorp/consul/api v1.3.0 - github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 github.com/sirupsen/logrus v1.4.2 + golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect ) diff --git a/go.sum b/go.sum index c2db0e1..a46b500 100644 --- a/go.sum +++ b/go.sum @@ -44,8 +44,6 @@ github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A= -github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= @@ -77,14 +75,20 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3 h1:KYQXGkl6vs02hK7pK4eIbw0NpNPedieTSTEiJ//bwGs= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc h1:a3CU5tJYVj92DY2LaA1kUkrsqD5/3mLDhx2NcNqyW+0= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/goldap/abandon_request.go b/goldap/abandon_request.go new file mode 100644 index 0000000..519641c --- /dev/null +++ b/goldap/abandon_request.go @@ -0,0 +1,25 @@ +package message + +import "fmt" + +// +// AbandonRequest ::= [APPLICATION 16] MessageID + +func readAbandonRequest(bytes *Bytes) (ret AbandonRequest, err error) { + var mes MessageID + mes, err = readTaggedMessageID(bytes, classApplication, TagAbandonRequest) + if err != nil { + err = LdapError{fmt.Sprintf("readAbandonRequest:\n%s", err.Error())} + return + } + ret = AbandonRequest(mes) + return +} + +func (abandon AbandonRequest) size() int { + return MessageID(abandon).sizeTagged(TagAbandonRequest) +} + +func (abandon AbandonRequest) write(bytes *Bytes) int { + return MessageID(abandon).writeTagged(bytes, classApplication, TagAbandonRequest) +} diff --git a/goldap/add_request.go b/goldap/add_request.go new file mode 100644 index 0000000..1ba4094 --- /dev/null +++ b/goldap/add_request.go @@ -0,0 +1,53 @@ +package message + +import "fmt" + +// +// AddRequest ::= [APPLICATION 8] SEQUENCE { +// entry LDAPDN, +// attributes AttributeList } + +func (add *AddRequest) Entry() LDAPDN { + return add.entry +} + +func (add *AddRequest) Attributes() AttributeList { + return add.attributes +} + +func readAddRequest(bytes *Bytes) (ret AddRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagAddRequest, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readAddRequest:\n%s", err.Error())} + return + } + return +} + +func (add *AddRequest) readComponents(bytes *Bytes) (err error) { + add.entry, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + add.attributes, err = readAttributeList(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +func (add AddRequest) size() (size int) { + size += add.entry.size() + size += add.attributes.size() + size += sizeTagAndLength(TagAddRequest, size) + return +} + +func (add AddRequest) write(bytes *Bytes) (size int) { + size += add.attributes.write(bytes) + size += add.entry.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagAddRequest, size) + return +} diff --git a/goldap/add_response.go b/goldap/add_response.go new file mode 100644 index 0000000..ab7298d --- /dev/null +++ b/goldap/add_response.go @@ -0,0 +1,28 @@ +package message + +import "fmt" + +// +// AddResponse ::= [APPLICATION 9] LDAPResult + +func (l *AddResponse) SetResultCode(code int) { + l.resultCode = ENUMERATED(code) +} + +func readAddResponse(bytes *Bytes) (ret AddResponse, err error) { + var res LDAPResult + res, err = readTaggedLDAPResult(bytes, classApplication, TagAddResponse) + if err != nil { + err = LdapError{fmt.Sprintf("readAddResponse:\n%s", err.Error())} + return + } + ret = AddResponse(res) + return +} + +func (a AddResponse) size() int { + return LDAPResult(a).sizeTagged(TagAddResponse) +} +func (a AddResponse) write(bytes *Bytes) int { + return LDAPResult(a).writeTagged(bytes, classApplication, TagAddResponse) +} diff --git a/goldap/asn1.go b/goldap/asn1.go new file mode 100644 index 0000000..3e7dc76 --- /dev/null +++ b/goldap/asn1.go @@ -0,0 +1,761 @@ +package message + +// Below code is largely inspired from the standard golang library encoding/asn +// If put BEGIN / END tags in the comments to give the original library name +import ( + // "errors" + "fmt" + "math/big" + // "strconv" + // "time" +) + +// +// BEGIN: encoding/asn1/common.go +// + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +const ( + tagBoolean = 1 + tagInteger = 2 + // tagBitString = 3 + tagOctetString = 4 + // tagOID = 6 + tagEnum = 10 + // tagUTF8String = 12 + tagSequence = 16 + tagSet = 17 + // tagPrintableString = 19 + // tagT61String = 20 + // tagIA5String = 22 + // tagUTCTime = 23 + // tagGeneralizedTime = 24 + tagGeneralString = 27 +) + +var tagNames = map[int]string{ + tagBoolean: "BOOLEAN", + tagInteger: "INTEGER", + tagOctetString: "OCTET STRING", + tagEnum: "ENUM", + tagSequence: "SEQUENCE", + tagSet: "SET", +} + +const ( + classUniversal = 0 + classApplication = 1 + classContextSpecific = 2 + // classPrivate = 3 +) + +var classNames = map[int]string{ + classUniversal: "UNIVERSAL", + classApplication: "APPLICATION", + classContextSpecific: "CONTEXT SPECIFIC", +} + +const ( + isCompound = true + isNotCompound = false +) + +var compoundNames = map[bool]string{ + isCompound: "COMPOUND", + isNotCompound: "NOT COMPOUND", +} + +type TagAndLength struct { + Class, Tag, Length int + IsCompound bool +} + +// +// END: encoding/asn1/common.go +// + +func (t *TagAndLength) Expect(class int, tag int, isCompound bool) (err error) { + err = t.ExpectClass(class) + if err != nil { + return LdapError{fmt.Sprintf("Expect: %s.", err)} + } + err = t.ExpectTag(tag) + if err != nil { + return LdapError{fmt.Sprintf("Expect: %s.", err)} + } + err = t.ExpectCompound(isCompound) + if err != nil { + return LdapError{fmt.Sprintf("Expect: %s.", err)} + } + return +} +func (t *TagAndLength) ExpectClass(class int) (err error) { + if class != t.Class { + err = SyntaxError{fmt.Sprintf("ExpectClass: wrong tag class: got %d (%s), expected %d (%s)", t.Class, classNames[t.Class], class, classNames[class])} + } + return +} +func (t *TagAndLength) ExpectTag(tag int) (err error) { + if tag != t.Tag { + err = SyntaxError{fmt.Sprintf("ExpectTag: wrong tag value: got %d (%s), expected %d (%s)", t.Tag, tagNames[t.Tag], tag, tagNames[tag])} + } + return +} +func (t *TagAndLength) ExpectCompound(isCompound bool) (err error) { + if isCompound != t.IsCompound { + err = SyntaxError{fmt.Sprintf("ExpectCompound: wrong tag compound: got %t (%s), expected %t (%s)", t.IsCompound, compoundNames[t.IsCompound], isCompound, compoundNames[isCompound])} + } + return +} + +func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) { + ret, offset, err = parseTagAndLength(bytes, initOffset) + return +} + +// +// BEGIN encoding/asn1/asn1.go +// + +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package asn1 implements parsing of DER-encoded ASN.1 data structures, +// as defined in ITU-T Rec X.690. +// +// See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' +// http://luca.ntop.org/Teaching/Appunti/asn1.html. +// package asn1 + +// ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc +// are different encoding formats for those objects. Here, we'll be dealing +// with DER, the Distinguished Encoding Rules. DER is used in X.509 because +// it's fast to parse and, unlike BER, has a unique encoding for every object. +// When calculating hashes over objects, it's important that the resulting +// bytes be the same at both ends and DER removes this margin of error. +// +// ASN.1 is very complex and this package doesn't attempt to implement +// everything by any means. + +//import ( +// "fmt" +// "math/big" +// "reflect" +// "strconv" +// "time" +//) + +// A StructuralError suggests that the ASN.1 data is valid, but the Go type +// which is receiving it doesn't match. +type StructuralError struct { + Msg string +} + +func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg } + +// A SyntaxError suggests that the ASN.1 data is invalid. +type SyntaxError struct { + Msg string +} + +func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg } + +// We start by dealing with each of the primitive types in turn. + +// BOOLEAN + +func parseBool(bytes []byte) (ret bool, err error) { + if len(bytes) > 1 { + err = SyntaxError{"invalid boolean: should be encoded on one byte only"} + return + } else if len(bytes) == 0 { + err = SyntaxError{"invalid boolean: no data to read"} + } + + // DER demands that "If the encoding represents the boolean value TRUE, + // its single contents octet shall have all eight bits set to one." + // Thus only 0 and 255 are valid encoded values. + switch bytes[0] { + case 0: + ret = false + case 0xff: + ret = true + default: + err = SyntaxError{"invalid boolean: should be 0x00 of 0xFF"} + } + + return +} + +func sizeBool(b bool) int { + return 1 +} + +func writeBool(bytes *Bytes, b bool) int { + if b == false { + return bytes.writeBytes([]byte{0x00}) + } else { + return bytes.writeBytes([]byte{0xff}) + } +} + +// INTEGER + +// parseInt64 treats the given bytes as a big-endian, signed integer and +// returns the result. +func parseInt64(bytes []byte) (ret int64, err error) { + if len(bytes) > 8 { + // We'll overflow an int64 in this case. + err = StructuralError{"integer too large"} + return + } + for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { + ret <<= 8 + ret |= int64(bytes[bytesRead]) + } + + // Shift up and down in order to sign extend the result. + ret <<= 64 - uint8(len(bytes))*8 + ret >>= 64 - uint8(len(bytes))*8 + return +} + +func sizeInt64(i int64) (size int) { + n := 1 + + for i > 127 { + n++ + i >>= 8 + } + + for i < -128 { + n++ + i >>= 8 + } + + return n +} + +func writeInt64(bytes *Bytes, i int64) (size int) { + n := sizeInt64(i) + buf := [8]byte{} + + for j := 0; j < n; j++ { + b := i >> uint((n-1-j)*8) + buf[j] = byte(b) + } + bytes.writeBytes(buf[:n]) + + return n +} + +// parseInt treats the given bytes as a big-endian, signed integer and returns +// the result. +func parseInt32(bytes []byte) (int32, error) { + ret64, err := parseInt64(bytes) + if err != nil { + return 0, err + } + if ret64 != int64(int32(ret64)) { + return 0, StructuralError{"integer too large"} + } + return int32(ret64), nil +} + +func sizeInt32(i int32) int { + return sizeInt64(int64(i)) +} + +func writeInt32(bytes *Bytes, i int32) int { + return writeInt64(bytes, int64(i)) +} + +var bigOne = big.NewInt(1) + +// // parseBigInt treats the given bytes as a big-endian, signed integer and returns +// // the result. +// func parseBigInt(bytes []byte) *big.Int { +// ret := new(big.Int) +// if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { +// // This is a negative number. +// notBytes := make([]byte, len(bytes)) +// for i := range notBytes { +// notBytes[i] = ^bytes[i] +// } +// ret.SetBytes(notBytes) +// ret.Add(ret, bigOne) +// ret.Neg(ret) +// return ret +// } +// ret.SetBytes(bytes) +// return ret +// } + +// // BIT STRING + +// // BitString is the structure to use when you want an ASN.1 BIT STRING type. A +// // bit string is padded up to the nearest byte in memory and the number of +// // valid bits is recorded. Padding bits will be zero. +// type BitString struct { +// Bytes []byte // bits packed into bytes. +// BitLength int // length in bits. +// } + +// // At returns the bit at the given index. If the index is out of range it +// // returns false. +// func (b BitString) At(i int) int { +// if i < 0 || i >= b.BitLength { +// return 0 +// } +// x := i / 8 +// y := 7 - uint(i%8) +// return int(b.Bytes[x]>>y) & 1 +// } + +// // RightAlign returns a slice where the padding bits are at the beginning. The +// // slice may share memory with the BitString. +// func (b BitString) RightAlign() []byte { +// shift := uint(8 - (b.BitLength % 8)) +// if shift == 8 || len(b.Bytes) == 0 { +// return b.Bytes +// } + +// a := make([]byte, len(b.Bytes)) +// a[0] = b.Bytes[0] >> shift +// for i := 1; i < len(b.Bytes); i++ { +// a[i] = b.Bytes[i-1] << (8 - shift) +// a[i] |= b.Bytes[i] >> shift +// } + +// return a +// } + +// // parseBitString parses an ASN.1 bit string from the given byte slice and returns it. +// func parseBitString(bytes []byte) (ret BitString, err error) { +// if len(bytes) == 0 { +// err = SyntaxError{"zero length BIT STRING"} +// return +// } +// paddingBits := int(bytes[0]) +// if paddingBits > 7 || +// len(bytes) == 1 && paddingBits > 0 || +// bytes[len(bytes)-1]&((1< 0 { +// s += "." +// } +// s += strconv.Itoa(v) +// } + +// return s +// } + +// // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and +// // returns it. An object identifier is a sequence of variable length integers +// // that are assigned in a hierarchy. +// func parseObjectIdentifier(bytes []byte) (s []int, err error) { +// if len(bytes) == 0 { +// err = SyntaxError{"zero length OBJECT IDENTIFIER"} +// return +// } + +// // In the worst case, we get two elements from the first byte (which is +// // encoded differently) and then every varint is a single byte long. +// s = make([]int, len(bytes)+1) + +// // The first varint is 40*value1 + value2: +// // According to this packing, value1 can take the values 0, 1 and 2 only. +// // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, +// // then there are no restrictions on value2. +// v, offset, err := parseBase128Int(bytes, 0) +// if err != nil { +// return +// } +// if v < 80 { +// s[0] = v / 40 +// s[1] = v % 40 +// } else { +// s[0] = 2 +// s[1] = v - 80 +// } + +// i := 2 +// for ; offset < len(bytes); i++ { +// v, offset, err = parseBase128Int(bytes, offset) +// if err != nil { +// return +// } +// s[i] = v +// } +// s = s[0:i] +// return +// } + +// ENUMERATED + +// An Enumerated is represented as a plain int. +type Enumerated int + +// FLAG + +// A Flag accepts any data and is set to true if present. +type Flag bool + +// parseBase128Int parses a base-128 encoded int from the given offset in the +// given byte slice. It returns the value and the new offset. +func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) { + offset = initOffset + for shifted := 0; offset < len(bytes); shifted++ { + if shifted > 4 { + err = StructuralError{"base 128 integer too large"} + return + } + ret <<= 7 + b := bytes[offset] + ret |= int(b & 0x7f) + offset++ + if b&0x80 == 0 { + return + } + } + err = SyntaxError{"truncated base 128 integer"} + return +} + +func sizeBase128Int(value int) (size int) { + for i := value; i > 0; i >>= 7 { + size++ + } + return +} + +// Write start as the end of the slice and goes back +// We assume we have enough size +func writeBase128Int(bytes *Bytes, value int) (size int) { + for ; value > 0 || size == 0; value >>= 7 { // Write at least one byte even if the value is 0 + // Get the 7 lowest bits + b := byte(value) & 0x7f + if value < 128 { + b |= 0x80 + } + bytes.writeBytes([]byte{b}) + size++ + } + return +} + +// // UTCTime + +// func parseUTCTime(bytes []byte) (ret time.Time, err error) { +// s := string(bytes) +// ret, err = time.Parse("0601021504Z0700", s) +// if err != nil { +// ret, err = time.Parse("060102150405Z0700", s) +// } +// if err == nil && ret.Year() >= 2050 { +// // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 +// ret = ret.AddDate(-100, 0, 0) +// } + +// return +// } + +// // parseGeneralizedTime parses the GeneralizedTime from the given byte slice +// // and returns the resulting time. +// func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { +// return time.Parse("20060102150405Z0700", string(bytes)) +// } + +// // PrintableString + +// // parsePrintableString parses a ASN.1 PrintableString from the given byte +// // array and returns it. +// func parsePrintableString(bytes []byte) (ret string, err error) { +// for _, b := range bytes { +// if !isPrintable(b) { +// err = SyntaxError{"PrintableString contains invalid character"} +// return +// } +// } +// ret = string(bytes) +// return +// } + +// // isPrintable returns true iff the given b is in the ASN.1 PrintableString set. +// func isPrintable(b byte) bool { +// return 'a' <= b && b <= 'z' || +// 'A' <= b && b <= 'Z' || +// '0' <= b && b <= '9' || +// '\'' <= b && b <= ')' || +// '+' <= b && b <= '/' || +// b == ' ' || +// b == ':' || +// b == '=' || +// b == '?' || +// // This is technically not allowed in a PrintableString. +// // However, x509 certificates with wildcard strings don't +// // always use the correct string type so we permit it. +// b == '*' +// } + +// // IA5String + +// // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given +// // byte slice and returns it. +// func parseIA5String(bytes []byte) (ret string, err error) { +// for _, b := range bytes { +// if b >= 0x80 { +// err = SyntaxError{"IA5String contains invalid character"} +// return +// } +// } +// ret = string(bytes) +// return +// } + +// // T61String + +// // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given +// // byte slice and returns it. +// func parseT61String(bytes []byte) (ret string, err error) { +// return string(bytes), nil +// } + +// UTF8String + +// parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte +// array and returns it. +// func parseUTF8String(bytes []byte) (ret string, err error) { +// return string(bytes), nil +// } +// func sizeUTF8String(s string) int { +// return len(s) +// } +// func writeUTF8String(bytes *Bytes, s string) int { +// return bytes.writeString(s) +// } + +// Octet string +func parseOctetString(bytes []byte) (ret []byte, err error) { + return bytes, nil +} +func sizeOctetString(s []byte) int { + return len(s) +} +func writeOctetString(bytes *Bytes, s []byte) int { + return bytes.writeBytes(s) +} + +// A RawValue represents an undecoded ASN.1 object. +type RawValue struct { + Class, Tag int + IsCompound bool + Bytes []byte + FullBytes []byte // includes the tag and length +} + +// RawContent is used to signal that the undecoded, DER data needs to be +// preserved for a struct. To use it, the first field of the struct must have +// this type. It's an error for any of the other fields to have this type. +type RawContent []byte + +// Tagging + +// parseTagAndLength parses an ASN.1 tag and length pair from the given offset +// into a byte slice. It returns the parsed data and the new offset. SET and +// SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we +// don't distinguish between ordered and unordered objects in this code. +func parseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) { + offset = initOffset + b := bytes[offset] + offset++ + ret.Class = int(b >> 6) + ret.IsCompound = b&0x20 == 0x20 + ret.Tag = int(b & 0x1f) + + // If the bottom five bits are set, then the tag number is actually base 128 + // encoded afterwards + if ret.Tag == 0x1f { + ret.Tag, offset, err = parseBase128Int(bytes, offset) + if err != nil { + return + } + } + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if b&0x80 == 0 { + // The length is encoded in the bottom 7 bits. + ret.Length = int(b & 0x7f) + } else { + // Bottom 7 bits give the number of length bytes to follow. + numBytes := int(b & 0x7f) + if numBytes == 0 { + err = SyntaxError{"indefinite length found (not DER)"} + return + } + ret.Length = 0 + for i := 0; i < numBytes; i++ { + if offset >= len(bytes) { + err = SyntaxError{"truncated tag or length"} + return + } + b = bytes[offset] + offset++ + if ret.Length >= 1<<23 { + // We can't shift ret.length up without + // overflowing. + err = StructuralError{"length too large"} + return + } + ret.Length <<= 8 + ret.Length |= int(b) + if ret.Length == 0 { + // DER requires that lengths be minimal. + err = StructuralError{"superfluous leading zeros in length"} + return + } + } + } + + return +} + +// func writeTagAndLength(out *forkableWriter, t tagAndLength) (err error) { +// b := uint8(t.class) << 6 +// if t.isCompound { +// b |= 0x20 +// } +// if t.tag >= 31 { +// b |= 0x1f +// err = out.WriteByte(b) +// if err != nil { +// return +// } +// err = marshalBase128Int(out, int64(t.tag)) +// if err != nil { +// return +// } +// } else { +// b |= uint8(t.tag) +// err = out.WriteByte(b) +// if err != nil { +// return +// } +// } + +// if t.length >= 128 { +// l := lengthLength(t.length) +// err = out.WriteByte(0x80 | byte(l)) +// if err != nil { +// return +// } +// err = marshalLength(out, t.length) +// if err != nil { +// return +// } +// } else { +// err = out.WriteByte(byte(t.length)) +// if err != nil { +// return +// } +// } + +// return nil +// } + +func sizeTagAndLength(tag int, length int) (size int) { + // Compute the size of the tag + size = 1 + if tag >= 31 { + // Long-form identifier if the tag is greater than 30 + // http://en.wikipedia.org/wiki/X.690#Identifier_tags_greater_than_30 + size += sizeBase128Int(tag) + } + // Compute the size of the length using the definite form + // http://en.wikipedia.org/wiki/X.690#The_definite_form + size += 1 + if length >= 128 { + size += 1 + for length > 255 { + size++ + length >>= 8 + } + } + return +} + +func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) { + // We are writing backward, so write the length bytes first + if t.Length < 0 { + panic("Can't have a negative length") + + } else if t.Length >= 128 { + //lengthBytes := writeInt64(bytes, int64(t.Length)) + lengthBytes := 0 + val := t.Length + for val > 0 { + lengthBytes++ + bytes.writeBytes([]byte{byte(val & 0xff)}) + val >>= 8 + } + bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))}) + size += lengthBytes + 1 + + } else if t.Length < 128 { + size += bytes.writeBytes([]byte{byte(t.Length)}) + } + // Then write the tag + b := uint8(t.Class) << 6 + if t.IsCompound { + b |= 0x20 + } + if t.Tag >= 31 { + b |= 0x1f + size += writeBase128Int(bytes, t.Tag) + } else { + b |= uint8(t.Tag) + } + size += bytes.writeBytes([]byte{byte(b)}) + return +} + +// +// END encoding/asn1/asn1.go +// diff --git a/goldap/asn1_test.go b/goldap/asn1_test.go new file mode 100644 index 0000000..eb11d29 --- /dev/null +++ b/goldap/asn1_test.go @@ -0,0 +1,54 @@ +package message + +import ( + "testing" + "bytes" +) + +func TestSizeInt64(t *testing.T) { + s := sizeInt64(0) + if s != 1 { + t.Errorf("computed size is %d, expected 1", s) + } + + s = sizeInt64(127) + if s != 1 { + t.Errorf("computed size is %d, expected 1", s) + } + + s = sizeInt64(128) + if s != 2 { + t.Errorf("computed size is %d, expected 2", s) + } + + s = sizeInt64(50000) + if s != 3 { + t.Errorf("computed size is %d, expected 3", s) + } + + s = sizeInt64(-12345) + if s != 2 { + t.Errorf("computed size is %d, expected 2", s) + } +} + +func TestWriteInt64(t *testing.T) { + vtests := []int64{0, 127, 128, 50000, -12345} + expsize := []int{1, 1, 2, 3, 2} + expresult := [][]byte{{0x00}, {0x7F}, {0x00, 0x80}, {0x00, 0xc3, 0x50}, {0xcf, 0xc7}} + + for idx, v := range vtests { + fs := sizeInt64(v) + b := NewBytes(fs, make([]byte, fs)) + t.Log("computing", v) + s := writeInt64(b, v) + if s != expsize[idx] { + t.Errorf("computed size is %d, expected %d", s, expsize[idx]) + } + if !bytes.Equal(b.Bytes(), expresult[idx]) { + t.Errorf("wrong computed bytes, got %v, expected %v", b.Bytes(), expresult[idx]) + } + a, e := parseInt64(b.Bytes()) + t.Log("parse", a, e) + } +} diff --git a/goldap/assertion_value.go b/goldap/assertion_value.go new file mode 100644 index 0000000..89706a0 --- /dev/null +++ b/goldap/assertion_value.go @@ -0,0 +1,44 @@ +package message + +import "fmt" + +// +// AssertionValue ::= OCTET STRING + +func readAssertionValue(bytes *Bytes) (assertionvalue AssertionValue, err error) { + var octetstring OCTETSTRING + octetstring, err = readOCTETSTRING(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readAssertionValue:\n%s", err.Error())} + return + } + assertionvalue = AssertionValue(octetstring) + return +} + +func readTaggedAssertionValue(bytes *Bytes, class int, tag int) (assertionvalue AssertionValue, err error) { + var octetstring OCTETSTRING + octetstring, err = readTaggedOCTETSTRING(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedAssertionValue:\n%s", err.Error())} + return + } + assertionvalue = AssertionValue(octetstring) + return +} + +func (assertion AssertionValue) size() int { + return OCTETSTRING(assertion).size() +} + +func (assertion AssertionValue) sizeTagged(tag int) int { + return OCTETSTRING(assertion).sizeTagged(tag) +} + +func (assertion AssertionValue) write(bytes *Bytes) int { + return OCTETSTRING(assertion).write(bytes) +} + +func (assertion AssertionValue) writeTagged(bytes *Bytes, class int, tag int) int { + return OCTETSTRING(assertion).writeTagged(bytes, class, tag) +} diff --git a/goldap/attribute.go b/goldap/attribute.go new file mode 100644 index 0000000..d76b311 --- /dev/null +++ b/goldap/attribute.go @@ -0,0 +1,40 @@ +package message + +import "fmt" + +// +// Attribute ::= PartialAttribute(WITH COMPONENTS { +// ..., +// vals (SIZE(1..MAX))}) + +func (attribute *Attribute) Type_() AttributeDescription { + return attribute.type_ +} + +func (attribute *Attribute) Vals() []AttributeValue { + return attribute.vals +} + +func readAttribute(bytes *Bytes) (ret Attribute, err error) { + var par PartialAttribute + par, err = readPartialAttribute(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readAttribute:\n%s", err.Error())} + return + } + if len(par.vals) == 0 { + err = LdapError{"readAttribute: expecting at least one value"} + return + } + ret = Attribute(par) + return + +} + +func (attribute Attribute) size() (size int) { + return PartialAttribute(attribute).size() +} + +func (attribute Attribute) write(bytes *Bytes) (size int) { + return PartialAttribute(attribute).write(bytes) +} diff --git a/goldap/attribute_description.go b/goldap/attribute_description.go new file mode 100644 index 0000000..04bb03d --- /dev/null +++ b/goldap/attribute_description.go @@ -0,0 +1,50 @@ +package message + +import "fmt" + +// +// AttributeDescription ::= LDAPString +// -- Constrained to +// -- [RFC4512] + +func (description AttributeDescription) Pointer() *AttributeDescription { return &description } + +func readAttributeDescription(bytes *Bytes) (ret AttributeDescription, err error) { + var ldapstring LDAPString + ldapstring, err = readLDAPString(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readAttributeDescription:\n%s", err.Error())} + return + } + // @TODO: check RFC4512 + ret = AttributeDescription(ldapstring) + return +} + +func readTaggedAttributeDescription(bytes *Bytes, class int, tag int) (ret AttributeDescription, err error) { + var ldapstring LDAPString + ldapstring, err = readTaggedLDAPString(bytes, class, tag) + // @TODO: check RFC4512 + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedAttributeDescription:\n%s", err.Error())} + return + } + ret = AttributeDescription(ldapstring) + return +} + +func (description AttributeDescription) size() int { + return LDAPString(description).size() +} + +func (description AttributeDescription) sizeTagged(tag int) int { + return LDAPString(description).sizeTagged(tag) +} + +func (description AttributeDescription) write(bytes *Bytes) int { + return LDAPString(description).write(bytes) +} + +func (description AttributeDescription) writeTagged(bytes *Bytes, class int, tag int) int { + return LDAPString(description).writeTagged(bytes, class, tag) +} diff --git a/goldap/attribute_list.go b/goldap/attribute_list.go new file mode 100644 index 0000000..bcf753c --- /dev/null +++ b/goldap/attribute_list.go @@ -0,0 +1,43 @@ +package message + +import "fmt" + +// +// AttributeList ::= SEQUENCE OF attribute Attribute + +func readAttributeList(bytes *Bytes) (ret AttributeList, err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readAttributeList:\n%s", err.Error())} + return + } + return +} +func (list *AttributeList) readComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var attr Attribute + attr, err = readAttribute(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + *list = append(*list, attr) + } + return +} + +func (list AttributeList) size() (size int) { + for _, att := range list { + size += att.size() + } + size += sizeTagAndLength(tagSequence, size) + return +} + +func (list AttributeList) write(bytes *Bytes) (size int) { + for i := len(list) - 1; i >= 0; i-- { + size += list[i].write(bytes) + } + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} diff --git a/goldap/attribute_selection.go b/goldap/attribute_selection.go new file mode 100644 index 0000000..f092606 --- /dev/null +++ b/goldap/attribute_selection.go @@ -0,0 +1,46 @@ +package message + +import "fmt" + +// +// AttributeSelection ::= SEQUENCE OF selector LDAPString +// -- The LDAPString is constrained to +// -- in Section 4.5.1.8 + +func readAttributeSelection(bytes *Bytes) (attributeSelection AttributeSelection, err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, attributeSelection.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readAttributeSelection:\n%s", err.Error())} + return + } + return +} +func (selection *AttributeSelection) readComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var ldapstring LDAPString + ldapstring, err = readLDAPString(bytes) + // @TOTO: check in Section 4.5.1.8 + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + *selection = append(*selection, ldapstring) + } + return +} + +func (selection AttributeSelection) write(bytes *Bytes) (size int) { + for i := len(selection) - 1; i >= 0; i-- { + size += selection[i].write(bytes) + } + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} + +func (selection AttributeSelection) size() (size int) { + for _, selector := range selection { + size += selector.size() + } + size += sizeTagAndLength(tagSequence, size) + return +} diff --git a/goldap/attribute_value.go b/goldap/attribute_value.go new file mode 100644 index 0000000..34fea97 --- /dev/null +++ b/goldap/attribute_value.go @@ -0,0 +1,24 @@ +package message + +import "fmt" + +// +// AttributeValue ::= OCTET STRING + +func readAttributeValue(bytes *Bytes) (ret AttributeValue, err error) { + octetstring, err := readOCTETSTRING(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readAttributeValue:\n%s", err.Error())} + return + } + ret = AttributeValue(octetstring) + return +} + +func (value AttributeValue) write(bytes *Bytes) int { + return OCTETSTRING(value).write(bytes) +} + +func (value AttributeValue) size() int { + return OCTETSTRING(value).size() +} diff --git a/goldap/attribute_value_assertion.go b/goldap/attribute_value_assertion.go new file mode 100644 index 0000000..bb00cbd --- /dev/null +++ b/goldap/attribute_value_assertion.go @@ -0,0 +1,77 @@ +package message + +import "fmt" + +// +// AttributeValueAssertion ::= SEQUENCE { +// attributeDesc AttributeDescription, +// assertionValue AssertionValue } + +func (assertion *AttributeValueAssertion) AttributeDesc() AttributeDescription { + return assertion.attributeDesc +} + +func (assertion *AttributeValueAssertion) AssertionValue() AssertionValue { + return assertion.assertionValue +} + +func readAttributeValueAssertion(bytes *Bytes) (ret AttributeValueAssertion, err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readAttributeValueAssertion:\n%s", err.Error())} + return + } + return + +} + +func readTaggedAttributeValueAssertion(bytes *Bytes, class int, tag int) (ret AttributeValueAssertion, err error) { + err = bytes.ReadSubBytes(class, tag, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedAttributeValueAssertion:\n%s", err.Error())} + return + } + return +} + +func (assertion *AttributeValueAssertion) readComponents(bytes *Bytes) (err error) { + assertion.attributeDesc, err = readAttributeDescription(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + assertion.assertionValue, err = readAssertionValue(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +func (assertion AttributeValueAssertion) write(bytes *Bytes) (size int) { + size += assertion.assertionValue.write(bytes) + size += assertion.attributeDesc.write(bytes) + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} + +func (assertion AttributeValueAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) { + size += assertion.assertionValue.write(bytes) + size += assertion.attributeDesc.write(bytes) + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +func (assertion AttributeValueAssertion) size() (size int) { + size += assertion.attributeDesc.size() + size += assertion.assertionValue.size() + size += sizeTagAndLength(tagSequence, size) + return +} + +func (assertion AttributeValueAssertion) sizeTagged(tag int) (size int) { + size += assertion.attributeDesc.size() + size += assertion.assertionValue.size() + size += sizeTagAndLength(tag, size) + return +} diff --git a/goldap/authentication_choice.go b/goldap/authentication_choice.go new file mode 100644 index 0000000..804d84d --- /dev/null +++ b/goldap/authentication_choice.go @@ -0,0 +1,37 @@ +package message + +import "fmt" + +// +// AuthenticationChoice ::= CHOICE { +// simple [0] OCTET STRING, +// -- 1 and 2 reserved +// sasl [3] SaslCredentials, +// ... } + +func readAuthenticationChoice(bytes *Bytes) (ret AuthenticationChoice, err error) { + tagAndLength, err := bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())} + return + } + err = tagAndLength.ExpectClass(classContextSpecific) + if err != nil { + err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())} + return + } + switch tagAndLength.Tag { + case TagAuthenticationChoiceSimple: + ret, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagAuthenticationChoiceSimple) + case TagAuthenticationChoiceSaslCredentials: + ret, err = readSaslCredentials(bytes) + default: + err = LdapError{fmt.Sprintf("readAuthenticationChoice: invalid tag value %d for AuthenticationChoice", tagAndLength.Tag)} + return + } + if err != nil { + err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())} + return + } + return +} diff --git a/goldap/bind_request.go b/goldap/bind_request.go new file mode 100644 index 0000000..d00a3ef --- /dev/null +++ b/goldap/bind_request.go @@ -0,0 +1,93 @@ +package message + +import "fmt" + +// BindRequest ::= [APPLICATION 0] SEQUENCE { +// version INTEGER (1 .. 127), +// name LDAPDN, +// authentication AuthenticationChoice } + +func (request *BindRequest) Name() LDAPDN { + return request.name +} + +func (request *BindRequest) Authentication() AuthenticationChoice { + return request.authentication +} + +func (request *BindRequest) AuthenticationSimple() OCTETSTRING { + return request.Authentication().(OCTETSTRING) +} + +func (request *BindRequest) AuthenticationChoice() string { + switch request.Authentication().(type) { + case OCTETSTRING: + return "simple" + case SaslCredentials: + return "sasl" + } + return "" +} + +func readBindRequest(bytes *Bytes) (bindrequest BindRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagBindRequest, bindrequest.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readBindRequest:\n%s", err.Error())} + return + } + return +} + +func (request *BindRequest) readComponents(bytes *Bytes) (err error) { + request.version, err = readINTEGER(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if !(request.version >= BindRequestVersionMin && request.version <= BindRequestVersionMax) { + err = LdapError{fmt.Sprintf("readComponents: invalid version %d, must be between %d and %d", request.version, BindRequestVersionMin, BindRequestVersionMax)} + return + } + request.name, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + request.authentication, err = readAuthenticationChoice(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +func (request BindRequest) write(bytes *Bytes) (size int) { + switch request.authentication.(type) { + case OCTETSTRING: + size += request.authentication.(OCTETSTRING).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSimple) + case SaslCredentials: + size += request.authentication.(SaslCredentials).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSaslCredentials) + default: + panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication)) + } + size += request.name.write(bytes) + size += request.version.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindRequest, size) + return +} + +func (request BindRequest) size() (size int) { + size += request.version.size() + size += request.name.size() + switch request.authentication.(type) { + case OCTETSTRING: + size += request.authentication.(OCTETSTRING).sizeTagged(TagAuthenticationChoiceSimple) + case SaslCredentials: + size += request.authentication.(SaslCredentials).sizeTagged(TagAuthenticationChoiceSaslCredentials) + default: + panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication)) + } + + size += sizeTagAndLength(TagBindRequest, size) + return +} diff --git a/goldap/bind_response.go b/goldap/bind_response.go new file mode 100644 index 0000000..2d9db30 --- /dev/null +++ b/goldap/bind_response.go @@ -0,0 +1,56 @@ +package message + +import "fmt" + +// BindResponse ::= [APPLICATION 1] SEQUENCE { +// COMPONENTS OF LDAPResult, +// serverSaslCreds [7] OCTET STRING OPTIONAL } + +func readBindResponse(bytes *Bytes) (bindresponse BindResponse, err error) { + err = bytes.ReadSubBytes(classApplication, TagBindResponse, bindresponse.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readBindResponse:\n%s", err.Error())} + return + } + return +} + +func (response *BindResponse) readComponents(bytes *Bytes) (err error) { + response.LDAPResult.readComponents(bytes) + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagBindResponseServerSaslCreds { + var serverSaslCreds OCTETSTRING + serverSaslCreds, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagBindResponseServerSaslCreds) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + response.serverSaslCreds = serverSaslCreds.Pointer() + } + } + return +} + +func (response BindResponse) write(bytes *Bytes) (size int) { + if response.serverSaslCreds != nil { + size += response.serverSaslCreds.writeTagged(bytes, classContextSpecific, TagBindResponseServerSaslCreds) + } + size += response.LDAPResult.writeComponents(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindResponse, size) + return +} + +func (response BindResponse) size() (size int) { + if response.serverSaslCreds != nil { + size += response.serverSaslCreds.sizeTagged(TagBindResponseServerSaslCreds) + } + size += response.LDAPResult.sizeComponents() + size += sizeTagAndLength(TagBindResponse, size) + return +} diff --git a/goldap/boolean.go b/goldap/boolean.go new file mode 100644 index 0000000..7e3b254 --- /dev/null +++ b/goldap/boolean.go @@ -0,0 +1,62 @@ +package message + +import "fmt" + +func readBOOLEAN(bytes *Bytes) (ret BOOLEAN, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagBoolean, tagBoolean) + if err != nil { + err = LdapError{fmt.Sprintf("readBOOLEAN:\n%s", err.Error())} + return + } + ret = BOOLEAN(value.(bool)) + return +} + +func (boolean BOOLEAN) write(bytes *Bytes) int { + return bytes.WritePrimitiveSubBytes(classUniversal, tagBoolean, boolean) +} + +func (boolean BOOLEAN) writeTagged(bytes *Bytes, class int, tag int) int { + return bytes.WritePrimitiveSubBytes(class, tag, boolean) +} + +func readTaggedBOOLEAN(bytes *Bytes, class int, tag int) (ret BOOLEAN, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagBoolean) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedBOOLEAN:\n%s", err.Error())} + return + } + ret = BOOLEAN(value.(bool)) + return +} + +func SizePrimitiveSubBytes(tag int, value interface{}) (size int) { + switch value.(type) { + case BOOLEAN: + size = sizeBool(bool(value.(BOOLEAN))) + case INTEGER: + size = sizeInt32(int32(value.(INTEGER))) + case ENUMERATED: + size = sizeInt32(int32(value.(ENUMERATED))) + case OCTETSTRING: + size = sizeOctetString([]byte(string(value.(OCTETSTRING)))) + default: + panic(fmt.Sprintf("SizePrimitiveSubBytes: invalid value type %v", value)) + } + size += sizeTagAndLength(tag, size) + return +} + +func (boolean BOOLEAN) size() int { + return SizePrimitiveSubBytes(tagBoolean, boolean) +} + +func (boolean BOOLEAN) sizeTagged(tag int) int { + return SizePrimitiveSubBytes(tag, boolean) +} + +func (boolean BOOLEAN) Bool() bool { + return bool(boolean) +} diff --git a/goldap/bytes.go b/goldap/bytes.go new file mode 100644 index 0000000..92c63a2 --- /dev/null +++ b/goldap/bytes.go @@ -0,0 +1,199 @@ +package message + +import ( + "fmt" +) + +type Bytes struct { + offset int + bytes []byte +} + +func (bytes *Bytes) getBytes() []byte { + return bytes.bytes +} +func NewBytes(offset int, bytes []byte) (ret *Bytes) { + return &Bytes{offset: offset, bytes: bytes} +} + +func (bytes Bytes) Debug() { + fmt.Printf("Offset: %d, Bytes: %+v\n", bytes.offset, bytes.bytes) +} + +// Return a string with the hex dump of the bytes around the current offset +// The current offset byte is put in brackets +// Example: 0x01, [0x02], 0x03 +func (bytes *Bytes) DumpCurrentBytes() (ret string) { + var strings [3]string + for i := -1; i <= 1; i++ { + if bytes.offset+i >= 0 && bytes.offset+i < len(bytes.bytes) { + strings[i+1] = fmt.Sprintf("%#x", bytes.bytes[bytes.offset+i]) + } + } + ret = fmt.Sprintf("%s, [%s], %s", strings[0], strings[1], strings[2]) + return +} + +func (bytes *Bytes) HasMoreData() bool { + return bytes.offset < len(bytes.bytes) +} + +func (bytes *Bytes) ParseTagAndLength() (ret TagAndLength, err error) { + var offset int + ret, offset, err = ParseTagAndLength(bytes.bytes, bytes.offset) + if err != nil { + err = LdapError{fmt.Sprintf("ParseTagAndLength: %s", err.Error())} + return + } else { + bytes.offset = offset + } + return +} + +func (bytes *Bytes) ReadSubBytes(class int, tag int, callback func(bytes *Bytes) error) (err error) { + // Check tag + tagAndLength, err := bytes.ParseTagAndLength() + if err != nil { + return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} + } + err = tagAndLength.Expect(class, tag, isCompound) + if err != nil { + return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} + } + + start := bytes.offset + end := bytes.offset + tagAndLength.Length + + // Check we got enough bytes to process + if end > len(bytes.bytes) { + return LdapError{fmt.Sprintf("ReadSubBytes: data truncated: expecting %d bytes at offset %d", tagAndLength.Length, bytes.offset)} + } + // Process sub-bytes + subBytes := Bytes{offset: 0, bytes: bytes.bytes[start:end]} + err = callback(&subBytes) + if err != nil { + bytes.offset += subBytes.offset + err = LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} + return + } + // Check we got no more bytes to process + if subBytes.HasMoreData() { + return LdapError{fmt.Sprintf("ReadSubBytes: data too long: %d more bytes to read at offset %d", end-bytes.offset, bytes.offset)} + } + // Move offset + bytes.offset = end + return +} + +func SizeSubBytes(tag int, callback func() int) (size int) { + size = callback() + size += sizeTagAndLength(tag, size) + return +} + +func (bytes *Bytes) WritePrimitiveSubBytes(class int, tag int, value interface{}) (size int) { + switch value.(type) { + case BOOLEAN: + size = writeBool(bytes, bool(value.(BOOLEAN))) + case INTEGER: + size = writeInt32(bytes, int32(value.(INTEGER))) + case ENUMERATED: + size = writeInt32(bytes, int32(value.(ENUMERATED))) + case OCTETSTRING: + size = writeOctetString(bytes, []byte(string(value.(OCTETSTRING)))) + default: + panic(fmt.Sprintf("WritePrimitiveSubBytes: invalid value type %v", value)) + } + size += bytes.WriteTagAndLength(class, isNotCompound, tag, size) + return +} + +func (bytes *Bytes) WriteTagAndLength(class int, compound bool, tag int, length int) int { + return writeTagAndLength(bytes, TagAndLength{Class: class, IsCompound: compound, Tag: tag, Length: length}) +} + +func (bytes *Bytes) writeString(s string) (size int) { + size = len(s) + start := bytes.offset - size + if start < 0 { + panic("Not enough space for string") + } + copy(bytes.bytes[start:], s) + bytes.offset = start + return +} + +func (bytes *Bytes) writeBytes(b []byte) (size int) { + size = len(b) + start := bytes.offset - size + if start < 0 { + panic("Not enough space for bytes") + } + copy(bytes.bytes[start:], b) + bytes.offset = start + return +} + +// +// Parse tag, length and read the a primitive value +// Supported types are: +// - boolean +// - integer (parsed as int32) +// - enumerated (parsed as int32) +// - UTF8 string +// - Octet string +// +// Parameters: +// - class: the expected class value(classUniversal, classApplication, classContextSpecific) +// - tag: the expected tag value +// - typeTag: the real primitive type to parse (tagBoolean, tagInteger, tagEnym, tagUTF8String, tagOctetString) +// +func (bytes *Bytes) ReadPrimitiveSubBytes(class int, tag int, typeTag int) (value interface{}, err error) { + // Check tag + tagAndLength, err := bytes.ParseTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} + return + } + err = tagAndLength.Expect(class, tag, isNotCompound) + if err != nil { + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} + return + } + + start := bytes.offset + end := bytes.offset + tagAndLength.Length + + // Check we got enough bytes to process + if end > len(bytes.bytes) { + // err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining (start: %d, length: %d, end: %d, len(b): %d, bytes: %#+v)", tagAndLength.Length, *b.offset, len(b.bytes)-start, start, tagAndLength.Length, end, len(b.bytes), b.bytes)} + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: data truncated: expecting %d bytes at offset %d but only %d bytes are remaining", tagAndLength.Length, bytes.offset, len(bytes.bytes)-start)} + return + } + // Process sub-bytes + subBytes := bytes.bytes[start:end] + switch typeTag { + case tagBoolean: + value, err = parseBool(subBytes) + case tagInteger: + value, err = parseInt32(subBytes) + case tagEnum: + value, err = parseInt32(subBytes) + case tagOctetString: + value, err = parseOctetString(subBytes) + default: + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: invalid type tag value %d", typeTag)} + return + } + if err != nil { + err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} + return + } + // Move offset + bytes.offset = end + return +} + +func (bytes *Bytes) Bytes() []byte { + return bytes.bytes +} diff --git a/goldap/bytes_test.go b/goldap/bytes_test.go new file mode 100644 index 0000000..c56489d --- /dev/null +++ b/goldap/bytes_test.go @@ -0,0 +1,172 @@ +package message + +import ( + "reflect" + "testing" +) + +func TestReadPrimitiveSubBytesTestData(t *testing.T) { + for i, test := range PrimitiveSubBytesTestData() { + value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag) + if err != nil { + t.Errorf("#%d failed: %s", i+1, err) + } else if !reflect.DeepEqual(test.value, value) { + t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value) + } else if test.offset != test.bytes.offset { + t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset) + } + } +} + +func TestSizePrimitiveSubBytesTestData(t *testing.T) { + for i, test := range PrimitiveSubBytesTestData() { + value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag) + if err != nil { + t.Errorf("#%d failed: %s", i+1, err) + } else if !reflect.DeepEqual(test.value, value) { + t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value) + } else if test.offset != test.bytes.offset { + t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset) + } + } +} + +func NewInt(value int) (ret *int) { + ret = &value + return +} + +type PrimitiveSubBytesTestSingleData struct { + bytes Bytes // Input + class int // Expected class + tag int // Expected tag + typeTag int // Expected type + value interface{} // Expected output + offset int // Expected offset after processing +} + +func PrimitiveSubBytesTestData() []PrimitiveSubBytesTestSingleData { + + return []PrimitiveSubBytesTestSingleData{ + // Test 1 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{0x02, 0x01, 0x09}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x09), + offset: 3, + }, + // Test 2 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{0x02, 0x02, 0x09, 0x87}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x0987), + offset: 4, + }, + // Test 3 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{0x02, 0x03, 0x09, 0x87, 0x65}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x098765), + offset: 5, + }, + // Test 4 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{0x02, 0x04, 0x09, 0x87, 0x65, 0x43}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x09876543), + offset: 6, + }, + // Test 5 + { + bytes: Bytes{ + offset: 2, + bytes: []byte{0x30, 0x03, 0x02, 0x01, 0x0f}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x0f), + offset: 5, + }, + // Test 6 + { + bytes: Bytes{ + offset: 2, + bytes: []byte{0x30, 0x16, 0x02, 0x01, 0x0f, 0x60, 0x11, 0x02, 0x01, 0x03, 0x04, 0x00, 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x0f), + offset: 5, + }, + // Test 7 + { + bytes: Bytes{ + offset: 2, + bytes: []byte{0x30, 0x19, 0x02, 0x04, 0x7f, 0xff, 0xff, 0xff, 0x60, 0x11, 0x02, 0x01, 0x03, 0x04, 0x00, 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35}, + }, + class: classUniversal, + tag: tagInteger, + typeTag: tagInteger, + value: int32(0x07fffffff), + offset: 8, + }, + // Test 8 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35}, + }, + class: classUniversal, + tag: tagOctetString, + typeTag: tagOctetString, + value: []byte("CRAM-MD5"), + offset: 10, + }, + // Test 9 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{0x04, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c}, + }, + class: classUniversal, + tag: tagOctetString, + typeTag: tagOctetString, + value: []byte("Hello, 世界"), + offset: 15, + }, + // Test 10 + { + bytes: Bytes{ + offset: 10, + bytes: []byte{0x30, 0x1d, 0x02, 0x01, 0x05, 0x60, 0x18, 0x02, 0x01, 0x03, 0x04, 0x07, 0x6d, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x80, 0x0a, 0x6d, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64}, + }, + class: classUniversal, + tag: tagOctetString, + typeTag: tagOctetString, + value: []byte("myLogin"), + offset: 19, + }, + } +} diff --git a/goldap/compare_request.go b/goldap/compare_request.go new file mode 100644 index 0000000..dfbc346 --- /dev/null +++ b/goldap/compare_request.go @@ -0,0 +1,53 @@ +package message + +import "fmt" + +// +// CompareRequest ::= [APPLICATION 14] SEQUENCE { +// entry LDAPDN, +// ava AttributeValueAssertion } + +func (request *CompareRequest) Entry() LDAPDN { + return request.entry +} + +func (request *CompareRequest) Ava() *AttributeValueAssertion { + return &request.ava +} + +func readCompareRequest(bytes *Bytes) (ret CompareRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagCompareRequest, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readCompareRequest:\n%s", err.Error())} + return + } + return +} + +func (request *CompareRequest) readComponents(bytes *Bytes) (err error) { + request.entry, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + request.ava, err = readAttributeValueAssertion(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +func (request CompareRequest) write(bytes *Bytes) (size int) { + size += request.ava.write(bytes) + size += request.entry.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagCompareRequest, size) + return +} + +func (request CompareRequest) size() (size int) { + size += request.entry.size() + size += request.ava.size() + size += sizeTagAndLength(TagCompareRequest, size) + return +} diff --git a/goldap/compare_response.go b/goldap/compare_response.go new file mode 100644 index 0000000..92083fc --- /dev/null +++ b/goldap/compare_response.go @@ -0,0 +1,29 @@ +package message + +import "fmt" + +// +// CompareResponse ::= [APPLICATION 15] LDAPResult + +func (response *CompareResponse) SetResultCode(code int) { + response.resultCode = ENUMERATED(code) +} + +func readCompareResponse(bytes *Bytes) (ret CompareResponse, err error) { + var res LDAPResult + res, err = readTaggedLDAPResult(bytes, classApplication, TagCompareResponse) + if err != nil { + err = LdapError{fmt.Sprintf("readCompareResponse:\n%s", err.Error())} + return + } + ret = CompareResponse(res) + return +} + +func (response CompareResponse) write(bytes *Bytes) int { + return LDAPResult(response).writeTagged(bytes, classApplication, TagCompareResponse) +} + +func (response CompareResponse) size() int { + return LDAPResult(response).sizeTagged(TagCompareResponse) +} diff --git a/goldap/control.go b/goldap/control.go new file mode 100644 index 0000000..3f94b67 --- /dev/null +++ b/goldap/control.go @@ -0,0 +1,94 @@ +package message + +import ( + "errors" + "fmt" +) + +// +// Control ::= SEQUENCE { +// controlType LDAPOID, +// criticality BOOLEAN DEFAULT FALSE, +// controlValue OCTET STRING OPTIONAL } + +func (control *Control) ControlType() LDAPOID { + return control.controlType +} + +func (control *Control) Criticality() BOOLEAN { + return control.criticality +} + +func (control *Control) ControlValue() *OCTETSTRING { + return control.controlValue +} + +func readControl(bytes *Bytes) (control Control, err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, control.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readControl:\n%s", err.Error())} + return + } + return +} + +func (control *Control) readComponents(bytes *Bytes) (err error) { + control.controlType, err = readLDAPOID(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == tagBoolean { + control.criticality, err = readBOOLEAN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if control.criticality == false { + err = errors.New(fmt.Sprintf("readComponents: criticality default value FALSE should not be specified")) + return + } + } + } + if bytes.HasMoreData() { + var octetstring OCTETSTRING + octetstring, err = readOCTETSTRING(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + control.controlValue = octetstring.Pointer() + } + return +} + +func (control Control) write(bytes *Bytes) (size int) { + if control.controlValue != nil { + size += control.controlValue.write(bytes) + } + if control.criticality != BOOLEAN(false) { + size += control.criticality.write(bytes) + } + size += control.controlType.write(bytes) + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} + +func (control Control) size() (size int) { + if control.controlValue != nil { + size += control.controlValue.size() + } + if control.criticality != BOOLEAN(false) { + size += control.criticality.size() + } + size += control.controlType.size() + size += sizeTagAndLength(tagSequence, size) + return +} diff --git a/goldap/controls.go b/goldap/controls.go new file mode 100644 index 0000000..da3ddaf --- /dev/null +++ b/goldap/controls.go @@ -0,0 +1,44 @@ +package message + +import "fmt" + +// +// Controls ::= SEQUENCE OF control Control + +func readTaggedControls(bytes *Bytes, class int, tag int) (controls Controls, err error) { + err = bytes.ReadSubBytes(class, tag, controls.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedControls:\n%s", err.Error())} + return + } + return +} +func (controls *Controls) readComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var control Control + control, err = readControl(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + *controls = append(*controls, control) + } + return +} +func (controls Controls) Pointer() *Controls { return &controls } + +func (controls Controls) writeTagged(bytes *Bytes, class int, tag int) (size int) { + for i := len(controls) - 1; i >= 0; i-- { + size += controls[i].write(bytes) + } + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +func (controls Controls) sizeTagged(tag int) (size int) { + for _, control := range controls { + size += control.size() + } + size += sizeTagAndLength(tag, size) + return +} diff --git a/goldap/del_request.go b/goldap/del_request.go new file mode 100644 index 0000000..ab24225 --- /dev/null +++ b/goldap/del_request.go @@ -0,0 +1,24 @@ +package message + +import "fmt" + +// +// DelRequest ::= [APPLICATION 10] LDAPDN +func readDelRequest(bytes *Bytes) (ret DelRequest, err error) { + var res LDAPDN + res, err = readTaggedLDAPDN(bytes, classApplication, TagDelRequest) + if err != nil { + err = LdapError{fmt.Sprintf("readDelRequest:\n%s", err.Error())} + return + } + ret = DelRequest(res) + return +} + +func (del DelRequest) write(bytes *Bytes) int { + return LDAPDN(del).writeTagged(bytes, classApplication, TagDelRequest) +} + +func (del DelRequest) size() int { + return LDAPDN(del).sizeTagged(TagDelRequest) +} diff --git a/goldap/del_response.go b/goldap/del_response.go new file mode 100644 index 0000000..f2e14a7 --- /dev/null +++ b/goldap/del_response.go @@ -0,0 +1,29 @@ +package message + +import "fmt" + +// +// DelResponse ::= [APPLICATION 11] LDAPResult + +func (del *DelResponse) SetResultCode(code int) { + del.resultCode = ENUMERATED(code) +} + +func readDelResponse(bytes *Bytes) (ret DelResponse, err error) { + var res LDAPResult + res, err = readTaggedLDAPResult(bytes, classApplication, TagDelResponse) + if err != nil { + err = LdapError{fmt.Sprintf("readDelResponse:\n%s", err.Error())} + return + } + ret = DelResponse(res) + return +} + +func (del DelResponse) write(bytes *Bytes) int { + return LDAPResult(del).writeTagged(bytes, classApplication, TagDelResponse) +} + +func (del DelResponse) size() int { + return LDAPResult(del).sizeTagged(TagDelResponse) +} diff --git a/goldap/dn.go b/goldap/dn.go new file mode 100644 index 0000000..c622fd8 --- /dev/null +++ b/goldap/dn.go @@ -0,0 +1,60 @@ +package message + +import "fmt" + +// +// LDAPDN ::= LDAPString -- Constrained to +// -- [RFC4514] + +func readLDAPDN(bytes *Bytes) (ret LDAPDN, err error) { + var str LDAPString + str, err = readLDAPString(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readLDAPDN:\n%s", err.Error())} + return + } + ret = LDAPDN(str) + return +} + +func readTaggedLDAPDN(bytes *Bytes, class int, tag int) (ret LDAPDN, err error) { + var ldapstring LDAPString + ldapstring, err = readTaggedLDAPString(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedLDAPDN:\n%s", err.Error())} + return + } + // @TODO: check RFC4514 + ret = LDAPDN(ldapstring) + return +} + +func (l LDAPDN) Pointer() *LDAPDN { return &l } + +func readRelativeLDAPDN(bytes *Bytes) (ret RelativeLDAPDN, err error) { + var ldapstring LDAPString + ldapstring, err = readLDAPString(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readRelativeLDAPDN:\n%s", err.Error())} + return + } + // @TODO: check RFC4514 + ret = RelativeLDAPDN(ldapstring) + return +} + +func (l LDAPDN) write(bytes *Bytes) int { + return LDAPString(l).write(bytes) +} + +func (l LDAPDN) writeTagged(bytes *Bytes, class int, tag int) int { + return LDAPString(l).writeTagged(bytes, class, tag) +} + +func (l LDAPDN) size() int { + return LDAPString(l).size() +} + +func (l LDAPDN) sizeTagged(tag int) int { + return LDAPString(l).sizeTagged(tag) +} diff --git a/goldap/enumerated.go b/goldap/enumerated.go new file mode 100644 index 0000000..6454688 --- /dev/null +++ b/goldap/enumerated.go @@ -0,0 +1,34 @@ +package message + +import "fmt" + +func (enum ENUMERATED) Int() int { + return int(enum) +} + +func readENUMERATED(bytes *Bytes, allowedValues map[ENUMERATED]string) (ret ENUMERATED, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagEnum, tagEnum) + if err != nil { + err = LdapError{fmt.Sprintf("readENUMERATED:\n%s", err.Error())} + return + } + ret = ENUMERATED(value.(int32)) + if _, ok := allowedValues[ret]; !ok { + err = LdapError{fmt.Sprintf("readENUMERATED: Invalid ENUMERATED VALUE %d", ret)} + return + } + return +} + +func (enum ENUMERATED) write(bytes *Bytes) int { + return bytes.WritePrimitiveSubBytes(classUniversal, tagEnum, enum) +} + +func (enum ENUMERATED) writeTagged(bytes *Bytes, class int, tag int) int { + return bytes.WritePrimitiveSubBytes(class, tag, enum) +} + +func (enum ENUMERATED) size() int { + return SizePrimitiveSubBytes(tagEnum, enum) +} diff --git a/goldap/error.go b/goldap/error.go new file mode 100644 index 0000000..7b5e1c6 --- /dev/null +++ b/goldap/error.go @@ -0,0 +1,9 @@ +package message + +type LdapError struct { + Msg string +} + +func (err LdapError) Error() string { + return err.Msg +} diff --git a/goldap/extended_request.go b/goldap/extended_request.go new file mode 100644 index 0000000..983ebf9 --- /dev/null +++ b/goldap/extended_request.go @@ -0,0 +1,69 @@ +package message + +import "fmt" + +// +// ExtendedRequest ::= [APPLICATION 23] SEQUENCE { +// requestName [0] LDAPOID, +// requestValue [1] OCTET STRING OPTIONAL } + +func (extended *ExtendedRequest) RequestName() LDAPOID { + return extended.requestName +} + +func (extended *ExtendedRequest) RequestValue() *OCTETSTRING { + return extended.requestValue +} + +func readExtendedRequest(bytes *Bytes) (ret ExtendedRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagExtendedRequest, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readExtendedRequest:\n%s", err.Error())} + return + } + return +} + +func (extended *ExtendedRequest) readComponents(bytes *Bytes) (err error) { + extended.requestName, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedRequestName) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagExtendedRequestValue { + var requestValue OCTETSTRING + requestValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedRequestValue) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + extended.requestValue = requestValue.Pointer() + } + } + return +} + +func (extended ExtendedRequest) write(bytes *Bytes) (size int) { + if extended.requestValue != nil { + size += extended.requestValue.writeTagged(bytes, classContextSpecific, TagExtendedRequestValue) + } + size += extended.requestName.writeTagged(bytes, classContextSpecific, TagExtendedRequestName) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedRequest, size) + return +} + +func (extended ExtendedRequest) size() (size int) { + size += extended.requestName.sizeTagged(TagExtendedRequestName) + if extended.requestValue != nil { + size += extended.requestValue.sizeTagged(TagExtendedRequestValue) + } + size += sizeTagAndLength(TagExtendedRequest, size) + return +} diff --git a/goldap/extended_response.go b/goldap/extended_response.go new file mode 100644 index 0000000..4e41b0e --- /dev/null +++ b/goldap/extended_response.go @@ -0,0 +1,85 @@ +package message + +import "fmt" + +// +// ExtendedResponse ::= [APPLICATION 24] SEQUENCE { +// COMPONENTS OF LDAPResult, +// responseName [10] LDAPOID OPTIONAL, +// responseValue [11] OCTET STRING OPTIONAL } + +func (extended *ExtendedResponse) SetResponseName(name LDAPOID) { + extended.responseName = &name +} + +func readExtendedResponse(bytes *Bytes) (ret ExtendedResponse, err error) { + err = bytes.ReadSubBytes(classApplication, TagExtendedResponse, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readExtendedResponse:\n%s", err.Error())} + return + } + return +} + +func (extended *ExtendedResponse) readComponents(bytes *Bytes) (err error) { + extended.LDAPResult.readComponents(bytes) + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagExtendedResponseName { + var oid LDAPOID + oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedResponseName) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + extended.responseName = oid.Pointer() + } + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagExtendedResponseValue { + var responseValue OCTETSTRING + responseValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedResponseValue) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + extended.responseValue = responseValue.Pointer() + } + } + return +} + +func (extended ExtendedResponse) write(bytes *Bytes) (size int) { + if extended.responseValue != nil { + size += extended.responseValue.writeTagged(bytes, classContextSpecific, TagExtendedResponseValue) + } + if extended.responseName != nil { + size += extended.responseName.writeTagged(bytes, classContextSpecific, TagExtendedResponseName) + } + size += extended.LDAPResult.writeComponents(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedResponse, size) + return +} + +func (extended ExtendedResponse) size() (size int) { + size += extended.LDAPResult.sizeComponents() + if extended.responseName != nil { + size += extended.responseName.sizeTagged(TagExtendedResponseName) + } + if extended.responseValue != nil { + size += extended.responseValue.sizeTagged(TagExtendedResponseValue) + } + size += sizeTagAndLength(TagExtendedResponse, size) + return +} diff --git a/goldap/filter.go b/goldap/filter.go new file mode 100644 index 0000000..ba045ad --- /dev/null +++ b/goldap/filter.go @@ -0,0 +1,70 @@ +package message + +import "fmt" + +// +// Filter ::= CHOICE { +// and [0] SET SIZE (1..MAX) OF filter Filter, +// or [1] SET SIZE (1..MAX) OF filter Filter, +// not [2] Filter, +// equalityMatch [3] AttributeValueAssertion, +// +// +// +//Sermersheim Standards Track [Page 57] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// substrings [4] SubstringFilter, +// greaterOrEqual [5] AttributeValueAssertion, +// lessOrEqual [6] AttributeValueAssertion, +// present [7] AttributeDescription, +// approxMatch [8] AttributeValueAssertion, +// extensibleMatch [9] MatchingRuleAssertion, +// ... } + +func readFilter(bytes *Bytes) (filter Filter, err error) { + var tagAndLength TagAndLength + tagAndLength, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())} + return + } + err = tagAndLength.ExpectClass(classContextSpecific) + if err != nil { + err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())} + return + } + switch tagAndLength.Tag { + case TagFilterAnd: + filter, err = readFilterAnd(bytes) + case TagFilterOr: + filter, err = readFilterOr(bytes) + case TagFilterNot: + filter, err = readFilterNot(bytes) + case TagFilterEqualityMatch: + filter, err = readFilterEqualityMatch(bytes) + case TagFilterSubstrings: + filter, err = readFilterSubstrings(bytes) + case TagFilterGreaterOrEqual: + filter, err = readFilterGreaterOrEqual(bytes) + case TagFilterLessOrEqual: + filter, err = readFilterLessOrEqual(bytes) + case TagFilterPresent: + filter, err = readFilterPresent(bytes) + case TagFilterApproxMatch: + filter, err = readFilterApproxMatch(bytes) + case TagFilterExtensibleMatch: + filter, err = readFilterExtensibleMatch(bytes) + default: + err = LdapError{fmt.Sprintf("readFilter: invalid tag value %d for filter", tagAndLength.Tag)} + return + } + if err != nil { + err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())} + return + } + return +} diff --git a/goldap/filter_and.go b/goldap/filter_and.go new file mode 100644 index 0000000..981284d --- /dev/null +++ b/goldap/filter_and.go @@ -0,0 +1,54 @@ +package message + +import "fmt" + +// and [0] SET SIZE (1..MAX) OF filter Filter, + +func (filterAnd FilterAnd) getFilterTag() int { + return TagFilterAnd +} + +func (filterAnd FilterAnd) size() (size int) { + for _, filter := range filterAnd { + size += filter.size() + } + size += sizeTagAndLength(TagFilterAnd, size) + return +} + +func (filterAnd *FilterAnd) readComponents(bytes *Bytes) (err error) { + count := 0 + for bytes.HasMoreData() { + count++ + var filter Filter + filter, err = readFilter(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents (filter %d):\n%s", count, err.Error())} + return + } + *filterAnd = append(*filterAnd, filter) + } + if len(*filterAnd) == 0 { + err = LdapError{"readComponents: expecting at least one Filter"} + return + } + return +} + +func (filterAnd FilterAnd) write(bytes *Bytes) (size int) { + + for i := len(filterAnd) - 1; i >= 0; i-- { + size += filterAnd[i].write(bytes) + } + size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterAnd, size) + return +} + +func readFilterAnd(bytes *Bytes) (filterand FilterAnd, err error) { + err = bytes.ReadSubBytes(classContextSpecific, TagFilterAnd, filterand.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterAnd:\n%s", err.Error())} + return + } + return +} diff --git a/goldap/filter_approx_match.go b/goldap/filter_approx_match.go new file mode 100644 index 0000000..65a802f --- /dev/null +++ b/goldap/filter_approx_match.go @@ -0,0 +1,34 @@ +package message + +import "fmt" + +// approxMatch [8] AttributeValueAssertion, +func readFilterApproxMatch(bytes *Bytes) (ret FilterApproxMatch, err error) { + var attributevalueassertion AttributeValueAssertion + attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterApproxMatch) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterApproxMatch:\n%s", err.Error())} + return + } + ret = FilterApproxMatch(attributevalueassertion) + return +} + +// approxMatch [8] AttributeValueAssertion, +func (f FilterApproxMatch) write(bytes *Bytes) int { + return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterApproxMatch) +} +func (filterAnd FilterApproxMatch) getFilterTag() int { + return TagFilterApproxMatch +} + +// approxMatch [8] AttributeValueAssertion, +func (f FilterApproxMatch) size() int { + return AttributeValueAssertion(f).sizeTagged(TagFilterApproxMatch) +} +func (a *FilterApproxMatch) AttributeDesc() AttributeDescription { + return a.attributeDesc +} +func (a *FilterApproxMatch) AssertionValue() AssertionValue { + return a.assertionValue +} diff --git a/goldap/filter_equality_match.go b/goldap/filter_equality_match.go new file mode 100644 index 0000000..19912ff --- /dev/null +++ b/goldap/filter_equality_match.go @@ -0,0 +1,34 @@ +package message + +import "fmt" + +// equalityMatch [3] AttributeValueAssertion, +func readFilterEqualityMatch(bytes *Bytes) (ret FilterEqualityMatch, err error) { + var attributevalueassertion AttributeValueAssertion + attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterEqualityMatch) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterEqualityMatch:\n%s", err.Error())} + return + } + ret = FilterEqualityMatch(attributevalueassertion) + return +} + +// equalityMatch [3] AttributeValueAssertion, +func (f FilterEqualityMatch) write(bytes *Bytes) int { + return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterEqualityMatch) +} +func (filter FilterEqualityMatch) getFilterTag() int { + return TagFilterEqualityMatch +} + +// equalityMatch [3] AttributeValueAssertion, +func (f FilterEqualityMatch) size() int { + return AttributeValueAssertion(f).sizeTagged(TagFilterEqualityMatch) +} +func (a *FilterEqualityMatch) AttributeDesc() AttributeDescription { + return a.attributeDesc +} +func (a *FilterEqualityMatch) AssertionValue() AssertionValue { + return a.assertionValue +} diff --git a/goldap/filter_extensible_match.go b/goldap/filter_extensible_match.go new file mode 100644 index 0000000..8c09c7f --- /dev/null +++ b/goldap/filter_extensible_match.go @@ -0,0 +1,28 @@ +package message + +import "fmt" + +// extensibleMatch [9] MatchingRuleAssertion, +func readFilterExtensibleMatch(bytes *Bytes) (filterextensiblematch FilterExtensibleMatch, err error) { + var matchingruleassertion MatchingRuleAssertion + matchingruleassertion, err = readTaggedMatchingRuleAssertion(bytes, classContextSpecific, TagFilterExtensibleMatch) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterExtensibleMatch:\n%s", err.Error())} + return + } + filterextensiblematch = FilterExtensibleMatch(matchingruleassertion) + return +} + +// extensibleMatch [9] MatchingRuleAssertion, +func (f FilterExtensibleMatch) write(bytes *Bytes) int { + return MatchingRuleAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterExtensibleMatch) +} +func (filterAnd FilterExtensibleMatch) getFilterTag() int { + return TagFilterExtensibleMatch +} + +// extensibleMatch [9] MatchingRuleAssertion, +func (f FilterExtensibleMatch) size() int { + return MatchingRuleAssertion(f).sizeTagged(TagFilterExtensibleMatch) +} diff --git a/goldap/filter_greater_or_equal.go b/goldap/filter_greater_or_equal.go new file mode 100644 index 0000000..450eefc --- /dev/null +++ b/goldap/filter_greater_or_equal.go @@ -0,0 +1,34 @@ +package message + +import "fmt" + +// greaterOrEqual [5] AttributeValueAssertion, +func readFilterGreaterOrEqual(bytes *Bytes) (ret FilterGreaterOrEqual, err error) { + var attributevalueassertion AttributeValueAssertion + attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterGreaterOrEqual) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterGreaterOrEqual:\n%s", err.Error())} + return + } + ret = FilterGreaterOrEqual(attributevalueassertion) + return +} + +// greaterOrEqual [5] AttributeValueAssertion, +func (filter FilterGreaterOrEqual) write(bytes *Bytes) int { + return AttributeValueAssertion(filter).writeTagged(bytes, classContextSpecific, TagFilterGreaterOrEqual) +} +func (filter FilterGreaterOrEqual) getFilterTag() int { + return TagFilterGreaterOrEqual +} + +// greaterOrEqual [5] AttributeValueAssertion, +func (filter FilterGreaterOrEqual) size() int { + return AttributeValueAssertion(filter).sizeTagged(TagFilterGreaterOrEqual) +} +func (filter *FilterGreaterOrEqual) AttributeDesc() AttributeDescription { + return filter.attributeDesc +} +func (filter *FilterGreaterOrEqual) AssertionValue() AssertionValue { + return filter.assertionValue +} diff --git a/goldap/filter_less_or_equal.go b/goldap/filter_less_or_equal.go new file mode 100644 index 0000000..0d22ae2 --- /dev/null +++ b/goldap/filter_less_or_equal.go @@ -0,0 +1,34 @@ +package message + +import "fmt" + +// lessOrEqual [6] AttributeValueAssertion, +func readFilterLessOrEqual(bytes *Bytes) (ret FilterLessOrEqual, err error) { + var attributevalueassertion AttributeValueAssertion + attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterLessOrEqual) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterLessOrEqual:\n%s", err.Error())} + return + } + ret = FilterLessOrEqual(attributevalueassertion) + return +} + +// lessOrEqual [6] AttributeValueAssertion, +func (f FilterLessOrEqual) write(bytes *Bytes) int { + return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterLessOrEqual) +} +func (filterAnd FilterLessOrEqual) getFilterTag() int { + return TagFilterLessOrEqual +} + +// lessOrEqual [6] AttributeValueAssertion, +func (f FilterLessOrEqual) size() int { + return AttributeValueAssertion(f).sizeTagged(TagFilterLessOrEqual) +} +func (a *FilterLessOrEqual) AttributeDesc() AttributeDescription { + return a.attributeDesc +} +func (a *FilterLessOrEqual) AssertionValue() AssertionValue { + return a.assertionValue +} diff --git a/goldap/filter_not.go b/goldap/filter_not.go new file mode 100644 index 0000000..35726ac --- /dev/null +++ b/goldap/filter_not.go @@ -0,0 +1,40 @@ +package message + +import "fmt" + +func (filterNot FilterNot) getFilterTag() int { + return TagFilterNot +} + +// not [2] Filter, +func (filterNot FilterNot) size() (size int) { + size = filterNot.Filter.size() + size += sizeTagAndLength(tagSequence, size) + return +} + +func (filterNot *FilterNot) readComponents(bytes *Bytes) (err error) { + filterNot.Filter, err = readFilter(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +// not [2] Filter, +func (filterNot FilterNot) write(bytes *Bytes) (size int) { + size = filterNot.Filter.write(bytes) + size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterNot, size) + return +} + +// not [2] Filter, +func readFilterNot(bytes *Bytes) (filternot FilterNot, err error) { + err = bytes.ReadSubBytes(classContextSpecific, TagFilterNot, filternot.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterNot:\n%s", err.Error())} + return + } + return +} diff --git a/goldap/filter_or.go b/goldap/filter_or.go new file mode 100644 index 0000000..9e5dc38 --- /dev/null +++ b/goldap/filter_or.go @@ -0,0 +1,52 @@ +package message + +import "fmt" + +// or [1] SET SIZE (1..MAX) OF filter Filter, +func readFilterOr(bytes *Bytes) (filteror FilterOr, err error) { + err = bytes.ReadSubBytes(classContextSpecific, TagFilterOr, filteror.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterOr:\n%s", err.Error())} + return + } + return +} +func (filteror *FilterOr) readComponents(bytes *Bytes) (err error) { + count := 0 + for bytes.HasMoreData() { + count++ + var filter Filter + filter, err = readFilter(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents (filter %d): %s", count, err.Error())} + return + } + *filteror = append(*filteror, filter) + } + if len(*filteror) == 0 { + err = LdapError{"readComponents: expecting at least one Filter"} + return + } + return +} + +// or [1] SET SIZE (1..MAX) OF filter Filter, +func (f FilterOr) write(bytes *Bytes) (size int) { + for i := len(f) - 1; i >= 0; i-- { + size += f[i].write(bytes) + } + size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterOr, size) + return +} +func (filter FilterOr) getFilterTag() int { + return TagFilterOr +} + +// or [1] SET SIZE (1..MAX) OF filter Filter, +func (f FilterOr) size() (size int) { + for _, filter := range f { + size += filter.size() + } + size += sizeTagAndLength(TagFilterOr, size) + return +} diff --git a/goldap/filter_present.go b/goldap/filter_present.go new file mode 100644 index 0000000..686b83a --- /dev/null +++ b/goldap/filter_present.go @@ -0,0 +1,28 @@ +package message + +import "fmt" + +// present [7] AttributeDescription, +func readFilterPresent(bytes *Bytes) (ret FilterPresent, err error) { + var attributedescription AttributeDescription + attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagFilterPresent) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterPresent:\n%s", err.Error())} + return + } + ret = FilterPresent(attributedescription) + return +} + +// present [7] AttributeDescription, +func (f FilterPresent) write(bytes *Bytes) int { + return AttributeDescription(f).writeTagged(bytes, classContextSpecific, TagFilterPresent) +} +func (filterAnd FilterPresent) getFilterTag() int { + return TagFilterPresent +} + +// present [7] AttributeDescription, +func (f FilterPresent) size() int { + return AttributeDescription(f).sizeTagged(TagFilterPresent) +} diff --git a/goldap/filter_substring.go b/goldap/filter_substring.go new file mode 100644 index 0000000..8667420 --- /dev/null +++ b/goldap/filter_substring.go @@ -0,0 +1,187 @@ +package message + +import "fmt" + +// substrings [4] SubstringFilter, +func readFilterSubstrings(bytes *Bytes) (filtersubstrings FilterSubstrings, err error) { + var substringfilter SubstringFilter + substringfilter, err = readTaggedSubstringFilter(bytes, classContextSpecific, TagFilterSubstrings) + if err != nil { + err = LdapError{fmt.Sprintf("readFilterSubstrings:\n%s", err.Error())} + return + } + filtersubstrings = FilterSubstrings(substringfilter) + return +} + +// +// SubstringFilter ::= SEQUENCE { +// type AttributeDescription, +// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { +// initial [0] AssertionValue, -- can occur at most once +// any [1] AssertionValue, +// final [2] AssertionValue } -- can occur at most once +// } +func readTaggedSubstringFilter(bytes *Bytes, class int, tag int) (substringfilter SubstringFilter, err error) { + err = bytes.ReadSubBytes(class, tag, substringfilter.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedSubstringFilter:\n%s", err.Error())} + return + } + return +} +func (substringfilter *SubstringFilter) readComponents(bytes *Bytes) (err error) { + substringfilter.type_, err = readAttributeDescription(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + err = substringfilter.readSubstrings(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} +func (substringfilter *SubstringFilter) readSubstrings(bytes *Bytes) (err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, substringfilter.readSubstringsComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readSubstrings:\n%s", err.Error())} + return + } + return +} +func (substringfilter *SubstringFilter) readSubstringsComponents(bytes *Bytes) (err error) { + var foundInitial = 0 + var foundFinal = 0 + var tagAndLength TagAndLength + for bytes.HasMoreData() { + tagAndLength, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} + return + } + var assertionvalue AssertionValue + switch tagAndLength.Tag { + case TagSubstringInitial: + foundInitial++ + if foundInitial > 1 { + err = LdapError{"readSubstringsComponents: initial can occur at most once"} + return + } + assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringInitial) + if err != nil { + err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} + return + } + substringfilter.substrings = append(substringfilter.substrings, SubstringInitial(assertionvalue)) + case TagSubstringAny: + assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringAny) + if err != nil { + err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} + return + } + substringfilter.substrings = append(substringfilter.substrings, SubstringAny(assertionvalue)) + case TagSubstringFinal: + foundFinal++ + if foundFinal > 1 { + err = LdapError{"readSubstringsComponents: final can occur at most once"} + return + } + assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringFinal) + if err != nil { + err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} + return + } + substringfilter.substrings = append(substringfilter.substrings, SubstringFinal(assertionvalue)) + default: + err = LdapError{fmt.Sprintf("readSubstringsComponents: invalid tag %d", tagAndLength.Tag)} + return + } + } + if len(substringfilter.substrings) == 0 { + err = LdapError{"readSubstringsComponents: expecting at least one substring"} + return + } + return +} + +// substrings [4] SubstringFilter, +func (f FilterSubstrings) write(bytes *Bytes) int { + return SubstringFilter(f).writeTagged(bytes, classContextSpecific, TagFilterSubstrings) +} +func (s SubstringFilter) writeTagged(bytes *Bytes, class int, tag int) (size int) { + for i := len(s.substrings) - 1; i >= 0; i-- { + substring := s.substrings[i] + switch substring.(type) { + case SubstringInitial: + size += AssertionValue(substring.(SubstringInitial)).writeTagged(bytes, classContextSpecific, TagSubstringInitial) + case SubstringAny: + size += AssertionValue(substring.(SubstringAny)).writeTagged(bytes, classContextSpecific, TagSubstringAny) + case SubstringFinal: + size += AssertionValue(substring.(SubstringFinal)).writeTagged(bytes, classContextSpecific, TagSubstringFinal) + default: + panic("Unknown type for SubstringFilter substring") + } + } + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + size += s.type_.write(bytes) + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +// +// SubstringFilter ::= SEQUENCE { +// type AttributeDescription, +// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { +// initial [0] AssertionValue, -- can occur at most once +// any [1] AssertionValue, +// final [2] AssertionValue } -- can occur at most once +// } +func (s SubstringFilter) write(bytes *Bytes) (size int) { + return s.writeTagged(bytes, classUniversal, tagSequence) +} +func (filter FilterSubstrings) getFilterTag() int { + return TagFilterSubstrings +} + +// substrings [4] SubstringFilter, +func (f FilterSubstrings) size() int { + return SubstringFilter(f).sizeTagged(TagFilterSubstrings) +} + +// +// SubstringFilter ::= SEQUENCE { +// type AttributeDescription, +// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { +// initial [0] AssertionValue, -- can occur at most once +// any [1] AssertionValue, +// final [2] AssertionValue } -- can occur at most once +// } +func (s SubstringFilter) size() (size int) { + return s.sizeTagged(tagSequence) +} +func (s SubstringFilter) sizeTagged(tag int) (size int) { + for _, substring := range s.substrings { + switch substring.(type) { + case SubstringInitial: + size += AssertionValue(substring.(SubstringInitial)).sizeTagged(TagSubstringInitial) + case SubstringAny: + size += AssertionValue(substring.(SubstringAny)).sizeTagged(TagSubstringAny) + case SubstringFinal: + size += AssertionValue(substring.(SubstringFinal)).sizeTagged(TagSubstringFinal) + default: + panic("Unknown type for SubstringFilter substring") + } + } + size += sizeTagAndLength(tagSequence, size) + size += s.type_.size() + size += sizeTagAndLength(tag, size) + return +} +func (s *FilterSubstrings) Type_() AttributeDescription { + return s.type_ +} +func (s *FilterSubstrings) Substrings() []Substring { + return s.substrings +} diff --git a/goldap/integer.go b/goldap/integer.go new file mode 100644 index 0000000..79e4248 --- /dev/null +++ b/goldap/integer.go @@ -0,0 +1,53 @@ +package message + +import "fmt" + +func readINTEGER(bytes *Bytes) (ret INTEGER, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagInteger, tagInteger) + if err != nil { + err = LdapError{fmt.Sprintf("readINTEGER:\n%s", err.Error())} + return + } + ret = INTEGER(value.(int32)) + return +} +func readTaggedINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagInteger) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedINTEGER:\n%s", err.Error())} + return + } + ret = INTEGER(value.(int32)) + return +} +func readTaggedPositiveINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) { + ret, err = readTaggedINTEGER(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER:\n%s", err.Error())} + return + } + if !(ret >= 0 && ret <= maxInt) { + err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER: Invalid INTEGER value %d ! Expected value between 0 and %d", ret, maxInt)} + } + return +} +func readPositiveINTEGER(bytes *Bytes) (ret INTEGER, err error) { + return readTaggedPositiveINTEGER(bytes, classUniversal, tagInteger) +} +func (i INTEGER) write(bytes *Bytes) int { + return bytes.WritePrimitiveSubBytes(classUniversal, tagInteger, i) +} +func (i INTEGER) writeTagged(bytes *Bytes, class int, tag int) int { + return bytes.WritePrimitiveSubBytes(class, tag, i) +} +func (i INTEGER) size() int { + return SizePrimitiveSubBytes(tagInteger, i) +} +func (i INTEGER) sizeTagged(tag int) int { + return SizePrimitiveSubBytes(tag, i) +} +func (l INTEGER) Int() int { + return int(l) +} diff --git a/goldap/intermediate_response.go b/goldap/intermediate_response.go new file mode 100644 index 0000000..90bf188 --- /dev/null +++ b/goldap/intermediate_response.go @@ -0,0 +1,89 @@ +package message + +import "fmt" + +// +// IntermediateResponse ::= [APPLICATION 25] SEQUENCE { +// responseName [0] LDAPOID OPTIONAL, +// responseValue [1] OCTET STRING OPTIONAL } +func readIntermediateResponse(bytes *Bytes) (ret IntermediateResponse, err error) { + err = bytes.ReadSubBytes(classApplication, TagIntermediateResponse, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readIntermediateResponse:\n%s", err.Error())} + return + } + return +} +func (bytes *Bytes) PreviewTagAndLength() (tagAndLength TagAndLength, err error) { + previousOffset := bytes.offset // Save offset + tagAndLength, err = bytes.ParseTagAndLength() + bytes.offset = previousOffset // Restore offset + return +} +func (res *IntermediateResponse) readComponents(bytes *Bytes) (err error) { + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagIntermediateResponseName { + var oid LDAPOID + oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagIntermediateResponseName) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + res.responseName = oid.Pointer() + } + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagIntermediateResponseValue { + var str OCTETSTRING + str, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagIntermediateResponseValue) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + res.responseValue = str.Pointer() + } + } + return +} + +// +// IntermediateResponse ::= [APPLICATION 25] SEQUENCE { +// responseName [0] LDAPOID OPTIONAL, +// responseValue [1] OCTET STRING OPTIONAL } +func (i IntermediateResponse) write(bytes *Bytes) (size int) { + if i.responseValue != nil { + size += i.responseValue.writeTagged(bytes, classContextSpecific, TagIntermediateResponseValue) + } + if i.responseName != nil { + size += i.responseName.writeTagged(bytes, classContextSpecific, TagIntermediateResponseName) + } + size += bytes.WriteTagAndLength(classApplication, isCompound, TagIntermediateResponse, size) + return +} + +// +// IntermediateResponse ::= [APPLICATION 25] SEQUENCE { +// responseName [0] LDAPOID OPTIONAL, +// responseValue [1] OCTET STRING OPTIONAL } +func (i IntermediateResponse) size() (size int) { + if i.responseName != nil { + size += i.responseName.sizeTagged(TagIntermediateResponseName) + } + if i.responseValue != nil { + size += i.responseValue.sizeTagged(TagIntermediateResponseValue) + } + size += sizeTagAndLength(TagIntermediateResponse, size) + return +} diff --git a/goldap/matching_rule_assertion.go b/goldap/matching_rule_assertion.go new file mode 100644 index 0000000..e137852 --- /dev/null +++ b/goldap/matching_rule_assertion.go @@ -0,0 +1,117 @@ +package message + +import "fmt" + +// +// MatchingRuleAssertion ::= SEQUENCE { +// matchingRule [1] MatchingRuleId OPTIONAL, +// type [2] AttributeDescription OPTIONAL, +// matchValue [3] AssertionValue, +// dnAttributes [4] BOOLEAN DEFAULT FALSE } +func readTaggedMatchingRuleAssertion(bytes *Bytes, class int, tag int) (ret MatchingRuleAssertion, err error) { + err = bytes.ReadSubBytes(class, tag, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedMatchingRuleAssertion:\n%s", err.Error())} + return + } + return +} +func (matchingruleassertion *MatchingRuleAssertion) readComponents(bytes *Bytes) (err error) { + err = matchingruleassertion.readMatchingRule(bytes) + if err != nil { + return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} + } + err = matchingruleassertion.readType(bytes) + if err != nil { + return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} + } + matchingruleassertion.matchValue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue) + if err != nil { + return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} + } + matchingruleassertion.dnAttributes, err = readTaggedBOOLEAN(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes) + if err != nil { + return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} + } + return +} +func (matchingruleassertion *MatchingRuleAssertion) readMatchingRule(bytes *Bytes) (err error) { + var tagAndLength TagAndLength + tagAndLength, err = bytes.PreviewTagAndLength() + if err != nil { + return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())} + } + if tagAndLength.Tag == TagMatchingRuleAssertionMatchingRule { + var matchingRule MatchingRuleId + matchingRule, err = readTaggedMatchingRuleId(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule) + if err != nil { + return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())} + } + matchingruleassertion.matchingRule = matchingRule.Pointer() + } + return +} +func (matchingruleassertion *MatchingRuleAssertion) readType(bytes *Bytes) (err error) { + var tagAndLength TagAndLength + tagAndLength, err = bytes.PreviewTagAndLength() + if err != nil { + return LdapError{fmt.Sprintf("readType: %s", err.Error())} + } + if tagAndLength.Tag == TagMatchingRuleAssertionType { + var attributedescription AttributeDescription + attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagMatchingRuleAssertionType) + if err != nil { + return LdapError{fmt.Sprintf("readType: %s", err.Error())} + } + matchingruleassertion.type_ = &attributedescription + } + return +} +func (m MatchingRuleAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) { + if m.dnAttributes != BOOLEAN(false) { + size += m.dnAttributes.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes) + } + size += m.matchValue.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue) + if m.type_ != nil { + size += m.type_.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionType) + } + if m.matchingRule != nil { + size += m.matchingRule.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule) + } + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +// +// MatchingRuleAssertion ::= SEQUENCE { +// matchingRule [1] MatchingRuleId OPTIONAL, +// type [2] AttributeDescription OPTIONAL, +// matchValue [3] AssertionValue, +// dnAttributes [4] BOOLEAN DEFAULT FALSE } +func (m MatchingRuleAssertion) write(bytes *Bytes) (size int) { + return m.writeTagged(bytes, classUniversal, tagSequence) +} + +// +// MatchingRuleAssertion ::= SEQUENCE { +// matchingRule [1] MatchingRuleId OPTIONAL, +// type [2] AttributeDescription OPTIONAL, +// matchValue [3] AssertionValue, +// dnAttributes [4] BOOLEAN DEFAULT FALSE } +func (m MatchingRuleAssertion) size() (size int) { + return m.sizeTagged(tagSequence) +} +func (m MatchingRuleAssertion) sizeTagged(tag int) (size int) { + if m.matchingRule != nil { + size += m.matchingRule.sizeTagged(TagMatchingRuleAssertionMatchingRule) + } + if m.type_ != nil { + size += m.type_.sizeTagged(TagMatchingRuleAssertionType) + } + size += m.matchValue.sizeTagged(TagMatchingRuleAssertionMatchValue) + if m.dnAttributes != BOOLEAN(false) { + size += m.dnAttributes.sizeTagged(TagMatchingRuleAssertionDnAttributes) + } + size += sizeTagAndLength(tag, size) + return +} diff --git a/goldap/matching_rule_id.go b/goldap/matching_rule_id.go new file mode 100644 index 0000000..77417c2 --- /dev/null +++ b/goldap/matching_rule_id.go @@ -0,0 +1,29 @@ +package message + +import "fmt" + +// +// MatchingRuleId ::= LDAPString +func readTaggedMatchingRuleId(bytes *Bytes, class int, tag int) (matchingruleid MatchingRuleId, err error) { + var ldapstring LDAPString + ldapstring, err = readTaggedLDAPString(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedMatchingRuleId:\n%s", err.Error())} + return + } + matchingruleid = MatchingRuleId(ldapstring) + return +} +func (m MatchingRuleId) Pointer() *MatchingRuleId { return &m } + +// +// MatchingRuleId ::= LDAPString +func (m MatchingRuleId) writeTagged(bytes *Bytes, class int, tag int) int { + return LDAPString(m).writeTagged(bytes, class, tag) +} + +// +// MatchingRuleId ::= LDAPString +func (m MatchingRuleId) sizeTagged(tag int) int { + return LDAPString(m).sizeTagged(tag) +} diff --git a/goldap/message.go b/goldap/message.go new file mode 100644 index 0000000..a227d16 --- /dev/null +++ b/goldap/message.go @@ -0,0 +1,139 @@ +package message + +import ( + "fmt" + "reflect" +) + +// This appendix is normative. +// +// Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18} +// -- Copyright (C) The Internet Society (2006). This version of +// -- this ASN.1 module is part of RFC 4511; see the RFC itself +// -- for full legal notices. +// DEFINITIONS +// IMPLICIT TAGS +// EXTENSIBILITY IMPLIED ::= +// +// BEGIN +// +// 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 } +// + +func NewLDAPMessage() *LDAPMessage { return &LDAPMessage{} } + +func (message *LDAPMessage) readComponents(bytes *Bytes) (err error) { + message.messageID, err = readMessageID(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + message.protocolOp, err = readProtocolOp(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagLDAPMessageControls { + var controls Controls + controls, err = readTaggedControls(bytes, classContextSpecific, TagLDAPMessageControls) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + message.controls = controls.Pointer() + } + } + return +} + +func (m *LDAPMessage) Write() (bytes *Bytes, err error) { + defer func() { + if e := recover(); e != nil { + err = LdapError{fmt.Sprintf("Error in LDAPMessage.Write: %s", e)} + } + }() + // Compute the needed size + totalSize := m.size() + // Initialize the structure + bytes = &Bytes{ + bytes: make([]byte, totalSize), + offset: totalSize, + } + + // Go ! + size := 0 + if m.controls != nil { + size += m.controls.writeTagged(bytes, classContextSpecific, TagLDAPMessageControls) + } + size += m.protocolOp.write(bytes) + size += m.messageID.write(bytes) + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + // Check + if size != totalSize || bytes.offset != 0 { + err = LdapError{fmt.Sprintf("Something went wrong while writing the message ! Size is %d instead of %d, final offset is %d instead of 0", size, totalSize, bytes.offset)} + } + return +} +func (m *LDAPMessage) size() (size int) { + size += m.messageID.size() + size += m.protocolOp.size() + if m.controls != nil { + size += m.controls.sizeTagged(TagLDAPMessageControls) + } + size += sizeTagAndLength(tagSequence, size) + return +} +func (l *LDAPMessage) MessageID() MessageID { + return l.messageID +} +func (l *LDAPMessage) SetMessageID(ID int) { + l.messageID = MessageID(ID) +} +func (l *LDAPMessage) Controls() *Controls { + return l.controls +} +func (l *LDAPMessage) ProtocolOp() ProtocolOp { + return l.protocolOp +} +func (l *LDAPMessage) ProtocolOpName() string { + return reflect.TypeOf(l.ProtocolOp()).Name() +} +func (l *LDAPMessage) ProtocolOpType() int { + switch l.protocolOp.(type) { + case BindRequest: + return TagBindRequest + } + return 0 +} diff --git a/goldap/message_id.go b/goldap/message_id.go new file mode 100644 index 0000000..05c7157 --- /dev/null +++ b/goldap/message_id.go @@ -0,0 +1,46 @@ +package message + +import "fmt" + +func readTaggedMessageID(bytes *Bytes, class int, tag int) (ret MessageID, err error) { + var integer INTEGER + integer, err = readTaggedPositiveINTEGER(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedMessageID:\n%s", err.Error())} + return + } + return MessageID(integer), err +} + +// MessageID ::= INTEGER (0 .. maxInt) +// +// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- +// +func readMessageID(bytes *Bytes) (ret MessageID, err error) { + return readTaggedMessageID(bytes, classUniversal, tagInteger) +} + +// MessageID ::= INTEGER (0 .. maxInt) +// +// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- +// +func (m MessageID) write(bytes *Bytes) int { + return INTEGER(m).write(bytes) +} +func (m MessageID) writeTagged(bytes *Bytes, class int, tag int) int { + return INTEGER(m).writeTagged(bytes, class, tag) +} + +// MessageID ::= INTEGER (0 .. maxInt) +// +// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- +// +func (m MessageID) size() int { + return INTEGER(m).size() +} +func (m MessageID) sizeTagged(tag int) int { + return INTEGER(m).sizeTagged(tag) +} +func (l MessageID) Int() int { + return int(l) +} diff --git a/goldap/message_test.go b/goldap/message_test.go new file mode 100644 index 0000000..9d10d98 --- /dev/null +++ b/goldap/message_test.go @@ -0,0 +1,76 @@ +package message + +import ( + "testing" + "fmt" +) + +func toHex(b []byte) (r string) { + r = "[ " + for _, e := range b { + r += fmt.Sprintf("0x%x ", e) + } + return r + "]" +} + +func TestMessageID(t *testing.T) { + m := NewLDAPMessageWithProtocolOp(UnbindRequest{}) + m.SetMessageID(128) + buf, err := m.Write() + if err != nil { + t.Errorf("marshalling failed with %v", err) + } + t.Logf("%v", toHex(buf.Bytes())) + + ret, err := ReadLDAPMessage(NewBytes(0, buf.Bytes())) + if err != nil { + t.Errorf("unmarshalling failed with %v", err) + } + if _, ok := ret.ProtocolOp().(UnbindRequest); !ok { + t.Errorf("should be an unbind request") + } + if ret.MessageID() != 128 { + t.Errorf("Expect message id 128, got %d", ret.MessageID()) + } + t.Log("Done, marshal/unmarshall worked") +} + +func TestSearchEntry(t *testing.T) { + m := NewLDAPMessageWithProtocolOp(SearchResultEntry{ + objectName:"cn=êige€nbgtz,ou=users,dc=deuxfleurs,dc=fr", + attributes: PartialAttributeList{ + PartialAttribute{ + type_:"displayname", + vals:[]AttributeValue{"êiGe€NBgTZ"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"inetOrgPerson"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"organizationalPerson"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"person"}, + }, + PartialAttribute{ + type_:"objectclass", + vals:[]AttributeValue{"top"}, + }, + PartialAttribute{ + type_:"structuralobjectclass", + vals:[]AttributeValue{"inetOrgPerson"}, + }, + }, + }) + m.SetMessageID(24) + buf, err := m.Write() + if err != nil { + t.Errorf("marshalling failed with %v", err) + } + if buf.Bytes()[0] != 0x30 { + t.Logf("Malformed message: %v", toHex(buf.Bytes())) + } +} diff --git a/goldap/modify_dn_request.go b/goldap/modify_dn_request.go new file mode 100644 index 0000000..8c4ab75 --- /dev/null +++ b/goldap/modify_dn_request.go @@ -0,0 +1,87 @@ +package message + +import "fmt" + +// +// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { +// entry LDAPDN, +// newrdn RelativeLDAPDN, +// deleteoldrdn BOOLEAN, +// newSuperior [0] LDAPDN OPTIONAL } +func readModifyDNRequest(bytes *Bytes) (ret ModifyDNRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagModifyDNRequest, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readModifyDNRequest:\n%s", err.Error())} + return + } + return +} +func (req *ModifyDNRequest) readComponents(bytes *Bytes) (err error) { + req.entry, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + req.newrdn, err = readRelativeLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + req.deleteoldrdn, err = readBOOLEAN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagModifyDNRequestNewSuperior { + var ldapdn LDAPDN + ldapdn, err = readTaggedLDAPDN(bytes, classContextSpecific, TagModifyDNRequestNewSuperior) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + req.newSuperior = ldapdn.Pointer() + } + } + return +} + +// +// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { +// entry LDAPDN, +// newrdn RelativeLDAPDN, +// deleteoldrdn BOOLEAN, +// newSuperior [0] LDAPDN OPTIONAL } +func (m ModifyDNRequest) write(bytes *Bytes) (size int) { + if m.newSuperior != nil { + size += m.newSuperior.writeTagged(bytes, classContextSpecific, TagModifyDNRequestNewSuperior) + } + size += m.deleteoldrdn.write(bytes) + size += m.newrdn.write(bytes) + size += m.entry.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyDNRequest, size) + return +} + +// +// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { +// entry LDAPDN, +// newrdn RelativeLDAPDN, +// deleteoldrdn BOOLEAN, +// newSuperior [0] LDAPDN OPTIONAL } +func (m ModifyDNRequest) size() (size int) { + size += m.entry.size() + size += m.newrdn.size() + size += m.deleteoldrdn.size() + if m.newSuperior != nil { + size += m.newSuperior.sizeTagged(TagModifyDNRequestNewSuperior) + } + size += sizeTagAndLength(TagModifyDNRequest, size) + return +} diff --git a/goldap/modify_dn_response.go b/goldap/modify_dn_response.go new file mode 100644 index 0000000..9282aba --- /dev/null +++ b/goldap/modify_dn_response.go @@ -0,0 +1,28 @@ +package message + +import "fmt" + +// +// ModifyDNResponse ::= [APPLICATION 13] LDAPResult +func readModifyDNResponse(bytes *Bytes) (ret ModifyDNResponse, err error) { + var res LDAPResult + res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyDNResponse) + if err != nil { + err = LdapError{fmt.Sprintf("readModifyDNResponse:\n%s", err.Error())} + return + } + ret = ModifyDNResponse(res) + return +} + +// +// ModifyDNResponse ::= [APPLICATION 13] LDAPResult +func (m ModifyDNResponse) write(bytes *Bytes) int { + return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyDNResponse) +} + +// +// ModifyDNResponse ::= [APPLICATION 13] LDAPResult +func (m ModifyDNResponse) size() int { + return LDAPResult(m).sizeTagged(TagModifyDNResponse) +} diff --git a/goldap/modify_request.go b/goldap/modify_request.go new file mode 100644 index 0000000..c66c00d --- /dev/null +++ b/goldap/modify_request.go @@ -0,0 +1,89 @@ +package message + +import "fmt" + +// +// ModifyRequest ::= [APPLICATION 6] SEQUENCE { +// object LDAPDN, +// changes SEQUENCE OF change SEQUENCE { +// operation ENUMERATED { +// add (0), +// delete (1), +// replace (2), +// ... }, +// modification PartialAttribute } } +func readModifyRequest(bytes *Bytes) (ret ModifyRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagModifyRequest, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readModifyRequest:\n%s", err.Error())} + return + } + return +} +func (m *ModifyRequest) readComponents(bytes *Bytes) (err error) { + m.object, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + err = bytes.ReadSubBytes(classUniversal, tagSequence, m.readChanges) + return +} +func (m *ModifyRequest) readChanges(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var c ModifyRequestChange + c, err = readModifyRequestChange(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readChanges:\n%s", err.Error())} + return + } + m.changes = append(m.changes, c) + } + return +} + +// +// ModifyRequest ::= [APPLICATION 6] SEQUENCE { +// object LDAPDN, +// changes SEQUENCE OF change SEQUENCE { +// operation ENUMERATED { +// add (0), +// delete (1), +// replace (2), +// ... }, +// modification PartialAttribute } } +func (m ModifyRequest) write(bytes *Bytes) (size int) { + for i := len(m.changes) - 1; i >= 0; i-- { + size += m.changes[i].write(bytes) + } + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + size += m.object.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyRequest, size) + return +} + +// +// ModifyRequest ::= [APPLICATION 6] SEQUENCE { +// object LDAPDN, +// changes SEQUENCE OF change SEQUENCE { +// operation ENUMERATED { +// add (0), +// delete (1), +// replace (2), +// ... }, +// modification PartialAttribute } } +func (m ModifyRequest) size() (size int) { + for _, change := range m.changes { + size += change.size() + } + size += sizeTagAndLength(tagSequence, size) + size += m.object.size() + size += sizeTagAndLength(TagModifyRequest, size) + return +} +func (m *ModifyRequest) Object() LDAPDN { + return m.object +} +func (m *ModifyRequest) Changes() []ModifyRequestChange { + return m.changes +} diff --git a/goldap/modify_request_change.go b/goldap/modify_request_change.go new file mode 100644 index 0000000..e3728c1 --- /dev/null +++ b/goldap/modify_request_change.go @@ -0,0 +1,43 @@ +package message + +import "fmt" + +func readModifyRequestChange(bytes *Bytes) (ret ModifyRequestChange, err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readModifyRequestChange:\n%s", err.Error())} + return + } + return +} +func (m *ModifyRequestChange) readComponents(bytes *Bytes) (err error) { + m.operation, err = readENUMERATED(bytes, EnumeratedModifyRequestChangeOperation) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + m.modification, err = readPartialAttribute(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} +func (m ModifyRequestChange) write(bytes *Bytes) (size int) { + size += m.modification.write(bytes) + size += m.operation.write(bytes) + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} +func (m ModifyRequestChange) size() (size int) { + size += m.operation.size() + size += m.modification.size() + size += sizeTagAndLength(tagSequence, size) + return +} +func (m *ModifyRequestChange) Operation() ENUMERATED { + return m.operation +} +func (m *ModifyRequestChange) Modification() *PartialAttribute { + return &m.modification +} diff --git a/goldap/modify_response.go b/goldap/modify_response.go new file mode 100644 index 0000000..2a214d2 --- /dev/null +++ b/goldap/modify_response.go @@ -0,0 +1,36 @@ +package message + +import "fmt" + +// +// ModifyResponse ::= [APPLICATION 7] LDAPResult +func readModifyResponse(bytes *Bytes) (ret ModifyResponse, err error) { + var res LDAPResult + res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyResponse) + if err != nil { + err = LdapError{fmt.Sprintf("readModifyResponse:\n%s", err.Error())} + return + } + ret = ModifyResponse(res) + return +} +func (l LDAPResult) writeTagged(bytes *Bytes, class int, tag int) (size int) { + size += l.writeComponents(bytes) + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +// +// ModifyResponse ::= [APPLICATION 7] LDAPResult +func (m ModifyResponse) write(bytes *Bytes) int { + return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyResponse) +} + +// +// ModifyResponse ::= [APPLICATION 7] LDAPResult +func (m ModifyResponse) size() int { + return LDAPResult(m).sizeTagged(TagModifyResponse) +} +func (l *ModifyResponse) SetResultCode(code int) { + l.resultCode = ENUMERATED(code) +} diff --git a/goldap/octetstring.go b/goldap/octetstring.go new file mode 100644 index 0000000..719f838 --- /dev/null +++ b/goldap/octetstring.go @@ -0,0 +1,44 @@ +package message + +import "fmt" + +func readOCTETSTRING(bytes *Bytes) (ret OCTETSTRING, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagOctetString, tagOctetString) + if err != nil { + err = LdapError{fmt.Sprintf("readOCTETSTRING:\n%s", err.Error())} + return + } + ret = OCTETSTRING(value.([]byte)) + return +} + +func readTaggedOCTETSTRING(bytes *Bytes, class int, tag int) (ret OCTETSTRING, err error) { + var value interface{} + value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagOctetString) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedOCTETSTRING:\n%s", err.Error())} + return + } + ret = OCTETSTRING(value.([]byte)) + return +} +func (o OCTETSTRING) Pointer() *OCTETSTRING { return &o } +func (o OCTETSTRING) write(bytes *Bytes) int { + return bytes.WritePrimitiveSubBytes(classUniversal, tagOctetString, o) +} +func (o OCTETSTRING) writeTagged(bytes *Bytes, class int, tag int) int { + return bytes.WritePrimitiveSubBytes(class, tag, o) +} +func (o OCTETSTRING) size() int { + return SizePrimitiveSubBytes(tagOctetString, o) +} +func (o OCTETSTRING) sizeTagged(tag int) int { + return SizePrimitiveSubBytes(tag, o) +} +func (l OCTETSTRING) String() string { + return string(l) +} +func (l OCTETSTRING) Bytes() []byte { + return []byte(l) +} diff --git a/goldap/oid.go b/goldap/oid.go new file mode 100644 index 0000000..b2c5c81 --- /dev/null +++ b/goldap/oid.go @@ -0,0 +1,50 @@ +package message + +import "fmt" + +// +// +// LDAPOID ::= OCTET STRING -- Constrained to +// -- [RFC4512] + +func (l LDAPOID) String() string { + return string(l) +} + +func (l LDAPOID) Bytes() []byte { + return []byte(l) +} + +func (l LDAPOID) Pointer() *LDAPOID { return &l } + +func readTaggedLDAPOID(bytes *Bytes, class int, tag int) (ret LDAPOID, err error) { + var octetstring OCTETSTRING + octetstring, err = readTaggedOCTETSTRING(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedLDAPOID:\n%s", err.Error())} + return + } + // @TODO: check RFC4512 for + ret = LDAPOID(octetstring) + return +} + +func readLDAPOID(bytes *Bytes) (ret LDAPOID, err error) { + return readTaggedLDAPOID(bytes, classUniversal, tagOctetString) +} + +func (l LDAPOID) write(bytes *Bytes) int { + return OCTETSTRING(l).write(bytes) +} + +func (l LDAPOID) writeTagged(bytes *Bytes, class int, tag int) int { + return OCTETSTRING(l).writeTagged(bytes, class, tag) +} + +func (l LDAPOID) size() int { + return OCTETSTRING(l).size() +} + +func (l LDAPOID) sizeTagged(tag int) int { + return OCTETSTRING(l).sizeTagged(tag) +} diff --git a/goldap/partial_attribute.go b/goldap/partial_attribute.go new file mode 100644 index 0000000..4cd0215 --- /dev/null +++ b/goldap/partial_attribute.go @@ -0,0 +1,76 @@ +package message + +import "fmt" + +// +// PartialAttribute ::= SEQUENCE { +// type AttributeDescription, +// vals SET OF value AttributeValue } +func readPartialAttribute(bytes *Bytes) (ret PartialAttribute, err error) { + ret = PartialAttribute{vals: make([]AttributeValue, 0, 10)} + err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readPartialAttribute:\n%s", err.Error())} + return + } + return +} +func (partialattribute *PartialAttribute) readComponents(bytes *Bytes) (err error) { + partialattribute.type_, err = readAttributeDescription(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + err = bytes.ReadSubBytes(classUniversal, tagSet, partialattribute.readValsComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} +func (partialattribute *PartialAttribute) readValsComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var attributevalue AttributeValue + attributevalue, err = readAttributeValue(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readValsComponents:\n%s", err.Error())} + return + } + partialattribute.vals = append(partialattribute.vals, attributevalue) + } + return +} + +// +// PartialAttribute ::= SEQUENCE { +// type AttributeDescription, +// vals SET OF value AttributeValue } +func (p PartialAttribute) write(bytes *Bytes) (size int) { + for i := len(p.vals) - 1; i >= 0; i-- { + size += p.vals[i].write(bytes) + } + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSet, size) + size += p.type_.write(bytes) + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} + +// +// PartialAttribute ::= SEQUENCE { +// type AttributeDescription, +// vals SET OF value AttributeValue } +func (p PartialAttribute) size() (size int) { + for _, value := range p.vals { + size += value.size() + } + size += sizeTagAndLength(tagSet, size) + size += p.type_.size() + size += sizeTagAndLength(tagSequence, size) + return +} +func (p *PartialAttribute) Type_() AttributeDescription { + return p.type_ +} +func (p *PartialAttribute) Vals() []AttributeValue { + return p.vals +} diff --git a/goldap/partial_attribute_list.go b/goldap/partial_attribute_list.go new file mode 100644 index 0000000..f55d4f9 --- /dev/null +++ b/goldap/partial_attribute_list.go @@ -0,0 +1,53 @@ +package message + +import "fmt" + +// +// PartialAttributeList ::= SEQUENCE OF +// partialAttribute PartialAttribute +func readPartialAttributeList(bytes *Bytes) (ret PartialAttributeList, err error) { + ret = PartialAttributeList(make([]PartialAttribute, 0, 10)) + err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readPartialAttributeList:\n%s", err.Error())} + return + } + return +} +func (partialattributelist *PartialAttributeList) readComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var partialattribute PartialAttribute + partialattribute, err = readPartialAttribute(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + *partialattributelist = append(*partialattributelist, partialattribute) + } + return +} + +// +// PartialAttributeList ::= SEQUENCE OF +// partialAttribute PartialAttribute +func (p PartialAttributeList) write(bytes *Bytes) (size int) { + for i := len(p) - 1; i >= 0; i-- { + size += p[i].write(bytes) + } + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} + +// +// PartialAttributeList ::= SEQUENCE OF +// partialAttribute PartialAttribute +func (p PartialAttributeList) size() (size int) { + for _, att := range p { + size += att.size() + } + size += sizeTagAndLength(tagSequence, size) + return +} +func (p *PartialAttributeList) add(a PartialAttribute) { + *p = append(*p, a) +} diff --git a/goldap/protocol_op.go b/goldap/protocol_op.go new file mode 100644 index 0000000..0ea3881 --- /dev/null +++ b/goldap/protocol_op.go @@ -0,0 +1,63 @@ +package message + +import "fmt" + +func readProtocolOp(bytes *Bytes) (ret ProtocolOp, err error) { + tagAndLength, err := bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())} + return + } + switch tagAndLength.Tag { + case TagBindRequest: + ret, err = readBindRequest(bytes) + case TagBindResponse: + ret, err = readBindResponse(bytes) + case TagUnbindRequest: + ret, err = readUnbindRequest(bytes) + case TagSearchRequest: + ret, err = readSearchRequest(bytes) + case TagSearchResultEntry: + ret, err = readSearchResultEntry(bytes) + case TagSearchResultDone: + ret, err = readSearchResultDone(bytes) + case TagSearchResultReference: + ret, err = readSearchResultReference(bytes) + case TagModifyRequest: + ret, err = readModifyRequest(bytes) + case TagModifyResponse: + ret, err = readModifyResponse(bytes) + case TagAddRequest: + ret, err = readAddRequest(bytes) + case TagAddResponse: + ret, err = readAddResponse(bytes) + case TagDelRequest: + ret, err = readDelRequest(bytes) + case TagDelResponse: + ret, err = readDelResponse(bytes) + case TagModifyDNRequest: + ret, err = readModifyDNRequest(bytes) + case TagModifyDNResponse: + ret, err = readModifyDNResponse(bytes) + case TagCompareRequest: + ret, err = readCompareRequest(bytes) + case TagCompareResponse: + ret, err = readCompareResponse(bytes) + case TagAbandonRequest: + ret, err = readAbandonRequest(bytes) + case TagExtendedRequest: + ret, err = readExtendedRequest(bytes) + case TagExtendedResponse: + ret, err = readExtendedResponse(bytes) + case TagIntermediateResponse: + ret, err = readIntermediateResponse(bytes) + default: + err = LdapError{fmt.Sprintf("readProtocolOp: invalid tag value %d for protocolOp", tagAndLength.Tag)} + return + } + if err != nil { + err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())} + return + } + return +} diff --git a/goldap/read.go b/goldap/read.go new file mode 100644 index 0000000..4ed9deb --- /dev/null +++ b/goldap/read.go @@ -0,0 +1,18 @@ +package message + +import ( + "fmt" +) + +func ReadLDAPMessage(bytes *Bytes) (message LDAPMessage, err error) { + err = bytes.ReadSubBytes(classUniversal, tagSequence, message.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("ReadLDAPMessage:\n%s", err.Error())} + return + } + return +} + +// +// END +// diff --git a/goldap/read_error_test.go b/goldap/read_error_test.go new file mode 100644 index 0000000..d2d3878 --- /dev/null +++ b/goldap/read_error_test.go @@ -0,0 +1,3146 @@ +package message + +import ( + "strings" + "testing" +) + +type LDAPMessageErrorTestData struct { + label string + bytes Bytes + err string +} + +func TestReadLDAPMessageError(t *testing.T) { + for i, test := range getLDAPMessageErrorTestData() { + message, err := ReadLDAPMessage(&test.bytes) + if err == nil { + t.Errorf("#%d: %s\nEXPECTED ERROR MESSAGE:\n%s\nGOT A LDAP STRUCT INSTEAD:\n%#+v", i, test.label, test.err, message) + } else if !strings.Contains(err.Error(), test.err) { + t.Errorf("#%d: %s\nGOT:\n%s\nEXPECTED:\n%s", i+1, test.label, err.Error(), test.err) + } + } +} + +func getLDAPMessageErrorTestData() (ret []LDAPMessageErrorTestData) { + return []LDAPMessageErrorTestData{ + // Request 1: client => bind request + // Wrong authentication tag + { + label: "Client => bind request, wrong authentication tag", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c020101600702010304008000 + 0x30, 0x0c, + 0x02, 0x01, 0x01, + // BindRequest ::= [APPLICATION 0] SEQUENCE { + 0x60, 0x07, + // version INTEGER (1 .. 127), + 0x02, 0x01, 0x03, + // name LDAPDN, + 0x04, 0x00, + // authentication AuthenticationChoice } + // AuthenticationChoice ::= CHOICE { + // simple [0] OCTET STRING, + 0x81, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readBindRequest: +ReadSubBytes: +readComponents: +readAuthenticationChoice: invalid tag value 1 for AuthenticationChoice`, + }, + + // Request 2: client => bind request + // Wrong version (too small) + { + label: "Client => bind request, wrong version (too small)", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c020101600702010304008000 + 0x30, 0x0c, + 0x02, 0x01, 0x01, + // BindRequest ::= [APPLICATION 0] SEQUENCE { + 0x60, 0x07, + // version INTEGER (1 .. 127), + 0x02, 0x01, 0x00, + // name LDAPDN, + 0x04, 0x00, + // authentication AuthenticationChoice } + // AuthenticationChoice ::= CHOICE { + // simple [0] OCTET STRING, + 0x81, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readBindRequest: +ReadSubBytes: +readComponents: invalid version 0, must be between 1 and 127`, + }, + + // Request 3: client => bind request + // Wrong version (too large) + { + label: "Client => bind request, wrong wrong version (too large)", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c020101600702010304008000 + 0x30, 0x0d, + 0x02, 0x01, 0x01, + // BindRequest ::= [APPLICATION 0] SEQUENCE { + 0x60, 0x08, + // version INTEGER (1 .. 127), + 0x02, 0x02, 0x00, 0xff, + // name LDAPDN, + 0x04, 0x00, + // authentication AuthenticationChoice } + // AuthenticationChoice ::= CHOICE { + // simple [0] OCTET STRING, + 0x81, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readBindRequest: +ReadSubBytes: +readComponents: invalid version 255, must be between 1 and 127`, + }, + + // Request 4: client => bind request + // Invalid type for version + { + label: "Client => bind request, invalid type for version", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c020101600702010304008000 + 0x30, 0x0d, + 0x02, 0x01, 0x01, + // BindRequest ::= [APPLICATION 0] SEQUENCE { + 0x60, 0x08, + // version INTEGER (1 .. 127), + 0x04, 0x02, 0x30, 0x30, + // name LDAPDN, + 0x04, 0x00, + // authentication AuthenticationChoice } + // AuthenticationChoice ::= CHOICE { + // simple [0] OCTET STRING, + 0x81, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readBindRequest: +ReadSubBytes: +readComponents: +readINTEGER: +ReadPrimitiveSubBytes: +Expect: asn1: syntax error: ExpectTag: wrong tag value: got 4 (OCTET STRING), expected 2 (INTEGER).`, + }, + + // Request 5: client SearchRequest with FilterExtensibleMatch + // Error: invalid tag and length for the extensible filter dnAttribute boolean + { + label: "Client SearchRequest with FilterExtensibleMatch, invalid tag and length for the extensible filter dnAttribute boolean", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3074020131636f04166f753d636f6e73756d6572732c6f753d73797374656d0a01020a0103020203e8020100010100a936811474656c6570686f6e654e756d6265724d61746368820f74656c6570686f6e654e756d626572830a30313233343536373839 + // 8401ff300d040b6f626a656374436c617373 + 0x30, 0x74, 0x02, 0x01, 0x31, 0x63, 0x6f, 0x04, 0x16, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa9, 0x36, 0x81, 0x14, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x82, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x83, 0x0a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + // dnAttributes [4] BOOLEAN DEFAULT FALSE + 0x84, 0x40, 0xff, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readSearchRequest: +ReadSubBytes: +readComponents: +readFilter: +readFilterExtensibleMatch: +readTaggedMatchingRuleAssertion: +ReadSubBytes: +readComponents: readTaggedBOOLEAN: +ReadPrimitiveSubBytes: data truncated: expecting 64 bytes at offset 53 but only 1 bytes are remaining`, + }, + + // Request 6: SERVER SearchResultDone with Controls + // Error: invalid boolean for the criticality of controls + { + label: "server SearchResultDone with Controls, invalid boolean 0x0f for the criticality of controls", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303402012465070a010004000400a02630240416312e322e3834302e3131333535362e312e342e3331390101ff040730050201000400 + 0x30, 0x34, 0x02, 0x01, 0x24, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, 0xa0, 0x26, 0x30, 0x24, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, + // Bad boolean here: 0x0f is not a 0x00 of 0xFF + 0x01, 0x01, 0x0f, + 0x04, 0x07, 0x30, 0x05, 0x02, 0x01, 0x00, 0x04, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readTaggedControls: +ReadSubBytes: +readComponents: +readControl: +ReadSubBytes: +readComponents: +readBOOLEAN: +ReadPrimitiveSubBytes: +asn1: syntax error: invalid boolean: should be 0x00 of 0xFF`, + }, + + // An abandon request with invalid negative abandon message ID + { + label: "client AbandonRequest, invalid negative abandon message ID : 0x9f = -97", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + 0x30, 0x06, + 0x02, 0x01, 0x0a, // messageID + 0x50, 0x01, 0x9f, // Abandon request [APPLICATION 16] MessageID = 0x9f = -97 (invalid !) + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readAbandonRequest: +readTaggedMessageID: +readTaggedPositiveINTEGER: Invalid INTEGER value -97 ! Expected value between 0 and 2147483647`, + }, + + // An abandon request with an invalid too large abandon message ID + { + label: "client AbandonRequest, invalid too large abandon message ID", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + 0x30, 0x0d, + 0x02, 0x01, 0x0a, // messageID + 0x50, 0x08, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // Abandon request [APPLICATION 16] MessageID = 0x0f ff ff ff ff ff ff ff + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readAbandonRequest: +readTaggedMessageID: +readTaggedPositiveINTEGER: +readTaggedINTEGER: +ReadPrimitiveSubBytes: +asn1: structure error: integer too large`, + }, + + { + label: "server SearchResultDone, invalid too large result code", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010b65070a010004000400 + 0x30, 0x13, 0x02, 0x01, 0x0b, 0x65, 0x0e, + 0x0a, 0x08, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x04, 0x00, 0x04, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readSearchResultDone: +readTaggedLDAPResult: +ReadSubBytes: +readComponents: +readENUMERATED: +ReadPrimitiveSubBytes: +asn1: structure error: integer too large`, + }, + + { + label: "server SearchResultDone, invalid result code", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010b65070a010004000400 + 0x30, 0x0d, 0x02, 0x01, 0x0b, 0x65, 0x08, + 0x0a, 0x02, 0x0f, 0xff, + 0x04, 0x00, 0x04, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readSearchResultDone: +readTaggedLDAPResult: +ReadSubBytes: +readComponents: +readENUMERATED: Invalid ENUMERATED VALUE 4095`, + }, + + { + label: "client delRequest, invalid length for the protocolOp", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30400201274a3b636e3d4120636f6d706c657820706572736f6e5c2c207665727920636f6d706c657820212c6f753d636f6e73756d6572732c6f753d73797374656d + 0x30, 0x40, 0x02, 0x01, 0x27, + 0x4a, 0x3c, 0x63, 0x6e, 0x3d, 0x41, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x5c, 0x2c, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x20, 0x21, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +readDelRequest: +readTaggedLDAPDN: +readTaggedLDAPString: +readTaggedOCTETSTRING: +ReadPrimitiveSubBytes: data truncated: expecting 60 bytes at offset 5 but only 59 bytes are remaining`, + }, + + { + label: "client searchRequest with controls, invalid length for the control value", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819c020124633704096f753d73797374656d0a01020a0103020203e8020100010100870b6f626a656374436c617373300d040b6f626a656374436c617373a05e301e0417312e332e362e312e342e312e343230332e312e31302e3104030101ff30190417322e31362e3834302e312e3131333733302e332e342e3230210416312e322e3834302e3131333535362e312e342e333139040730050201030400 + 0x30, 0x81, 0x9c, 0x02, 0x01, 0x24, 0x63, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0xa0, 0x5e, 0x30, 0x1e, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x04, 0x03, 0x01, 0x01, 0xff, 0x30, 0x19, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x30, 0x21, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, + // controlValue: OCTETSTRING + 0x04, + 0x08, // Wrong length here + 0x30, 0x05, 0x02, 0x01, 0x03, 0x04, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readTaggedControls: +ReadSubBytes: +readComponents: +readControl: +ReadSubBytes: +readComponents: +readOCTETSTRING: +ReadPrimitiveSubBytes: data truncated: expecting 8 bytes at offset 26 but only 7 bytes are remaining`, + }, + + { + label: "client AbandonRequest, invalid type for the MessageID", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + 0x30, 0x06, + 0x04, 0x01, 0x0a, // messageID + 0x50, 0x01, 0x05, // Abandon request [APPLICATION 16] MessageID = 0x05 + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readTaggedMessageID: +readTaggedPositiveINTEGER: +readTaggedINTEGER: +ReadPrimitiveSubBytes: +Expect: asn1: syntax error: ExpectTag: wrong tag value: got 4 (OCTET STRING), expected 2 (INTEGER).`, + }, + + { + label: "client searchRequest with controls, invalid length for the controls", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819c020124633704096f753d73797374656d0a01020a0103020203e8020100010100870b6f626a656374436c617373300d040b6f626a656374436c617373a05e301e0417312e332e362e312e342e312e343230332e312e31302e3104030101ff30190417322e31362e3834302e312e3131333733302e332e342e3230210416312e322e3834302e3131333535362e312e342e333139040730050201030400 + 0x30, 0x81, 0x9c, 0x02, 0x01, 0x24, 0x63, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0xa0, 0x82, 0x00, 0x5e, // invalid length: not in DER format + 0x30, 0x1e, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x04, 0x03, 0x01, 0x01, 0xff, 0x30, 0x19, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x30, 0x21, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, 0x04, 0x07, 0x30, 0x05, 0x02, 0x01, 0x03, 0x04, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +ParseTagAndLength: asn1: structure error: superfluous leading zeros in length`, + }, + + { + label: "client BindRequest, invalid length for the ProtocolOp", + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c020101600702010304008000 + 0x30, 0x0e, + 0x02, 0x01, 0x01, + // BindRequest ::= [APPLICATION 0] SEQUENCE { + 0x60, 0x82, 0x00, 0x07, // Invalid length here + // version INTEGER (1 .. 127), + 0x02, 0x01, 0x03, + // name LDAPDN, + 0x04, 0x00, + // authentication AuthenticationChoice } + // AuthenticationChoice ::= CHOICE { + // simple [0] OCTET STRING, + 0x80, 0x00, + }, + }, + err: `ReadLDAPMessage: +ReadSubBytes: +readComponents: +readProtocolOp: +ParseTagAndLength: asn1: structure error: superfluous leading zeros in length`, + }, + + // // Request 2: server => bind response + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010161070a010004000400 + // 0x30, 0x0c, + // 0x02, 0x01, 0x01, + // // BindResponse ::= [APPLICATION 1] SEQUENCE { + // // COMPONENTS OF LDAPResult, + // 0x61, 0x07, + // // LDAPResult ::= SEQUENCE { + // // resultCode ENUMERATED { + // // success (0), + // // ... }, + // 0x0a, 0x01, 0x00, + // // matchedDN LDAPDN, + // 0x04, 0x00, + // // diagnosticMessage LDAPString, + // 0x04, 0x00, + // // referral [3] Referral OPTIONAL } + // // serverSaslCreds [7] OCTET STRING OPTIONAL } + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x01)), + // protocolOp: BindResponse{ + // LDAPResult: LDAPResult{ + // resultCode: 0, + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // }, + // }, + // }, + // }, + + // // Request 3: client => search request + // // select "subschemaSubentry" from entries where "objectClass" is present + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3038020102633304000a01000a0103020100020100010100870b6f626a656374436c61737330130411737562736368656d61537562656e747279 + // 0x30, 0x38, + // 0x02, 0x01, 0x02, + // // SearchRequest ::= [APPLICATION 3] SEQUENCE { + // 0x63, 0x33, + // // baseObject LDAPDN, + // 0x04, 0x00, + // // scope ENUMERATED { + // // baseObject (0), + // // ... }, + // 0x0a, 0x01, 0x00, + // // derefAliases ENUMERATED { + // // derefAlways (3) }, + // 0x0a, 0x01, 0x03, + // // sizeLimit INTEGER (0 .. maxInt), + // 0x02, 0x01, 0x00, + // // timeLimit INTEGER (0 .. maxInt), + // 0x02, 0x01, 0x00, + // // typesOnly BOOLEAN, + // 0x01, 0x01, 0x00, + // // filter Filter, + // // Filter ::= CHOICE { + // // present [7] AttributeDescription, + // // ... } + // // AttributeDescription ::= LDAPString + // // -- Constrained to + // // -- [RFC4512] + // 0x87, 0x0b, + // // "objectClass" + // 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // // attributes AttributeSelection } + // // AttributeSelection ::= SEQUENCE OF selector LDAPString + // // -- The LDAPString is constrained to + // // -- in Section 4.5.1.8 + // 0x30, 0x13, + // 0x04, 0x11, + // // "subschemaSubentry" + // 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x02)), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN(""), + // scope: SearchRequestScopeBaseObject, + // derefAliases: SearchRequetDerefAliasesDerefAlways, + // sizeLimit: 0, + // timeLimit: 0, + // typesOnly: false, + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection([]LDAPString{"subschemaSubentry"}), + // }, + // }, + // }, + + // // Request 4: server => search result entry + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 302b02010264260400302230200411737562736368656d61537562656e747279310b0409636e3d736368656d61 + // 0x30, 0x2b, + // 0x02, 0x01, 0x02, + // // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { + // 0x64, 0x26, + // // objectName LDAPDN, + // 0x04, 0x00, + // // attributes PartialAttributeList } + // // PartialAttributeList ::= SEQUENCE OF + // // partialAttribute PartialAttribute + // 0x30, 0x22, + // // PartialAttribute ::= SEQUENCE { + // 0x30, 0x20, + // // type AttributeDescription, + // 0x04, 0x11, + // // "subschemaSubentry" + // 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, + // // vals SET OF value AttributeValue } + // // AttributeValue ::= OCTET STRING + // 0x31, 0x0b, + // 0x04, 0x09, + // // "cn=schema" + // 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x02)), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN(""), + // attributes: PartialAttributeList( + // []PartialAttribute{ + // PartialAttribute{ + // type_: AttributeDescription("subschemaSubentry"), + // vals: []AttributeValue{AttributeValue("cn=schema")}, + // }, + // }, + // ), + // }, + // }, + // }, + + // // Request 5: server => search result done + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010265070a010004000400 + // 0x30, 0x0c, + // 0x02, 0x01, 0x02, + // // SearchResultDone ::= [APPLICATION 5] LDAPResult + // // LDAPResult ::= SEQUENCE { + // 0x65, 0x07, + // // resultCode ENUMERATED { + // // success (0), + // // ... }, + // 0x0a, 0x01, 0x00, + // // matchedDN LDAPDN, + // 0x04, 0x00, + // // diagnosticMessage LDAPString, + // 0x04, 0x00, + // // referral [3] Referral OPTIONAL } + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x02)), + // protocolOp: SearchResultDone(LDAPResult{ + // resultCode: ResultCodeSuccess, + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // }), + // }, + // }, + + // // Request 6: client => search request + // // select + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 305d02010363580409636e3d736368656d610a01000a0103020100020100010100a318040b6f626a656374436c6173730409737562736368656d613022040f63726561746554696d657374616d70040f6d6f6469667954696d657374616d70 + // 0x30, 0x5d, + // 0x02, 0x01, 0x03, + // // SearchRequest ::= [APPLICATION 3] SEQUENCE { + // 0x63, 0x58, + // // baseObject LDAPDN, + // // "cn=schema" + // 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // // scope ENUMERATED { + // // baseObject (0), + // // ... }, + // 0x0a, 0x01, 0x00, + // // derefAliases ENUMERATED { + // // derefAlways (3) }, + // 0x0a, 0x01, 0x03, + // // sizeLimit INTEGER (0 .. maxInt), + // 0x02, 0x01, 0x00, + // // timeLimit INTEGER (0 .. maxInt), + // 0x02, 0x01, 0x00, + // // typesOnly BOOLEAN, + // 0x01, 0x01, 0x00, + // // filter Filter, + // // Filter ::= CHOICE { + // // equalityMatch [3] AttributeValueAssertion, + // // AttributeValueAssertion ::= SEQUENCE { + // 0xa3, 0x18, + // // attributeDesc AttributeDescription, + // // "objectClass" + // 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // // assertionValue AssertionValue } + // // "subschema" + // 0x04, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // // attributes AttributeSelection } + // // AttributeSelection ::= SEQUENCE OF selector LDAPString + // // -- The LDAPString is constrained to + // // -- in Section 4.5.1.8 + // 0x30, 0x22, + // // "createTimestamp" + // 0x04, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // // "modifyTimestamp" + // 0x04, 0x0f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x03)), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("cn=schema"), + // scope: SearchRequestScopeBaseObject, + // derefAliases: SearchRequetDerefAliasesDerefAlways, + // sizeLimit: 0, + // timeLimit: 0, + // typesOnly: false, + // filter: FilterEqualityMatch( + // AttributeValueAssertion{ + // attributeDesc: AttributeDescription("objectClass"), + // assertionValue: AssertionValue("subschema"), + // }), + // attributes: AttributeSelection([]LDAPString{"createTimestamp", "modifyTimestamp"}), + // }, + // }, + // }, + + // // Request 7: server => search result entry + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 305e02010364590409636e3d736368656d61304c3024040f6d6f6469667954696d657374616d703111040f32303039303831383032323733335a3024040f63726561746554696d657374616d703111040f32303039303831383032323733335a + // 0x30, 0x5e, + // 0x02, 0x01, 0x03, + // // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { + // 0x64, 0x59, + // // objectName LDAPDN, + // // "cn=schema" + // 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // // attributes PartialAttributeList } + // // PartialAttributeList ::= SEQUENCE OF + // // partialAttribute PartialAttribute + // 0x30, 0x4c, + // // PartialAttribute ::= SEQUENCE { + // 0x30, 0x24, + // // type AttributeDescription, + // // "modifyTimestamp" + // 0x04, 0x0f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // // vals SET OF value AttributeValue } + // 0x31, 0x11, + // // "20090818022733Z" + // 0x04, 0x0f, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x31, 0x38, 0x30, 0x32, 0x32, 0x37, 0x33, 0x33, 0x5a, + // // PartialAttribute ::= SEQUENCE { + // 0x30, 0x24, + // // type AttributeDescription, + // // "createTimestamp" + // 0x04, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // // vals SET OF value AttributeValue } + // 0x31, 0x11, + // // "20090818022733Z" + // 0x04, 0x0f, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x31, 0x38, 0x30, 0x32, 0x32, 0x37, 0x33, 0x33, 0x5a, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x03)), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("cn=schema"), + // attributes: PartialAttributeList( + // []PartialAttribute{ + // PartialAttribute{ + // type_: AttributeDescription("modifyTimestamp"), + // vals: []AttributeValue{AttributeValue("20090818022733Z")}, + // }, + // PartialAttribute{ + // type_: AttributeDescription("createTimestamp"), + // vals: []AttributeValue{AttributeValue("20090818022733Z")}, + // }, + // }, + // ), + // }, + // }, + // }, + + // // Request 8 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010365070a010004000400 + // 0x30, 0x0c, + // 0x02, 0x01, 0x03, + // // SearchResultDone ::= [APPLICATION 5] LDAPResult + // // LDAPResult ::= SEQUENCE { + // 0x65, 0x07, + // // resultCode ENUMERATED { + // // success (0), + // // ... }, + // 0x0a, 0x01, 0x00, + // // matchedDN LDAPDN, + // 0x04, 0x00, + // // diagnosticMessage LDAPString, + // 0x04, 0x00, + // // referral [3] Referral OPTIONAL } + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x03)), + // protocolOp: SearchResultDone(LDAPResult{ + // resultCode: ResultCodeSuccess, + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // }), + // }, + // }, + + // // Request 9: client => search request + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081dd0201046381d704000a01000a0100020100020100010100870b6f626a656374436c6173733081b6040e6e616d696e67436f6e74657874730411737562736368656d61537562656e7472790414737570706f727465644c44415056657273696f6e0417737570706f727465645341534c4d656368616e69736d730412737570706f72746564457874656e73696f6e0410737570706f72746564436f6e74726f6c0411737570706f727465644665617475726573040a76656e646f724e616d65040d76656e646f7256657273696f6e04012b040b6f626a656374436c617373 + // 0x30, 0x81, 0xdd, + // 0x02, 0x01, 0x04, + // // SearchRequest ::= [APPLICATION 3] SEQUENCE { + // 0x63, 0x81, 0xd7, + // // baseObject LDAPDN, + // 0x04, 0x00, + // // scope ENUMERATED { + // // baseObject (0), + // // ... }, + // 0x0a, 0x01, 0x00, + // // derefAliases ENUMERATED { + // // neverDerefAliases (0) }, + // 0x0a, 0x01, 0x00, + // // sizeLimit INTEGER (0 .. maxInt), + // 0x02, 0x01, 0x00, + // // timeLimit INTEGER (0 .. maxInt), + // 0x02, 0x01, 0x00, + // // typesOnly BOOLEAN, + // 0x01, 0x01, 0x00, + // // filter Filter, + // // Filter ::= CHOICE { + // // present [7] AttributeDescription, + // // "objetClass" + // 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // // attributes AttributeSelection } + // // AttributeSelection ::= SEQUENCE OF selector LDAPString + // // -- The LDAPString is constrained to + // // -- in Section 4.5.1.8 + // 0x30, 0x81, 0xb6, + // // namingContexts + // 0x04, 0x0e, 0x6e, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, + // // subschemaSubentry + // 0x04, 0x11, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, + // // supportedLDAPVersion + // 0x04, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4c, 0x44, 0x41, 0x50, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + // // supportedSASLMechanisms + // 0x04, 0x17, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x41, 0x53, 0x4c, 0x4d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0x73, + // // supportedExtension + // 0x04, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + // // supportedControl + // 0x04, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + // // supportedFeatures + // 0x04, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + // // vendorName + // 0x04, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, + // // vendorVersion + // 0x04, 0x0d, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + // // + + // 0x04, 0x01, 0x2b, + // // objectClass + // 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x04)), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN(""), + // scope: SearchRequestScopeBaseObject, + // derefAliases: SearchRequetDerefAliasesNeverDerefAliases, + // sizeLimit: 0, + // timeLimit: 0, + // typesOnly: false, + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection([]LDAPString{ + // "namingContexts", + // "subschemaSubentry", + // "supportedLDAPVersion", + // "supportedSASLMechanisms", + // "supportedExtension", + // "supportedControl", + // "supportedFeatures", + // "vendorName", + // "vendorVersion", + // "+", + // "objectClass", + // }), + // }, + // }, + // }, + + // // Request 10 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 308203850201046482037e040030820378302a040a76656e646f724e616d65311c041a41706163686520536f66747761726520466f756e646174696f6e301c040d76656e646f7256657273696f6e310b0409322e302e302d4d31343026040b6f626a656374436c61737331170403746f700410657874656e7369626c654f626a65637430200411737562736368656d61537562656e747279310b0409636e3d736368656d61301b0414737570706f727465644c44415056657273696f6e31030401333082012e0410737570706f72746564436f6e74726f6c318201180417322e31362e3834302e312e3131333733302e332e342e330417312e332e362e312e342e312e343230332e312e31302e310417322e31362e3834302e312e3131333733302e332e342e320418312e332e362e312e342e312e343230332e312e392e312e340419312e332e362e312e342e312e34322e322e32372e382e352e310418312e332e362e312e342e312e343230332e312e392e312e310418312e332e362e312e342e312e343230332e312e392e312e330418312e332e362e312e342e312e343230332e312e392e312e320417312e332e362e312e342e312e31383036302e302e302e310417322e31362e3834302e312e3131333733302e332e342e370416312e322e3834302e3131333535362e312e342e3331393081910412737570706f72746564457874656e73696f6e317b0416312e332e362e312e342e312e313436362e32303033360416312e332e362e312e342e312e313436362e32303033370417312e332e362e312e342e312e31383036302e302e312e350417312e332e362e312e342e312e31383036302e302e312e330417312e332e362e312e342e312e343230332e312e31312e3130530417737570706f727465645341534c4d656368616e69736d73313804044e544c4d0406475353415049040a4753532d53504e45474f04084352414d2d4d4435040653494d504c45040a4449474553542d4d443530330409656e747279555549443126042466323930343235632d383237322d346536322d386136372d3932623036663338646266353046040e6e616d696e67436f6e7465787473313404096f753d73797374656d041164633d6578616d706c652c64633d636f6d04096f753d736368656d6104096f753d636f6e666967302d0411737570706f72746564466561747572657331180416312e332e362e312e342e312e343230332e312e352e31 + // 0x30, 0x82, 0x03, 0x85, 0x02, 0x01, 0x04, 0x64, 0x82, 0x03, 0x7e, 0x04, 0x00, 0x30, 0x82, 0x03, 0x78, 0x30, 0x2a, 0x04, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x31, 0x1c, 0x04, 0x1a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x1c, 0x04, 0x0d, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x0b, 0x04, 0x09, 0x32, 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x4d, 0x31, 0x34, 0x30, 0x26, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x17, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x10, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x30, 0x20, 0x04, 0x11, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x31, 0x0b, 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x30, 0x1b, 0x04, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4c, 0x44, 0x41, 0x50, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x03, 0x04, 0x01, 0x33, 0x30, 0x82, 0x01, 0x2e, 0x04, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x31, 0x82, 0x01, 0x18, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x33, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x34, 0x04, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x31, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x33, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x32, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x38, 0x30, 0x36, 0x30, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x37, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, 0x30, 0x81, 0x91, 0x04, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x7b, 0x04, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x36, 0x04, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x38, 0x30, 0x36, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2e, 0x35, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x38, 0x30, 0x36, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2e, 0x33, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x31, 0x30, 0x53, 0x04, 0x17, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x41, 0x53, 0x4c, 0x4d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0x73, 0x31, 0x38, 0x04, 0x04, 0x4e, 0x54, 0x4c, 0x4d, 0x04, 0x06, 0x47, 0x53, 0x53, 0x41, 0x50, 0x49, 0x04, 0x0a, 0x47, 0x53, 0x53, 0x2d, 0x53, 0x50, 0x4e, 0x45, 0x47, 0x4f, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35, 0x04, 0x06, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x04, 0x0a, 0x44, 0x49, 0x47, 0x45, 0x53, 0x54, 0x2d, 0x4d, 0x44, 0x35, 0x30, 0x33, 0x04, 0x09, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x55, 0x55, 0x49, 0x44, 0x31, 0x26, 0x04, 0x24, 0x66, 0x32, 0x39, 0x30, 0x34, 0x32, 0x35, 0x63, 0x2d, 0x38, 0x32, 0x37, 0x32, 0x2d, 0x34, 0x65, 0x36, 0x32, 0x2d, 0x38, 0x61, 0x36, 0x37, 0x2d, 0x39, 0x32, 0x62, 0x30, 0x36, 0x66, 0x33, 0x38, 0x64, 0x62, 0x66, 0x35, 0x30, 0x46, 0x04, 0x0e, 0x6e, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x31, 0x34, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x04, 0x11, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2d, 0x04, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x31, 0x18, 0x04, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x35, 0x2e, 0x31, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(4), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN(""), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("vendorName"), + // vals: []AttributeValue{ + // AttributeValue("Apache Software Foundation"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("vendorVersion"), + // vals: []AttributeValue{ + // AttributeValue("2.0.0-M14"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("objectClass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("extensibleObject"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("subschemaSubentry"), + // vals: []AttributeValue{ + // AttributeValue("cn=schema"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("supportedLDAPVersion"), + // vals: []AttributeValue{ + // AttributeValue("3"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("supportedControl"), + // vals: []AttributeValue{ + // AttributeValue("2.16.840.1.113730.3.4.3"), + // AttributeValue("1.3.6.1.4.1.4203.1.10.1"), + // AttributeValue("2.16.840.1.113730.3.4.2"), + // AttributeValue("1.3.6.1.4.1.4203.1.9.1.4"), + // AttributeValue("1.3.6.1.4.1.42.2.27.8.5.1"), + // AttributeValue("1.3.6.1.4.1.4203.1.9.1.1"), + // AttributeValue("1.3.6.1.4.1.4203.1.9.1.3"), + // AttributeValue("1.3.6.1.4.1.4203.1.9.1.2"), + // AttributeValue("1.3.6.1.4.1.18060.0.0.1"), + // AttributeValue("2.16.840.1.113730.3.4.7"), + // AttributeValue("1.2.840.113556.1.4.319"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("supportedExtension"), + // vals: []AttributeValue{ + // AttributeValue("1.3.6.1.4.1.1466.20036"), + // AttributeValue("1.3.6.1.4.1.1466.20037"), + // AttributeValue("1.3.6.1.4.1.18060.0.1.5"), + // AttributeValue("1.3.6.1.4.1.18060.0.1.3"), + // AttributeValue("1.3.6.1.4.1.4203.1.11.1"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("supportedSASLMechanisms"), + // vals: []AttributeValue{ + // AttributeValue("NTLM"), + // AttributeValue("GSSAPI"), + // AttributeValue("GSS-SPNEGO"), + // AttributeValue("CRAM-MD5"), + // AttributeValue("SIMPLE"), + // AttributeValue("DIGEST-MD5"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("entryUUID"), + // vals: []AttributeValue{ + // AttributeValue("f290425c-8272-4e62-8a67-92b06f38dbf5"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("namingContexts"), + // vals: []AttributeValue{ + // AttributeValue("ou=system"), + // AttributeValue("dc=example,dc=com"), + // AttributeValue("ou=schema"), + // AttributeValue("ou=config"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("supportedFeatures"), + // vals: []AttributeValue{ + // AttributeValue("1.3.6.1.4.1.4203.1.5.1"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 11 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010465070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x04, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(4), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 12 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3028020105632304000a01000a0100020100020100010100870b6f626a656374436c617373300304012a + // 0x30, 0x28, 0x02, 0x01, 0x05, 0x63, 0x23, 0x04, 0x00, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(5), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN(""), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(0), + // sizeLimit: INTEGER(0), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("*"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 13 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3031020105642c040030283026040b6f626a656374436c61737331170403746f700410657874656e7369626c654f626a656374 + // 0x30, 0x31, 0x02, 0x01, 0x05, 0x64, 0x2c, 0x04, 0x00, 0x30, 0x28, 0x30, 0x26, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x17, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x10, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(5), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN(""), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectClass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("extensibleObject"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 14 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010565070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x05, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(5), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 15 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304c020106634704096f753d736368656d610a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x4c, 0x02, 0x01, 0x06, 0x63, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(6), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=schema"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 16 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 303c020106643704096f753d736368656d61302a3028040b6f626a656374636c617373311904126f7267616e697a6174696f6e616c556e69740403746f70 + // 0x30, 0x3c, 0x02, 0x01, 0x06, 0x64, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, 0x04, 0x03, 0x74, 0x6f, 0x70, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(6), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=schema"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("organizationalUnit"), + // AttributeValue("top"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 17 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010665070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x06, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(6), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 18 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304c020107634704096f753d636f6e6669670a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x4c, 0x02, 0x01, 0x07, 0x63, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(7), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=config"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 19 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 303c020107643704096f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x3c, 0x02, 0x01, 0x07, 0x64, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(7), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 20 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010765070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x07, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(7), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 21 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304c020108634704096f753d73797374656d0a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x4c, 0x02, 0x01, 0x08, 0x63, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(8), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=system"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 22 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 303c020108643704096f753d73797374656d302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x3c, 0x02, 0x01, 0x08, 0x64, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(8), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=system"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 23 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010865070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x08, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(8), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 24 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304c02010963470409636e3d736368656d610a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x4c, 0x02, 0x01, 0x09, 0x63, 0x47, 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(9), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("cn=schema"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 25 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304e02010964490409636e3d736368656d61303c303a040b6f626a656374436c617373312b0403746f700409737562736368656d610408737562656e747279040f617061636865537562736368656d61 + // 0x30, 0x4e, 0x02, 0x01, 0x09, 0x64, 0x49, 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x30, 0x3c, 0x30, 0x3a, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x2b, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x04, 0x08, 0x73, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x04, 0x0f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(9), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("cn=schema"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectClass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("subschema"), + // AttributeValue("subentry"), + // AttributeValue("apacheSubschema"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 26 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010965070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x09, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(9), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 27 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 305402010a634f041164633d6578616d706c652c64633d636f6d0a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x54, 0x02, 0x01, 0x0a, 0x63, 0x4f, 0x04, 0x11, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(10), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("dc=example,dc=com"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 28 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 303802010a6433041164633d6578616d706c652c64633d636f6d301e301c040b6f626a656374636c617373310d0406646f6d61696e0403746f70 + // 0x30, 0x38, 0x02, 0x01, 0x0a, 0x64, 0x33, 0x04, 0x11, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x30, 0x1c, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x0d, 0x04, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x04, 0x03, 0x74, 0x6f, 0x70, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(10), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("dc=example,dc=com"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("domain"), + // AttributeValue("top"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 29 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010a65070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x0a, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(10), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 30 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304d02010b634804096f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x4d, 0x02, 0x01, 0x0b, 0x63, 0x48, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(11), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=config"), + // scope: ENUMERATED(1), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 31 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 306702010b646204286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e66696730363034040b6f626a656374636c61737331250403746f7004146164732d6469726563746f72795365727669636504086164732d62617365 + // 0x30, 0x67, 0x02, 0x01, 0x0b, 0x64, 0x62, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x36, 0x30, 0x34, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x25, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(11), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("ads-directoryService"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 32 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010b65070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x0b, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(11), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 33 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 303102010c632c04096f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + // 0x30, 0x31, 0x02, 0x01, 0x0c, 0x63, 0x2c, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(12), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=config"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(0), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("*"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 34 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 304c02010c644704096f753d636f6e666967303a300e04026f7531080406636f6e6669673028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x4c, 0x02, 0x01, 0x0c, 0x64, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x3a, 0x30, 0x0e, 0x04, 0x02, 0x6f, 0x75, 0x31, 0x08, 0x04, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(12), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("ou"), + // vals: []AttributeValue{ + // AttributeValue("config"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 35 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010c65070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x0c, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(12), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 36 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 306c02010d636704286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x6c, 0x02, 0x01, 0x0d, 0x63, 0x67, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ads-directoryServiceId=default,ou=config"), + // scope: ENUMERATED(1), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 37 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 307b02010d647604456164732d6a6f75726e616c49643d64656661756c744a6f75726e616c2c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302d302b040b6f626a656374636c617373311c0403746f70040b6164732d6a6f75726e616c04086164732d62617365 + // 0x30, 0x7b, 0x02, 0x01, 0x0d, 0x64, 0x76, 0x04, 0x45, 0x61, 0x64, 0x73, 0x2d, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2d, 0x30, 0x2b, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x1c, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x0b, 0x61, 0x64, 0x73, 0x2d, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-journalId=defaultJournal,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("ads-journal"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 38 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 306b02010d646604386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x6b, 0x02, 0x01, 0x0d, 0x64, 0x66, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 39 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 306902010d646404366f753d706172746974696f6e732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x69, 0x02, 0x01, 0x0d, 0x64, 0x64, 0x04, 0x36, 0x6f, 0x75, 0x3d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=partitions,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 40 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30818102010d647c04496164732d6368616e67654c6f6749643d64656661756c744368616e67654c6f672c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302f302d040b6f626a656374636c617373311e0403746f7004086164732d62617365040d6164732d6368616e67654c6f67 + // 0x30, 0x81, 0x81, 0x02, 0x01, 0x0d, 0x64, 0x7c, 0x04, 0x49, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2f, 0x30, 0x2d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x1e, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x04, 0x0d, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-changeLogId=defaultChangeLog,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("ads-base"), + // AttributeValue("ads-changeLog"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 41 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 306602010d646104336f753d736572766572732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x66, 0x02, 0x01, 0x0d, 0x64, 0x61, 0x04, 0x33, 0x6f, 0x75, 0x3d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=servers,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 42 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010d65070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x0d, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 43 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 305002010e634b04286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + // 0x30, 0x50, 0x02, 0x01, 0x0e, 0x63, 0x4b, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(14), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ads-directoryServiceId=default,ou=config"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(0), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("*"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 44 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3082017c02010e6482017504286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e66696730820147302304166164732d6469726563746f72797365727669636569643109040764656661756c74302104166164732d647373796e63706572696f646d696c6c69733107040531353030303024041a6164732d6473616c6c6f77616e6f6e796d6f757361636365737331060404545255453016040f6164732d64737265706c696361696431030401313025041a6164732d6473616363657373636f6e74726f6c656e61626c65643107040546414c5345301f04146164732d647370617373776f726468696464656e3107040546414c5345302a041f6164732d647364656e6f726d616c697a656f706174747273656e61626c65643107040546414c53453015040b6164732d656e61626c656431060404545255453034040b6f626a656374636c61737331250403746f7004146164732d6469726563746f72795365727669636504086164732d62617365 + // 0x30, 0x82, 0x01, 0x7c, 0x02, 0x01, 0x0e, 0x64, 0x82, 0x01, 0x75, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x82, 0x01, 0x47, 0x30, 0x23, 0x04, 0x16, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x31, 0x09, 0x04, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x30, 0x21, 0x04, 0x16, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x73, 0x79, 0x6e, 0x63, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x31, 0x07, 0x04, 0x05, 0x31, 0x35, 0x30, 0x30, 0x30, 0x30, 0x24, 0x04, 0x1a, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x31, 0x06, 0x04, 0x04, 0x54, 0x52, 0x55, 0x45, 0x30, 0x16, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x69, 0x64, 0x31, 0x03, 0x04, 0x01, 0x31, 0x30, 0x25, 0x04, 0x1a, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x07, 0x04, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x30, 0x1f, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x31, 0x07, 0x04, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x30, 0x2a, 0x04, 0x1f, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x64, 0x65, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x6f, 0x70, 0x61, 0x74, 0x74, 0x72, 0x73, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x07, 0x04, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x30, 0x15, 0x04, 0x0b, 0x61, 0x64, 0x73, 0x2d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x06, 0x04, 0x04, 0x54, 0x52, 0x55, 0x45, 0x30, 0x34, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x25, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(14), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("ads-directoryserviceid"), + // vals: []AttributeValue{ + // AttributeValue("default"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-dssyncperiodmillis"), + // vals: []AttributeValue{ + // AttributeValue("15000"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-dsallowanonymousaccess"), + // vals: []AttributeValue{ + // AttributeValue("TRUE"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-dsreplicaid"), + // vals: []AttributeValue{ + // AttributeValue("1"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-dsaccesscontrolenabled"), + // vals: []AttributeValue{ + // AttributeValue("FALSE"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-dspasswordhidden"), + // vals: []AttributeValue{ + // AttributeValue("FALSE"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-dsdenormalizeopattrsenabled"), + // vals: []AttributeValue{ + // AttributeValue("FALSE"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-enabled"), + // vals: []AttributeValue{ + // AttributeValue("TRUE"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("ads-directoryService"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 45 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010e65070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x0e, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(14), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 46 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 306002010f635b04386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + // 0x30, 0x60, 0x02, 0x01, 0x0f, 0x63, 0x5b, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(15), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(0), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("*"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 47 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30818102010f647c04386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673040301404026f75310e040c696e746572636570746f72733028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + // 0x30, 0x81, 0x81, 0x02, 0x01, 0x0f, 0x64, 0x7c, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x40, 0x30, 0x14, 0x04, 0x02, 0x6f, 0x75, 0x31, 0x0e, 0x04, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(15), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("ou"), + // vals: []AttributeValue{ + // AttributeValue("interceptors"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("organizationalUnit"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 48 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02010f65070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x0f, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(15), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 49 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 307c020110637704386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x7c, 0x02, 0x01, 0x10, 0x63, 0x77, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + // scope: ENUMERATED(1), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 50 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30819a020110648194045f6164732d696e746572636570746f7249643d657863657074696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x9a, 0x02, 0x01, 0x10, 0x64, 0x81, 0x94, 0x04, 0x5f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=exceptionInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 51 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 308196020110648190045b6164732d696e746572636570746f7249643d6576656e74496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x96, 0x02, 0x01, 0x10, 0x64, 0x81, 0x90, 0x04, 0x5b, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=eventInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 52 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081a502011064819f046a6164732d696e746572636570746f7249643d6f7065726174696f6e616c417474726962757465496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0xa5, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9f, 0x04, 0x6a, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=operationalAttributeInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 53 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081be0201106481b804646164732d696e746572636570746f7249643d61757468656e7469636174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673050304e040b6f626a656374636c617373313f040f6164732d696e746572636570746f720403746f7004086164732d62617365041d6164732d61757468656e7469636174696f6e496e746572636570746f72 + // 0x30, 0x81, 0xbe, 0x02, 0x01, 0x10, 0x64, 0x81, 0xb8, 0x04, 0x64, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x50, 0x30, 0x4e, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x3f, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x04, 0x1d, 0x61, 0x64, 0x73, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // AttributeValue("ads-authenticationInterceptor"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 54 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081a202011064819c04676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0xa2, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9c, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 55 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081a002011064819a04656164732d696e746572636570746f7249643d70617373776f726448617368696e67496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0xa0, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9a, 0x04, 0x65, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=passwordHashingInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 56 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 308197020110648191045c6164732d696e746572636570746f7249643d736368656d61496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x97, 0x02, 0x01, 0x10, 0x64, 0x81, 0x91, 0x04, 0x5c, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=schemaInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 57 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081a402011064819e04696164732d696e746572636570746f7249643d61646d696e697374726174697665506f696e74496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0xa4, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9e, 0x04, 0x69, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=administrativePointInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 58 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 308199020110648193045e6164732d696e746572636570746f7249643d726566657272616c496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x99, 0x02, 0x01, 0x10, 0x64, 0x81, 0x93, 0x04, 0x5e, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x61, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=referralInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 59 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30819e02011064819804636164732d696e746572636570746f7249643d6b657944657269766174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x9e, 0x02, 0x01, 0x10, 0x64, 0x81, 0x98, 0x04, 0x63, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=keyDerivationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 60 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30819e02011064819804636164732d696e746572636570746f7249643d6e6f726d616c697a6174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x9e, 0x02, 0x01, 0x10, 0x64, 0x81, 0x98, 0x04, 0x63, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=normalizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 61 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 308199020110648193045e6164732d696e746572636570746f7249643d737562656e747279496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x99, 0x02, 0x01, 0x10, 0x64, 0x81, 0x93, 0x04, 0x5e, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x73, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=subentryInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 62 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081a502011064819f046a6164732d696e746572636570746f7249643d64656661756c74417574686f72697a6174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0xa5, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9f, 0x04, 0x6a, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=defaultAuthorizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 63 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 308198020110648192045d6164732d696e746572636570746f7249643d74726967676572496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0x98, 0x02, 0x01, 0x10, 0x64, 0x81, 0x92, 0x04, 0x5d, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=triggerInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 64 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081a402011064819e04696164732d696e746572636570746f7249643d636f6c6c656374697665417474726962757465496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + // 0x30, 0x81, 0xa4, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9e, 0x04, 0x69, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=collectiveAttributeInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 65 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02011065070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x10, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(16), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 66 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30819002011163818a04676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + // 0x30, 0x81, 0x90, 0x02, 0x01, 0x11, 0x63, 0x81, 0x8a, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(17), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // scope: ENUMERATED(0), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(0), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("*"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 67 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3082018d0201116482018604676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e66696730820119302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d626173653015040b6164732d656e61626c65643106040454525545306004186164732d696e746572636570746f72636c6173736e616d65314404426f72672e6170616368652e6469726563746f72792e7365727665722e636f72652e617574687a2e416369417574686f72697a6174696f6e496e746572636570746f72301b04146164732d696e746572636570746f726f726465723103040134305004116164732d696e746572636570746f726964313b041b616369417574686f72697a6174696f6e496e746572636570746f72041c616369417574686f72697a6174696f6e496e746572636570746f7232 + // 0x30, 0x82, 0x01, 0x8d, 0x02, 0x01, 0x11, 0x64, 0x82, 0x01, 0x86, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x82, 0x01, 0x19, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x30, 0x15, 0x04, 0x0b, 0x61, 0x64, 0x73, 0x2d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x06, 0x04, 0x04, 0x54, 0x52, 0x55, 0x45, 0x30, 0x60, 0x04, 0x18, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x31, 0x44, 0x04, 0x42, 0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x7a, 0x2e, 0x41, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x30, 0x1b, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x31, 0x03, 0x04, 0x01, 0x34, 0x30, 0x50, 0x04, 0x11, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x69, 0x64, 0x31, 0x3b, 0x04, 0x1b, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x1c, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(17), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectclass"), + // vals: []AttributeValue{ + // AttributeValue("ads-interceptor"), + // AttributeValue("top"), + // AttributeValue("ads-base"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-enabled"), + // vals: []AttributeValue{ + // AttributeValue("TRUE"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-interceptorclassname"), + // vals: []AttributeValue{ + // AttributeValue("org.apache.directory.server.core.authz.AciAuthorizationInterceptor"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-interceptororder"), + // vals: []AttributeValue{ + // AttributeValue("4"), + // }, + // }, + // PartialAttribute{ + // type_: AttributeDescription("ads-interceptorid"), + // vals: []AttributeValue{ + // AttributeValue("aciAuthorizationInterceptor"), + // AttributeValue("aciAuthorizationInterceptor2"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 68 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02011165070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x11, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(17), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 69 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081ac0201126381a604676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + // 0x30, 0x81, 0xac, 0x02, 0x01, 0x12, 0x63, 0x81, 0xa6, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(18), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + // scope: ENUMERATED(1), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("hasSubordinates"), + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 70 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02011265070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x12, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(18), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 71 + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30050201134200 + // 0x30, 0x05, 0x02, 0x01, 0x13, 0x42, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(19), + // protocolOp: UnbindRequest{}, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 72: CLIENT AddRequest + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3081b60201156881b0044a636e3d723030582b636f6d6d6f6e4e616d653d54686520723030582b6465736372697074696f6e3d41207465737420757365722c6f753d636f6e73756d6572732c6f753d73797374656d3062301c040b6465736372697074696f6e310d040b4120746573742075736572300c0402736e310604047230307830160402636e311004047230305804085468652072303058301c040b6f626a656374436c617373310d0406706572736f6e0403746f70 + // 0x30, 0x81, 0xb6, 0x02, 0x01, 0x15, 0x68, 0x81, 0xb0, 0x04, 0x4a, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x3d, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x62, 0x30, 0x1c, 0x04, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x0d, 0x04, 0x0b, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x30, 0x0c, 0x04, 0x02, 0x73, 0x6e, 0x31, 0x06, 0x04, 0x04, 0x72, 0x30, 0x30, 0x78, 0x30, 0x16, 0x04, 0x02, 0x63, 0x6e, 0x31, 0x10, 0x04, 0x04, 0x72, 0x30, 0x30, 0x58, 0x04, 0x08, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x30, 0x1c, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x0d, 0x04, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x04, 0x03, 0x74, 0x6f, 0x70, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(21), + // protocolOp: AddRequest{ + // entry: LDAPDN("cn=r00X+commonName=The r00X+description=A test user,ou=consumers,ou=system"), + // attributes: AttributeList{ + // Attribute{ + // type_: AttributeDescription("description"), + // vals: []AttributeValue{ + // AttributeValue("A test user"), + // }, + // }, + // Attribute{ + // type_: AttributeDescription("sn"), + // vals: []AttributeValue{ + // AttributeValue("r00x"), + // }, + // }, + // Attribute{ + // type_: AttributeDescription("cn"), + // vals: []AttributeValue{ + // AttributeValue("r00X"), + // AttributeValue("The r00X"), + // }, + // }, + // Attribute{ + // type_: AttributeDescription("objectClass"), + // vals: []AttributeValue{ + // AttributeValue("person"), + // AttributeValue("top"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 73: SERVER AddResponse + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02011569070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x15, 0x69, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(21), + // protocolOp: AddResponse{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 74: CLIENT ModifyRequest + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30790201196674044a636e3d723030582b636f6d6d6f6e6e616d653d54686520723030582b6465736372697074696f6e3d41207465737420757365722c6f753d636f6e73756d6572732c6f753d73797374656d302630240a0100301f040f74656c6570686f6e654e756d626572310c040a30313233343536373839 + // 0x30, 0x79, 0x02, 0x01, 0x19, 0x66, 0x74, 0x04, 0x4a, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x26, 0x30, 0x24, 0x0a, 0x01, 0x00, 0x30, 0x1f, 0x04, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x31, 0x0c, 0x04, 0x0a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(25), + // protocolOp: ModifyRequest{ + // object: LDAPDN("cn=r00X+commonname=The r00X+description=A test user,ou=consumers,ou=system"), + // changes: []ModifyRequestChange{ + // ModifyRequestChange{ + // operation: ENUMERATED(0), + // modification: PartialAttribute{ + // type_: AttributeDescription("telephoneNumber"), + // vals: []AttributeValue{ + // AttributeValue("0123456789"), + // }, + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 75: SERVER ModifyResponse + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02011967070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x19, 0x67, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(25), + // protocolOp: ModifyResponse{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 76: CLIENT ModifyDNrequest + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 307502011b6c70044a636e3d723030582b636f6d6d6f6e6e616d653d54686520723030582b6465736372697074696f6e3d41207465737420757365722c6f753d636f6e73756d6572732c6f753d73797374656d0407636e3d723030580101ff80166f753d636f6e73756d6572732c6f753d73797374656d + // 0x30, 0x75, 0x02, 0x01, 0x1b, 0x6c, 0x70, 0x04, 0x4a, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x04, 0x07, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x01, 0x01, 0xff, 0x80, 0x16, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(27), + // protocolOp: ModifyDNRequest{ + // entry: LDAPDN("cn=r00X+commonname=The r00X+description=A test user,ou=consumers,ou=system"), + // newrdn: RelativeLDAPDN("cn=r00X"), + // deleteoldrdn: BOOLEAN(true), + // newSuperior: LDAPDN("ou=consumers,ou=system").Pointer(), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 77: SERVER ModifyDNResponse + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02011b6d070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x1b, 0x6d, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(27), + // protocolOp: ModifyDNResponse{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 78: CLIENT + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 303b020121633604096f753d73797374656d0a01020a0103020203e8020100010100a30a0402636e040472303058300d040b6f626a656374436c617373 + // 0x30, 0x3b, 0x02, 0x01, 0x21, 0x63, 0x36, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa3, 0x0a, 0x04, 0x02, 0x63, 0x6e, 0x04, 0x04, 0x72, 0x30, 0x30, 0x58, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(33), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=system"), + // scope: ENUMERATED(2), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterEqualityMatch{ + // attributeDesc: AttributeDescription("cn"), + // assertionValue: AssertionValue("r00X"), + // }, + // attributes: AttributeSelection{ + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 79: SERVER + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30450201216440041e636e3d723030582c6f753d636f6e73756d6572732c6f753d73797374656d301e301c040b6f626a656374436c617373310d0403746f700406706572736f6e + // 0x30, 0x45, 0x02, 0x01, 0x21, 0x64, 0x40, 0x04, 0x1e, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x1e, 0x30, 0x1c, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x0d, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(33), + // protocolOp: SearchResultEntry{ + // objectName: LDAPDN("cn=r00X,ou=consumers,ou=system"), + // attributes: PartialAttributeList{ + // PartialAttribute{ + // type_: AttributeDescription("objectClass"), + // vals: []AttributeValue{ + // AttributeValue("top"), + // AttributeValue("person"), + // }, + // }, + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 80: SERVER + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c02012165070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x21, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(33), + // protocolOp: SearchResultDone{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 81: CLIENT SearchRequest with controls + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30819c020124633704096f753d73797374656d0a01020a0103020203e8020100010100870b6f626a656374436c617373300d040b6f626a656374436c617373a05e301e0417312e332e362e312e342e312e343230332e312e31302e3104030101ff30190417322e31362e3834302e312e3131333733302e332e342e3230210416312e322e3834302e3131333535362e312e342e333139040730050201030400 + // 0x30, 0x81, 0x9c, 0x02, 0x01, 0x24, 0x63, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0xa0, 0x5e, 0x30, 0x1e, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x04, 0x03, 0x01, 0x01, 0xff, 0x30, 0x19, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x30, 0x21, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, 0x04, 0x07, 0x30, 0x05, 0x02, 0x01, 0x03, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(36), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=system"), + // scope: ENUMERATED(2), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterPresent("objectClass"), + // attributes: AttributeSelection{ + // LDAPString("objectClass"), + // }, + // }, + // controls: &Controls{ + // Control{ + // controlType: LDAPOID("1.3.6.1.4.1.4203.1.10.1"), + // criticality: BOOLEAN(false), + // controlValue: OCTETSTRING("\x01\x01\xff").Pointer(), + // }, + // Control{ + // controlType: LDAPOID("2.16.840.1.113730.3.4.2"), + // criticality: BOOLEAN(false), + // controlValue: (*OCTETSTRING)(nil), + // }, + // Control{ + // controlType: LDAPOID("1.2.840.113556.1.4.319"), + // criticality: BOOLEAN(false), + // controlValue: OCTETSTRING("0\x05\x02\x01\x03\x04\x00").Pointer(), + // }, + // }, + // }, + // }, + + // // (| + // // (& + // // (cn=r00x) + // // (telephoneNumber=*) + // // ) + // // (cn~=The) + // // (& + // // (!(description=Toto)) + // // (ou=co*f*g*r*on) + // // ) + // // ) + // // Request 83: CLIENT + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30818e02010d63818804096f753d73797374656d0a01020a0103020203e8020100010100a15ca01da30a0402636e040472303078870f74656c6570686f6e654e756d626572a8090402636e0403546865a030a215a313040b6465736372697074696f6e0404546f746fa41704026f7530118002636f81016681016781017282026f6e300d040b6f626a656374436c617373 + // 0x30, 0x81, 0x8e, 0x02, 0x01, 0x0d, 0x63, 0x81, 0x88, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa1, 0x5c, 0xa0, 0x1d, 0xa3, 0x0a, 0x04, 0x02, 0x63, 0x6e, 0x04, 0x04, 0x72, 0x30, 0x30, 0x78, 0x87, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xa8, 0x09, 0x04, 0x02, 0x63, 0x6e, 0x04, 0x03, 0x54, 0x68, 0x65, 0xa0, 0x30, 0xa2, 0x15, 0xa3, 0x13, 0x04, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x04, 0x04, 0x54, 0x6f, 0x74, 0x6f, 0xa4, 0x17, 0x04, 0x02, 0x6f, 0x75, 0x30, 0x11, 0x80, 0x02, 0x63, 0x6f, 0x81, 0x01, 0x66, 0x81, 0x01, 0x67, 0x81, 0x01, 0x72, 0x82, 0x02, 0x6f, 0x6e, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(13), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=system"), + // scope: ENUMERATED(2), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterOr{ + // FilterAnd{ + // FilterEqualityMatch{ + // attributeDesc: AttributeDescription("cn"), + // assertionValue: AssertionValue("r00x"), + // }, + // FilterPresent("telephoneNumber"), + // }, + // FilterApproxMatch{ + // attributeDesc: AttributeDescription("cn"), + // assertionValue: AssertionValue("The"), + // }, + // FilterAnd{ + // FilterNot{ + // Filter: FilterEqualityMatch{ + // attributeDesc: AttributeDescription("description"), + // assertionValue: AssertionValue("Toto"), + // }, + // }, + // FilterSubstrings{ + // type_: AttributeDescription("ou"), + // substrings: []Substring{ + // SubstringInitial("co"), + // SubstringAny("f"), + // SubstringAny("g"), + // SubstringAny("r"), + // SubstringFinal("on"), + // }, + // }, + // }, + // }, + // attributes: AttributeSelection{ + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 84: CLIENT SearchRequest with FilterGreaterOrEqual and FilterLessOrEqual + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3072020119636d04000a01020a0103020203e8020100010100a04aa523041e6164732d636867507764506f6c69637950617373776f72644c656e677468040133a623041e6164732d636867507764506f6c69637950617373776f72644c656e677468040135300d040b6f626a656374436c617373 + // 0x30, 0x72, 0x02, 0x01, 0x19, 0x63, 0x6d, 0x04, 0x00, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x4a, 0xa5, 0x23, 0x04, 0x1e, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x67, 0x50, 0x77, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x04, 0x01, 0x33, 0xa6, 0x23, 0x04, 0x1e, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x67, 0x50, 0x77, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x04, 0x01, 0x35, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(25), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN(""), + // scope: ENUMERATED(2), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterAnd{ + // FilterGreaterOrEqual{ + // attributeDesc: AttributeDescription("ads-chgPwdPolicyPasswordLength"), + // assertionValue: AssertionValue("3"), + // }, + // FilterLessOrEqual{ + // attributeDesc: AttributeDescription("ads-chgPwdPolicyPasswordLength"), + // assertionValue: AssertionValue("5"), + // }, + // }, + // attributes: AttributeSelection{ + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 85: CLIENT SearchRequest with FilterExtensibleMatch + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 3074020131636f04166f753d636f6e73756d6572732c6f753d73797374656d0a01020a0103020203e8020100010100a936811474656c6570686f6e654e756d6265724d61746368820f74656c6570686f6e654e756d626572830a303132333435363738398401ff300d040b6f626a656374436c617373 + // 0x30, 0x74, 0x02, 0x01, 0x31, 0x63, 0x6f, 0x04, 0x16, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa9, 0x36, 0x81, 0x14, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x82, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x83, 0x0a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x84, 0x01, 0xff, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(49), + // protocolOp: SearchRequest{ + // baseObject: LDAPDN("ou=consumers,ou=system"), + // scope: ENUMERATED(2), + // derefAliases: ENUMERATED(3), + // sizeLimit: INTEGER(1000), + // timeLimit: INTEGER(0), + // typesOnly: BOOLEAN(false), + // filter: FilterExtensibleMatch{ + // matchingRule: MatchingRuleId("telephoneNumberMatch").Pointer(), + // type_: AttributeDescription("telephoneNumber").Pointer(), + // matchValue: AssertionValue("0123456789"), + // dnAttributes: BOOLEAN(true), + // }, + // attributes: AttributeSelection{ + // LDAPString("objectClass"), + // }, + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 86: CLIENT + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 30400201274a3b636e3d4120636f6d706c657820706572736f6e5c2c207665727920636f6d706c657820212c6f753d636f6e73756d6572732c6f753d73797374656d + // 0x30, 0x40, 0x02, 0x01, 0x27, 0x4a, 0x3b, 0x63, 0x6e, 0x3d, 0x41, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x5c, 0x2c, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x20, 0x21, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(39), + // protocolOp: DelRequest("cn=A complex person\\, very complex !,ou=consumers,ou=system"), + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 87: SERVER + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 300c0201276b070a010004000400 + // 0x30, 0x0c, 0x02, 0x01, 0x27, 0x6b, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(39), + // protocolOp: DelResponse{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 88: CLIENT ExtendedRequest: Start TLS (OID 1.3.6.1.4.1.1466.20037) + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 301d02010177188016312e332e362e312e342e312e313436362e3230303337 + // 0x30, 0x1d, 0x02, 0x01, 0x01, 0x77, 0x18, 0x80, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(1), + // protocolOp: ExtendedRequest{ + // requestName: LDAPOID("1.3.6.1.4.1.1466.20037"), + // requestValue: (*OCTETSTRING)(nil), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // Request 89: SERVER ExtendedResponse: Start TLS (OID 1.3.6.1.4.1.1466.20037) + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // // 302602010178210a0100040004008a16312e332e362e312e342e312e313436362e32303033378b00 + // 0x30, 0x26, 0x02, 0x01, 0x01, 0x78, 0x21, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, 0x8a, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37, 0x8b, 0x00, + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(1), + // protocolOp: ExtendedResponse{ + // LDAPResult: LDAPResult{ + // resultCode: ENUMERATED(0), + // matchedDN: LDAPDN(""), + // diagnosticMessage: LDAPString(""), + // referral: (*Referral)(nil), + // }, + // responseName: LDAPOID("1.3.6.1.4.1.1466.20037").Pointer(), + // responseValue: OCTETSTRING("").Pointer(), + // }, + // controls: (*Controls)(nil), + // }, + // }, + + // // A bind request with a simple login / password authentication + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // 0x30, 0x1d, + // 0x02, 0x01, 0x01, // messageID + // 0x60, 0x18, // Application, tag 0 => this is a Bind request + // 0x02, 0x01, 0x03, // Version 3 + // 0x04, 0x07, 0x6d, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, // login = myLogin + // 0x80, 0x0a, 0x6d, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, // simple authentication: myPassword + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x01)), + // protocolOp: BindRequest{ + // version: 0x03, + // name: LDAPDN("myLogin"), + // authentication: OCTETSTRING([]byte("myPassword")), + // }, + // }, + // }, + // // A bind request with SASL (CRAM-MD5) + // { + // bytes: Bytes{ + // offset: 0, + // bytes: []byte{ + // 0x30, 0x16, + // 0x02, 0x01, 0x01, // messageID + // 0x60, 0x11, + // 0x02, 0x01, 0x03, // version 3 + // 0x04, 0x00, // no login + // 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35, // SASL mechanism "CRAM-MD5", no credentials + // }, + // }, + // out: LDAPMessage{ + // messageID: MessageID(int(0x01)), + // protocolOp: BindRequest{ + // version: 0x03, + // name: LDAPDN(""), + // authentication: SaslCredentials{ + // mechanism: LDAPString("CRAM-MD5"), + // }, + // }, + // }, + // }, + } +} diff --git a/goldap/read_test.go b/goldap/read_test.go new file mode 100644 index 0000000..bc25fcc --- /dev/null +++ b/goldap/read_test.go @@ -0,0 +1,2936 @@ +package message + +import ( + "reflect" + "testing" +) + +func TestReadLDAPMessage(t *testing.T) { + for i, test := range getLDAPMessageTestData() { + message, err := ReadLDAPMessage(&test.bytes) + if err != nil { + t.Errorf("#%d failed reading bytes at offset %d (%s): %s", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err) + } else if !reflect.DeepEqual(message, test.out) { + t.Errorf("#%d:\nGOT:\n%#+v\nEXPECTED:\n%#+v", i, message, test.out) + } + } +} + +type LDAPMessageTestData struct { + bytes Bytes + out LDAPMessage +} + +func getLDAPMessageTestData() (ret []LDAPMessageTestData) { + return []LDAPMessageTestData{ + // Request 1: client => bind request + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c020101600702010304008000 + 0x30, 0x0c, + 0x02, 0x01, 0x01, + // BindRequest ::= [APPLICATION 0] SEQUENCE { + 0x60, 0x07, + // version INTEGER (1 .. 127), + 0x02, 0x01, 0x03, + // name LDAPDN, + 0x04, 0x00, + // authentication AuthenticationChoice } + // AuthenticationChoice ::= CHOICE { + // simple [0] OCTET STRING, + 0x80, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(1), + protocolOp: BindRequest{ + version: INTEGER(3), + name: LDAPDN(""), + authentication: OCTETSTRING(""), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 2: server => bind response + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010161070a010004000400 + 0x30, 0x0c, + 0x02, 0x01, 0x01, + // BindResponse ::= [APPLICATION 1] SEQUENCE { + // COMPONENTS OF LDAPResult, + 0x61, 0x07, + // LDAPResult ::= SEQUENCE { + // resultCode ENUMERATED { + // success (0), + // ... }, + 0x0a, 0x01, 0x00, + // matchedDN LDAPDN, + 0x04, 0x00, + // diagnosticMessage LDAPString, + 0x04, 0x00, + // referral [3] Referral OPTIONAL } + // serverSaslCreds [7] OCTET STRING OPTIONAL } + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x01)), + protocolOp: BindResponse{ + LDAPResult: LDAPResult{ + resultCode: 0, + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + }, + }, + }, + }, + + // Request 3: client => search request + // select "subschemaSubentry" from entries where "objectClass" is present + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3038020102633304000a01000a0103020100020100010100870b6f626a656374436c61737330130411737562736368656d61537562656e747279 + 0x30, 0x38, + 0x02, 0x01, 0x02, + // SearchRequest ::= [APPLICATION 3] SEQUENCE { + 0x63, 0x33, + // baseObject LDAPDN, + 0x04, 0x00, + // scope ENUMERATED { + // baseObject (0), + // ... }, + 0x0a, 0x01, 0x00, + // derefAliases ENUMERATED { + // derefAlways (3) }, + 0x0a, 0x01, 0x03, + // sizeLimit INTEGER (0 .. maxInt), + 0x02, 0x01, 0x00, + // timeLimit INTEGER (0 .. maxInt), + 0x02, 0x01, 0x00, + // typesOnly BOOLEAN, + 0x01, 0x01, 0x00, + // filter Filter, + // Filter ::= CHOICE { + // present [7] AttributeDescription, + // ... } + // AttributeDescription ::= LDAPString + // -- Constrained to + // -- [RFC4512] + 0x87, 0x0b, + // "objectClass" + 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // attributes AttributeSelection } + // AttributeSelection ::= SEQUENCE OF selector LDAPString + // -- The LDAPString is constrained to + // -- in Section 4.5.1.8 + 0x30, 0x13, + 0x04, 0x11, + // "subschemaSubentry" + 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x02)), + protocolOp: SearchRequest{ + baseObject: LDAPDN(""), + scope: SearchRequestScopeBaseObject, + derefAliases: SearchRequetDerefAliasesDerefAlways, + sizeLimit: 0, + timeLimit: 0, + typesOnly: false, + filter: FilterPresent("objectClass"), + attributes: AttributeSelection([]LDAPString{"subschemaSubentry"}), + }, + }, + }, + + // Request 4: server => search result entry + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 302b02010264260400302230200411737562736368656d61537562656e747279310b0409636e3d736368656d61 + 0x30, 0x2b, + 0x02, 0x01, 0x02, + // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { + 0x64, 0x26, + // objectName LDAPDN, + 0x04, 0x00, + // attributes PartialAttributeList } + // PartialAttributeList ::= SEQUENCE OF + // partialAttribute PartialAttribute + 0x30, 0x22, + // PartialAttribute ::= SEQUENCE { + 0x30, 0x20, + // type AttributeDescription, + 0x04, 0x11, + // "subschemaSubentry" + 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, + // vals SET OF value AttributeValue } + // AttributeValue ::= OCTET STRING + 0x31, 0x0b, + 0x04, 0x09, + // "cn=schema" + 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x02)), + protocolOp: SearchResultEntry{ + objectName: LDAPDN(""), + attributes: PartialAttributeList( + []PartialAttribute{ + { + type_: AttributeDescription("subschemaSubentry"), + vals: []AttributeValue{AttributeValue("cn=schema")}, + }, + }, + ), + }, + }, + }, + + // Request 5: server => search result done + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010265070a010004000400 + 0x30, 0x0c, + 0x02, 0x01, 0x02, + // SearchResultDone ::= [APPLICATION 5] LDAPResult + // LDAPResult ::= SEQUENCE { + 0x65, 0x07, + // resultCode ENUMERATED { + // success (0), + // ... }, + 0x0a, 0x01, 0x00, + // matchedDN LDAPDN, + 0x04, 0x00, + // diagnosticMessage LDAPString, + 0x04, 0x00, + // referral [3] Referral OPTIONAL } + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x02)), + protocolOp: SearchResultDone(LDAPResult{ + resultCode: ResultCodeSuccess, + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + }), + }, + }, + + // Request 6: client => search request + // select + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 305d02010363580409636e3d736368656d610a01000a0103020100020100010100a318040b6f626a656374436c6173730409737562736368656d613022040f63726561746554696d657374616d70040f6d6f6469667954696d657374616d70 + 0x30, 0x5d, + 0x02, 0x01, 0x03, + // SearchRequest ::= [APPLICATION 3] SEQUENCE { + 0x63, 0x58, + // baseObject LDAPDN, + // "cn=schema" + 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // scope ENUMERATED { + // baseObject (0), + // ... }, + 0x0a, 0x01, 0x00, + // derefAliases ENUMERATED { + // derefAlways (3) }, + 0x0a, 0x01, 0x03, + // sizeLimit INTEGER (0 .. maxInt), + 0x02, 0x01, 0x00, + // timeLimit INTEGER (0 .. maxInt), + 0x02, 0x01, 0x00, + // typesOnly BOOLEAN, + 0x01, 0x01, 0x00, + // filter Filter, + // Filter ::= CHOICE { + // equalityMatch [3] AttributeValueAssertion, + // AttributeValueAssertion ::= SEQUENCE { + 0xa3, 0x18, + // attributeDesc AttributeDescription, + // "objectClass" + 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // assertionValue AssertionValue } + // "subschema" + 0x04, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // attributes AttributeSelection } + // AttributeSelection ::= SEQUENCE OF selector LDAPString + // -- The LDAPString is constrained to + // -- in Section 4.5.1.8 + 0x30, 0x22, + // "createTimestamp" + 0x04, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // "modifyTimestamp" + 0x04, 0x0f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x03)), + protocolOp: SearchRequest{ + baseObject: LDAPDN("cn=schema"), + scope: SearchRequestScopeBaseObject, + derefAliases: SearchRequetDerefAliasesDerefAlways, + sizeLimit: 0, + timeLimit: 0, + typesOnly: false, + filter: FilterEqualityMatch( + AttributeValueAssertion{ + attributeDesc: AttributeDescription("objectClass"), + assertionValue: AssertionValue("subschema"), + }), + attributes: AttributeSelection([]LDAPString{"createTimestamp", "modifyTimestamp"}), + }, + }, + }, + + // Request 7: server => search result entry + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 305e02010364590409636e3d736368656d61304c3024040f6d6f6469667954696d657374616d703111040f32303039303831383032323733335a3024040f63726561746554696d657374616d703111040f32303039303831383032323733335a + 0x30, 0x5e, + 0x02, 0x01, 0x03, + // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { + 0x64, 0x59, + // objectName LDAPDN, + // "cn=schema" + 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + // attributes PartialAttributeList } + // PartialAttributeList ::= SEQUENCE OF + // partialAttribute PartialAttribute + 0x30, 0x4c, + // PartialAttribute ::= SEQUENCE { + 0x30, 0x24, + // type AttributeDescription, + // "modifyTimestamp" + 0x04, 0x0f, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // vals SET OF value AttributeValue } + 0x31, 0x11, + // "20090818022733Z" + 0x04, 0x0f, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x31, 0x38, 0x30, 0x32, 0x32, 0x37, 0x33, 0x33, 0x5a, + // PartialAttribute ::= SEQUENCE { + 0x30, 0x24, + // type AttributeDescription, + // "createTimestamp" + 0x04, 0x0f, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + // vals SET OF value AttributeValue } + 0x31, 0x11, + // "20090818022733Z" + 0x04, 0x0f, 0x32, 0x30, 0x30, 0x39, 0x30, 0x38, 0x31, 0x38, 0x30, 0x32, 0x32, 0x37, 0x33, 0x33, 0x5a, + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x03)), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("cn=schema"), + attributes: PartialAttributeList( + []PartialAttribute{ + { + type_: AttributeDescription("modifyTimestamp"), + vals: []AttributeValue{AttributeValue("20090818022733Z")}, + }, + { + type_: AttributeDescription("createTimestamp"), + vals: []AttributeValue{AttributeValue("20090818022733Z")}, + }, + }, + ), + }, + }, + }, + + // Request 8 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010365070a010004000400 + 0x30, 0x0c, + 0x02, 0x01, 0x03, + // SearchResultDone ::= [APPLICATION 5] LDAPResult + // LDAPResult ::= SEQUENCE { + 0x65, 0x07, + // resultCode ENUMERATED { + // success (0), + // ... }, + 0x0a, 0x01, 0x00, + // matchedDN LDAPDN, + 0x04, 0x00, + // diagnosticMessage LDAPString, + 0x04, 0x00, + // referral [3] Referral OPTIONAL } + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x03)), + protocolOp: SearchResultDone(LDAPResult{ + resultCode: ResultCodeSuccess, + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + }), + }, + }, + + // Request 9: client => search request + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081dd0201046381d704000a01000a0100020100020100010100870b6f626a656374436c6173733081b6040e6e616d696e67436f6e74657874730411737562736368656d61537562656e7472790414737570706f727465644c44415056657273696f6e0417737570706f727465645341534c4d656368616e69736d730412737570706f72746564457874656e73696f6e0410737570706f72746564436f6e74726f6c0411737570706f727465644665617475726573040a76656e646f724e616d65040d76656e646f7256657273696f6e04012b040b6f626a656374436c617373 + 0x30, 0x81, 0xdd, + 0x02, 0x01, 0x04, + // SearchRequest ::= [APPLICATION 3] SEQUENCE { + 0x63, 0x81, 0xd7, + // baseObject LDAPDN, + 0x04, 0x00, + // scope ENUMERATED { + // baseObject (0), + // ... }, + 0x0a, 0x01, 0x00, + // derefAliases ENUMERATED { + // neverDerefAliases (0) }, + 0x0a, 0x01, 0x00, + // sizeLimit INTEGER (0 .. maxInt), + 0x02, 0x01, 0x00, + // timeLimit INTEGER (0 .. maxInt), + 0x02, 0x01, 0x00, + // typesOnly BOOLEAN, + 0x01, 0x01, 0x00, + // filter Filter, + // Filter ::= CHOICE { + // present [7] AttributeDescription, + // "objetClass" + 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + // attributes AttributeSelection } + // AttributeSelection ::= SEQUENCE OF selector LDAPString + // -- The LDAPString is constrained to + // -- in Section 4.5.1.8 + 0x30, 0x81, 0xb6, + // namingContexts + 0x04, 0x0e, 0x6e, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, + // subschemaSubentry + 0x04, 0x11, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, + // supportedLDAPVersion + 0x04, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4c, 0x44, 0x41, 0x50, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + // supportedSASLMechanisms + 0x04, 0x17, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x41, 0x53, 0x4c, 0x4d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0x73, + // supportedExtension + 0x04, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + // supportedControl + 0x04, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, + // supportedFeatures + 0x04, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, + // vendorName + 0x04, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, + // vendorVersion + 0x04, 0x0d, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + // + + 0x04, 0x01, 0x2b, + // objectClass + 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x04)), + protocolOp: SearchRequest{ + baseObject: LDAPDN(""), + scope: SearchRequestScopeBaseObject, + derefAliases: SearchRequetDerefAliasesNeverDerefAliases, + sizeLimit: 0, + timeLimit: 0, + typesOnly: false, + filter: FilterPresent("objectClass"), + attributes: AttributeSelection([]LDAPString{ + "namingContexts", + "subschemaSubentry", + "supportedLDAPVersion", + "supportedSASLMechanisms", + "supportedExtension", + "supportedControl", + "supportedFeatures", + "vendorName", + "vendorVersion", + "+", + "objectClass", + }), + }, + }, + }, + + // Request 10 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 308203850201046482037e040030820378302a040a76656e646f724e616d65311c041a41706163686520536f66747761726520466f756e646174696f6e301c040d76656e646f7256657273696f6e310b0409322e302e302d4d31343026040b6f626a656374436c61737331170403746f700410657874656e7369626c654f626a65637430200411737562736368656d61537562656e747279310b0409636e3d736368656d61301b0414737570706f727465644c44415056657273696f6e31030401333082012e0410737570706f72746564436f6e74726f6c318201180417322e31362e3834302e312e3131333733302e332e342e330417312e332e362e312e342e312e343230332e312e31302e310417322e31362e3834302e312e3131333733302e332e342e320418312e332e362e312e342e312e343230332e312e392e312e340419312e332e362e312e342e312e34322e322e32372e382e352e310418312e332e362e312e342e312e343230332e312e392e312e310418312e332e362e312e342e312e343230332e312e392e312e330418312e332e362e312e342e312e343230332e312e392e312e320417312e332e362e312e342e312e31383036302e302e302e310417322e31362e3834302e312e3131333733302e332e342e370416312e322e3834302e3131333535362e312e342e3331393081910412737570706f72746564457874656e73696f6e317b0416312e332e362e312e342e312e313436362e32303033360416312e332e362e312e342e312e313436362e32303033370417312e332e362e312e342e312e31383036302e302e312e350417312e332e362e312e342e312e31383036302e302e312e330417312e332e362e312e342e312e343230332e312e31312e3130530417737570706f727465645341534c4d656368616e69736d73313804044e544c4d0406475353415049040a4753532d53504e45474f04084352414d2d4d4435040653494d504c45040a4449474553542d4d443530330409656e747279555549443126042466323930343235632d383237322d346536322d386136372d3932623036663338646266353046040e6e616d696e67436f6e7465787473313404096f753d73797374656d041164633d6578616d706c652c64633d636f6d04096f753d736368656d6104096f753d636f6e666967302d0411737570706f72746564466561747572657331180416312e332e362e312e342e312e343230332e312e352e31 + 0x30, 0x82, 0x03, 0x85, 0x02, 0x01, 0x04, 0x64, 0x82, 0x03, 0x7e, 0x04, 0x00, 0x30, 0x82, 0x03, 0x78, 0x30, 0x2a, 0x04, 0x0a, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x31, 0x1c, 0x04, 0x1a, 0x41, 0x70, 0x61, 0x63, 0x68, 0x65, 0x20, 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65, 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x30, 0x1c, 0x04, 0x0d, 0x76, 0x65, 0x6e, 0x64, 0x6f, 0x72, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x0b, 0x04, 0x09, 0x32, 0x2e, 0x30, 0x2e, 0x30, 0x2d, 0x4d, 0x31, 0x34, 0x30, 0x26, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x17, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x10, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x30, 0x20, 0x04, 0x11, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x53, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x31, 0x0b, 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x30, 0x1b, 0x04, 0x14, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x4c, 0x44, 0x41, 0x50, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x03, 0x04, 0x01, 0x33, 0x30, 0x82, 0x01, 0x2e, 0x04, 0x10, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x31, 0x82, 0x01, 0x18, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x33, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x34, 0x04, 0x19, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x2e, 0x32, 0x2e, 0x32, 0x37, 0x2e, 0x38, 0x2e, 0x35, 0x2e, 0x31, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x31, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x33, 0x04, 0x18, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x39, 0x2e, 0x31, 0x2e, 0x32, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x38, 0x30, 0x36, 0x30, 0x2e, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x37, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, 0x30, 0x81, 0x91, 0x04, 0x12, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x45, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x31, 0x7b, 0x04, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x36, 0x04, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x38, 0x30, 0x36, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2e, 0x35, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x38, 0x30, 0x36, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x2e, 0x33, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x2e, 0x31, 0x30, 0x53, 0x04, 0x17, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x53, 0x41, 0x53, 0x4c, 0x4d, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x69, 0x73, 0x6d, 0x73, 0x31, 0x38, 0x04, 0x04, 0x4e, 0x54, 0x4c, 0x4d, 0x04, 0x06, 0x47, 0x53, 0x53, 0x41, 0x50, 0x49, 0x04, 0x0a, 0x47, 0x53, 0x53, 0x2d, 0x53, 0x50, 0x4e, 0x45, 0x47, 0x4f, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35, 0x04, 0x06, 0x53, 0x49, 0x4d, 0x50, 0x4c, 0x45, 0x04, 0x0a, 0x44, 0x49, 0x47, 0x45, 0x53, 0x54, 0x2d, 0x4d, 0x44, 0x35, 0x30, 0x33, 0x04, 0x09, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x55, 0x55, 0x49, 0x44, 0x31, 0x26, 0x04, 0x24, 0x66, 0x32, 0x39, 0x30, 0x34, 0x32, 0x35, 0x63, 0x2d, 0x38, 0x32, 0x37, 0x32, 0x2d, 0x34, 0x65, 0x36, 0x32, 0x2d, 0x38, 0x61, 0x36, 0x37, 0x2d, 0x39, 0x32, 0x62, 0x30, 0x36, 0x66, 0x33, 0x38, 0x64, 0x62, 0x66, 0x35, 0x30, 0x46, 0x04, 0x0e, 0x6e, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x31, 0x34, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x04, 0x11, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2d, 0x04, 0x11, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x31, 0x18, 0x04, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x35, 0x2e, 0x31, + }, + }, + out: LDAPMessage{ + messageID: MessageID(4), + protocolOp: SearchResultEntry{ + objectName: LDAPDN(""), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("vendorName"), + vals: []AttributeValue{ + AttributeValue("Apache Software Foundation"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("vendorVersion"), + vals: []AttributeValue{ + AttributeValue("2.0.0-M14"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("objectClass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("extensibleObject"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("subschemaSubentry"), + vals: []AttributeValue{ + AttributeValue("cn=schema"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("supportedLDAPVersion"), + vals: []AttributeValue{ + AttributeValue("3"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("supportedControl"), + vals: []AttributeValue{ + AttributeValue("2.16.840.1.113730.3.4.3"), + AttributeValue("1.3.6.1.4.1.4203.1.10.1"), + AttributeValue("2.16.840.1.113730.3.4.2"), + AttributeValue("1.3.6.1.4.1.4203.1.9.1.4"), + AttributeValue("1.3.6.1.4.1.42.2.27.8.5.1"), + AttributeValue("1.3.6.1.4.1.4203.1.9.1.1"), + AttributeValue("1.3.6.1.4.1.4203.1.9.1.3"), + AttributeValue("1.3.6.1.4.1.4203.1.9.1.2"), + AttributeValue("1.3.6.1.4.1.18060.0.0.1"), + AttributeValue("2.16.840.1.113730.3.4.7"), + AttributeValue("1.2.840.113556.1.4.319"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("supportedExtension"), + vals: []AttributeValue{ + AttributeValue("1.3.6.1.4.1.1466.20036"), + AttributeValue("1.3.6.1.4.1.1466.20037"), + AttributeValue("1.3.6.1.4.1.18060.0.1.5"), + AttributeValue("1.3.6.1.4.1.18060.0.1.3"), + AttributeValue("1.3.6.1.4.1.4203.1.11.1"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("supportedSASLMechanisms"), + vals: []AttributeValue{ + AttributeValue("NTLM"), + AttributeValue("GSSAPI"), + AttributeValue("GSS-SPNEGO"), + AttributeValue("CRAM-MD5"), + AttributeValue("SIMPLE"), + AttributeValue("DIGEST-MD5"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("entryUUID"), + vals: []AttributeValue{ + AttributeValue("f290425c-8272-4e62-8a67-92b06f38dbf5"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("namingContexts"), + vals: []AttributeValue{ + AttributeValue("ou=system"), + AttributeValue("dc=example,dc=com"), + AttributeValue("ou=schema"), + AttributeValue("ou=config"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("supportedFeatures"), + vals: []AttributeValue{ + AttributeValue("1.3.6.1.4.1.4203.1.5.1"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 11 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010465070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x04, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(4), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 12 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3028020105632304000a01000a0100020100020100010100870b6f626a656374436c617373300304012a + 0x30, 0x28, 0x02, 0x01, 0x05, 0x63, 0x23, 0x04, 0x00, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x00, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + }, + }, + out: LDAPMessage{ + messageID: MessageID(5), + protocolOp: SearchRequest{ + baseObject: LDAPDN(""), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(0), + sizeLimit: INTEGER(0), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("*"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 13 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3031020105642c040030283026040b6f626a656374436c61737331170403746f700410657874656e7369626c654f626a656374 + 0x30, 0x31, 0x02, 0x01, 0x05, 0x64, 0x2c, 0x04, 0x00, 0x30, 0x28, 0x30, 0x26, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x17, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x10, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x62, 0x6c, 0x65, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(5), + protocolOp: SearchResultEntry{ + objectName: LDAPDN(""), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectClass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("extensibleObject"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 14 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010565070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x05, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(5), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 15 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304c020106634704096f753d736368656d610a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x4c, 0x02, 0x01, 0x06, 0x63, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(6), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=schema"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 16 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303c020106643704096f753d736368656d61302a3028040b6f626a656374636c617373311904126f7267616e697a6174696f6e616c556e69740403746f70 + 0x30, 0x3c, 0x02, 0x01, 0x06, 0x64, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, 0x04, 0x03, 0x74, 0x6f, 0x70, + }, + }, + out: LDAPMessage{ + messageID: MessageID(6), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=schema"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("organizationalUnit"), + AttributeValue("top"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 17 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010665070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x06, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(6), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 18 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304c020107634704096f753d636f6e6669670a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x4c, 0x02, 0x01, 0x07, 0x63, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(7), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=config"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 19 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303c020107643704096f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x3c, 0x02, 0x01, 0x07, 0x64, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(7), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 20 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010765070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x07, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(7), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 21 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304c020108634704096f753d73797374656d0a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x4c, 0x02, 0x01, 0x08, 0x63, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(8), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=system"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 22 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303c020108643704096f753d73797374656d302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x3c, 0x02, 0x01, 0x08, 0x64, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(8), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=system"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 23 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010865070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x08, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(8), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 24 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304c02010963470409636e3d736368656d610a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x4c, 0x02, 0x01, 0x09, 0x63, 0x47, 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(9), + protocolOp: SearchRequest{ + baseObject: LDAPDN("cn=schema"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 25 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304e02010964490409636e3d736368656d61303c303a040b6f626a656374436c617373312b0403746f700409737562736368656d610408737562656e747279040f617061636865537562736368656d61 + 0x30, 0x4e, 0x02, 0x01, 0x09, 0x64, 0x49, 0x04, 0x09, 0x63, 0x6e, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x30, 0x3c, 0x30, 0x3a, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x2b, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x09, 0x73, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x04, 0x08, 0x73, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x04, 0x0f, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x53, 0x75, 0x62, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, + }, + }, + out: LDAPMessage{ + messageID: MessageID(9), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("cn=schema"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectClass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("subschema"), + AttributeValue("subentry"), + AttributeValue("apacheSubschema"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 26 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010965070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x09, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(9), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 27 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 305402010a634f041164633d6578616d706c652c64633d636f6d0a01000a0103020101020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x54, 0x02, 0x01, 0x0a, 0x63, 0x4f, 0x04, 0x11, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x01, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(10), + protocolOp: SearchRequest{ + baseObject: LDAPDN("dc=example,dc=com"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 28 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303802010a6433041164633d6578616d706c652c64633d636f6d301e301c040b6f626a656374636c617373310d0406646f6d61696e0403746f70 + 0x30, 0x38, 0x02, 0x01, 0x0a, 0x64, 0x33, 0x04, 0x11, 0x64, 0x63, 0x3d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c, 0x64, 0x63, 0x3d, 0x63, 0x6f, 0x6d, 0x30, 0x1e, 0x30, 0x1c, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x0d, 0x04, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x04, 0x03, 0x74, 0x6f, 0x70, + }, + }, + out: LDAPMessage{ + messageID: MessageID(10), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("dc=example,dc=com"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("domain"), + AttributeValue("top"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 29 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010a65070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x0a, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(10), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 30 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304d02010b634804096f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x4d, 0x02, 0x01, 0x0b, 0x63, 0x48, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(11), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=config"), + scope: ENUMERATED(1), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 31 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 306702010b646204286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e66696730363034040b6f626a656374636c61737331250403746f7004146164732d6469726563746f72795365727669636504086164732d62617365 + 0x30, 0x67, 0x02, 0x01, 0x0b, 0x64, 0x62, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x36, 0x30, 0x34, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x25, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(11), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("ads-directoryService"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 32 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010b65070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x0b, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(11), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 33 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303102010c632c04096f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + 0x30, 0x31, 0x02, 0x01, 0x0c, 0x63, 0x2c, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + }, + }, + out: LDAPMessage{ + messageID: MessageID(12), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=config"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(0), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("*"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 34 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304c02010c644704096f753d636f6e666967303a300e04026f7531080406636f6e6669673028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x4c, 0x02, 0x01, 0x0c, 0x64, 0x47, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x3a, 0x30, 0x0e, 0x04, 0x02, 0x6f, 0x75, 0x31, 0x08, 0x04, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(12), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("ou"), + vals: []AttributeValue{ + AttributeValue("config"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 35 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010c65070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x0c, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(12), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 36 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 306c02010d636704286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x6c, 0x02, 0x01, 0x0d, 0x63, 0x67, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ads-directoryServiceId=default,ou=config"), + scope: ENUMERATED(1), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 37 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 307b02010d647604456164732d6a6f75726e616c49643d64656661756c744a6f75726e616c2c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302d302b040b6f626a656374636c617373311c0403746f70040b6164732d6a6f75726e616c04086164732d62617365 + 0x30, 0x7b, 0x02, 0x01, 0x0d, 0x64, 0x76, 0x04, 0x45, 0x61, 0x64, 0x73, 0x2d, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2d, 0x30, 0x2b, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x1c, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x0b, 0x61, 0x64, 0x73, 0x2d, 0x6a, 0x6f, 0x75, 0x72, 0x6e, 0x61, 0x6c, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-journalId=defaultJournal,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("ads-journal"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 38 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 306b02010d646604386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x6b, 0x02, 0x01, 0x0d, 0x64, 0x66, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 39 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 306902010d646404366f753d706172746974696f6e732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x69, 0x02, 0x01, 0x0d, 0x64, 0x64, 0x04, 0x36, 0x6f, 0x75, 0x3d, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=partitions,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 40 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30818102010d647c04496164732d6368616e67654c6f6749643d64656661756c744368616e67654c6f672c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302f302d040b6f626a656374636c617373311e0403746f7004086164732d62617365040d6164732d6368616e67654c6f67 + 0x30, 0x81, 0x81, 0x02, 0x01, 0x0d, 0x64, 0x7c, 0x04, 0x49, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2f, 0x30, 0x2d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x1e, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x04, 0x0d, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x4c, 0x6f, 0x67, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-changeLogId=defaultChangeLog,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("ads-base"), + AttributeValue("ads-changeLog"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 41 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 306602010d646104336f753d736572766572732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e666967302a3028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x66, 0x02, 0x01, 0x0d, 0x64, 0x61, 0x04, 0x33, 0x6f, 0x75, 0x3d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x2a, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=servers,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 42 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010d65070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x0d, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 43 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 305002010e634b04286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + 0x30, 0x50, 0x02, 0x01, 0x0e, 0x63, 0x4b, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + }, + }, + out: LDAPMessage{ + messageID: MessageID(14), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ads-directoryServiceId=default,ou=config"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(0), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("*"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 44 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3082017c02010e6482017504286164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e66696730820147302304166164732d6469726563746f72797365727669636569643109040764656661756c74302104166164732d647373796e63706572696f646d696c6c69733107040531353030303024041a6164732d6473616c6c6f77616e6f6e796d6f757361636365737331060404545255453016040f6164732d64737265706c696361696431030401313025041a6164732d6473616363657373636f6e74726f6c656e61626c65643107040546414c5345301f04146164732d647370617373776f726468696464656e3107040546414c5345302a041f6164732d647364656e6f726d616c697a656f706174747273656e61626c65643107040546414c53453015040b6164732d656e61626c656431060404545255453034040b6f626a656374636c61737331250403746f7004146164732d6469726563746f72795365727669636504086164732d62617365 + 0x30, 0x82, 0x01, 0x7c, 0x02, 0x01, 0x0e, 0x64, 0x82, 0x01, 0x75, 0x04, 0x28, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x82, 0x01, 0x47, 0x30, 0x23, 0x04, 0x16, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x69, 0x64, 0x31, 0x09, 0x04, 0x07, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x30, 0x21, 0x04, 0x16, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x73, 0x79, 0x6e, 0x63, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x6d, 0x69, 0x6c, 0x6c, 0x69, 0x73, 0x31, 0x07, 0x04, 0x05, 0x31, 0x35, 0x30, 0x30, 0x30, 0x30, 0x24, 0x04, 0x1a, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x61, 0x6c, 0x6c, 0x6f, 0x77, 0x61, 0x6e, 0x6f, 0x6e, 0x79, 0x6d, 0x6f, 0x75, 0x73, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x31, 0x06, 0x04, 0x04, 0x54, 0x52, 0x55, 0x45, 0x30, 0x16, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x69, 0x64, 0x31, 0x03, 0x04, 0x01, 0x31, 0x30, 0x25, 0x04, 0x1a, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x07, 0x04, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x30, 0x1f, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x68, 0x69, 0x64, 0x64, 0x65, 0x6e, 0x31, 0x07, 0x04, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x30, 0x2a, 0x04, 0x1f, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x73, 0x64, 0x65, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x6f, 0x70, 0x61, 0x74, 0x74, 0x72, 0x73, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x07, 0x04, 0x05, 0x46, 0x41, 0x4c, 0x53, 0x45, 0x30, 0x15, 0x04, 0x0b, 0x61, 0x64, 0x73, 0x2d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x06, 0x04, 0x04, 0x54, 0x52, 0x55, 0x45, 0x30, 0x34, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x25, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(14), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("ads-directoryserviceid"), + vals: []AttributeValue{ + AttributeValue("default"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-dssyncperiodmillis"), + vals: []AttributeValue{ + AttributeValue("15000"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-dsallowanonymousaccess"), + vals: []AttributeValue{ + AttributeValue("TRUE"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-dsreplicaid"), + vals: []AttributeValue{ + AttributeValue("1"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-dsaccesscontrolenabled"), + vals: []AttributeValue{ + AttributeValue("FALSE"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-dspasswordhidden"), + vals: []AttributeValue{ + AttributeValue("FALSE"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-dsdenormalizeopattrsenabled"), + vals: []AttributeValue{ + AttributeValue("FALSE"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-enabled"), + vals: []AttributeValue{ + AttributeValue("TRUE"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("ads-directoryService"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 45 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010e65070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x0e, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(14), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 46 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 306002010f635b04386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + 0x30, 0x60, 0x02, 0x01, 0x0f, 0x63, 0x5b, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + }, + }, + out: LDAPMessage{ + messageID: MessageID(15), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(0), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("*"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 47 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30818102010f647c04386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673040301404026f75310e040c696e746572636570746f72733028040b6f626a656374636c61737331190403746f7004126f7267616e697a6174696f6e616c556e6974 + 0x30, 0x81, 0x81, 0x02, 0x01, 0x0f, 0x64, 0x7c, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x40, 0x30, 0x14, 0x04, 0x02, 0x6f, 0x75, 0x31, 0x0e, 0x04, 0x0c, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x30, 0x28, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x19, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x12, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x55, 0x6e, 0x69, 0x74, + }, + }, + out: LDAPMessage{ + messageID: MessageID(15), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("ou"), + vals: []AttributeValue{ + AttributeValue("interceptors"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("organizationalUnit"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 48 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02010f65070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x0f, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(15), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 49 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 307c020110637704386f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x7c, 0x02, 0x01, 0x10, 0x63, 0x77, 0x04, 0x38, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=interceptors,ads-directoryServiceId=default,ou=config"), + scope: ENUMERATED(1), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 50 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819a020110648194045f6164732d696e746572636570746f7249643d657863657074696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x9a, 0x02, 0x01, 0x10, 0x64, 0x81, 0x94, 0x04, 0x5f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x65, 0x78, 0x63, 0x65, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=exceptionInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 51 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 308196020110648190045b6164732d696e746572636570746f7249643d6576656e74496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x96, 0x02, 0x01, 0x10, 0x64, 0x81, 0x90, 0x04, 0x5b, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=eventInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 52 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081a502011064819f046a6164732d696e746572636570746f7249643d6f7065726174696f6e616c417474726962757465496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0xa5, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9f, 0x04, 0x6a, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x6f, 0x70, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=operationalAttributeInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 53 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081be0201106481b804646164732d696e746572636570746f7249643d61757468656e7469636174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673050304e040b6f626a656374636c617373313f040f6164732d696e746572636570746f720403746f7004086164732d62617365041d6164732d61757468656e7469636174696f6e496e746572636570746f72 + 0x30, 0x81, 0xbe, 0x02, 0x01, 0x10, 0x64, 0x81, 0xb8, 0x04, 0x64, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x50, 0x30, 0x4e, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x3f, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x04, 0x1d, 0x61, 0x64, 0x73, 0x2d, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=authenticationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + AttributeValue("ads-authenticationInterceptor"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 54 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081a202011064819c04676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0xa2, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9c, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 55 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081a002011064819a04656164732d696e746572636570746f7249643d70617373776f726448617368696e67496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0xa0, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9a, 0x04, 0x65, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x48, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=passwordHashingInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 56 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 308197020110648191045c6164732d696e746572636570746f7249643d736368656d61496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x97, 0x02, 0x01, 0x10, 0x64, 0x81, 0x91, 0x04, 0x5c, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=schemaInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 57 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081a402011064819e04696164732d696e746572636570746f7249643d61646d696e697374726174697665506f696e74496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0xa4, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9e, 0x04, 0x69, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x64, 0x6d, 0x69, 0x6e, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=administrativePointInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 58 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 308199020110648193045e6164732d696e746572636570746f7249643d726566657272616c496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x99, 0x02, 0x01, 0x10, 0x64, 0x81, 0x93, 0x04, 0x5e, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x72, 0x65, 0x66, 0x65, 0x72, 0x72, 0x61, 0x6c, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=referralInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 59 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819e02011064819804636164732d696e746572636570746f7249643d6b657944657269766174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x9e, 0x02, 0x01, 0x10, 0x64, 0x81, 0x98, 0x04, 0x63, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x6b, 0x65, 0x79, 0x44, 0x65, 0x72, 0x69, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=keyDerivationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 60 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819e02011064819804636164732d696e746572636570746f7249643d6e6f726d616c697a6174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x9e, 0x02, 0x01, 0x10, 0x64, 0x81, 0x98, 0x04, 0x63, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x6e, 0x6f, 0x72, 0x6d, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=normalizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 61 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 308199020110648193045e6164732d696e746572636570746f7249643d737562656e747279496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x99, 0x02, 0x01, 0x10, 0x64, 0x81, 0x93, 0x04, 0x5e, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x73, 0x75, 0x62, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=subentryInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 62 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081a502011064819f046a6164732d696e746572636570746f7249643d64656661756c74417574686f72697a6174696f6e496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0xa5, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9f, 0x04, 0x6a, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=defaultAuthorizationInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 63 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 308198020110648192045d6164732d696e746572636570746f7249643d74726967676572496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0x98, 0x02, 0x01, 0x10, 0x64, 0x81, 0x92, 0x04, 0x5d, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=triggerInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 64 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081a402011064819e04696164732d696e746572636570746f7249643d636f6c6c656374697665417474726962757465496e746572636570746f722c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669673031302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d62617365 + 0x30, 0x81, 0xa4, 0x02, 0x01, 0x10, 0x64, 0x81, 0x9e, 0x04, 0x69, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x76, 0x65, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x31, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=collectiveAttributeInterceptor,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 65 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02011065070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x10, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(16), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 66 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819002011163818a04676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01000a0103020100020100010100870b6f626a656374436c617373300304012a + 0x30, 0x81, 0x90, 0x02, 0x01, 0x11, 0x63, 0x81, 0x8a, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x00, 0x0a, 0x01, 0x03, 0x02, 0x01, 0x00, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x03, 0x04, 0x01, 0x2a, + }, + }, + out: LDAPMessage{ + messageID: MessageID(17), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + scope: ENUMERATED(0), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(0), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("*"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 67 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3082018d0201116482018604676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e66696730820119302f040b6f626a656374636c6173733120040f6164732d696e746572636570746f720403746f7004086164732d626173653015040b6164732d656e61626c65643106040454525545306004186164732d696e746572636570746f72636c6173736e616d65314404426f72672e6170616368652e6469726563746f72792e7365727665722e636f72652e617574687a2e416369417574686f72697a6174696f6e496e746572636570746f72301b04146164732d696e746572636570746f726f726465723103040134305004116164732d696e746572636570746f726964313b041b616369417574686f72697a6174696f6e496e746572636570746f72041c616369417574686f72697a6174696f6e496e746572636570746f7232 + 0x30, 0x82, 0x01, 0x8d, 0x02, 0x01, 0x11, 0x64, 0x82, 0x01, 0x86, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x30, 0x82, 0x01, 0x19, 0x30, 0x2f, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x20, 0x04, 0x0f, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x08, 0x61, 0x64, 0x73, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x30, 0x15, 0x04, 0x0b, 0x61, 0x64, 0x73, 0x2d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x31, 0x06, 0x04, 0x04, 0x54, 0x52, 0x55, 0x45, 0x30, 0x60, 0x04, 0x18, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x6e, 0x61, 0x6d, 0x65, 0x31, 0x44, 0x04, 0x42, 0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x7a, 0x2e, 0x41, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x30, 0x1b, 0x04, 0x14, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x31, 0x03, 0x04, 0x01, 0x34, 0x30, 0x50, 0x04, 0x11, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x69, 0x64, 0x31, 0x3b, 0x04, 0x1b, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x04, 0x1c, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, + }, + }, + out: LDAPMessage{ + messageID: MessageID(17), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectclass"), + vals: []AttributeValue{ + AttributeValue("ads-interceptor"), + AttributeValue("top"), + AttributeValue("ads-base"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-enabled"), + vals: []AttributeValue{ + AttributeValue("TRUE"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-interceptorclassname"), + vals: []AttributeValue{ + AttributeValue("org.apache.directory.server.core.authz.AciAuthorizationInterceptor"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-interceptororder"), + vals: []AttributeValue{ + AttributeValue("4"), + }, + }, + PartialAttribute{ + type_: AttributeDescription("ads-interceptorid"), + vals: []AttributeValue{ + AttributeValue("aciAuthorizationInterceptor"), + AttributeValue("aciAuthorizationInterceptor2"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 68 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02011165070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x11, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(17), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 69 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081ac0201126381a604676164732d696e746572636570746f7249643d616369417574686f72697a6174696f6e496e746572636570746f72322c6f753d696e746572636570746f72732c6164732d6469726563746f72795365727669636549643d64656661756c742c6f753d636f6e6669670a01010a0103020203e8020100010100870b6f626a656374436c617373301e040f6861735375626f7264696e61746573040b6f626a656374436c617373 + 0x30, 0x81, 0xac, 0x02, 0x01, 0x12, 0x63, 0x81, 0xa6, 0x04, 0x67, 0x61, 0x64, 0x73, 0x2d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x49, 0x64, 0x3d, 0x61, 0x63, 0x69, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x32, 0x2c, 0x6f, 0x75, 0x3d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x73, 0x2c, 0x61, 0x64, 0x73, 0x2d, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x3d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x0a, 0x01, 0x01, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x1e, 0x04, 0x0f, 0x68, 0x61, 0x73, 0x53, 0x75, 0x62, 0x6f, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x74, 0x65, 0x73, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(18), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ads-interceptorId=aciAuthorizationInterceptor2,ou=interceptors,ads-directoryServiceId=default,ou=config"), + scope: ENUMERATED(1), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("hasSubordinates"), + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 70 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02011265070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x12, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(18), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 71 + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30050201134200 + 0x30, 0x05, 0x02, 0x01, 0x13, 0x42, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(19), + protocolOp: UnbindRequest{}, + controls: (*Controls)(nil), + }, + }, + + // Request 72: CLIENT AddRequest + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3081b60201156881b0044a636e3d723030582b636f6d6d6f6e4e616d653d54686520723030582b6465736372697074696f6e3d41207465737420757365722c6f753d636f6e73756d6572732c6f753d73797374656d3062301c040b6465736372697074696f6e310d040b4120746573742075736572300c0402736e310604047230307830160402636e311004047230305804085468652072303058301c040b6f626a656374436c617373310d0406706572736f6e0403746f70 + 0x30, 0x81, 0xb6, 0x02, 0x01, 0x15, 0x68, 0x81, 0xb0, 0x04, 0x4a, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x3d, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x62, 0x30, 0x1c, 0x04, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x0d, 0x04, 0x0b, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x30, 0x0c, 0x04, 0x02, 0x73, 0x6e, 0x31, 0x06, 0x04, 0x04, 0x72, 0x30, 0x30, 0x78, 0x30, 0x16, 0x04, 0x02, 0x63, 0x6e, 0x31, 0x10, 0x04, 0x04, 0x72, 0x30, 0x30, 0x58, 0x04, 0x08, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x30, 0x1c, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x0d, 0x04, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x04, 0x03, 0x74, 0x6f, 0x70, + }, + }, + out: LDAPMessage{ + messageID: MessageID(21), + protocolOp: AddRequest{ + entry: LDAPDN("cn=r00X+commonName=The r00X+description=A test user,ou=consumers,ou=system"), + attributes: AttributeList{ + Attribute{ + type_: AttributeDescription("description"), + vals: []AttributeValue{ + AttributeValue("A test user"), + }, + }, + Attribute{ + type_: AttributeDescription("sn"), + vals: []AttributeValue{ + AttributeValue("r00x"), + }, + }, + Attribute{ + type_: AttributeDescription("cn"), + vals: []AttributeValue{ + AttributeValue("r00X"), + AttributeValue("The r00X"), + }, + }, + Attribute{ + type_: AttributeDescription("objectClass"), + vals: []AttributeValue{ + AttributeValue("person"), + AttributeValue("top"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 73: SERVER AddResponse + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02011569070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x15, 0x69, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(21), + protocolOp: AddResponse{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 74: CLIENT ModifyRequest + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30790201196674044a636e3d723030582b636f6d6d6f6e6e616d653d54686520723030582b6465736372697074696f6e3d41207465737420757365722c6f753d636f6e73756d6572732c6f753d73797374656d302630240a0100301f040f74656c6570686f6e654e756d626572310c040a30313233343536373839 + 0x30, 0x79, 0x02, 0x01, 0x19, 0x66, 0x74, 0x04, 0x4a, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x26, 0x30, 0x24, 0x0a, 0x01, 0x00, 0x30, 0x1f, 0x04, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x31, 0x0c, 0x04, 0x0a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + }, + }, + out: LDAPMessage{ + messageID: MessageID(25), + protocolOp: ModifyRequest{ + object: LDAPDN("cn=r00X+commonname=The r00X+description=A test user,ou=consumers,ou=system"), + changes: []ModifyRequestChange{ + { + operation: ENUMERATED(0), + modification: PartialAttribute{ + type_: AttributeDescription("telephoneNumber"), + vals: []AttributeValue{ + AttributeValue("0123456789"), + }, + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 75: SERVER ModifyResponse + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02011967070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x19, 0x67, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(25), + protocolOp: ModifyResponse{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 76: CLIENT ModifyDNrequest + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 307502011b6c70044a636e3d723030582b636f6d6d6f6e6e616d653d54686520723030582b6465736372697074696f6e3d41207465737420757365722c6f753d636f6e73756d6572732c6f753d73797374656d0407636e3d723030580101ff80166f753d636f6e73756d6572732c6f753d73797374656d + 0x30, 0x75, 0x02, 0x01, 0x1b, 0x6c, 0x70, 0x04, 0x4a, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x6e, 0x61, 0x6d, 0x65, 0x3d, 0x54, 0x68, 0x65, 0x20, 0x72, 0x30, 0x30, 0x58, 0x2b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x3d, 0x41, 0x20, 0x74, 0x65, 0x73, 0x74, 0x20, 0x75, 0x73, 0x65, 0x72, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x04, 0x07, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x01, 0x01, 0xff, 0x80, 0x16, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + }, + }, + out: LDAPMessage{ + messageID: MessageID(27), + protocolOp: ModifyDNRequest{ + entry: LDAPDN("cn=r00X+commonname=The r00X+description=A test user,ou=consumers,ou=system"), + newrdn: RelativeLDAPDN("cn=r00X"), + deleteoldrdn: BOOLEAN(true), + newSuperior: LDAPDN("ou=consumers,ou=system").Pointer(), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 77: SERVER ModifyDNResponse + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02011b6d070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x1b, 0x6d, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(27), + protocolOp: ModifyDNResponse{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 78: CLIENT + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303b020121633604096f753d73797374656d0a01020a0103020203e8020100010100a30a0402636e040472303058300d040b6f626a656374436c617373 + 0x30, 0x3b, 0x02, 0x01, 0x21, 0x63, 0x36, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa3, 0x0a, 0x04, 0x02, 0x63, 0x6e, 0x04, 0x04, 0x72, 0x30, 0x30, 0x58, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(33), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=system"), + scope: ENUMERATED(2), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterEqualityMatch{ + attributeDesc: AttributeDescription("cn"), + assertionValue: AssertionValue("r00X"), + }, + attributes: AttributeSelection{ + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 79: SERVER + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30450201216440041e636e3d723030582c6f753d636f6e73756d6572732c6f753d73797374656d301e301c040b6f626a656374436c617373310d0403746f700406706572736f6e + 0x30, 0x45, 0x02, 0x01, 0x21, 0x64, 0x40, 0x04, 0x1e, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x1e, 0x30, 0x1c, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x31, 0x0d, 0x04, 0x03, 0x74, 0x6f, 0x70, 0x04, 0x06, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, + }, + }, + out: LDAPMessage{ + messageID: MessageID(33), + protocolOp: SearchResultEntry{ + objectName: LDAPDN("cn=r00X,ou=consumers,ou=system"), + attributes: PartialAttributeList{ + PartialAttribute{ + type_: AttributeDescription("objectClass"), + vals: []AttributeValue{ + AttributeValue("top"), + AttributeValue("person"), + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 80: SERVER + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c02012165070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x21, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(33), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 81: CLIENT SearchRequest with controls + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30819c020124633704096f753d73797374656d0a01020a0103020203e8020100010100870b6f626a656374436c617373300d040b6f626a656374436c617373a05e301e0417312e332e362e312e342e312e343230332e312e31302e3104030101ff30190417322e31362e3834302e312e3131333733302e332e342e3230210416312e322e3834302e3131333535362e312e342e333139040730050201030400 + 0x30, 0x81, 0x9c, 0x02, 0x01, 0x24, 0x63, 0x37, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0x87, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, 0xa0, 0x5e, 0x30, 0x1e, 0x04, 0x17, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x34, 0x32, 0x30, 0x33, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x2e, 0x31, 0x04, 0x03, 0x01, 0x01, 0xff, 0x30, 0x19, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x30, 0x21, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, 0x04, 0x07, 0x30, 0x05, 0x02, 0x01, 0x03, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(36), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=system"), + scope: ENUMERATED(2), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterPresent("objectClass"), + attributes: AttributeSelection{ + LDAPString("objectClass"), + }, + }, + controls: &Controls{ + Control{ + controlType: LDAPOID("1.3.6.1.4.1.4203.1.10.1"), + criticality: BOOLEAN(false), + controlValue: OCTETSTRING("\x01\x01\xff").Pointer(), + }, + Control{ + controlType: LDAPOID("2.16.840.1.113730.3.4.2"), + criticality: BOOLEAN(false), + controlValue: (*OCTETSTRING)(nil), + }, + Control{ + controlType: LDAPOID("1.2.840.113556.1.4.319"), + criticality: BOOLEAN(false), + controlValue: OCTETSTRING("0\x05\x02\x01\x03\x04\x00").Pointer(), + }, + }, + }, + }, + + // Request 82: SERVER SearchResultDone with Controls + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 303402012465070a010004000400a02630240416312e322e3834302e3131333535362e312e342e3331390101ff040730050201000400 + 0x30, 0x34, 0x02, 0x01, 0x24, 0x65, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, 0xa0, 0x26, 0x30, 0x24, 0x04, 0x16, 0x31, 0x2e, 0x32, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x31, 0x33, 0x35, 0x35, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x33, 0x31, 0x39, 0x01, 0x01, 0xff, 0x04, 0x07, 0x30, 0x05, 0x02, 0x01, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(36), + protocolOp: SearchResultDone{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: &Controls{ + Control{ + controlType: LDAPOID("1.2.840.113556.1.4.319"), + criticality: BOOLEAN(true), + controlValue: OCTETSTRING("0\x05\x02\x01\x00\x04\x00").Pointer(), + }, + }, + }, + }, + + // (| + // (& + // (cn=r00x) + // (telephoneNumber=*) + // ) + // (cn~=The) + // (& + // (!(description=Toto)) + // (ou=co*f*g*r*on) + // ) + // ) + // Request 83: CLIENT + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30818e02010d63818804096f753d73797374656d0a01020a0103020203e8020100010100a15ca01da30a0402636e040472303078870f74656c6570686f6e654e756d626572a8090402636e0403546865a030a215a313040b6465736372697074696f6e0404546f746fa41704026f7530118002636f81016681016781017282026f6e300d040b6f626a656374436c617373 + // 0x30, 0x81, 0x8e, 0x02, 0x01, 0x0d, 0x63, 0x81, 0x88, 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa1, 0x5c, 0xa0, 0x1d, 0xa3, 0x0a, 0x04, 0x02, 0x63, 0x6e, 0x04, 0x04, 0x72, 0x30, 0x30, 0x78, 0x87, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0xa8, 0x09, 0x04, 0x02, 0x63, 0x6e, 0x04, 0x03, 0x54, 0x68, 0x65, 0xa0, 0x30, 0xa2, 0x15, 0xa3, 0x13, 0x04, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x04, 0x04, 0x54, 0x6f, 0x74, 0x6f, 0xa4, 0x17, 0x04, 0x02, 0x6f, 0x75, 0x30, 0x11, 0x80, 0x02, 0x63, 0x6f, 0x81, 0x01, 0x66, 0x81, 0x01, 0x67, 0x81, 0x01, 0x72, 0x82, 0x02, 0x6f, 0x6e, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x30, 0x81, 0x8e, + // messageID + 0x02, 0x01, 0x0d, + // protocolOp + 0x63, 0x81, 0x88, + // baseObject + 0x04, 0x09, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + // scope + 0x0a, 0x01, 0x02, + // derefAliases + 0x0a, 0x01, 0x03, + // sizeLimit + 0x02, 0x02, 0x03, 0xe8, + // timeLimit + 0x02, 0x01, 0x00, + // typesOnly + 0x01, 0x01, 0x00, + // filter + // filterOr [1] + 0xa1, 0x5c, + // filterAnd [0] + 0xa0, 0x1d, + // filterEqualityMatch [3] + 0xa3, 0x0a, + // cn + 0x04, 0x02, 0x63, 0x6e, + // r00x + 0x04, 0x04, 0x72, 0x30, 0x30, 0x78, + // filterPresent [7] + // telephoneNumber + 0x87, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + // filterApproxMatch + 0xa8, 0x09, + // cn + 0x04, 0x02, 0x63, 0x6e, + // the + 0x04, 0x03, 0x54, 0x68, 0x65, + // filterAnd [0] + 0xa0, 0x30, + // filterNot [2] + 0xa2, 0x15, + // FilterEqualityMatch [3] + 0xa3, 0x13, + // description + 0x04, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + // Toto + 0x04, 0x04, 0x54, 0x6f, 0x74, 0x6f, + // filterSubstrings + 0xa4, 0x17, + // type = ou + 0x04, 0x02, 0x6f, 0x75, + // substrings + 0x30, 0x11, + // substringInitial = co + 0x80, 0x02, 0x63, 0x6f, + // substringAny = f + 0x81, 0x01, 0x66, + // substringAny = g + 0x81, 0x01, 0x67, + // substringAny = r + 0x81, 0x01, 0x72, + // substringFinal = on + 0x82, 0x02, 0x6f, 0x6e, + // attributes + 0x30, 0x0d, + // AttributeSelection = objectClass + 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(13), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=system"), + scope: ENUMERATED(2), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterOr{ + FilterAnd{ + FilterEqualityMatch{ + attributeDesc: AttributeDescription("cn"), + assertionValue: AssertionValue("r00x"), + }, + FilterPresent("telephoneNumber"), + }, + FilterApproxMatch{ + attributeDesc: AttributeDescription("cn"), + assertionValue: AssertionValue("The"), + }, + FilterAnd{ + FilterNot{ + Filter: FilterEqualityMatch{ + attributeDesc: AttributeDescription("description"), + assertionValue: AssertionValue("Toto"), + }, + }, + FilterSubstrings{ + type_: AttributeDescription("ou"), + substrings: []Substring{ + SubstringInitial("co"), + SubstringAny("f"), + SubstringAny("g"), + SubstringAny("r"), + SubstringFinal("on"), + }, + }, + }, + }, + attributes: AttributeSelection{ + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 84: CLIENT SearchRequest with FilterGreaterOrEqual and FilterLessOrEqual + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3072020119636d04000a01020a0103020203e8020100010100a04aa523041e6164732d636867507764506f6c69637950617373776f72644c656e677468040133a623041e6164732d636867507764506f6c69637950617373776f72644c656e677468040135300d040b6f626a656374436c617373 + 0x30, 0x72, 0x02, 0x01, 0x19, 0x63, 0x6d, 0x04, 0x00, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa0, 0x4a, 0xa5, 0x23, 0x04, 0x1e, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x67, 0x50, 0x77, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x04, 0x01, 0x33, 0xa6, 0x23, 0x04, 0x1e, 0x61, 0x64, 0x73, 0x2d, 0x63, 0x68, 0x67, 0x50, 0x77, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x4c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x04, 0x01, 0x35, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(25), + protocolOp: SearchRequest{ + baseObject: LDAPDN(""), + scope: ENUMERATED(2), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterAnd{ + FilterGreaterOrEqual{ + attributeDesc: AttributeDescription("ads-chgPwdPolicyPasswordLength"), + assertionValue: AssertionValue("3"), + }, + FilterLessOrEqual{ + attributeDesc: AttributeDescription("ads-chgPwdPolicyPasswordLength"), + assertionValue: AssertionValue("5"), + }, + }, + attributes: AttributeSelection{ + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 85: CLIENT SearchRequest with FilterExtensibleMatch + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 3074020131636f04166f753d636f6e73756d6572732c6f753d73797374656d0a01020a0103020203e8020100010100a936811474656c6570686f6e654e756d6265724d61746368820f74656c6570686f6e654e756d626572830a303132333435363738398401ff300d040b6f626a656374436c617373 + 0x30, 0x74, 0x02, 0x01, 0x31, 0x63, 0x6f, 0x04, 0x16, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x0a, 0x01, 0x02, 0x0a, 0x01, 0x03, 0x02, 0x02, 0x03, 0xe8, 0x02, 0x01, 0x00, 0x01, 0x01, 0x00, 0xa9, 0x36, 0x81, 0x14, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x82, 0x0f, 0x74, 0x65, 0x6c, 0x65, 0x70, 0x68, 0x6f, 0x6e, 0x65, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x83, 0x0a, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x84, 0x01, 0xff, 0x30, 0x0d, 0x04, 0x0b, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x43, 0x6c, 0x61, 0x73, 0x73, + }, + }, + out: LDAPMessage{ + messageID: MessageID(49), + protocolOp: SearchRequest{ + baseObject: LDAPDN("ou=consumers,ou=system"), + scope: ENUMERATED(2), + derefAliases: ENUMERATED(3), + sizeLimit: INTEGER(1000), + timeLimit: INTEGER(0), + typesOnly: BOOLEAN(false), + filter: FilterExtensibleMatch{ + matchingRule: MatchingRuleId("telephoneNumberMatch").Pointer(), + type_: AttributeDescription("telephoneNumber").Pointer(), + matchValue: AssertionValue("0123456789"), + dnAttributes: BOOLEAN(true), + }, + attributes: AttributeSelection{ + LDAPString("objectClass"), + }, + }, + controls: (*Controls)(nil), + }, + }, + + // Request 86: CLIENT + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 30400201274a3b636e3d4120636f6d706c657820706572736f6e5c2c207665727920636f6d706c657820212c6f753d636f6e73756d6572732c6f753d73797374656d + 0x30, 0x40, 0x02, 0x01, 0x27, 0x4a, 0x3b, 0x63, 0x6e, 0x3d, 0x41, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x20, 0x70, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x5c, 0x2c, 0x20, 0x76, 0x65, 0x72, 0x79, 0x20, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x78, 0x20, 0x21, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, + }, + }, + out: LDAPMessage{ + messageID: MessageID(39), + protocolOp: DelRequest("cn=A complex person\\, very complex !,ou=consumers,ou=system"), + controls: (*Controls)(nil), + }, + }, + + // Request 87: SERVER + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 300c0201276b070a010004000400 + 0x30, 0x0c, 0x02, 0x01, 0x27, 0x6b, 0x07, 0x0a, 0x01, 0x00, 0x04, 0x00, 0x04, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(39), + protocolOp: DelResponse{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 88: CLIENT ExtendedRequest: Start TLS (OID 1.3.6.1.4.1.1466.20037) + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 301d02010177188016312e332e362e312e342e312e313436362e3230303337 + 0x30, 0x1d, 0x02, 0x01, 0x01, 0x77, 0x18, 0x80, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37, + }, + }, + out: LDAPMessage{ + messageID: MessageID(1), + protocolOp: ExtendedRequest{ + requestName: LDAPOID("1.3.6.1.4.1.1466.20037"), + requestValue: (*OCTETSTRING)(nil), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 89: SERVER ExtendedResponse: Start TLS (OID 1.3.6.1.4.1.1466.20037) + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 302602010178210a0100040004008a16312e332e362e312e342e312e313436362e32303033378b00 + 0x30, 0x26, + 0x02, 0x01, 0x01, + 0x78, 0x21, 0x0a, + 0x01, 0x00, + 0x04, 0x00, + 0x04, 0x00, + 0x8a, 0x16, 0x31, 0x2e, 0x33, 0x2e, 0x36, 0x2e, 0x31, 0x2e, 0x34, 0x2e, 0x31, 0x2e, 0x31, 0x34, 0x36, 0x36, 0x2e, 0x32, 0x30, 0x30, 0x33, 0x37, + 0x8b, 0x00, + }, + }, + out: LDAPMessage{ + messageID: MessageID(1), + protocolOp: ExtendedResponse{ + LDAPResult: LDAPResult{ + resultCode: ENUMERATED(0), + matchedDN: LDAPDN(""), + diagnosticMessage: LDAPString(""), + referral: (*Referral)(nil), + }, + responseName: LDAPOID("1.3.6.1.4.1.1466.20037").Pointer(), + responseValue: OCTETSTRING("").Pointer(), + }, + controls: (*Controls)(nil), + }, + }, + + // Request 90: A bind request with a simple login / password authentication + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + 0x30, 0x1d, + 0x02, 0x01, 0x01, // messageID + 0x60, 0x18, // Application, tag 0 => this is a Bind request + 0x02, 0x01, 0x03, // Version 3 + 0x04, 0x07, 0x6d, 0x79, 0x4c, 0x6f, 0x67, 0x69, 0x6e, // login = myLogin + 0x80, 0x0a, 0x6d, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, // simple authentication: myPassword + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x01)), + protocolOp: BindRequest{ + version: 0x03, + name: LDAPDN("myLogin"), + authentication: OCTETSTRING([]byte("myPassword")), + }, + }, + }, + // Request 91: A bind request with SASL (CRAM-MD5) + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + 0x30, 0x16, + 0x02, 0x01, 0x01, // messageID + 0x60, 0x11, + 0x02, 0x01, 0x03, // version 3 + 0x04, 0x00, // no login + 0xa3, 0x0a, 0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35, // SASL mechanism "CRAM-MD5", no credentials + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x01)), + protocolOp: BindRequest{ + version: 0x03, + name: LDAPDN(""), + authentication: SaslCredentials{ + mechanism: LDAPString("CRAM-MD5"), + }, + }, + }, + }, + // Request 92: An abandon request + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + 0x30, 0x06, + 0x02, 0x01, 0x0a, // messageID + 0x50, 0x01, 0x05, // Abandon request [APPLICATION 16] MessageID = 0x05 + }, + }, + out: LDAPMessage{ + messageID: MessageID(int(0x0a)), + protocolOp: AbandonRequest(0x05), + controls: (*Controls)(nil), + }, + }, + // Request 93: CLIENT + { + bytes: Bytes{ + offset: 0, + bytes: []byte{ + // 304c0201156647041e636e3d723030582c6f753d636f6e73756d6572732c6f753d73797374656d302530230a0102301e040b6465736372697074696f6e310f040d48656c6c6f2c20e4b896e7958c + 0x30, 0x4c, 0x02, 0x01, 0x15, 0x66, 0x47, 0x04, 0x1e, 0x63, 0x6e, 0x3d, 0x72, 0x30, 0x30, 0x58, 0x2c, 0x6f, 0x75, 0x3d, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x73, 0x2c, 0x6f, 0x75, 0x3d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x30, 0x25, 0x30, 0x23, 0x0a, 0x01, 0x02, 0x30, 0x1e, 0x04, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x31, 0x0f, 0x04, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c, + }, + }, + out: LDAPMessage{ + messageID: MessageID(21), + protocolOp: ModifyRequest{ + object: LDAPDN("cn=r00X,ou=consumers,ou=system"), + changes: []ModifyRequestChange{ + { + operation: ENUMERATED(2), + modification: PartialAttribute{ + type_: AttributeDescription("description"), + vals: []AttributeValue{ + AttributeValue("Hello, 世界"), + }, + }, + }, + }, + }, + controls: (*Controls)(nil), + }, + }, + } +} diff --git a/goldap/referral.go b/goldap/referral.go new file mode 100644 index 0000000..8a04d85 --- /dev/null +++ b/goldap/referral.go @@ -0,0 +1,50 @@ +package message + +import "fmt" + +// +// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI +func readTaggedReferral(bytes *Bytes, class int, tag int) (referral Referral, err error) { + err = bytes.ReadSubBytes(class, tag, referral.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedReferral:\n%s", err.Error())} + return + } + return +} +func (referral *Referral) readComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var uri URI + uri, err = readURI(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + *referral = append(*referral, uri) + } + if len(*referral) == 0 { + return LdapError{"readComponents: expecting at least one URI"} + } + return +} +func (referral Referral) Pointer() *Referral { return &referral } + +// +// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI +func (r Referral) writeTagged(bytes *Bytes, class int, tag int) (size int) { + for i := len(r) - 1; i >= 0; i-- { + size += r[i].write(bytes) + } + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +// +// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI +func (r Referral) sizeTagged(tag int) (size int) { + for _, uri := range r { + size += uri.size() + } + size += sizeTagAndLength(tag, size) + return +} diff --git a/goldap/relative_ldap_dn.go b/goldap/relative_ldap_dn.go new file mode 100644 index 0000000..00bc73c --- /dev/null +++ b/goldap/relative_ldap_dn.go @@ -0,0 +1,15 @@ +package message + +// +// RelativeLDAPDN ::= LDAPString -- Constrained to +// -- [RFC4514] +func (r RelativeLDAPDN) write(bytes *Bytes) int { + return LDAPString(r).write(bytes) +} + +// +// RelativeLDAPDN ::= LDAPString -- Constrained to +// -- [RFC4514] +func (r RelativeLDAPDN) size() int { + return LDAPString(r).size() +} diff --git a/goldap/result.go b/goldap/result.go new file mode 100644 index 0000000..8a2c7ae --- /dev/null +++ b/goldap/result.go @@ -0,0 +1,282 @@ +package message + +import "fmt" + +// +// LDAPResult ::= SEQUENCE { +// resultCode ENUMERATED { +// success (0), +// operationsError (1), +// protocolError (2), +// timeLimitExceeded (3), +// sizeLimitExceeded (4), +// compareFalse (5), +// compareTrue (6), +// authMethodNotSupported (7), +// strongerAuthRequired (8), +// -- 9 reserved -- +// referral (10), +// adminLimitExceeded (11), +// unavailableCriticalExtension (12), +// confidentialityRequired (13), +// saslBindInProgress (14), +// +// +// +//Sermersheim Standards Track [Page 55] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// noSuchAttribute (16), +// undefinedAttributeType (17), +// inappropriateMatching (18), +// constraintViolation (19), +// attributeOrValueExists (20), +// invalidAttributeSyntax (21), +// -- 22-31 unused -- +// noSuchObject (32), +// aliasProblem (33), +// invalidDNSyntax (34), +// -- 35 reserved for undefined isLeaf -- +// aliasDereferencingProblem (36), +// -- 37-47 unused -- +// inappropriateAuthentication (48), +// invalidCredentials (49), +// insufficientAccessRights (50), +// busy (51), +// unavailable (52), +// unwillingToPerform (53), +// loopDetect (54), +// -- 55-63 unused -- +// namingViolation (64), +// objectClassViolation (65), +// notAllowedOnNonLeaf (66), +// notAllowedOnRDN (67), +// entryAlreadyExists (68), +// objectClassModsProhibited (69), +// -- 70 reserved for CLDAP -- +// affectsMultipleDSAs (71), +// -- 72-79 unused -- +// other (80), +// ... }, +// matchedDN LDAPDN, +// diagnosticMessage LDAPString, +// referral [3] Referral OPTIONAL } +func readTaggedLDAPResult(bytes *Bytes, class int, tag int) (ret LDAPResult, err error) { + err = bytes.ReadSubBytes(class, tag, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedLDAPResult:\n%s", err.Error())} + } + return +} +func readLDAPResult(bytes *Bytes) (ldapresult LDAPResult, err error) { + return readTaggedLDAPResult(bytes, classUniversal, tagSequence) +} +func (ldapresult *LDAPResult) readComponents(bytes *Bytes) (err error) { + ldapresult.resultCode, err = readENUMERATED(bytes, EnumeratedLDAPResultCode) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + ldapresult.matchedDN, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + ldapresult.diagnosticMessage, err = readLDAPString(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if bytes.HasMoreData() { + var tag TagAndLength + tag, err = bytes.PreviewTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if tag.Tag == TagLDAPResultReferral { + var referral Referral + referral, err = readTaggedReferral(bytes, classContextSpecific, TagLDAPResultReferral) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + ldapresult.referral = referral.Pointer() + } + } + return +} + +// +// LDAPResult ::= SEQUENCE { +// resultCode ENUMERATED { +// success (0), +// operationsError (1), +// protocolError (2), +// timeLimitExceeded (3), +// sizeLimitExceeded (4), +// compareFalse (5), +// compareTrue (6), +// authMethodNotSupported (7), +// strongerAuthRequired (8), +// -- 9 reserved -- +// referral (10), +// adminLimitExceeded (11), +// unavailableCriticalExtension (12), +// confidentialityRequired (13), +// saslBindInProgress (14), +// +// +// +//Sermersheim Standards Track [Page 55] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// noSuchAttribute (16), +// undefinedAttributeType (17), +// inappropriateMatching (18), +// constraintViolation (19), +// attributeOrValueExists (20), +// invalidAttributeSyntax (21), +// -- 22-31 unused -- +// noSuchObject (32), +// aliasProblem (33), +// invalidDNSyntax (34), +// -- 35 reserved for undefined isLeaf -- +// aliasDereferencingProblem (36), +// -- 37-47 unused -- +// inappropriateAuthentication (48), +// invalidCredentials (49), +// insufficientAccessRights (50), +// busy (51), +// unavailable (52), +// unwillingToPerform (53), +// loopDetect (54), +// -- 55-63 unused -- +// namingViolation (64), +// objectClassViolation (65), +// notAllowedOnNonLeaf (66), +// notAllowedOnRDN (67), +// entryAlreadyExists (68), +// objectClassModsProhibited (69), +// -- 70 reserved for CLDAP -- +// affectsMultipleDSAs (71), +// -- 72-79 unused -- +// other (80), +// ... }, +// matchedDN LDAPDN, +// diagnosticMessage LDAPString, +// referral [3] Referral OPTIONAL } +func (l LDAPResult) write(bytes *Bytes) (size int) { + size += l.writeComponents(bytes) + size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) + return +} +func (l LDAPResult) writeComponents(bytes *Bytes) (size int) { + if l.referral != nil { + size += l.referral.writeTagged(bytes, classContextSpecific, TagLDAPResultReferral) + } + size += l.diagnosticMessage.write(bytes) + size += l.matchedDN.write(bytes) + size += l.resultCode.write(bytes) + return +} + +// +// LDAPResult ::= SEQUENCE { +// resultCode ENUMERATED { +// success (0), +// operationsError (1), +// protocolError (2), +// timeLimitExceeded (3), +// sizeLimitExceeded (4), +// compareFalse (5), +// compareTrue (6), +// authMethodNotSupported (7), +// strongerAuthRequired (8), +// -- 9 reserved -- +// referral (10), +// adminLimitExceeded (11), +// unavailableCriticalExtension (12), +// confidentialityRequired (13), +// saslBindInProgress (14), +// +// +// +//Sermersheim Standards Track [Page 55] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// noSuchAttribute (16), +// undefinedAttributeType (17), +// inappropriateMatching (18), +// constraintViolation (19), +// attributeOrValueExists (20), +// invalidAttributeSyntax (21), +// -- 22-31 unused -- +// noSuchObject (32), +// aliasProblem (33), +// invalidDNSyntax (34), +// -- 35 reserved for undefined isLeaf -- +// aliasDereferencingProblem (36), +// -- 37-47 unused -- +// inappropriateAuthentication (48), +// invalidCredentials (49), +// insufficientAccessRights (50), +// busy (51), +// unavailable (52), +// unwillingToPerform (53), +// loopDetect (54), +// -- 55-63 unused -- +// namingViolation (64), +// objectClassViolation (65), +// notAllowedOnNonLeaf (66), +// notAllowedOnRDN (67), +// entryAlreadyExists (68), +// objectClassModsProhibited (69), +// -- 70 reserved for CLDAP -- +// affectsMultipleDSAs (71), +// -- 72-79 unused -- +// other (80), +// ... }, +// matchedDN LDAPDN, +// diagnosticMessage LDAPString, +// referral [3] Referral OPTIONAL } +func (l LDAPResult) size() (size int) { + size += l.sizeComponents() + size += sizeTagAndLength(tagSequence, size) + return +} +func (l LDAPResult) sizeTagged(tag int) (size int) { + size += l.sizeComponents() + size += sizeTagAndLength(tag, size) + return +} +func (l LDAPResult) sizeComponents() (size int) { + if l.referral != nil { + size += l.referral.sizeTagged(TagLDAPResultReferral) + } + size += l.diagnosticMessage.size() + size += l.matchedDN.size() + size += l.resultCode.size() + return +} +func (l *LDAPResult) SetResultCode(code int) { + l.resultCode = ENUMERATED(code) +} +func (l *LDAPResult) SeMatchedDN(code string) { + l.matchedDN = LDAPDN(code) +} +func (l *LDAPResult) SetDiagnosticMessage(code string) { + l.diagnosticMessage = LDAPString(code) +} +func (l *LDAPResult) SetReferral(r *Referral) { + l.referral = r +} diff --git a/goldap/sasl_credentials.go b/goldap/sasl_credentials.go new file mode 100644 index 0000000..1a56b3c --- /dev/null +++ b/goldap/sasl_credentials.go @@ -0,0 +1,63 @@ +package message + +import "fmt" + +// +// SaslCredentials ::= SEQUENCE { +// mechanism LDAPString, +// credentials OCTET STRING OPTIONAL } +// +func readSaslCredentials(bytes *Bytes) (authentication SaslCredentials, err error) { + authentication = SaslCredentials{} + err = bytes.ReadSubBytes(classContextSpecific, TagAuthenticationChoiceSaslCredentials, authentication.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readSaslCredentials:\n%s", err.Error())} + return + } + return +} +func (authentication *SaslCredentials) readComponents(bytes *Bytes) (err error) { + authentication.mechanism, err = readLDAPString(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + if bytes.HasMoreData() { + var credentials OCTETSTRING + credentials, err = readOCTETSTRING(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + authentication.credentials = credentials.Pointer() + } + return +} + +// +// SaslCredentials ::= SEQUENCE { +// mechanism LDAPString, +// credentials OCTET STRING OPTIONAL } +// +func (s SaslCredentials) writeTagged(bytes *Bytes, class int, tag int) (size int) { + if s.credentials != nil { + size += s.credentials.write(bytes) + } + size += s.mechanism.write(bytes) + size += bytes.WriteTagAndLength(class, isCompound, tag, size) + return +} + +// +// SaslCredentials ::= SEQUENCE { +// mechanism LDAPString, +// credentials OCTET STRING OPTIONAL } +// +func (s SaslCredentials) sizeTagged(tag int) (size int) { + if s.credentials != nil { + size += s.credentials.size() + } + size += s.mechanism.size() + size += sizeTagAndLength(tag, size) + return +} diff --git a/goldap/search_request.go b/goldap/search_request.go new file mode 100644 index 0000000..9d84f23 --- /dev/null +++ b/goldap/search_request.go @@ -0,0 +1,246 @@ +package message + +import ( + "errors" + "fmt" +) + +// +// SearchRequest ::= [APPLICATION 3] SEQUENCE { +// baseObject LDAPDN, +// scope ENUMERATED { +// baseObject (0), +// singleLevel (1), +// wholeSubtree (2), +// ... }, +// derefAliases ENUMERATED { +// neverDerefAliases (0), +// derefInSearching (1), +// derefFindingBaseObj (2), +// derefAlways (3) }, +// sizeLimit INTEGER (0 .. maxInt), +// timeLimit INTEGER (0 .. maxInt), +// typesOnly BOOLEAN, +// filter Filter, +// attributes AttributeSelection } +func readSearchRequest(bytes *Bytes) (searchrequest SearchRequest, err error) { + err = bytes.ReadSubBytes(classApplication, TagSearchRequest, searchrequest.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readSearchRequest:\n%s", err.Error())} + return + } + return +} +func (searchrequest *SearchRequest) readComponents(bytes *Bytes) (err error) { + searchrequest.baseObject, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.scope, err = readENUMERATED(bytes, EnumeratedSearchRequestScope) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.derefAliases, err = readENUMERATED(bytes, EnumeratedSearchRequestDerefAliases) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.sizeLimit, err = readPositiveINTEGER(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.timeLimit, err = readPositiveINTEGER(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.typesOnly, err = readBOOLEAN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.filter, err = readFilter(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchrequest.attributes, err = readAttributeSelection(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +// +// SearchRequest ::= [APPLICATION 3] SEQUENCE { +// baseObject LDAPDN, +// scope ENUMERATED { +// baseObject (0), +// singleLevel (1), +// wholeSubtree (2), +// ... }, +// derefAliases ENUMERATED { +// neverDerefAliases (0), +// derefInSearching (1), +// derefFindingBaseObj (2), +// derefAlways (3) }, +// sizeLimit INTEGER (0 .. maxInt), +// timeLimit INTEGER (0 .. maxInt), +// typesOnly BOOLEAN, +// filter Filter, +// attributes AttributeSelection } +func (s SearchRequest) write(bytes *Bytes) (size int) { + size += s.attributes.write(bytes) + size += s.filter.write(bytes) + size += s.typesOnly.write(bytes) + size += s.timeLimit.write(bytes) + size += s.sizeLimit.write(bytes) + size += s.derefAliases.write(bytes) + size += s.scope.write(bytes) + size += s.baseObject.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchRequest, size) + return +} + +// +// SearchRequest ::= [APPLICATION 3] SEQUENCE { +// baseObject LDAPDN, +// scope ENUMERATED { +// baseObject (0), +// singleLevel (1), +// wholeSubtree (2), +// ... }, +// derefAliases ENUMERATED { +// neverDerefAliases (0), +// derefInSearching (1), +// derefFindingBaseObj (2), +// derefAlways (3) }, +// sizeLimit INTEGER (0 .. maxInt), +// timeLimit INTEGER (0 .. maxInt), +// typesOnly BOOLEAN, +// filter Filter, +// attributes AttributeSelection } +func (s SearchRequest) size() (size int) { + size += s.baseObject.size() + size += s.scope.size() + size += s.derefAliases.size() + size += s.sizeLimit.size() + size += s.timeLimit.size() + size += s.typesOnly.size() + size += s.filter.size() + size += s.attributes.size() + size += sizeTagAndLength(TagSearchRequest, size) + return +} +func (s *SearchRequest) BaseObject() LDAPDN { + return s.baseObject +} +func (s *SearchRequest) Scope() ENUMERATED { + return s.scope +} +func (s *SearchRequest) DerefAliases() ENUMERATED { + return s.derefAliases +} +func (s *SearchRequest) SizeLimit() INTEGER { + return s.sizeLimit +} +func (s *SearchRequest) TimeLimit() INTEGER { + return s.timeLimit +} +func (s *SearchRequest) TypesOnly() BOOLEAN { + return s.typesOnly +} +func (s *SearchRequest) Attributes() AttributeSelection { + return s.attributes +} +func (s *SearchRequest) Filter() Filter { + return s.filter +} +func (s *SearchRequest) FilterString() string { + str, _ := s.decompileFilter(s.Filter()) + return str +} +func (s *SearchRequest) decompileFilter(packet Filter) (ret string, err error) { + defer func() { + if r := recover(); r != nil { + err = errors.New("error decompiling filter") + } + }() + + ret = "(" + err = nil + childStr := "" + + switch f := packet.(type) { + case FilterAnd: + ret += "&" + for _, child := range f { + childStr, err = s.decompileFilter(child) + if err != nil { + return + } + ret += childStr + } + case FilterOr: + ret += "|" + for _, child := range f { + childStr, err = s.decompileFilter(child) + if err != nil { + return + } + ret += childStr + } + case FilterNot: + ret += "!" + childStr, err = s.decompileFilter(f.Filter) + if err != nil { + return + } + ret += childStr + + case FilterSubstrings: + ret += string(f.Type_()) + ret += "=" + for _, fs := range f.Substrings() { + switch fsv := fs.(type) { + case SubstringInitial: + ret += string(fsv) + "*" + case SubstringAny: + ret += "*" + string(fsv) + "*" + case SubstringFinal: + ret += "*" + string(fsv) + } + } + case FilterEqualityMatch: + ret += string(f.AttributeDesc()) + ret += "=" + ret += string(f.AssertionValue()) + case FilterGreaterOrEqual: + ret += string(f.AttributeDesc()) + ret += ">=" + ret += string(f.AssertionValue()) + case FilterLessOrEqual: + ret += string(f.AttributeDesc()) + ret += "<=" + ret += string(f.AssertionValue()) + case FilterPresent: + // if 0 == len(packet.Children) { + // ret += ber.DecodeString(packet.Data.Bytes()) + // } else { + // ret += ber.DecodeString(packet.Children[0].Data.Bytes()) + // } + ret += string(f) + ret += "=*" + case FilterApproxMatch: + ret += string(f.AttributeDesc()) + ret += "~=" + ret += string(f.AssertionValue()) + } + + ret += ")" + return +} diff --git a/goldap/search_result_done.go b/goldap/search_result_done.go new file mode 100644 index 0000000..9666aef --- /dev/null +++ b/goldap/search_result_done.go @@ -0,0 +1,31 @@ +package message + +import "fmt" + +// +// SearchResultDone ::= [APPLICATION 5] LDAPResult +func readSearchResultDone(bytes *Bytes) (ret SearchResultDone, err error) { + var ldapresult LDAPResult + ldapresult, err = readTaggedLDAPResult(bytes, classApplication, TagSearchResultDone) + if err != nil { + err = LdapError{fmt.Sprintf("readSearchResultDone:\n%s", err.Error())} + return + } + ret = SearchResultDone(ldapresult) + return +} + +// +// SearchResultDone ::= [APPLICATION 5] LDAPResult +func (s SearchResultDone) write(bytes *Bytes) int { + return LDAPResult(s).writeTagged(bytes, classApplication, TagSearchResultDone) +} + +// +// SearchResultDone ::= [APPLICATION 5] LDAPResult +func (s SearchResultDone) size() int { + return LDAPResult(s).sizeTagged(TagSearchResultDone) +} +func (l *SearchResultDone) SetResultCode(code int) { + l.resultCode = ENUMERATED(code) +} diff --git a/goldap/search_result_entry.go b/goldap/search_result_entry.go new file mode 100644 index 0000000..05d8e10 --- /dev/null +++ b/goldap/search_result_entry.go @@ -0,0 +1,58 @@ +package message + +import "fmt" + +// +// SearchResultEntry ::= [APPLICATION 4] SEQUENCE { +// objectName LDAPDN, +// attributes PartialAttributeList } +func readSearchResultEntry(bytes *Bytes) (searchresultentry SearchResultEntry, err error) { + err = bytes.ReadSubBytes(classApplication, TagSearchResultEntry, searchresultentry.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readSearchResultEntry:\n%s", err.Error())} + return + } + return +} +func (searchresultentry *SearchResultEntry) readComponents(bytes *Bytes) (err error) { + searchresultentry.objectName, err = readLDAPDN(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + searchresultentry.attributes, err = readPartialAttributeList(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + return +} + +// +// SearchResultEntry ::= [APPLICATION 4] SEQUENCE { +// objectName LDAPDN, +// attributes PartialAttributeList } +func (s SearchResultEntry) write(bytes *Bytes) (size int) { + size += s.attributes.write(bytes) + size += s.objectName.write(bytes) + size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultEntry, size) + return +} + +// +// SearchResultEntry ::= [APPLICATION 4] SEQUENCE { +// objectName LDAPDN, +// attributes PartialAttributeList } +func (s SearchResultEntry) size() (size int) { + size += s.objectName.size() + size += s.attributes.size() + size += sizeTagAndLength(tagSequence, size) + return +} +func (s *SearchResultEntry) SetObjectName(on string) { + s.objectName = LDAPDN(on) +} +func (s *SearchResultEntry) AddAttribute(name AttributeDescription, values ...AttributeValue) { + var ea = PartialAttribute{type_: name, vals: values} + s.attributes.add(ea) +} diff --git a/goldap/search_result_reference.go b/goldap/search_result_reference.go new file mode 100644 index 0000000..20c14d5 --- /dev/null +++ b/goldap/search_result_reference.go @@ -0,0 +1,53 @@ +package message + +import "fmt" + +// +// SearchResultReference ::= [APPLICATION 19] SEQUENCE +// SIZE (1..MAX) OF uri URI +func readSearchResultReference(bytes *Bytes) (ret SearchResultReference, err error) { + err = bytes.ReadSubBytes(classApplication, TagSearchResultReference, ret.readComponents) + if err != nil { + err = LdapError{fmt.Sprintf("readSearchResultReference:\n%s", err.Error())} + return + } + return +} +func (s *SearchResultReference) readComponents(bytes *Bytes) (err error) { + for bytes.HasMoreData() { + var uri URI + uri, err = readURI(bytes) + if err != nil { + err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} + return + } + *s = append(*s, uri) + } + if len(*s) == 0 { + err = LdapError{"readComponents: expecting at least one URI"} + return + } + return +} + +// +// SearchResultReference ::= [APPLICATION 19] SEQUENCE +// SIZE (1..MAX) OF uri URI +func (s SearchResultReference) write(bytes *Bytes) (size int) { + for i := len(s) - 1; i >= 0; i-- { + size += s[i].write(bytes) + } + size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultReference, size) + return +} + +// +// SearchResultReference ::= [APPLICATION 19] SEQUENCE +// SIZE (1..MAX) OF uri URI +func (s SearchResultReference) size() (size int) { + for _, uri := range s { + size += uri.size() + } + size += sizeTagAndLength(tagSequence, size) + return +} diff --git a/goldap/size_test.go b/goldap/size_test.go new file mode 100644 index 0000000..eef6e53 --- /dev/null +++ b/goldap/size_test.go @@ -0,0 +1,85 @@ +package message + +import ( + "testing" +) + +func TestSizeLDAPMessage(t *testing.T) { + + var testData = getLDAPMessageTestData() + for i, test := range testData { + message, err := ReadLDAPMessage(&test.bytes) + if err != nil { + t.Errorf("#%d error at offset %d (%s): %s", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err) + } + size := message.size() + expected := len(test.bytes.bytes) + if size != expected { + t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, expected) + } + } +} + +type tagAndLengthTestData struct { + tag int + length int + expectedSize int +} + +func getSizeTagAndLengthTestData() (ret []tagAndLengthTestData) { + return []tagAndLengthTestData{ + // Length between 0 and 127 are encoded on one byte + { + tag: tagSequence, + length: 0, + expectedSize: 2, + }, + { + tag: tagSequence, + length: 127, + expectedSize: 2, + }, + // Length between 128 and 255 are encoded on two bytes + { + tag: tagSequence, + length: 128, + expectedSize: 3, + }, + { + tag: tagSequence, + length: 255, + expectedSize: 3, + }, + // Length between 256 (2^8) and 65535 (2^16-1) are encoded on three bytes + { + tag: tagSequence, + length: 256, + expectedSize: 4, + }, + { + tag: tagSequence, + length: 65535, + expectedSize: 4, + }, + // Length between 65536 (2^16) and 16777215 (2^24-1) are encoded on four bytes + { + tag: tagSequence, + length: 65536, + expectedSize: 5, + }, + { + tag: tagSequence, + length: 16777215, + expectedSize: 5, + }, + } +} +func TestSizeTagAndLength(t *testing.T) { + for i, test := range getSizeTagAndLengthTestData() { + size := sizeTagAndLength(test.tag, test.length) + if test.expectedSize != size { + t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, test.expectedSize) + } + } + +} diff --git a/goldap/string.go b/goldap/string.go new file mode 100644 index 0000000..d2949ac --- /dev/null +++ b/goldap/string.go @@ -0,0 +1,38 @@ +package message + +import "fmt" + +func readTaggedLDAPString(bytes *Bytes, class int, tag int) (ldapstring LDAPString, err error) { + var octetstring OCTETSTRING + octetstring, err = readTaggedOCTETSTRING(bytes, class, tag) + if err != nil { + err = LdapError{fmt.Sprintf("readTaggedLDAPString:\n%s", err.Error())} + return + } + ldapstring = LDAPString(octetstring) + return +} + +// LDAPString ::= OCTET STRING -- UTF-8 encoded, +// -- [ISO10646] characters +func readLDAPString(bytes *Bytes) (ldapstring LDAPString, err error) { + return readTaggedLDAPString(bytes, classUniversal, tagOctetString) +} + +// LDAPString ::= OCTET STRING -- UTF-8 encoded, +// -- [ISO10646] characters +func (s LDAPString) write(bytes *Bytes) int { + return OCTETSTRING(s).write(bytes) +} +func (s LDAPString) writeTagged(bytes *Bytes, class int, tag int) int { + return OCTETSTRING(s).writeTagged(bytes, class, tag) +} + +// LDAPString ::= OCTET STRING -- UTF-8 encoded, +// -- [ISO10646] characters +func (s LDAPString) size() int { + return OCTETSTRING(s).size() +} +func (s LDAPString) sizeTagged(tag int) int { + return OCTETSTRING(s).sizeTagged(tag) +} diff --git a/goldap/struct.go b/goldap/struct.go new file mode 100644 index 0000000..ae751e2 --- /dev/null +++ b/goldap/struct.go @@ -0,0 +1,739 @@ +package message + +type OCTETSTRING string +type INTEGER int32 // In this RFC the max INTEGER value is 2^31 - 1, so int32 is enough +type BOOLEAN bool +type ENUMERATED int32 + +// This appendix is normative. +// +// Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18} +// -- Copyright (C) The Internet Society (2006). This version of +// -- this ASN.1 module is part of RFC 4511; see the RFC itself +// -- for full legal notices. +// DEFINITIONS +// IMPLICIT TAGS +// EXTENSIBILITY IMPLIED ::= +// +// BEGIN +// +// 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 } +// +type LDAPMessage struct { + messageID MessageID + protocolOp ProtocolOp + controls *Controls +} + +const TagLDAPMessageControls = 0 + +type ProtocolOp interface { + size() int + write(*Bytes) int +} + +// MessageID ::= INTEGER (0 .. maxInt) +// +type MessageID INTEGER + +// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- +const maxInt = INTEGER(2147483647) + +// +// LDAPString ::= OCTET STRING -- UTF-8 encoded, +// -- [ISO10646] characters +type LDAPString OCTETSTRING + +// +// +// +// +//Sermersheim Standards Track [Page 54] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// LDAPOID ::= OCTET STRING -- Constrained to +// -- [RFC4512] +type LDAPOID OCTETSTRING + +// +// LDAPDN ::= LDAPString -- Constrained to +// -- [RFC4514] +type LDAPDN LDAPString + +// +// RelativeLDAPDN ::= LDAPString -- Constrained to +// -- [RFC4514] +type RelativeLDAPDN LDAPString + +// +// AttributeDescription ::= LDAPString +// -- Constrained to +// -- [RFC4512] +type AttributeDescription LDAPString + +// +// AttributeValue ::= OCTET STRING +type AttributeValue OCTETSTRING + +// +// AttributeValueAssertion ::= SEQUENCE { +// attributeDesc AttributeDescription, +// assertionValue AssertionValue } +type AttributeValueAssertion struct { + attributeDesc AttributeDescription + assertionValue AssertionValue +} + +// +// AssertionValue ::= OCTET STRING +type AssertionValue OCTETSTRING + +// +// PartialAttribute ::= SEQUENCE { +// type AttributeDescription, +// vals SET OF value AttributeValue } +type PartialAttribute struct { + type_ AttributeDescription + vals []AttributeValue +} + +// +// Attribute ::= PartialAttribute(WITH COMPONENTS { +// ..., +// vals (SIZE(1..MAX))}) +type Attribute PartialAttribute + +// +// MatchingRuleId ::= LDAPString +type MatchingRuleId LDAPString + +// +// LDAPResult ::= SEQUENCE { +// resultCode ENUMERATED { +// success (0), +// operationsError (1), +// protocolError (2), +// timeLimitExceeded (3), +// sizeLimitExceeded (4), +// compareFalse (5), +// compareTrue (6), +// authMethodNotSupported (7), +// strongerAuthRequired (8), +// -- 9 reserved -- +// referral (10), +// adminLimitExceeded (11), +// unavailableCriticalExtension (12), +// confidentialityRequired (13), +// saslBindInProgress (14), +// +// +// +//Sermersheim Standards Track [Page 55] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// noSuchAttribute (16), +// undefinedAttributeType (17), +// inappropriateMatching (18), +// constraintViolation (19), +// attributeOrValueExists (20), +// invalidAttributeSyntax (21), +// -- 22-31 unused -- +// noSuchObject (32), +// aliasProblem (33), +// invalidDNSyntax (34), +// -- 35 reserved for undefined isLeaf -- +// aliasDereferencingProblem (36), +// -- 37-47 unused -- +// inappropriateAuthentication (48), +// invalidCredentials (49), +// insufficientAccessRights (50), +// busy (51), +// unavailable (52), +// unwillingToPerform (53), +// loopDetect (54), +// -- 55-63 unused -- +// namingViolation (64), +// objectClassViolation (65), +// notAllowedOnNonLeaf (66), +// notAllowedOnRDN (67), +// entryAlreadyExists (68), +// objectClassModsProhibited (69), +// -- 70 reserved for CLDAP -- +// affectsMultipleDSAs (71), +// -- 72-79 unused -- +// other (80), +// ... }, +// matchedDN LDAPDN, +// diagnosticMessage LDAPString, +// referral [3] Referral OPTIONAL } +// +type LDAPResult struct { + resultCode ENUMERATED + matchedDN LDAPDN + diagnosticMessage LDAPString + referral *Referral +} + +const TagLDAPResultReferral = 3 + +const ResultCodeSuccess = 0 +const ResultCodeOperationsError = 1 +const ResultCodeProtocolError = 2 +const ResultCodeTimeLimitExceeded = 3 +const ResultCodeSizeLimitExceeded = 4 +const ResultCodeCompareFalse = 5 +const ResultCodeCompareTrue = 6 +const ResultCodeAuthMethodNotSupported = 7 +const ResultCodeStrongerAuthRequired = 8 +const ResultCodeReferral = 10 +const ResultCodeAdminLimitExceeded = 11 +const ResultCodeUnavailableCriticalExtension = 12 +const ResultCodeConfidentialityRequired = 13 +const ResultCodeSaslBindInProgress = 14 +const ResultCodeNoSuchAttribute = 16 +const ResultCodeUndefinedAttributeType = 17 +const ResultCodeInappropriateMatching = 18 +const ResultCodeConstraintViolation = 19 +const ResultCodeAttributeOrValueExists = 20 +const ResultCodeInvalidAttributeSyntax = 21 +const ResultCodeNoSuchObject = 32 +const ResultCodeAliasProblem = 33 +const ResultCodeInvalidDNSyntax = 34 +const ResultCodeAliasDereferencingProblem = 36 +const ResultCodeInappropriateAuthentication = 48 +const ResultCodeInvalidCredentials = 49 +const ResultCodeInsufficientAccessRights = 50 +const ResultCodeBusy = 51 +const ResultCodeUnavailable = 52 +const ResultCodeUnwillingToPerform = 53 +const ResultCodeLoopDetect = 54 +const ResultCodeNamingViolation = 64 +const ResultCodeObjectClassViolation = 65 +const ResultCodeNotAllowedOnNonLeaf = 66 +const ResultCodeNotAllowedOnRDN = 67 +const ResultCodeEntryAlreadyExists = 68 +const ResultCodeObjectClassModsProhibited = 69 +const ResultCodeAffectsMultipleDSAs = 71 +const ResultCodeOther = 80 + +var EnumeratedLDAPResultCode = map[ENUMERATED]string{ + ResultCodeSuccess: "success", + ResultCodeOperationsError: "operationsError", + ResultCodeProtocolError: "protocolError", + ResultCodeTimeLimitExceeded: "timeLimitExceeded", + ResultCodeSizeLimitExceeded: "sizeLimitExceeded", + ResultCodeCompareFalse: "compareFalse", + ResultCodeCompareTrue: "compareTrue", + ResultCodeAuthMethodNotSupported: "authMethodNotSupported", + ResultCodeStrongerAuthRequired: "strongerAuthRequired", + // -- 9 reserved -- + ResultCodeReferral: "referral", + ResultCodeAdminLimitExceeded: "adminLimitExceeded", + ResultCodeUnavailableCriticalExtension: "unavailableCriticalExtension", + ResultCodeConfidentialityRequired: "confidentialityRequired", + ResultCodeSaslBindInProgress: "saslBindInProgress", + ResultCodeNoSuchAttribute: "noSuchAttribute", + ResultCodeUndefinedAttributeType: "undefinedAttributeType", + ResultCodeInappropriateMatching: "inappropriateMatching", + ResultCodeConstraintViolation: "constraintViolation", + ResultCodeAttributeOrValueExists: "attributeOrValueExists", + ResultCodeInvalidAttributeSyntax: "invalidAttributeSyntax", + // -- 22-31 unused -- + ResultCodeNoSuchObject: "noSuchObject", + ResultCodeAliasProblem: "aliasProblem", + ResultCodeInvalidDNSyntax: "invalidDNSyntax", + // -- 35 reserved for undefined isLeaf -- + ResultCodeAliasDereferencingProblem: "aliasDereferencingProblem", + // -- 37-47 unused -- + ResultCodeInappropriateAuthentication: "inappropriateAuthentication", + ResultCodeInvalidCredentials: "invalidCredentials", + ResultCodeInsufficientAccessRights: "insufficientAccessRights", + ResultCodeBusy: "busy", + ResultCodeUnavailable: "unavailable", + ResultCodeUnwillingToPerform: "unwillingToPerform", + ResultCodeLoopDetect: "loopDetect", + // -- 55-63 unused -- + ResultCodeNamingViolation: "namingViolation", + ResultCodeObjectClassViolation: "objectClassViolation", + ResultCodeNotAllowedOnNonLeaf: "notAllowedOnNonLeaf", + ResultCodeNotAllowedOnRDN: "notAllowedOnRDN", + ResultCodeEntryAlreadyExists: "entryAlreadyExists", + ResultCodeObjectClassModsProhibited: "objectClassModsProhibited", + // -- 70 reserved for CLDAP -- + ResultCodeAffectsMultipleDSAs: "affectsMultipleDSAs", + // -- 72-79 unused -- + ResultCodeOther: "other", +} + +// Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI +type Referral []URI + +// +// URI ::= LDAPString -- limited to characters permitted in +// -- URIs +type URI LDAPString + +// +// Controls ::= SEQUENCE OF control Control +type Controls []Control + +// +// Control ::= SEQUENCE { +// controlType LDAPOID, +// criticality BOOLEAN DEFAULT FALSE, +// controlValue OCTET STRING OPTIONAL } +type Control struct { + controlType LDAPOID + criticality BOOLEAN + controlValue *OCTETSTRING +} + +// +// +// +// +//Sermersheim Standards Track [Page 56] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// BindRequest ::= [APPLICATION 0] SEQUENCE { +// version INTEGER (1 .. 127), +// name LDAPDN, +// authentication AuthenticationChoice } +const TagBindRequest = 0 +const BindRequestVersionMin = 1 +const BindRequestVersionMax = 127 + +type BindRequest struct { + version INTEGER + name LDAPDN + authentication AuthenticationChoice +} + +// +// AuthenticationChoice ::= CHOICE { +// simple [0] OCTET STRING, +// -- 1 and 2 reserved +// sasl [3] SaslCredentials, +// ... } +const TagAuthenticationChoiceSimple = 0 +const TagAuthenticationChoiceSaslCredentials = 3 + +type AuthenticationChoice interface { + sizeTagged(int) int +} + +// +// SaslCredentials ::= SEQUENCE { +// mechanism LDAPString, +// credentials OCTET STRING OPTIONAL } +type SaslCredentials struct { + mechanism LDAPString + credentials *OCTETSTRING +} + +// +// BindResponse ::= [APPLICATION 1] SEQUENCE { +// COMPONENTS OF LDAPResult, +// serverSaslCreds [7] OCTET STRING OPTIONAL } +const TagBindResponse = 1 +const TagBindResponseServerSaslCreds = 7 + +type BindResponse struct { + LDAPResult + serverSaslCreds *OCTETSTRING +} + +// +// UnbindRequest ::= [APPLICATION 2] NULL +const TagUnbindRequest = 2 + +type UnbindRequest struct { +} + +// +// SearchRequest ::= [APPLICATION 3] SEQUENCE { +// baseObject LDAPDN, +// scope ENUMERATED { +// baseObject (0), +// singleLevel (1), +// wholeSubtree (2), +// ... }, +// derefAliases ENUMERATED { +// neverDerefAliases (0), +// derefInSearching (1), +// derefFindingBaseObj (2), +// derefAlways (3) }, +// sizeLimit INTEGER (0 .. maxInt), +// timeLimit INTEGER (0 .. maxInt), +// typesOnly BOOLEAN, +// filter Filter, +// attributes AttributeSelection } +const TagSearchRequest = 3 + +type SearchRequest struct { + baseObject LDAPDN + scope ENUMERATED + derefAliases ENUMERATED + sizeLimit INTEGER + timeLimit INTEGER + typesOnly BOOLEAN + filter Filter + attributes AttributeSelection +} + +const SearchRequestScopeBaseObject = 0 +const SearchRequestSingleLevel = 1 +const SearchRequestHomeSubtree = 2 + +var EnumeratedSearchRequestScope = map[ENUMERATED]string{ + SearchRequestScopeBaseObject: "baseObject", + SearchRequestSingleLevel: "singleLevel", + SearchRequestHomeSubtree: "homeSubtree", +} + +const SearchRequetDerefAliasesNeverDerefAliases = 0 +const SearchRequetDerefAliasesDerefInSearching = 1 +const SearchRequetDerefAliasesDerefFindingBaseObj = 2 +const SearchRequetDerefAliasesDerefAlways = 3 + +var EnumeratedSearchRequestDerefAliases = map[ENUMERATED]string{ + SearchRequetDerefAliasesNeverDerefAliases: "neverDerefAliases", + SearchRequetDerefAliasesDerefInSearching: "derefInSearching", + SearchRequetDerefAliasesDerefFindingBaseObj: "derefFindingBaseObj", + SearchRequetDerefAliasesDerefAlways: "derefAlways", +} + +// +// AttributeSelection ::= SEQUENCE OF selector LDAPString +// -- The LDAPString is constrained to +// -- in Section 4.5.1.8 +type AttributeSelection []LDAPString + +// +// Filter ::= CHOICE { +// and [0] SET SIZE (1..MAX) OF filter Filter, +// or [1] SET SIZE (1..MAX) OF filter Filter, +// not [2] Filter, +// equalityMatch [3] AttributeValueAssertion, +// +// +// +//Sermersheim Standards Track [Page 57] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// substrings [4] SubstringFilter, +// greaterOrEqual [5] AttributeValueAssertion, +// lessOrEqual [6] AttributeValueAssertion, +// present [7] AttributeDescription, +// approxMatch [8] AttributeValueAssertion, +// extensibleMatch [9] MatchingRuleAssertion, +// ... } +const TagFilterAnd = 0 +const TagFilterOr = 1 +const TagFilterNot = 2 +const TagFilterEqualityMatch = 3 +const TagFilterSubstrings = 4 +const TagFilterGreaterOrEqual = 5 +const TagFilterLessOrEqual = 6 +const TagFilterPresent = 7 +const TagFilterApproxMatch = 8 +const TagFilterExtensibleMatch = 9 + +type Filter interface { + size() int + write(*Bytes) int + getFilterTag() int +} +type FilterAnd []Filter +type FilterOr []Filter +type FilterNot struct { + Filter +} +type FilterEqualityMatch AttributeValueAssertion +type FilterSubstrings SubstringFilter +type FilterGreaterOrEqual AttributeValueAssertion +type FilterLessOrEqual AttributeValueAssertion +type FilterPresent AttributeDescription +type FilterApproxMatch AttributeValueAssertion +type FilterExtensibleMatch MatchingRuleAssertion + +// +// SubstringFilter ::= SEQUENCE { +// type AttributeDescription, +// substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { +// initial [0] AssertionValue, -- can occur at most once +// any [1] AssertionValue, +// final [2] AssertionValue } -- can occur at most once +// } +type SubstringFilter struct { + type_ AttributeDescription + substrings []Substring +} + +type Substring interface{} + +const TagSubstringInitial = 0 +const TagSubstringAny = 1 +const TagSubstringFinal = 2 + +type SubstringInitial AssertionValue +type SubstringAny AssertionValue +type SubstringFinal AssertionValue + +// +// MatchingRuleAssertion ::= SEQUENCE { +// matchingRule [1] MatchingRuleId OPTIONAL, +// type [2] AttributeDescription OPTIONAL, +// matchValue [3] AssertionValue, +// dnAttributes [4] BOOLEAN DEFAULT FALSE } +type MatchingRuleAssertion struct { + matchingRule *MatchingRuleId + type_ *AttributeDescription + matchValue AssertionValue + dnAttributes BOOLEAN +} + +const TagMatchingRuleAssertionMatchingRule = 1 +const TagMatchingRuleAssertionType = 2 +const TagMatchingRuleAssertionMatchValue = 3 +const TagMatchingRuleAssertionDnAttributes = 4 + +// +// SearchResultEntry ::= [APPLICATION 4] SEQUENCE { +// objectName LDAPDN, +// attributes PartialAttributeList } +const TagSearchResultEntry = 4 + +type SearchResultEntry struct { + objectName LDAPDN + attributes PartialAttributeList +} + +// +// PartialAttributeList ::= SEQUENCE OF +// partialAttribute PartialAttribute +type PartialAttributeList []PartialAttribute + +// +// SearchResultReference ::= [APPLICATION 19] SEQUENCE +// SIZE (1..MAX) OF uri URI +const TagSearchResultReference = 19 + +type SearchResultReference []URI + +// +// SearchResultDone ::= [APPLICATION 5] LDAPResult +const TagSearchResultDone = 5 + +type SearchResultDone LDAPResult + +// +// ModifyRequest ::= [APPLICATION 6] SEQUENCE { +// object LDAPDN, +// changes SEQUENCE OF change SEQUENCE { +// operation ENUMERATED { +// add (0), +// delete (1), +// replace (2), +// ... }, +// modification PartialAttribute } } +const TagModifyRequest = 6 + +type ModifyRequest struct { + object LDAPDN + changes []ModifyRequestChange +} +type ModifyRequestChange struct { + operation ENUMERATED + modification PartialAttribute +} + +const ModifyRequestChangeOperationAdd = 0 +const ModifyRequestChangeOperationDelete = 1 +const ModifyRequestChangeOperationReplace = 2 + +var EnumeratedModifyRequestChangeOperation = map[ENUMERATED]string{ + ModifyRequestChangeOperationAdd: "add", + ModifyRequestChangeOperationDelete: "delete", + ModifyRequestChangeOperationReplace: "replace", +} + +// +// ModifyResponse ::= [APPLICATION 7] LDAPResult +const TagModifyResponse = 7 + +type ModifyResponse LDAPResult + +// +// +// +// +// +// +//Sermersheim Standards Track [Page 58] +// +// +//RFC 4511 LDAPv3 June 2006 +// +// +// AddRequest ::= [APPLICATION 8] SEQUENCE { +// entry LDAPDN, +// attributes AttributeList } +const TagAddRequest = 8 + +type AddRequest struct { + entry LDAPDN + attributes AttributeList +} + +// +// AttributeList ::= SEQUENCE OF attribute Attribute +type AttributeList []Attribute + +// +// AddResponse ::= [APPLICATION 9] LDAPResult +const TagAddResponse = 9 + +type AddResponse LDAPResult + +// +// DelRequest ::= [APPLICATION 10] LDAPDN +const TagDelRequest = 10 + +type DelRequest LDAPDN + +// +// DelResponse ::= [APPLICATION 11] LDAPResult +const TagDelResponse = 11 + +type DelResponse LDAPResult + +// +// ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { +// entry LDAPDN, +// newrdn RelativeLDAPDN, +// deleteoldrdn BOOLEAN, +// newSuperior [0] LDAPDN OPTIONAL } +const TagModifyDNRequest = 12 + +type ModifyDNRequest struct { + entry LDAPDN + newrdn RelativeLDAPDN + deleteoldrdn BOOLEAN + newSuperior *LDAPDN +} + +const TagModifyDNRequestNewSuperior = 0 + +// +// ModifyDNResponse ::= [APPLICATION 13] LDAPResult +const TagModifyDNResponse = 13 + +type ModifyDNResponse LDAPResult + +// +// CompareRequest ::= [APPLICATION 14] SEQUENCE { +// entry LDAPDN, +// ava AttributeValueAssertion } +const TagCompareRequest = 14 + +type CompareRequest struct { + entry LDAPDN + ava AttributeValueAssertion +} + +// CompareResponse ::= [APPLICATION 15] LDAPResult +const TagCompareResponse = 15 + +type CompareResponse LDAPResult + +// +// AbandonRequest ::= [APPLICATION 16] MessageID +const TagAbandonRequest = 16 + +type AbandonRequest MessageID + +// +// ExtendedRequest ::= [APPLICATION 23] SEQUENCE { +// requestName [0] LDAPOID, +// requestValue [1] OCTET STRING OPTIONAL } +const TagExtendedRequest = 23 + +type ExtendedRequest struct { + requestName LDAPOID + requestValue *OCTETSTRING +} + +const TagExtendedRequestName = 0 +const TagExtendedRequestValue = 1 + +// +// ExtendedResponse ::= [APPLICATION 24] SEQUENCE { +// COMPONENTS OF LDAPResult, +// responseName [10] LDAPOID OPTIONAL, +// responseValue [11] OCTET STRING OPTIONAL } +const TagExtendedResponse = 24 + +type ExtendedResponse struct { + LDAPResult + responseName *LDAPOID + responseValue *OCTETSTRING +} + +const TagExtendedResponseName = 10 +const TagExtendedResponseValue = 11 + +// +// IntermediateResponse ::= [APPLICATION 25] SEQUENCE { +// responseName [0] LDAPOID OPTIONAL, +// responseValue [1] OCTET STRING OPTIONAL } +const TagIntermediateResponse = 25 + +type IntermediateResponse struct { + responseName *LDAPOID + responseValue *OCTETSTRING +} + +const TagIntermediateResponseName = 0 +const TagIntermediateResponseValue = 1 + +// +// END +// diff --git a/goldap/struct_methods.go b/goldap/struct_methods.go new file mode 100644 index 0000000..a43cb63 --- /dev/null +++ b/goldap/struct_methods.go @@ -0,0 +1,7 @@ +package message + +func NewLDAPMessageWithProtocolOp(po ProtocolOp) *LDAPMessage { + m := NewLDAPMessage() + m.protocolOp = po + return m +} diff --git a/goldap/unbind_request.go b/goldap/unbind_request.go new file mode 100644 index 0000000..57f49e5 --- /dev/null +++ b/goldap/unbind_request.go @@ -0,0 +1,38 @@ +package message + +import "fmt" + +// +// UnbindRequest ::= [APPLICATION 2] NULL +func readUnbindRequest(bytes *Bytes) (unbindrequest UnbindRequest, err error) { + var tagAndLength TagAndLength + tagAndLength, err = bytes.ParseTagAndLength() + if err != nil { + err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())} + return + } + err = tagAndLength.Expect(classApplication, TagUnbindRequest, isNotCompound) + if err != nil { + err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())} + return + } + if tagAndLength.Length != 0 { + err = LdapError{"readUnbindRequest: expecting NULL"} + return + } + return +} + +// +// UnbindRequest ::= [APPLICATION 2] NULL +func (u UnbindRequest) write(bytes *Bytes) (size int) { + size += bytes.WriteTagAndLength(classApplication, isNotCompound, TagUnbindRequest, 0) + return +} + +// +// UnbindRequest ::= [APPLICATION 2] NULL +func (u UnbindRequest) size() (size int) { + size = sizeTagAndLength(TagUnbindRequest, 0) + return +} diff --git a/goldap/uri.go b/goldap/uri.go new file mode 100644 index 0000000..b57874a --- /dev/null +++ b/goldap/uri.go @@ -0,0 +1,32 @@ +package message + +import "fmt" + +// +// URI ::= LDAPString -- limited to characters permitted in +// -- URIs +func readURI(bytes *Bytes) (uri URI, err error) { + var ldapstring LDAPString + ldapstring, err = readLDAPString(bytes) + // @TODO: check permitted chars in URI + if err != nil { + err = LdapError{fmt.Sprintf("readURI:\n%s", err.Error())} + return + } + uri = URI(ldapstring) + return +} + +// +// URI ::= LDAPString -- limited to characters permitted in +// -- URIs +func (u URI) write(bytes *Bytes) int { + return LDAPString(u).write(bytes) +} + +// +// URI ::= LDAPString -- limited to characters permitted in +// -- URIs +func (u URI) size() int { + return LDAPString(u).size() +} diff --git a/goldap/write.go b/goldap/write.go new file mode 100644 index 0000000..970cb02 --- /dev/null +++ b/goldap/write.go @@ -0,0 +1,6 @@ +package message + +type Writable interface { + write(bytes *Bytes) int + writeTagged(bytes *Bytes, class int, tag int) int +} diff --git a/goldap/write_test.go b/goldap/write_test.go new file mode 100644 index 0000000..7c82bd7 --- /dev/null +++ b/goldap/write_test.go @@ -0,0 +1,19 @@ +package message + +import ( + "reflect" + "testing" +) + +func TestWriteLDAPMessage(t *testing.T) { + + var testData = getLDAPMessageTestData() + for i, test := range testData { + bytes, err := test.out.Write() + if err != nil { + t.Errorf("#%d error at offset %d (%s): %s\nEXPECTED BYTES: %#v\nWRITTEN BYTES: %#v\n", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err, test.bytes.getBytes(), bytes.getBytes()) + } else if !reflect.DeepEqual(bytes.getBytes(), test.bytes.getBytes()) { + t.Errorf("#%d:\nGOT:\n%#+v\nEXPECTED:\n%#+v", i, bytes.getBytes(), test.bytes.getBytes()) + } + } +} diff --git a/ldapserver/client.go b/ldapserver/client.go index 91a5980..3674ba4 100644 --- a/ldapserver/client.go +++ b/ldapserver/client.go @@ -6,7 +6,7 @@ import ( "sync" "time" - ldap "github.com/lor00x/goldap/message" + ldap "bottin/goldap" ) type UserState interface{} @@ -206,10 +206,14 @@ func (c *client) close() { } func (c *client) writeMessage(m *ldap.LDAPMessage) { - data, _ := m.Write() + data, err := m.Write() + if err != nil { + Logger.Errorf("bottin: unable to marshal response message: %v", err) + } //Logger.Printf(">>> %d - %s - hex=%x", c.Numero, m.ProtocolOpName(), data.Bytes()) Logger.Tracef(">>> [%d] %#v", c.Numero, m) + Logger.Tracef("%v", data.Bytes()) c.bw.Write(data.Bytes()) c.bw.Flush() diff --git a/ldapserver/constants.go b/ldapserver/constants.go index de6d229..a04ca43 100644 --- a/ldapserver/constants.go +++ b/ldapserver/constants.go @@ -1,6 +1,6 @@ package ldapserver -import ldap "github.com/lor00x/goldap/message" +import ldap "bottin/goldap" // LDAP Application Codes const ( diff --git a/ldapserver/message.go b/ldapserver/message.go index f6e609f..7033e06 100644 --- a/ldapserver/message.go +++ b/ldapserver/message.go @@ -3,7 +3,7 @@ package ldapserver import ( "fmt" - ldap "github.com/lor00x/goldap/message" + ldap "bottin/goldap" ) type Message struct { diff --git a/ldapserver/packet.go b/ldapserver/packet.go index 77b4926..a60a44a 100644 --- a/ldapserver/packet.go +++ b/ldapserver/packet.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - ldap "github.com/lor00x/goldap/message" + ldap "bottin/goldap" ) type messagePacket struct { @@ -141,6 +141,7 @@ func readBytes(conn *bufio.Reader, bytes *[]byte, length int) (b byte, err error if n != length { fmt.Errorf("%d bytes read instead of %d", n, length) } else if err != nil { + fmt.Println("ooopsie an error occured here:", err) return } *bytes = append(*bytes, newbytes...) diff --git a/ldapserver/responsemessage.go b/ldapserver/responsemessage.go index 23527b9..9dc0406 100644 --- a/ldapserver/responsemessage.go +++ b/ldapserver/responsemessage.go @@ -1,6 +1,6 @@ package ldapserver -import ldap "github.com/lor00x/goldap/message" +import ldap "bottin/goldap" func NewBindResponse(resultCode int) ldap.BindResponse { r := ldap.BindResponse{} diff --git a/ldapserver/route.go b/ldapserver/route.go index dfd5240..3ba5618 100644 --- a/ldapserver/route.go +++ b/ldapserver/route.go @@ -3,7 +3,7 @@ package ldapserver import ( "strings" - ldap "github.com/lor00x/goldap/message" + ldap "bottin/goldap" ) // Constant to LDAP Request protocol Type names diff --git a/main.go b/main.go index 13d3da6..4e5abce 100644 --- a/main.go +++ b/main.go @@ -13,9 +13,9 @@ import ( "syscall" ldap "bottin/ldapserver" + message "bottin/goldap" consul "github.com/hashicorp/consul/api" - message "github.com/lor00x/goldap/message" log "github.com/sirupsen/logrus" ) @@ -320,12 +320,19 @@ func (server *Server) init() error { return err } - admin_pass := make([]byte, 8) - _, err = rand.Read(admin_pass) - if err != nil { - return err + + admin_pass_str, environnement_variable_exist := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW") + if !environnement_variable_exist { + admin_pass := make([]byte, 8) + _, err = rand.Read(admin_pass) + if err != nil { + return err + } + admin_pass_str = base64.RawURLEncoding.EncodeToString(admin_pass) + } else { + server.logger.Debug("BOTTIN_DEFAULT_ADMIN_PW environment variable is set, using it for admin's password") } - admin_pass_str := base64.RawURLEncoding.EncodeToString(admin_pass) + admin_pass_hash := SSHAEncode([]byte(admin_pass_str)) admin_dn := "cn=admin," + server.config.Suffix diff --git a/read.go b/read.go index 7887c93..06e54b2 100644 --- a/read.go +++ b/read.go @@ -6,7 +6,7 @@ import ( ldap "bottin/ldapserver" - message "github.com/lor00x/goldap/message" + message "bottin/goldap" ) // Generic read utility functions ---------- diff --git a/test/bottin_test.go b/test/bottin_test.go new file mode 100644 index 0000000..f4ed7fe --- /dev/null +++ b/test/bottin_test.go @@ -0,0 +1,160 @@ +package main + +import ( + "testing" +) + +func TestAddThenDelete(t *testing.T) { + t.Parallel() + //SetUp - Create Users and Groups + inst, err := Init() + if err != nil { + t.Error(err) + } + + //TearDown - Delete all the users and groups created + err = inst.Clean() + if err != nil { + t.Error(err) + } +} + +func TestConfirmAddAttributes(t *testing.T) { + t.Parallel() + //SetUp - Create Users and Groups + inst, err := Init() + if err != nil { + t.Error(err) + } + + //Test search_attribute to confirm the Add + if ok, err := inst.CompareOurDataWithConsul(); !ok { + t.Error(err) + } + //TearDown - Delete all the users and groups created + err = inst.Clean() + if err != nil { + t.Error(err) + } +} + +//Modifyrequest Test +func TestModifyRequest(t *testing.T) { + t.Parallel() + //SetUp - Create Users and Groups + inst, err := Init() + if err != nil { + t.Error(err) + } + + //Test modify all data (groups and users) + err = inst.ModifyRandomAllData() + if err != nil { + t.Error(err) + } + + //TearDown - Delete all the users and groups created + err = inst.Clean() + if err != nil { + t.Error(err) + } +} + +func TestModifyRequestAndCheck(t *testing.T) { + t.Parallel() + //SetUp - Create Users and Groups + inst, err := Init() + if err != nil { + t.Error(err) + } + + //Test modify all data (groups and users) + err = inst.ModifyRandomAllData() + if err != nil { + t.Error(err) + } + + //Check if the data was modify on Consul + if ok, err := inst.CompareOurDataWithConsul(); !ok { + t.Error(err) + } + + //TearDown - Delete all the users and groups created + err = inst.Clean() + if err != nil { + t.Error(err) + } +} + +func TestAddUserInGroup(t *testing.T) { + t.Parallel() + //SetUp - Create Users and Groups + inst, err := Init() + if err != nil { + t.Error(err) + } + + //Add users in group + err = inst.AddAllUsersInGroup() + if err != nil { + t.Error(err) + } + + //TearDown - Delete all the users and groups created + err = inst.Clean() + if err != nil { + t.Error(err) + } +} + +func TestDeleteGroupsAfterAddedUsers(t *testing.T) { + t.Parallel() + //SetUp - Create Users and Groups + inst, err := Init() + if err != nil { + t.Error(err) + } + + //Add users in group + err = inst.AddAllUsersInGroup() + if err != nil { + t.Error(err) + } + + //Delete the half groups + number := len(inst.dataGroups) / 2 + err = inst.clean(inst.dataGroups[0:number]) + if err != nil { + t.Error(err) + } + inst.dataGroups = inst.dataGroups[number:len(inst.dataGroups)] + + //Check all the groups in memberOf exist + ok, err := inst.CheckMemberOf() + if err != nil { + t.Error(err) + } + if !ok { + t.Errorf("Found group in memberOf that isn't in Consul.") + } + + //TearDown - Delete all the users and groups created + err = inst.Clean() + if err != nil { + t.Error(err) + } +} + +//Example of paralellism Test +func TestPrincipal(t *testing.T) { + + t.Run("A=Add and delete", TestAddThenDelete) + t.Run("A=Modify", TestModifyRequest) + if !testing.Short() { + t.Run("B=Add attributes", TestConfirmAddAttributes) + t.Run("B=Modify and check", TestModifyRequestAndCheck) + t.Run("C=Add user in group", TestAddUserInGroup) + t.Run("C=Delete group", TestDeleteGroupsAfterAddedUsers) + } + +} diff --git a/test/config.json.test b/test/config.json.test new file mode 100644 index 0000000..bc1eeec --- /dev/null +++ b/test/config.json.test @@ -0,0 +1,13 @@ +{ + "suffix": "dc=deuxfleurs,dc=fr", + "bind": "127.0.0.1:1389", + "acl": [ + "ANONYMOUS::bind:*,ou=users,dc=deuxfleurs,dc=fr:", + "ANONYMOUS::bind:cn=admin,dc=deuxfleurs,dc=fr:", + "*,dc=deuxfleurs,dc=fr::read:*:* !userpassword", + "*::read modify:SELF:*", + "cn=admin,dc=deuxfleurs,dc=fr::read add modify delete:*:*", + "*:cn=admin,ou=groups,dc=deuxfleurs,dc=fr:read add modify delete:*:*" + ] +} + diff --git a/test/create.go b/test/create.go new file mode 100644 index 0000000..4ad8106 --- /dev/null +++ b/test/create.go @@ -0,0 +1,281 @@ +package main + +import ( + "fmt" + "strings" + "sync" + + "github.com/go-ldap/ldap/v3" + "github.com/sirupsen/logrus" +) + +//Mux value, this value permits do not have two identicals values in the parallel instances +type StoreAllCN struct { + mu sync.Mutex + cn map[string]struct{} +} + +var allNames = StoreAllCN{cn: make(map[string]struct{})} + +//Type used for the tests +type attributes struct { + Name string + Data []string +} + +type data_DN struct { + DN string + Attributes []attributes +} + +type instance struct { + numberUsers, numberGroups int + dataGroups, dataUsers []data_DN + logging *ldap.Conn +} + +//Create a new object instance +//With this instance, we can obtain an isolated container where +//we have our users and groups. It allows to run tests in parallel. +func NewInstance(numberUsers, numberGroups int) (*instance, error) { + l, err := Connect() + if err != nil { + return nil, err + } + + logging.Level = logrus.InfoLevel + + inst := instance{ + numberUsers: numberUsers, + numberGroups: numberGroups, + dataGroups: []data_DN{}, + dataUsers: []data_DN{}, + logging: l, + } + + err = inst.createOrganizationnalUnit() + if ldap.IsErrorWithCode(err, uint16(68)) { + logging.Warn("OrganizationnalUnit already created") + err = nil + } + if err != nil { + return nil, err + } + + err = inst.CreateGroups() + if err != nil { + return nil, err + } + err = inst.CreateUsers() + if err != nil { + return nil, err + } + + return &inst, nil +} + +//Part: Created users or groups or OU + +func (inst *instance) createOrganizationnalUnit() error { + dn := []string{"ou=groups,dc=deuxfleurs,dc=fr", "ou=users,dc=deuxfleurs,dc=fr"} + attributes := []map[string][]string{{ + "description": []string{"OrganizationalUnit qui regroupe tous les groupes"}, + "objectclass": []string{"organizationalUnit", "top"}, + "ou": []string{"groups"}, + "structuralobjectclass": []string{"organizationalUnit"}, + }, + { + "description": []string{"OrganizationalUnit qui regroupe tous les users"}, + "objectclass": []string{"organizationalUnit", "top"}, + "ou": []string{"users"}, + "structuralobjectclass": []string{"organizationalUnit"}, + }, + } + + for index := range dn { + err := inst.Add_Request(dn[index], attributes[index]) + if err != nil { + return err + } + } + return nil + +} + +//Part: Create User or group + +func (inst *instance) CreateUsers() (err error) { + + dn := "cn=%s,ou=users,dc=deuxfleurs,dc=fr" + attributes := map[string][]string{ + "displayname": {}, + "objectclass": {"inetOrgPerson", "organizationalPerson", "person", "top"}, + "structuralobjectclass": {"inetOrgPerson"}, + } + + du, err := inst.create(dn, []string{"displayname"}, inst.numberUsers, attributes, inst.dataUsers) + if err == nil { + inst.dataUsers = du + } + return err +} + +func (inst *instance) CreateGroups() error { + dn := "cn=%s,ou=groups,dc=deuxfleurs,dc=fr" + attributes := map[string][]string{ + "description": {}, + "objectclass": {"groupOfNames", "top"}, + "structuralobjectclass": {"groupOfNames"}, + } + + dg, err := inst.create(dn, []string{"description"}, inst.numberGroups, attributes, inst.dataGroups) + if err == nil { + inst.dataGroups = dg + } + + return err +} + +//Hard Function: She does: +//- generate an unique name +//- store the Data of each AddRequest in instance struct +//- send AddRequest to Bottin +func (inst *instance) create(dn string, unique_attr []string, number int, attributes map[string][]string, data []data_DN) ([]data_DN, error) { + for i := 0; i < number; i++ { + name := inst.GenerateName() + + datDn := data_DN{DN: fmt.Sprintf(dn, name)} + + for _, value := range unique_attr { + attributes[value] = []string{name} + } + + datDn.Attributes = MapAttToStruct(attributes) + data = append(data, datDn) + + err := inst.Add_Request(fmt.Sprintf(dn, name), attributes) + if err != nil { + return nil, err + } + } + return data, nil +} + +//Part: clean + +func (inst *instance) Clean() error { + err := inst.CleanGroups() + if err != nil { + return err + } + err = inst.CleanUsers() + return err +} + +func (inst *instance) CleanUsers() error { + err := inst.clean(inst.dataUsers) + if err != nil { + return err + } + inst.dataUsers = []data_DN{} + return err +} + +func (inst *instance) CleanGroups() error { + err := inst.clean(inst.dataGroups) + if err != nil { + return err + } + inst.dataGroups = []data_DN{} + return err +} + +func (inst *instance) clean(stock []data_DN) error { + logging.Debugf("Delete %d elements.", len(stock)) + for _, value := range stock { + err := inst.Delete_Request(value.DN) + if err != nil { + return err + } + } + return nil +} + +//Part: Verify if a data_Dn is a group or an user +func (inst *instance) VerifyUser(user data_DN) (bool, error) { + dn := "ou=users,dc=deuxfleurs,dc=fr" + cn := strings.Split(user.DN, ",")[0] + filter := fmt.Sprintf("(%s)", cn) + + res, err := inst.Search_Request(dn, filter, []string{"cn"}) + + return len(res.Entries) == 1, err +} + +func (inst *instance) VerifyGroup(group data_DN) (bool, error) { + dn := "ou=groups,dc=deuxfleurs,dc=fr" + cn := strings.Split(group.DN, ",")[0] + filter := fmt.Sprintf("(%s)", cn) + + res, err := inst.Search_Request(dn, filter, []string{"cn"}) + + return len(res.Entries) == 1, err +} + +//Part: Add user in a group +func (inst *instance) AddUserInGroup(user, group data_DN) error { + + err := inst.Modify_Request(group.DN, nil, nil, map[string][]string{ + "member": {user.DN}, + }) + return err +} + +func (inst *instance) AddUserSliceInGroup(users_cn []string, group_dn string) error { + + err := inst.Modify_Request(group_dn, nil, nil, map[string][]string{ + "member": users_cn, + }) + return err +} + +//Part: modify, add, delete data_DN struct + +func AddAtt(name string, data []string, dat data_DN) data_DN { + dat.Attributes = append(dat.Attributes, attributes{ + Name: name, + Data: data, + }) + + logging.Debug(fmt.Sprintf("Attributes %s add from %s.", name, dat.DN)) + return dat +} + +func DelAtt(name string, dat data_DN) data_DN { + for index, value := range dat.Attributes { + if value.Name == name { + dat.Attributes[index] = dat.Attributes[len(dat.Attributes)-1] + //tmp := dat.Attributes[:len(dat.Attributes)-1] + dat.Attributes = []attributes{} + logging.Debugf("Attributes %s delete from %s.", name, dat.DN) + return dat + } + } + logging.Debugf("Can't delete attribute %s from %s.", name, dat.DN) + return dat +} + +func ReplaceAtt(name string, data []string, dat data_DN) data_DN { + for index, value := range dat.Attributes { + if value.Name == name { + dat.Attributes[index] = attributes{ + Name: name, + Data: data, + } + logging.Debugf("Replace attributes %s from %s succesful..", name, dat.DN) + return dat + } + } + logging.Debugf("Can't replace attributes %s from %s.", name, dat.DN) + return dat +} diff --git a/test/functionTest.go b/test/functionTest.go new file mode 100644 index 0000000..e8d95ab --- /dev/null +++ b/test/functionTest.go @@ -0,0 +1,173 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/go-ldap/ldap/v3" +) + +const default_users, default_groups = 1000, 1000 + +func Init() (*instance, error) { + inst, err := NewInstance(default_users, default_groups) + return inst, err +} + +//Part to compare our datas +func (inst *instance) CompareOurDataWithConsul() (bool, error) { + if ok, err := inst.VerifyOurData(inst.dataUsers); !ok { + return false, err + } + if ok, err := inst.VerifyOurData(inst.dataGroups); !ok { + return false, err + } + return true, nil +} + +func (inst *instance) VerifyOurData(tabData []data_DN) (bool, error) { + for _, value := range tabData { + names := getNamesAtt(value) + cn := strings.Split(value.DN, ",")[0] + res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), names) + if err != nil { + return false, err + } + if len(res.Entries) != 1 { + return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries)) + } + if !Compare(value, res.Entries[0]) { + return false, fmt.Errorf("no match with the DN: %s", value.DN) + } + } + return true, nil +} + +func Compare(dat data_DN, ent *ldap.Entry) bool { + for _, value := range dat.Attributes { + logging.Debugf("Attributes from %s is now: %s.", dat.DN, dat.Attributes) + entVal := GetAttributeValuesBottin(ent, value.Name) + logging.Debugf("Values of the Entry: attributName: %s, Values: %s.", value.Name, entVal) + if !CompareSliceString(entVal, value.Data) { + logging.Debugf("Values expected: %s, values found: %s.", value.Data, entVal) + return false + } + } + return true +} + +//Part modify datas +func (inst *instance) ModifyRandomAllData() error { + dg, err := inst.ModifyRandom(inst.dataGroups, []string{"description"}) + if err != nil { + return err + } else { + inst.dataGroups = dg + } + + dg, err = inst.ModifyRandom(inst.dataUsers, []string{"displayname"}) + if err != nil { + return err + } else { + inst.dataUsers = dg + } + return nil +} + +//Function which modify random way the attributes in attName of a data_DN's slice, it can delete, replace and delete +//The function modify also in the dat object +func (inst *instance) ModifyRandom(dat []data_DN, attName []string) ([]data_DN, error) { + for index, value := range dat { + del := make(map[string][]string) + add := make(map[string][]string) + replace := make(map[string][]string) + + for _, att := range attName { + + switch selNumber := R.Intn(3); selNumber { + case 0: + del[att] = []string{} + value = DelAtt(att, value) + logging.Debug(fmt.Sprintf("Delete the attribute %s of the DN %s.", att, value.DN)) + case 1: + name := inst.GenerateName() + value = AddAtt(name, []string{name}, value) + add[name] = []string{name} + logging.Debug(fmt.Sprintf("Add the attribute %s with value %s of the DN %s.", name, name, value.DN)) + case 2: + name := inst.GenerateName() + value = ReplaceAtt(att, []string{name}, value) + replace[att] = []string{name} + logging.Debug(fmt.Sprintf("Replace the attribute %s with value %s of the DN %s.", att, name, value.DN)) + } + + } + + err := inst.Modify_Request(value.DN, add, del, replace) + if err != nil { + return dat, err + } + dat[index] = value + } + return dat, nil +} + +//Add all users in a random group +func (inst *instance) AddAllUsersInGroup() error { + for _, value := range inst.dataGroups { + valueRand := (len(inst.dataUsers) + 1) / 30 + if valueRand == 0 { + valueRand = 1 + } + numberOfMembers := R.Intn(valueRand) + 1 + logging.Debugf("%s will be have %d members.", value.DN, numberOfMembers) + + groupMemory := make(map[int]struct{}) + users_cn := []string{} + + for i := 0; i < numberOfMembers; i++ { + selectGroup := R.Intn(len(inst.dataUsers)) + for _, ok := groupMemory[selectGroup]; ok; _, ok = groupMemory[selectGroup] { + selectGroup = R.Intn(len(inst.dataUsers)) + + logging.Debugf("Search an other member. The value is %d , and we have %d members available.", selectGroup, len(inst.dataUsers)) + } + groupMemory[selectGroup] = struct{}{} + + users_cn = append(users_cn, inst.dataGroups[selectGroup].DN) + + } + err := inst.AddUserSliceInGroup(users_cn, value.DN) + if err != nil { + return err + } + + } + return nil +} + +//Check if the groups in memberOf exist in Consul +func (inst *instance) CheckMemberOf() (bool, error) { + for _, value := range inst.dataUsers { + cn := strings.Split(value.DN, ",")[0] + res, err := inst.Search_Request(value.DN, fmt.Sprintf("(&(%s))", cn), []string{"memberOf"}) + if err != nil { + return false, err + } + if len(res.Entries) != 1 { + return false, fmt.Errorf("expected 1 entry, but found %d entry/ies", len(res.Entries)) + } + attValues := GetAttributeValuesBottin(res.Entries[0], "memberOf") + for _, dnGroup := range attValues { + logging.Debugf("Verify if the group %s exist...", dnGroup) + ok, err := inst.VerifyGroup(data_DN{DN: dnGroup}) + if err != nil { + return false, err + } + if !ok { + return false, fmt.Errorf("don't found the group: %s", dnGroup) + } + } + } + return true, nil +} diff --git a/test/go.mod b/test/go.mod new file mode 100644 index 0000000..74ed1ce --- /dev/null +++ b/test/go.mod @@ -0,0 +1,8 @@ +module bottin/integration + +go 1.14 + +require ( + github.com/go-ldap/ldap/v3 v3.3.0 + github.com/sirupsen/logrus v1.4.2 +) diff --git a/test/go.sum b/test/go.sum new file mode 100644 index 0000000..9c882d3 --- /dev/null +++ b/test/go.sum @@ -0,0 +1,26 @@ +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28= +github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8= +github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-ldap/ldap/v3 v3.3.0 h1:lwx+SJpgOHd8tG6SumBQZXCmNX51zM8B1cfxJ5gv4tQ= +github.com/go-ldap/ldap/v3 v3.3.0/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg= +github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 h1:vEg9joUBmeBcK9iSJftGNf3coIG4HqZElCPehJsfAYM= +golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/test/handler.go b/test/handler.go new file mode 100644 index 0000000..fdee34f --- /dev/null +++ b/test/handler.go @@ -0,0 +1,141 @@ +package main + +import ( + "fmt" + "math/rand" + "os" + + ldap "github.com/go-ldap/ldap/v3" + "github.com/sirupsen/logrus" +) + +const maxlength_generateName, minlength_generateName = 25, 3 + +const bindusername = "cn=admin,dc=deuxfleurs,dc=fr" +const adresse = "127.0.0.1" +const port = 1389 + +var logging = logrus.New() + +const seed = 654258 + +var R = rand.New(rand.NewSource(seed)) + +var bindpassword = "sf7yO52NCuE" + +//Handler just to facilite the print error +func PrintError(LDAPError error) { + if LDAPError != nil { + logging.Fatal(LDAPError) + } +} + +//Generate an unique name, which store in all_names +func (inst *instance) GenerateName() (name string) { + alphabet := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", + "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", + "Z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "é", "è", "ê", "ë", "à", "@", "â", "ä", "û", "ü", "ù", "$", "£", "%", "ø", "€"} + length := R.Intn(maxlength_generateName) + minlength_generateName + + //Check if this name not exist already + //Lock thhis variable because she is hared with other goroutine + allNames.mu.Lock() + for only_one := true; only_one; _, only_one = allNames.cn[name] { + //Create the name + for i := 0; i < length; i++ { + name += alphabet[R.Intn(len(alphabet))] + } + + } + //Add the new name in the map to store this one + allNames.cn[name] = struct{}{} + allNames.mu.Unlock() + logging.Debug(fmt.Sprintf("Name generated: %s.", name)) + return +} + +//Handler to around the bug with MessageId +func (inst *instance) Reconnect() (err error) { + inst.logging.Close() + inst.logging, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port)) + if err != nil { + return + } + err = inst.logging.Bind(bindusername, bindpassword) + //logging.Debug("Reconnect succesful") + return +} + +//Transform attributes in map format to the struct attributes +func MapAttToStruct(att map[string][]string) []attributes { + resultat := []attributes{} + for key, value := range att { + logging.Debug(fmt.Sprintf("Transform: key: %s, values: %s to attributes struct.\n", key, value)) + resultat = append(resultat, attributes{ + Name: key, + Data: value, + }) + } + return resultat +} + +func Connect() (*ldap.Conn, error) { + + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", adresse, port)) + if err != nil { + return nil, err + } + + if key, ok := os.LookupEnv("BOTTIN_DEFAULT_ADMIN_PW"); ok { + bindpassword = key + } + //l.Debug.Enable(true) + err = l.Bind(bindusername, bindpassword) + logging.Debug("Connection succesful") + return l, err +} + +//Handler to get only attributes names +func getNamesAtt(dat data_DN) []string { + resultat := []string{} + for _, values := range dat.Attributes { + resultat = append(resultat, values.Name) + } + return resultat +} + +//Handler to compare slice string +func CompareSliceString(string1, string2 []string) bool { + if len(string1) != len(string2) { + return false + } else { + for index := range string1 { + if string1[index] != string2[index] { + return false + } + } + } + return true +} + +//Handler to remove an element in slice string +func DeleteElementSliceString(s string, sSlice []string) []string { + + for index, value := range sSlice { + if value == s { + sSlice[index] = sSlice[len(sSlice)-1] + return sSlice[:len(sSlice)-1] + } + } + return sSlice +} + +//Get attributes entry values bottin bug +func GetAttributeValuesBottin(ent *ldap.Entry, name string) (res []string) { + for _, val := range ent.Attributes { + if val.Name == name { + res = append(res, val.Values...) + } + } + return +} diff --git a/test/request.go b/test/request.go new file mode 100644 index 0000000..adde22e --- /dev/null +++ b/test/request.go @@ -0,0 +1,59 @@ +package main + +import ( + ldap "github.com/go-ldap/ldap/v3" +) + +func (inst *instance) Add_Request(dn string, attributes map[string][]string) error { + //Create the AddRequest + req := ldap.NewAddRequest(dn, nil) + for key, value := range attributes { + req.Attribute(key, value) + } + + //Send the request + err := inst.logging.Add(req) + return err + +} + +//Use enum to select Replace,Delete,Modify +func (inst *instance) Modify_Request(dn string, add_attributes, delete_attributes, replace_attributes map[string][]string) error { + modifyReq := ldap.NewModifyRequest(dn, nil) + + for key, value := range add_attributes { + modifyReq.Add(key, value) + } + + for key, value := range delete_attributes { + modifyReq.Delete(key, value) + } + + for key, value := range replace_attributes { + modifyReq.Replace(key, value) + } + + err := inst.logging.Modify(modifyReq) + return err +} + +func (inst *instance) Delete_Request(dn string) error { + del := ldap.NewDelRequest(dn, nil) + + err := inst.logging.Del(del) + return err +} + +func (inst *instance) Search_Request(dn, filter string, name_attributes []string) (*ldap.SearchResult, error) { + searchReq := ldap.NewSearchRequest( + dn, + ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, + filter, + name_attributes, + nil, + ) + + res, err := inst.logging.Search(searchReq) + logging.Debugf("Search Request made with: dn: %s, filter: %s, attributes: %s. \n", dn, filter, name_attributes) + return res, err +} diff --git a/test/runner.sh b/test/runner.sh new file mode 100755 index 0000000..04e42be --- /dev/null +++ b/test/runner.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +set -ex + +echo $BOTTIN_DEFAULT_ADMIN_PW +consul agent -dev > /dev/null 2>&1 & +sleep 2 +cp test/config.json.test config.json +./bottin > /tmp/bottin.log 2>&1 & +sleep 1 +./test/test -test.v -test.failfast -test.short -test.run TestPrincipal +./test/test -test.v -test.failfast -test.run TestPrincipal/B= + +jobs +kill %2 +kill %1 diff --git a/write.go b/write.go index 482f971..2dd42c6 100644 --- a/write.go +++ b/write.go @@ -8,7 +8,7 @@ import ( ldap "bottin/ldapserver" consul "github.com/hashicorp/consul/api" - message "github.com/lor00x/goldap/message" + message "bottin/goldap" ) // Generic item modification function -------- -- cgit v1.2.3