Browse Source

Vendor goldap

main
Quentin 4 months ago
parent
commit
477d7014ed
Signed by: quentin GPG Key ID: A98E9B769E4FF428
  1. 25
      goldap/abandon_request.go
  2. 53
      goldap/add_request.go
  3. 28
      goldap/add_response.go
  4. 740
      goldap/asn1.go
  5. 44
      goldap/assertion_value.go
  6. 40
      goldap/attribute.go
  7. 50
      goldap/attribute_description.go
  8. 43
      goldap/attribute_list.go
  9. 46
      goldap/attribute_selection.go
  10. 24
      goldap/attribute_value.go
  11. 77
      goldap/attribute_value_assertion.go
  12. 37
      goldap/authentication_choice.go
  13. 93
      goldap/bind_request.go
  14. 56
      goldap/bind_response.go
  15. 62
      goldap/boolean.go
  16. 199
      goldap/bytes.go
  17. 172
      goldap/bytes_test.go
  18. 53
      goldap/compare_request.go
  19. 29
      goldap/compare_response.go
  20. 94
      goldap/control.go
  21. 44
      goldap/controls.go
  22. 24
      goldap/del_request.go
  23. 29
      goldap/del_response.go
  24. 60
      goldap/dn.go
  25. 34
      goldap/enumerated.go
  26. 9
      goldap/error.go
  27. 69
      goldap/extended_request.go
  28. 85
      goldap/extended_response.go
  29. 70
      goldap/filter.go
  30. 54
      goldap/filter_and.go
  31. 34
      goldap/filter_approx_match.go
  32. 34
      goldap/filter_equality_match.go
  33. 28
      goldap/filter_extensible_match.go
  34. 34
      goldap/filter_greater_or_equal.go
  35. 34
      goldap/filter_less_or_equal.go
  36. 40
      goldap/filter_not.go
  37. 52
      goldap/filter_or.go
  38. 28
      goldap/filter_present.go
  39. 187
      goldap/filter_substring.go
  40. 53
      goldap/integer.go
  41. 89
      goldap/intermediate_response.go
  42. 117
      goldap/matching_rule_assertion.go
  43. 29
      goldap/matching_rule_id.go
  44. 139
      goldap/message.go
  45. 46
      goldap/message_id.go
  46. 87
      goldap/modify_dn_request.go
  47. 28
      goldap/modify_dn_response.go
  48. 89
      goldap/modify_request.go
  49. 43
      goldap/modify_request_change.go
  50. 36
      goldap/modify_response.go
  51. 44
      goldap/octetstring.go
  52. 50
      goldap/oid.go
  53. 76
      goldap/partial_attribute.go
  54. 53
      goldap/partial_attribute_list.go
  55. 63
      goldap/protocol_op.go
  56. 18
      goldap/read.go
  57. 3146
      goldap/read_error_test.go
  58. 2936
      goldap/read_test.go
  59. 50
      goldap/referral.go
  60. 15
      goldap/relative_ldap_dn.go
  61. 282
      goldap/result.go
  62. 63
      goldap/sasl_credentials.go
  63. 246
      goldap/search_request.go
  64. 31
      goldap/search_result_done.go
  65. 58
      goldap/search_result_entry.go
  66. 53
      goldap/search_result_reference.go
  67. 85
      goldap/size_test.go
  68. 38
      goldap/string.go
  69. 739
      goldap/struct.go
  70. 7
      goldap/struct_methods.go
  71. 38
      goldap/unbind_request.go
  72. 32
      goldap/uri.go
  73. 6
      goldap/write.go
  74. 19
      goldap/write_test.go

25
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)
}

53
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
}

28
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)
}

740
goldap/asn1.go

@ -0,0 +1,740 @@
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) {
for ; i != 0 || size == 0; i >>= 8 {
size++
}
return
}
func writeInt64(bytes *Bytes, i int64) (size int) {
for ; i != 0 || size == 0; i >>= 8 { // Write at least one byte even if the value is 0
bytes.writeBytes([]byte{byte(i)})
size++
}
return
}
// 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<<bytes[0])-1) != 0 {
// err = SyntaxError{"invalid padding bits in BIT STRING"}
// return
// }
// ret.BitLength = (len(bytes)-1)*8 - paddingBits
// ret.Bytes = bytes[1:]
// return
// }
// OBJECT IDENTIFIER
// An ObjectIdentifier represents an ASN.1 OBJECT IDENTIFIER.
// type ObjectIdentifier []int
// // Equal reports whether oi and other represent the same identifier.
// func (oi ObjectIdentifier) Equal(other ObjectIdentifier) bool {
// if len(oi) != len(other) {
// return false
// }
// for i := 0; i < len(oi); i++ {
// if oi[i] != other[i] {
// return false
// }
// }
// return true
// }
// func (oi ObjectIdentifier) String() string {
// var s string
// for i, v := range oi {
// if i > 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))
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
//

44
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)
}

40
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)
}

50
goldap/attribute_description.go

@ -0,0 +1,50 @@
package message
import "fmt"
//
// AttributeDescription ::= LDAPString
// -- Constrained to <attributedescription>
// -- [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)
}

43
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
}

46
goldap/attribute_selection.go

@ -0,0 +1,46 @@
package message
import "fmt"
//
// AttributeSelection ::= SEQUENCE OF selector LDAPString
// -- The LDAPString is constrained to
// -- <attributeSelector> 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 <attributeSelector> 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
}

24
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()
}

77
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
}

37
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
}

93
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
}

56
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
}

62
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)
}

199
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)}