├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── doc ├── RFC 4511 - LDAP The Protocol.txt ├── RFC 4511 - Lightweight Directory Access Protocol (LDAP) The Protocol.html ├── RFC 4512 - LDAP Directory Information Models.txt ├── RFC 4514 - LDAP String Representation of Distinguished Names.txt └── asn1js │ ├── ASN.1 JavaScript decoder.html │ └── ASN.1 JavaScript decoder_files │ ├── asn1.js │ ├── base64.js │ ├── dom.js │ ├── hex.js │ ├── index.css │ ├── index.js │ ├── int10.js │ └── oids.js ├── message ├── abandon_request.go ├── add_request.go ├── add_response.go ├── asn1.go ├── assertion_value.go ├── attribute.go ├── attribute_description.go ├── attribute_list.go ├── attribute_selection.go ├── attribute_value.go ├── attribute_value_assertion.go ├── authentication_choice.go ├── bind_request.go ├── bind_response.go ├── boolean.go ├── bytes.go ├── bytes_test.go ├── compare_request.go ├── compare_response.go ├── control.go ├── controls.go ├── del_request.go ├── del_response.go ├── dn.go ├── enumerated.go ├── error.go ├── extended_request.go ├── extended_response.go ├── filter.go ├── filter_and.go ├── filter_approx_match.go ├── filter_equality_match.go ├── filter_extensible_match.go ├── filter_greater_or_equal.go ├── filter_less_or_equal.go ├── filter_not.go ├── filter_or.go ├── filter_present.go ├── filter_substring.go ├── integer.go ├── intermediate_response.go ├── matching_rule_assertion.go ├── matching_rule_id.go ├── message.go ├── message_id.go ├── modify_dn_request.go ├── modify_dn_response.go ├── modify_request.go ├── modify_request_change.go ├── modify_response.go ├── octetstring.go ├── oid.go ├── partial_attribute.go ├── partial_attribute_list.go ├── protocol_op.go ├── read.go ├── read_error_test.go ├── read_test.go ├── referral.go ├── relative_ldap_dn.go ├── result.go ├── sasl_credentials.go ├── search_request.go ├── search_result_done.go ├── search_result_entry.go ├── search_result_reference.go ├── size_test.go ├── string.go ├── struct.go ├── struct_methods.go ├── unbind_request.go ├── uri.go ├── write.go └── write_test.go └── perf ├── cpu_ondemand.sh ├── cpu_performance.sh ├── cpu_powersave.sh ├── main.go ├── perf.sh └── xtime.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .project 3 | asn1/* 4 | doc/asn1js 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | install: 3 | - go get -v golang.org/x/tools/cmd/cover 4 | script: 5 | - go test -v -tags=testcgo ./message -covermode=count -coverprofile=profile.cov 6 | after_success: 7 | - go get -v github.com/mattn/goveralls 8 | - export PATH=$PATH:$HOME/gopath/bin 9 | - goveralls -coverprofile=profile.cov -service=travis-ci 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Thomas Guillier 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![GoDoc](https://godoc.org/github.com/lor00x/goldap?status.svg)](https://godoc.org/github.com/lor00x/goldap) 2 | [![Build Status](https://travis-ci.org/lor00x/goldap.svg)](https://travis-ci.org/lor00x/goldap) 3 | [![Coverage Status](https://coveralls.io/repos/lor00x/goldap/badge.png?branch=master)](https://coveralls.io/r/lor00x/goldap?branch=master) 4 | 5 | # LDAP library in Golang 6 | 7 | This library performs decoding and encoding of LDAP message. 8 | It still requires a lot of testing. 9 | 10 | # Usage example 11 | See my [LDAP proxy](https://github.com/lor00x/goldap-proxy). 12 | See [LDAP Server](https://github.com/vjeantet/ldapserver). 13 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lor00x/goldap/8d785c64d1c87d6fa9c95591edf9d8abc603a34c/doc/asn1js/ASN.1 JavaScript decoder.html -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/asn1.js: -------------------------------------------------------------------------------- 1 | // ASN.1 JavaScript decoder 2 | // Copyright (c) 2008-2014 Lapo Luchini 3 | 4 | // Permission to use, copy, modify, and/or distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ 17 | /*global oids */ 18 | (function (undefined) { 19 | "use strict"; 20 | 21 | var Int10 = (typeof module !== 'undefined') ? require('./int10.js') : window.Int10, 22 | ellipsis = "\u2026", 23 | reTime = /^((?:1[89]|2\d)?\d\d)(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])([01]\d|2[0-3])(?:([0-5]\d)(?:([0-5]\d)(?:[.,](\d{1,3}))?)?)?(Z|[-+](?:[0]\d|1[0-2])([0-5]\d)?)?$/; 24 | 25 | function stringCut(str, len) { 26 | if (str.length > len) 27 | str = str.substring(0, len) + ellipsis; 28 | return str; 29 | } 30 | 31 | function Stream(enc, pos) { 32 | if (enc instanceof Stream) { 33 | this.enc = enc.enc; 34 | this.pos = enc.pos; 35 | } else { 36 | this.enc = enc; 37 | this.pos = pos; 38 | } 39 | } 40 | Stream.prototype.get = function (pos) { 41 | if (pos === undefined) 42 | pos = this.pos++; 43 | if (pos >= this.enc.length) 44 | throw 'Requesting byte offset ' + pos + ' on a stream of length ' + this.enc.length; 45 | return this.enc[pos]; 46 | }; 47 | Stream.prototype.hexDigits = "0123456789ABCDEF"; 48 | Stream.prototype.hexByte = function (b) { 49 | return this.hexDigits.charAt((b >> 4) & 0xF) + this.hexDigits.charAt(b & 0xF); 50 | }; 51 | Stream.prototype.hexDump = function (start, end, raw) { 52 | var s = ""; 53 | for (var i = start; i < end; ++i) { 54 | s += this.hexByte(this.get(i)); 55 | if (raw !== true) 56 | switch (i & 0xF) { 57 | case 0x7: s += " "; break; 58 | case 0xF: s += "\n"; break; 59 | default: s += " "; 60 | } 61 | } 62 | return s; 63 | }; 64 | Stream.prototype.isASCII = function (start, end) { 65 | for (var i = start; i < end; ++i) { 66 | var c = this.get(i); 67 | if (c < 32 || c > 176) 68 | return false; 69 | } 70 | return true; 71 | }; 72 | Stream.prototype.parseStringISO = function (start, end) { 73 | var s = ""; 74 | for (var i = start; i < end; ++i) 75 | s += String.fromCharCode(this.get(i)); 76 | return s; 77 | }; 78 | Stream.prototype.parseStringUTF = function (start, end) { 79 | var s = ""; 80 | for (var i = start; i < end; ) { 81 | var c = this.get(i++); 82 | if (c < 128) 83 | s += String.fromCharCode(c); 84 | else if ((c > 191) && (c < 224)) 85 | s += String.fromCharCode(((c & 0x1F) << 6) | (this.get(i++) & 0x3F)); 86 | else 87 | s += String.fromCharCode(((c & 0x0F) << 12) | ((this.get(i++) & 0x3F) << 6) | (this.get(i++) & 0x3F)); 88 | } 89 | return s; 90 | }; 91 | Stream.prototype.parseStringBMP = function (start, end) { 92 | var str = "", hi, lo; 93 | for (var i = start; i < end; ) { 94 | hi = this.get(i++); 95 | lo = this.get(i++); 96 | str += String.fromCharCode((hi << 8) | lo); 97 | } 98 | return str; 99 | }; 100 | Stream.prototype.parseTime = function (start, end, shortYear) { 101 | var s = this.parseStringISO(start, end), 102 | m = reTime.exec(s); 103 | if (!m) 104 | return "Unrecognized time: " + s; 105 | if (shortYear) { 106 | // to avoid querying the timer, use the fixed range [1970, 2069] 107 | // it will conform with ITU X.400 [-10, +40] sliding window until 2030 108 | m[1] = +m[1]; 109 | m[1] += (m[1] < 70) ? 2000 : 1900; 110 | } 111 | s = m[1] + "-" + m[2] + "-" + m[3] + " " + m[4]; 112 | if (m[5]) { 113 | s += ":" + m[5]; 114 | if (m[6]) { 115 | s += ":" + m[6]; 116 | if (m[7]) 117 | s += "." + m[7]; 118 | } 119 | } 120 | if (m[8]) { 121 | s += " UTC"; 122 | if (m[8] != 'Z') { 123 | s += m[8]; 124 | if (m[9]) 125 | s += ":" + m[9]; 126 | } 127 | } 128 | return s; 129 | }; 130 | Stream.prototype.parseInteger = function (start, end) { 131 | var v = this.get(start), 132 | neg = (v > 127), 133 | pad = neg ? 255 : 0, 134 | len, 135 | s = ''; 136 | // skip unuseful bits (not allowed in DER) 137 | while (v == pad && start < end) 138 | v = this.get(++start); 139 | len = end - start; 140 | if (len === 0) 141 | return neg ? -1 : 0; 142 | // show bit length of huge integers 143 | if (len > 4) { 144 | s = v; 145 | len <<= 3; 146 | while (((s ^ pad) & 0x80) == 0) { 147 | s <<= 1; 148 | --len; 149 | } 150 | s = "(" + len + " bit)\n"; 151 | } 152 | // decode the integer 153 | if (neg) v = v - 256; 154 | var n = new Int10(v); 155 | for (var i = start + 1; i < end; ++i) 156 | n.mulAdd(256, this.get(i)); 157 | return s + n.toString(); 158 | }; 159 | Stream.prototype.parseBitString = function (start, end, maxLength) { 160 | var unusedBit = this.get(start), 161 | lenBit = ((end - start - 1) << 3) - unusedBit, 162 | intro = "(" + lenBit + " bit)\n", 163 | s = "", 164 | skip = unusedBit; 165 | for (var i = end - 1; i > start; --i) { 166 | var b = this.get(i); 167 | for (var j = skip; j < 8; ++j) 168 | s += (b >> j) & 1 ? "1" : "0"; 169 | skip = 0; 170 | if (s.length > maxLength) 171 | return intro + stringCut(s, maxLength); 172 | } 173 | return intro + s; 174 | }; 175 | Stream.prototype.parseOctetString = function (start, end, maxLength) { 176 | if (this.isASCII(start, end)) 177 | return stringCut(this.parseStringISO(start, end), maxLength); 178 | var len = end - start, 179 | s = "(" + len + " byte)\n"; 180 | maxLength /= 2; // we work in bytes 181 | if (len > maxLength) 182 | end = start + maxLength; 183 | for (var i = start; i < end; ++i) 184 | s += this.hexByte(this.get(i)); 185 | if (len > maxLength) 186 | s += ellipsis; 187 | return s; 188 | }; 189 | Stream.prototype.parseOID = function (start, end, maxLength) { 190 | var s = '', 191 | n = new Int10(), 192 | bits = 0; 193 | for (var i = start; i < end; ++i) { 194 | var v = this.get(i); 195 | n.mulAdd(128, v & 0x7F); 196 | bits += 7; 197 | if (!(v & 0x80)) { // finished 198 | if (s === '') { 199 | n = n.simplify(); 200 | var m = n < 80 ? n < 40 ? 0 : 1 : 2; 201 | s = m + "." + (n - m * 40); 202 | } else 203 | s += "." + n.toString(); 204 | if (s.length > maxLength) 205 | return stringCut(s, maxLength); 206 | n = new Int10(); 207 | bits = 0; 208 | } 209 | } 210 | if (bits > 0) 211 | s += ".incomplete"; 212 | return s; 213 | }; 214 | 215 | function ASN1(stream, header, length, tag, sub) { 216 | if (!(tag instanceof ASN1Tag)) throw 'Invalid tag value.'; 217 | this.stream = stream; 218 | this.header = header; 219 | this.length = length; 220 | this.tag = tag; 221 | this.sub = sub; 222 | } 223 | ASN1.prototype.typeName = function () { 224 | switch (this.tag.tagClass) { 225 | case 0: // universal 226 | switch (this.tag.tagNumber) { 227 | case 0x00: return "EOC"; 228 | case 0x01: return "BOOLEAN"; 229 | case 0x02: return "INTEGER"; 230 | case 0x03: return "BIT_STRING"; 231 | case 0x04: return "OCTET_STRING"; 232 | case 0x05: return "NULL"; 233 | case 0x06: return "OBJECT_IDENTIFIER"; 234 | case 0x07: return "ObjectDescriptor"; 235 | case 0x08: return "EXTERNAL"; 236 | case 0x09: return "REAL"; 237 | case 0x0A: return "ENUMERATED"; 238 | case 0x0B: return "EMBEDDED_PDV"; 239 | case 0x0C: return "UTF8String"; 240 | case 0x10: return "SEQUENCE"; 241 | case 0x11: return "SET"; 242 | case 0x12: return "NumericString"; 243 | case 0x13: return "PrintableString"; // ASCII subset 244 | case 0x14: return "TeletexString"; // aka T61String 245 | case 0x15: return "VideotexString"; 246 | case 0x16: return "IA5String"; // ASCII 247 | case 0x17: return "UTCTime"; 248 | case 0x18: return "GeneralizedTime"; 249 | case 0x19: return "GraphicString"; 250 | case 0x1A: return "VisibleString"; // ASCII subset 251 | case 0x1B: return "GeneralString"; 252 | case 0x1C: return "UniversalString"; 253 | case 0x1E: return "BMPString"; 254 | } 255 | return "Universal_" + this.tag.tagNumber.toString(); 256 | case 1: return "Application_" + this.tag.tagNumber.toString(); 257 | case 2: return "[" + this.tag.tagNumber.toString() + "]"; // Context 258 | case 3: return "Private_" + this.tag.tagNumber.toString(); 259 | } 260 | }; 261 | ASN1.prototype.content = function (maxLength) { // a preview of the content (intended for humans) 262 | if (this.tag === undefined) 263 | return null; 264 | if (maxLength === undefined) 265 | maxLength = Infinity; 266 | var content = this.posContent(), 267 | len = Math.abs(this.length); 268 | if (!this.tag.isUniversal()) { 269 | if (this.sub !== null) 270 | return "(" + this.sub.length + " elem)"; 271 | return this.stream.parseOctetString(content, content + len, maxLength); 272 | } 273 | switch (this.tag.tagNumber) { 274 | case 0x01: // BOOLEAN 275 | return (this.stream.get(content) === 0) ? "false" : "true"; 276 | case 0x02: // INTEGER 277 | return this.stream.parseInteger(content, content + len); 278 | case 0x03: // BIT_STRING 279 | return this.sub ? "(" + this.sub.length + " elem)" : 280 | this.stream.parseBitString(content, content + len, maxLength); 281 | case 0x04: // OCTET_STRING 282 | return this.sub ? "(" + this.sub.length + " elem)" : 283 | this.stream.parseOctetString(content, content + len, maxLength); 284 | //case 0x05: // NULL 285 | case 0x06: // OBJECT_IDENTIFIER 286 | return this.stream.parseOID(content, content + len, maxLength); 287 | //case 0x07: // ObjectDescriptor 288 | //case 0x08: // EXTERNAL 289 | //case 0x09: // REAL 290 | //case 0x0A: // ENUMERATED 291 | //case 0x0B: // EMBEDDED_PDV 292 | case 0x10: // SEQUENCE 293 | case 0x11: // SET 294 | return "(" + this.sub.length + " elem)"; 295 | case 0x0C: // UTF8String 296 | return stringCut(this.stream.parseStringUTF(content, content + len), maxLength); 297 | case 0x12: // NumericString 298 | case 0x13: // PrintableString 299 | case 0x14: // TeletexString 300 | case 0x15: // VideotexString 301 | case 0x16: // IA5String 302 | //case 0x19: // GraphicString 303 | case 0x1A: // VisibleString 304 | //case 0x1B: // GeneralString 305 | //case 0x1C: // UniversalString 306 | return stringCut(this.stream.parseStringISO(content, content + len), maxLength); 307 | case 0x1E: // BMPString 308 | return stringCut(this.stream.parseStringBMP(content, content + len), maxLength); 309 | case 0x17: // UTCTime 310 | case 0x18: // GeneralizedTime 311 | return this.stream.parseTime(content, content + len, (this.tag.tagNumber == 0x17)); 312 | } 313 | return null; 314 | }; 315 | ASN1.prototype.toString = function () { 316 | return this.typeName() + "@" + this.stream.pos + "[header:" + this.header + ",length:" + this.length + ",sub:" + ((this.sub === null) ? 'null' : this.sub.length) + "]"; 317 | }; 318 | ASN1.prototype.toPrettyString = function (indent) { 319 | if (indent === undefined) indent = ''; 320 | var s = indent + this.typeName() + " @" + this.stream.pos; 321 | if (this.length >= 0) 322 | s += "+"; 323 | s += this.length; 324 | if (this.tag.tagConstructed) 325 | s += " (constructed)"; 326 | else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) 327 | s += " (encapsulates)"; 328 | s += "\n"; 329 | if (this.sub !== null) { 330 | indent += ' '; 331 | for (var i = 0, max = this.sub.length; i < max; ++i) 332 | s += this.sub[i].toPrettyString(indent); 333 | } 334 | return s; 335 | }; 336 | ASN1.prototype.posStart = function () { 337 | return this.stream.pos; 338 | }; 339 | ASN1.prototype.posContent = function () { 340 | return this.stream.pos + this.header; 341 | }; 342 | ASN1.prototype.posEnd = function () { 343 | return this.stream.pos + this.header + Math.abs(this.length); 344 | }; 345 | ASN1.prototype.toHexString = function (root) { 346 | return this.stream.hexDump(this.posStart(), this.posEnd(), true); 347 | }; 348 | ASN1.decodeLength = function (stream) { 349 | var buf = stream.get(), 350 | len = buf & 0x7F; 351 | if (len == buf) 352 | return len; 353 | if (len > 6) // no reason to use Int10, as it would be a huge buffer anyways 354 | throw "Length over 48 bits not supported at position " + (stream.pos - 1); 355 | if (len === 0) 356 | return null; // undefined 357 | buf = 0; 358 | for (var i = 0; i < len; ++i) 359 | buf = (buf * 256) + stream.get(); 360 | return buf; 361 | }; 362 | function ASN1Tag(stream) { 363 | var buf = stream.get(); 364 | this.tagClass = buf >> 6; 365 | this.tagConstructed = ((buf & 0x20) !== 0); 366 | this.tagNumber = buf & 0x1F; 367 | if (this.tagNumber == 0x1F) { // long tag 368 | var n = new Int10(); 369 | do { 370 | buf = stream.get(); 371 | n.mulAdd(128, buf & 0x7F); 372 | } while (buf & 0x80); 373 | this.tagNumber = n.simplify(); 374 | } 375 | } 376 | ASN1Tag.prototype.isUniversal = function () { 377 | return this.tagClass === 0x00; 378 | }; 379 | ASN1Tag.prototype.isEOC = function () { 380 | return this.tagClass === 0x00 && this.tagNumber === 0x00; 381 | }; 382 | ASN1.decode = function (stream) { 383 | if (!(stream instanceof Stream)) 384 | stream = new Stream(stream, 0); 385 | var streamStart = new Stream(stream), 386 | tag = new ASN1Tag(stream), 387 | len = ASN1.decodeLength(stream), 388 | start = stream.pos, 389 | header = start - streamStart.pos, 390 | sub = null, 391 | getSub = function () { 392 | sub = []; 393 | if (len !== null) { 394 | // definite length 395 | var end = start + len; 396 | while (stream.pos < end) 397 | sub[sub.length] = ASN1.decode(stream); 398 | if (stream.pos != end) 399 | throw "Content size is not correct for container starting at offset " + start; 400 | } else { 401 | // undefined length 402 | try { 403 | for (;;) { 404 | var s = ASN1.decode(stream); 405 | if (s.tag.isEOC()) 406 | break; 407 | sub[sub.length] = s; 408 | } 409 | len = start - stream.pos; // undefined lengths are represented as negative values 410 | } catch (e) { 411 | throw "Exception while decoding undefined length content: " + e; 412 | } 413 | } 414 | }; 415 | if (tag.tagConstructed) { 416 | // must have valid content 417 | getSub(); 418 | } else if (tag.isUniversal() && ((tag.tagNumber == 0x03) || (tag.tagNumber == 0x04))) { 419 | if (tag.tagNumber == 0x03) stream.get(); // skip BitString unused bits, must be in [0, 7] 420 | // sometimes BitString and OctetString do contain ASN.1 421 | try { 422 | getSub(); 423 | for (var i = 0; i < sub.length; ++i) 424 | if (sub[i].tag.isEOC()) 425 | throw 'EOC is not supposed to be actual content.'; 426 | } catch (e) { 427 | // but silently ignore when they don't 428 | sub = null; 429 | } 430 | } 431 | if (sub === null) { 432 | if (len === null) 433 | throw "We can't skip over an invalid tag with undefined length at offset " + start; 434 | stream.pos = start + Math.abs(len); 435 | } 436 | return new ASN1(streamStart, header, len, tag, sub); 437 | }; 438 | 439 | // export globals 440 | if (typeof module !== 'undefined') { module.exports = ASN1; } else { window.ASN1 = ASN1; } 441 | })(); 442 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/base64.js: -------------------------------------------------------------------------------- 1 | // Base64 JavaScript decoder 2 | // Copyright (c) 2008-2014 Lapo Luchini 3 | 4 | // Permission to use, copy, modify, and/or distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ 17 | (function (undefined) { 18 | "use strict"; 19 | 20 | var Base64 = {}, 21 | decoder; 22 | 23 | Base64.decode = function (a) { 24 | var i; 25 | if (decoder === undefined) { 26 | var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", 27 | ignore = "= \f\n\r\t\u00A0\u2028\u2029"; 28 | decoder = []; 29 | for (i = 0; i < 64; ++i) 30 | decoder[b64.charAt(i)] = i; 31 | for (i = 0; i < ignore.length; ++i) 32 | decoder[ignore.charAt(i)] = -1; 33 | } 34 | var out = []; 35 | var bits = 0, char_count = 0; 36 | for (i = 0; i < a.length; ++i) { 37 | var c = a.charAt(i); 38 | if (c == '=') 39 | break; 40 | c = decoder[c]; 41 | if (c == -1) 42 | continue; 43 | if (c === undefined) 44 | throw 'Illegal character at offset ' + i; 45 | bits |= c; 46 | if (++char_count >= 4) { 47 | out[out.length] = (bits >> 16); 48 | out[out.length] = (bits >> 8) & 0xFF; 49 | out[out.length] = bits & 0xFF; 50 | bits = 0; 51 | char_count = 0; 52 | } else { 53 | bits <<= 6; 54 | } 55 | } 56 | switch (char_count) { 57 | case 1: 58 | throw "Base64 encoding incomplete: at least 2 bits missing"; 59 | case 2: 60 | out[out.length] = (bits >> 10); 61 | break; 62 | case 3: 63 | out[out.length] = (bits >> 16); 64 | out[out.length] = (bits >> 8) & 0xFF; 65 | break; 66 | } 67 | return out; 68 | }; 69 | 70 | Base64.re = /-----BEGIN [^-]+-----([A-Za-z0-9+\/=\s]+)-----END [^-]+-----|begin-base64[^\n]+\n([A-Za-z0-9+\/=\s]+)====/; 71 | Base64.unarmor = function (a) { 72 | var m = Base64.re.exec(a); 73 | if (m) { 74 | if (m[1]) 75 | a = m[1]; 76 | else if (m[2]) 77 | a = m[2]; 78 | else 79 | throw "RegExp out of sync"; 80 | } 81 | return Base64.decode(a); 82 | }; 83 | 84 | // export globals 85 | if (typeof module !== 'undefined') { module.exports = Base64; } else { window.Base64 = Base64; } 86 | })(); 87 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/dom.js: -------------------------------------------------------------------------------- 1 | // ASN.1 JavaScript decoder 2 | // Copyright (c) 2008-2014 Lapo Luchini 3 | 4 | // Permission to use, copy, modify, and/or distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ 17 | /*global oids, ASN1 */ 18 | (function (undefined) { 19 | "use strict"; 20 | 21 | var ASN1 = (typeof module !== 'undefined') ? require('./asn1.js') : window.ASN1, 22 | lineLength = 80, 23 | contentLength = 8 * lineLength, 24 | DOM = { 25 | ellipsis: "\u2026", 26 | tag: function (tagName, className) { 27 | var t = document.createElement(tagName); 28 | t.className = className; 29 | return t; 30 | }, 31 | text: function (str) { 32 | return document.createTextNode(str); 33 | }, 34 | breakLines: function (str, length) { 35 | var lines = str.split(/\r?\n/), 36 | o = ''; 37 | for (var i = 0; i < lines.length; ++i) { 38 | var line = lines[i]; 39 | if (i > 0) o += "\n"; 40 | while (line.length > length) { 41 | o += line.substring(0, length); 42 | o += "\n"; 43 | line = line.substring(length); 44 | } 45 | o += line; 46 | } 47 | return o; 48 | } 49 | }; 50 | 51 | ASN1.prototype.toDOM = function () { 52 | var node = DOM.tag("div", "node"); 53 | node.asn1 = this; 54 | var head = DOM.tag("div", "head"); 55 | var s = this.typeName().replace(/_/g, " "); 56 | head.innerHTML = s; 57 | var content = this.content(contentLength); 58 | if (content !== null) { 59 | var preview = DOM.tag("span", "preview"), 60 | shortContent; 61 | content = String(content); // it might be a number 62 | shortContent = (content.length > lineLength) ? content.substring(0, lineLength) + DOM.ellipsis : content; 63 | preview.appendChild(DOM.text(shortContent)); 64 | head.appendChild(preview); 65 | content = DOM.breakLines(content, lineLength); 66 | content = content.replace(/"); 68 | } 69 | node.appendChild(head); 70 | this.node = node; 71 | this.head = head; 72 | var value = DOM.tag("div", "value"); 73 | s = "Offset: " + this.stream.pos + "
"; 74 | s += "Length: " + this.header + "+"; 75 | if (this.length >= 0) 76 | s += this.length; 77 | else 78 | s += (-this.length) + " (undefined)"; 79 | if (this.tag.tagConstructed) 80 | s += "
(constructed)"; 81 | else if ((this.tag.isUniversal() && ((this.tag.tagNumber == 0x03) || (this.tag.tagNumber == 0x04))) && (this.sub !== null)) 82 | s += "
(encapsulates)"; 83 | //TODO if (this.tag.isUniversal() && this.tag.tagNumber == 0x03) s += "Unused bits: " 84 | if (content !== null) { 85 | s += "
Value:
" + content + ""; 86 | if ((typeof oids === 'object') && (this.tag.isUniversal() && (this.tag.tagNumber == 0x06))) { 87 | var oid = oids[content]; 88 | if (oid) { 89 | if (oid.d) s += "
" + oid.d; 90 | if (oid.c) s += "
" + oid.c; 91 | if (oid.w) s += "
(warning!)"; 92 | } 93 | } 94 | } 95 | value.innerHTML = s; 96 | node.appendChild(value); 97 | var sub = DOM.tag("div", "sub"); 98 | if (this.sub !== null) { 99 | for (var i = 0, max = this.sub.length; i < max; ++i) 100 | sub.appendChild(this.sub[i].toDOM()); 101 | } 102 | node.appendChild(sub); 103 | head.onclick = function () { 104 | node.className = (node.className == "node collapsed") ? "node" : "node collapsed"; 105 | }; 106 | return node; 107 | }; 108 | ASN1.prototype.fakeHover = function (current) { 109 | this.node.className += " hover"; 110 | if (current) 111 | this.head.className += " hover"; 112 | }; 113 | ASN1.prototype.fakeOut = function (current) { 114 | var re = / ?hover/; 115 | this.node.className = this.node.className.replace(re, ""); 116 | if (current) 117 | this.head.className = this.head.className.replace(re, ""); 118 | }; 119 | ASN1.prototype.toHexDOM_sub = function (node, className, stream, start, end) { 120 | if (start >= end) 121 | return; 122 | var sub = DOM.tag("span", className); 123 | sub.appendChild(DOM.text( 124 | stream.hexDump(start, end))); 125 | node.appendChild(sub); 126 | }; 127 | ASN1.prototype.toHexDOM = function (root) { 128 | var node = DOM.tag("span", "hex"); 129 | if (root === undefined) root = node; 130 | this.head.hexNode = node; 131 | this.head.onmouseover = function () { this.hexNode.className = "hexCurrent"; }; 132 | this.head.onmouseout = function () { this.hexNode.className = "hex"; }; 133 | node.asn1 = this; 134 | node.onmouseover = function () { 135 | var current = !root.selected; 136 | if (current) { 137 | root.selected = this.asn1; 138 | this.className = "hexCurrent"; 139 | } 140 | this.asn1.fakeHover(current); 141 | }; 142 | node.onmouseout = function () { 143 | var current = (root.selected == this.asn1); 144 | this.asn1.fakeOut(current); 145 | if (current) { 146 | root.selected = null; 147 | this.className = "hex"; 148 | } 149 | }; 150 | this.toHexDOM_sub(node, "tag", this.stream, this.posStart(), this.posStart() + 1); 151 | this.toHexDOM_sub(node, (this.length >= 0) ? "dlen" : "ulen", this.stream, this.posStart() + 1, this.posContent()); 152 | if (this.sub === null) 153 | node.appendChild(DOM.text( 154 | this.stream.hexDump(this.posContent(), this.posEnd()))); 155 | else if (this.sub.length > 0) { 156 | var first = this.sub[0]; 157 | var last = this.sub[this.sub.length - 1]; 158 | this.toHexDOM_sub(node, "intro", this.stream, this.posContent(), first.posStart()); 159 | for (var i = 0, max = this.sub.length; i < max; ++i) 160 | node.appendChild(this.sub[i].toHexDOM(root)); 161 | this.toHexDOM_sub(node, "outro", this.stream, last.posEnd(), this.posEnd()); 162 | } else 163 | this.toHexDOM_sub(node, "outro", this.stream, this.posContent(), this.posEnd()); 164 | return node; 165 | }; 166 | 167 | })(); 168 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/hex.js: -------------------------------------------------------------------------------- 1 | // Hex JavaScript decoder 2 | // Copyright (c) 2008-2014 Lapo Luchini 3 | 4 | // Permission to use, copy, modify, and/or distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ 17 | (function (undefined) { 18 | "use strict"; 19 | 20 | var Hex = {}, 21 | decoder; 22 | 23 | Hex.decode = function(a) { 24 | var i; 25 | if (decoder === undefined) { 26 | var hex = "0123456789ABCDEF", 27 | ignore = " \f\n\r\t\u00A0\u2028\u2029"; 28 | decoder = []; 29 | for (i = 0; i < 16; ++i) 30 | decoder[hex.charAt(i)] = i; 31 | hex = hex.toLowerCase(); 32 | for (i = 10; i < 16; ++i) 33 | decoder[hex.charAt(i)] = i; 34 | for (i = 0; i < ignore.length; ++i) 35 | decoder[ignore.charAt(i)] = -1; 36 | } 37 | var out = [], 38 | bits = 0, 39 | char_count = 0; 40 | for (i = 0; i < a.length; ++i) { 41 | var c = a.charAt(i); 42 | if (c == '=') 43 | break; 44 | c = decoder[c]; 45 | if (c == -1) 46 | continue; 47 | if (c === undefined) 48 | throw 'Illegal character at offset ' + i; 49 | bits |= c; 50 | if (++char_count >= 2) { 51 | out[out.length] = bits; 52 | bits = 0; 53 | char_count = 0; 54 | } else { 55 | bits <<= 4; 56 | } 57 | } 58 | if (char_count) 59 | throw "Hex encoding incomplete: 4 bits missing"; 60 | return out; 61 | }; 62 | 63 | // export globals 64 | if (typeof module !== 'undefined') { module.exports = Hex; } else { window.Hex = Hex; } 65 | })(); 66 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/index.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | background-color: #C0C0C0; 3 | font-family: Arial, Helvetica, sans-serif; 4 | text-align: justify; 5 | } 6 | .tt { 7 | font-family: monospace; 8 | } 9 | .license .ref { 10 | position: relative; 11 | } 12 | .license .hidden { 13 | visibility: hidden; 14 | position: absolute; 15 | bottom: 0em; 16 | /*white-space: pre;*/ 17 | background-color: #D0D0D0; 18 | border: solid 1px white; 19 | padding: 2px; 20 | margin-left: 15%; 21 | margin-right: 15%; 22 | } 23 | .license:hover .hidden { 24 | /*display: block;*/ 25 | visibility: visible; 26 | } 27 | .node { 28 | position: relative; 29 | } 30 | .sub { 31 | padding-left: 1.5em; 32 | border-left: solid 1px #E0E0E0; 33 | } 34 | .head { 35 | height: 1em; 36 | white-space: nowrap; 37 | } 38 | .node:hover > .head, .node.hover > .head { 39 | color: darkblue; 40 | font-weight: bold; 41 | } 42 | .node:hover > .head:hover, .node.hover > .head.hover { 43 | color: blue; 44 | } 45 | .node.collapsed { 46 | font-style: italic; 47 | } 48 | .node.collapsed > .sub { 49 | display: none; 50 | } 51 | .node.collapsed.hover > .sub { 52 | display: block; 53 | } 54 | .value { 55 | display: none; 56 | position: absolute; 57 | z-index: 2; 58 | top: 1.2em; 59 | left: 0; 60 | background-color: #D0D0D0; 61 | border: solid 1px white; 62 | padding: 2px; 63 | } 64 | .head:hover + .value, .head.hover + .value { 65 | display: block; 66 | } 67 | .preview { 68 | margin-left: 1em; 69 | color: #505050; 70 | font-weight: normal; 71 | } 72 | #tree { 73 | font-family: monospace; 74 | } 75 | #dump { 76 | z-index: 1; 77 | background-color: #C0C0C0; 78 | border: solid 1px #E0E0E0; 79 | font-family: monospace; 80 | white-space: pre; 81 | padding: 2px; 82 | } 83 | #dump .tag { color: blue; } 84 | #dump .dlen { color: darkcyan; } 85 | #dump .ulen { color: darkgreen; } 86 | #dump .intro { color: blue; } 87 | #dump .outro { color: darkgreen; } 88 | #dump .hexCurrent { background-color: #808080; } 89 | #dump .hexCurrent .hex { background-color: #A0A0A0; } 90 | #file { display: none; } 91 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/index.js: -------------------------------------------------------------------------------- 1 | /*jshint browser: true, strict: true, globalstrict: true, indent: 4, immed: true, latedef: true, undef: true, regexdash: false */ 2 | /*global Hex, Base64, ASN1 */ 3 | "use strict"; 4 | 5 | var reHex = /^\s*(?:[0-9A-Fa-f][0-9A-Fa-f]\s*)+$/, 6 | hash = null; 7 | function id(elem) { 8 | return document.getElementById(elem); 9 | } 10 | function toHTML(obj) { 11 | return String(obj).replace(/ 0) 106 | read(e.dataTransfer.files[0]); 107 | } 108 | window.onload = function () { 109 | if ('onhashchange' in window) 110 | window.onhashchange = loadFromHash; 111 | loadFromHash(); 112 | document.ondragover = stop; 113 | document.ondragleave = stop; 114 | if ('FileReader' in window) { 115 | id('file').style.display = 'block'; 116 | id('file').onchange = load; 117 | document.ondrop = dragAccept; 118 | } 119 | }; 120 | -------------------------------------------------------------------------------- /doc/asn1js/ASN.1 JavaScript decoder_files/int10.js: -------------------------------------------------------------------------------- 1 | // Big integer base-10 printing library 2 | // Copyright (c) 2014 Lapo Luchini 3 | 4 | // Permission to use, copy, modify, and/or distribute this software for any 5 | // purpose with or without fee is hereby granted, provided that the above 6 | // copyright notice and this permission notice appear in all copies. 7 | // 8 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | 16 | /*jshint browser: true, strict: true, immed: true, latedef: true, undef: true, regexdash: false */ 17 | (function () { 18 | "use strict"; 19 | 20 | var max = 10000000000000; // biggest integer that can still fit 2^53 when multiplied by 256 21 | 22 | function Int10(value) { 23 | this.buf = [+value || 0]; 24 | } 25 | 26 | Int10.prototype.mulAdd = function (m, c) { 27 | // assert(m <= 256) 28 | var b = this.buf, 29 | l = b.length, 30 | i, t; 31 | for (i = 0; i < l; ++i) { 32 | t = b[i] * m + c; 33 | if (t < max) 34 | c = 0; 35 | else { 36 | c = 0|(t / max); 37 | t -= c * max; 38 | } 39 | b[i] = t; 40 | } 41 | if (c > 0) 42 | b[i] = c; 43 | }; 44 | 45 | Int10.prototype.toString = function (base) { 46 | if ((base || 10) != 10) 47 | throw 'only base 10 is supported'; 48 | var b = this.buf, 49 | s = b[b.length - 1].toString(); 50 | for (var i = b.length - 2; i >= 0; --i) 51 | s += (max + b[i]).toString().substring(1); 52 | return s; 53 | }; 54 | 55 | Int10.prototype.valueOf = function () { 56 | var b = this.buf, 57 | v = 0; 58 | for (var i = b.length - 1; i >= 0; --i) 59 | v = v * max + b[i]; 60 | return v; 61 | }; 62 | 63 | Int10.prototype.simplify = function () { 64 | var b = this.buf; 65 | return (b.length == 1) ? b[0] : this; 66 | }; 67 | 68 | // export globals 69 | if (typeof module !== 'undefined') { module.exports = Int10; } else { window.Int10 = Int10; } 70 | })(); 71 | -------------------------------------------------------------------------------- /message/abandon_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AbandonRequest ::= [APPLICATION 16] MessageID 7 | 8 | func readAbandonRequest(bytes *Bytes) (ret AbandonRequest, err error) { 9 | var mes MessageID 10 | mes, err = readTaggedMessageID(bytes, classApplication, TagAbandonRequest) 11 | if err != nil { 12 | err = LdapError{fmt.Sprintf("readAbandonRequest:\n%s", err.Error())} 13 | return 14 | } 15 | ret = AbandonRequest(mes) 16 | return 17 | } 18 | 19 | func (abandon AbandonRequest) size() int { 20 | return MessageID(abandon).sizeTagged(TagAbandonRequest) 21 | } 22 | 23 | func (abandon AbandonRequest) write(bytes *Bytes) int { 24 | return MessageID(abandon).writeTagged(bytes, classApplication, TagAbandonRequest) 25 | } 26 | -------------------------------------------------------------------------------- /message/add_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AddRequest ::= [APPLICATION 8] SEQUENCE { 7 | // entry LDAPDN, 8 | // attributes AttributeList } 9 | 10 | func (add *AddRequest) Entry() LDAPDN { 11 | return add.entry 12 | } 13 | 14 | func (add *AddRequest) Attributes() AttributeList { 15 | return add.attributes 16 | } 17 | 18 | func readAddRequest(bytes *Bytes) (ret AddRequest, err error) { 19 | err = bytes.ReadSubBytes(classApplication, TagAddRequest, ret.readComponents) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readAddRequest:\n%s", err.Error())} 22 | return 23 | } 24 | return 25 | } 26 | 27 | func (add *AddRequest) readComponents(bytes *Bytes) (err error) { 28 | add.entry, err = readLDAPDN(bytes) 29 | if err != nil { 30 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 31 | return 32 | } 33 | add.attributes, err = readAttributeList(bytes) 34 | if err != nil { 35 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 36 | return 37 | } 38 | return 39 | } 40 | 41 | func (add AddRequest) size() (size int) { 42 | size += add.entry.size() 43 | size += add.attributes.size() 44 | size += sizeTagAndLength(TagAddRequest, size) 45 | return 46 | } 47 | 48 | func (add AddRequest) write(bytes *Bytes) (size int) { 49 | size += add.attributes.write(bytes) 50 | size += add.entry.write(bytes) 51 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagAddRequest, size) 52 | return 53 | } 54 | -------------------------------------------------------------------------------- /message/add_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AddResponse ::= [APPLICATION 9] LDAPResult 7 | 8 | func (l *AddResponse) SetResultCode(code int) { 9 | l.resultCode = ENUMERATED(code) 10 | } 11 | 12 | func readAddResponse(bytes *Bytes) (ret AddResponse, err error) { 13 | var res LDAPResult 14 | res, err = readTaggedLDAPResult(bytes, classApplication, TagAddResponse) 15 | if err != nil { 16 | err = LdapError{fmt.Sprintf("readAddResponse:\n%s", err.Error())} 17 | return 18 | } 19 | ret = AddResponse(res) 20 | return 21 | } 22 | 23 | func (a AddResponse) size() int { 24 | return LDAPResult(a).sizeTagged(TagAddResponse) 25 | } 26 | func (a AddResponse) write(bytes *Bytes) int { 27 | return LDAPResult(a).writeTagged(bytes, classApplication, TagAddResponse) 28 | } 29 | -------------------------------------------------------------------------------- /message/asn1.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | // Below code is largely inspired from the standard golang library encoding/asn 4 | // If put BEGIN / END tags in the comments to give the original library name 5 | import ( 6 | // "errors" 7 | "fmt" 8 | "math/big" 9 | // "strconv" 10 | // "time" 11 | ) 12 | 13 | // 14 | // BEGIN: encoding/asn1/common.go 15 | // 16 | 17 | // Copyright 2009 The Go Authors. All rights reserved. 18 | // Use of this source code is governed by a BSD-style 19 | // license that can be found in the LICENSE file. 20 | const ( 21 | tagBoolean = 1 22 | tagInteger = 2 23 | // tagBitString = 3 24 | tagOctetString = 4 25 | // tagOID = 6 26 | tagEnum = 10 27 | // tagUTF8String = 12 28 | tagSequence = 16 29 | tagSet = 17 30 | // tagPrintableString = 19 31 | // tagT61String = 20 32 | // tagIA5String = 22 33 | // tagUTCTime = 23 34 | // tagGeneralizedTime = 24 35 | tagGeneralString = 27 36 | ) 37 | 38 | var tagNames = map[int]string{ 39 | tagBoolean: "BOOLEAN", 40 | tagInteger: "INTEGER", 41 | tagOctetString: "OCTET STRING", 42 | tagEnum: "ENUM", 43 | tagSequence: "SEQUENCE", 44 | tagSet: "SET", 45 | } 46 | 47 | const ( 48 | classUniversal = 0 49 | classApplication = 1 50 | classContextSpecific = 2 51 | // classPrivate = 3 52 | ) 53 | 54 | var classNames = map[int]string{ 55 | classUniversal: "UNIVERSAL", 56 | classApplication: "APPLICATION", 57 | classContextSpecific: "CONTEXT SPECIFIC", 58 | } 59 | 60 | const ( 61 | isCompound = true 62 | isNotCompound = false 63 | ) 64 | 65 | var compoundNames = map[bool]string{ 66 | isCompound: "COMPOUND", 67 | isNotCompound: "NOT COMPOUND", 68 | } 69 | 70 | type TagAndLength struct { 71 | Class, Tag, Length int 72 | IsCompound bool 73 | } 74 | 75 | // 76 | // END: encoding/asn1/common.go 77 | // 78 | 79 | func (t *TagAndLength) Expect(class int, tag int, isCompound bool) (err error) { 80 | err = t.ExpectClass(class) 81 | if err != nil { 82 | return LdapError{fmt.Sprintf("Expect: %s.", err)} 83 | } 84 | err = t.ExpectTag(tag) 85 | if err != nil { 86 | return LdapError{fmt.Sprintf("Expect: %s.", err)} 87 | } 88 | err = t.ExpectCompound(isCompound) 89 | if err != nil { 90 | return LdapError{fmt.Sprintf("Expect: %s.", err)} 91 | } 92 | return 93 | } 94 | func (t *TagAndLength) ExpectClass(class int) (err error) { 95 | if class != t.Class { 96 | err = SyntaxError{fmt.Sprintf("ExpectClass: wrong tag class: got %d (%s), expected %d (%s)", t.Class, classNames[t.Class], class, classNames[class])} 97 | } 98 | return 99 | } 100 | func (t *TagAndLength) ExpectTag(tag int) (err error) { 101 | if tag != t.Tag { 102 | err = SyntaxError{fmt.Sprintf("ExpectTag: wrong tag value: got %d (%s), expected %d (%s)", t.Tag, tagNames[t.Tag], tag, tagNames[tag])} 103 | } 104 | return 105 | } 106 | func (t *TagAndLength) ExpectCompound(isCompound bool) (err error) { 107 | if isCompound != t.IsCompound { 108 | err = SyntaxError{fmt.Sprintf("ExpectCompound: wrong tag compound: got %t (%s), expected %t (%s)", t.IsCompound, compoundNames[t.IsCompound], isCompound, compoundNames[isCompound])} 109 | } 110 | return 111 | } 112 | 113 | func ParseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) { 114 | ret, offset, err = parseTagAndLength(bytes, initOffset) 115 | return 116 | } 117 | 118 | // 119 | // BEGIN encoding/asn1/asn1.go 120 | // 121 | 122 | // Copyright 2009 The Go Authors. All rights reserved. 123 | // Use of this source code is governed by a BSD-style 124 | // license that can be found in the LICENSE file. 125 | 126 | // Package asn1 implements parsing of DER-encoded ASN.1 data structures, 127 | // as defined in ITU-T Rec X.690. 128 | // 129 | // See also ``A Layman's Guide to a Subset of ASN.1, BER, and DER,'' 130 | // http://luca.ntop.org/Teaching/Appunti/asn1.html. 131 | // package asn1 132 | 133 | // ASN.1 is a syntax for specifying abstract objects and BER, DER, PER, XER etc 134 | // are different encoding formats for those objects. Here, we'll be dealing 135 | // with DER, the Distinguished Encoding Rules. DER is used in X.509 because 136 | // it's fast to parse and, unlike BER, has a unique encoding for every object. 137 | // When calculating hashes over objects, it's important that the resulting 138 | // bytes be the same at both ends and DER removes this margin of error. 139 | // 140 | // ASN.1 is very complex and this package doesn't attempt to implement 141 | // everything by any means. 142 | 143 | //import ( 144 | // "fmt" 145 | // "math/big" 146 | // "reflect" 147 | // "strconv" 148 | // "time" 149 | //) 150 | 151 | // A StructuralError suggests that the ASN.1 data is valid, but the Go type 152 | // which is receiving it doesn't match. 153 | type StructuralError struct { 154 | Msg string 155 | } 156 | 157 | func (e StructuralError) Error() string { return "asn1: structure error: " + e.Msg } 158 | 159 | // A SyntaxError suggests that the ASN.1 data is invalid. 160 | type SyntaxError struct { 161 | Msg string 162 | } 163 | 164 | func (e SyntaxError) Error() string { return "asn1: syntax error: " + e.Msg } 165 | 166 | // We start by dealing with each of the primitive types in turn. 167 | 168 | // BOOLEAN 169 | 170 | func parseBool(bytes []byte) (ret bool, err error) { 171 | if len(bytes) > 1 { 172 | err = SyntaxError{"invalid boolean: should be encoded on one byte only"} 173 | return 174 | } else if len(bytes) == 0 { 175 | err = SyntaxError{"invalid boolean: no data to read"} 176 | } 177 | 178 | // DER demands that "If the encoding represents the boolean value TRUE, 179 | // its single contents octet shall have all eight bits set to one." 180 | // Thus only 0 and 255 are valid encoded values. 181 | switch bytes[0] { 182 | case 0: 183 | ret = false 184 | case 0xff: 185 | ret = true 186 | default: 187 | err = SyntaxError{"invalid boolean: should be 0x00 of 0xFF"} 188 | } 189 | 190 | return 191 | } 192 | 193 | func sizeBool(b bool) int { 194 | return 1 195 | } 196 | 197 | func writeBool(bytes *Bytes, b bool) int { 198 | if b == false { 199 | return bytes.writeBytes([]byte{0x00}) 200 | } else { 201 | return bytes.writeBytes([]byte{0xff}) 202 | } 203 | } 204 | 205 | // INTEGER 206 | 207 | // parseInt64 treats the given bytes as a big-endian, signed integer and 208 | // returns the result. 209 | func parseInt64(bytes []byte) (ret int64, err error) { 210 | if len(bytes) > 8 { 211 | // We'll overflow an int64 in this case. 212 | err = StructuralError{"integer too large"} 213 | return 214 | } 215 | for bytesRead := 0; bytesRead < len(bytes); bytesRead++ { 216 | ret <<= 8 217 | ret |= int64(bytes[bytesRead]) 218 | } 219 | 220 | // Shift up and down in order to sign extend the result. 221 | ret <<= 64 - uint8(len(bytes))*8 222 | ret >>= 64 - uint8(len(bytes))*8 223 | return 224 | } 225 | 226 | func sizeInt64(i int64) (size int) { 227 | for ; i != 0 || size == 0; i >>= 8 { 228 | size++ 229 | } 230 | return 231 | } 232 | 233 | func writeInt64(bytes *Bytes, i int64) (size int) { 234 | for ; i != 0 || size == 0; i >>= 8 { // Write at least one byte even if the value is 0 235 | bytes.writeBytes([]byte{byte(i)}) 236 | size++ 237 | } 238 | return 239 | } 240 | 241 | // parseInt treats the given bytes as a big-endian, signed integer and returns 242 | // the result. 243 | func parseInt32(bytes []byte) (int32, error) { 244 | ret64, err := parseInt64(bytes) 245 | if err != nil { 246 | return 0, err 247 | } 248 | if ret64 != int64(int32(ret64)) { 249 | return 0, StructuralError{"integer too large"} 250 | } 251 | return int32(ret64), nil 252 | } 253 | 254 | func sizeInt32(i int32) int { 255 | return sizeInt64(int64(i)) 256 | } 257 | 258 | func writeInt32(bytes *Bytes, i int32) int { 259 | return writeInt64(bytes, int64(i)) 260 | } 261 | 262 | var bigOne = big.NewInt(1) 263 | 264 | // // parseBigInt treats the given bytes as a big-endian, signed integer and returns 265 | // // the result. 266 | // func parseBigInt(bytes []byte) *big.Int { 267 | // ret := new(big.Int) 268 | // if len(bytes) > 0 && bytes[0]&0x80 == 0x80 { 269 | // // This is a negative number. 270 | // notBytes := make([]byte, len(bytes)) 271 | // for i := range notBytes { 272 | // notBytes[i] = ^bytes[i] 273 | // } 274 | // ret.SetBytes(notBytes) 275 | // ret.Add(ret, bigOne) 276 | // ret.Neg(ret) 277 | // return ret 278 | // } 279 | // ret.SetBytes(bytes) 280 | // return ret 281 | // } 282 | 283 | // // BIT STRING 284 | 285 | // // BitString is the structure to use when you want an ASN.1 BIT STRING type. A 286 | // // bit string is padded up to the nearest byte in memory and the number of 287 | // // valid bits is recorded. Padding bits will be zero. 288 | // type BitString struct { 289 | // Bytes []byte // bits packed into bytes. 290 | // BitLength int // length in bits. 291 | // } 292 | 293 | // // At returns the bit at the given index. If the index is out of range it 294 | // // returns false. 295 | // func (b BitString) At(i int) int { 296 | // if i < 0 || i >= b.BitLength { 297 | // return 0 298 | // } 299 | // x := i / 8 300 | // y := 7 - uint(i%8) 301 | // return int(b.Bytes[x]>>y) & 1 302 | // } 303 | 304 | // // RightAlign returns a slice where the padding bits are at the beginning. The 305 | // // slice may share memory with the BitString. 306 | // func (b BitString) RightAlign() []byte { 307 | // shift := uint(8 - (b.BitLength % 8)) 308 | // if shift == 8 || len(b.Bytes) == 0 { 309 | // return b.Bytes 310 | // } 311 | 312 | // a := make([]byte, len(b.Bytes)) 313 | // a[0] = b.Bytes[0] >> shift 314 | // for i := 1; i < len(b.Bytes); i++ { 315 | // a[i] = b.Bytes[i-1] << (8 - shift) 316 | // a[i] |= b.Bytes[i] >> shift 317 | // } 318 | 319 | // return a 320 | // } 321 | 322 | // // parseBitString parses an ASN.1 bit string from the given byte slice and returns it. 323 | // func parseBitString(bytes []byte) (ret BitString, err error) { 324 | // if len(bytes) == 0 { 325 | // err = SyntaxError{"zero length BIT STRING"} 326 | // return 327 | // } 328 | // paddingBits := int(bytes[0]) 329 | // if paddingBits > 7 || 330 | // len(bytes) == 1 && paddingBits > 0 || 331 | // bytes[len(bytes)-1]&((1< 0 { 364 | // s += "." 365 | // } 366 | // s += strconv.Itoa(v) 367 | // } 368 | 369 | // return s 370 | // } 371 | 372 | // // parseObjectIdentifier parses an OBJECT IDENTIFIER from the given bytes and 373 | // // returns it. An object identifier is a sequence of variable length integers 374 | // // that are assigned in a hierarchy. 375 | // func parseObjectIdentifier(bytes []byte) (s []int, err error) { 376 | // if len(bytes) == 0 { 377 | // err = SyntaxError{"zero length OBJECT IDENTIFIER"} 378 | // return 379 | // } 380 | 381 | // // In the worst case, we get two elements from the first byte (which is 382 | // // encoded differently) and then every varint is a single byte long. 383 | // s = make([]int, len(bytes)+1) 384 | 385 | // // The first varint is 40*value1 + value2: 386 | // // According to this packing, value1 can take the values 0, 1 and 2 only. 387 | // // When value1 = 0 or value1 = 1, then value2 is <= 39. When value1 = 2, 388 | // // then there are no restrictions on value2. 389 | // v, offset, err := parseBase128Int(bytes, 0) 390 | // if err != nil { 391 | // return 392 | // } 393 | // if v < 80 { 394 | // s[0] = v / 40 395 | // s[1] = v % 40 396 | // } else { 397 | // s[0] = 2 398 | // s[1] = v - 80 399 | // } 400 | 401 | // i := 2 402 | // for ; offset < len(bytes); i++ { 403 | // v, offset, err = parseBase128Int(bytes, offset) 404 | // if err != nil { 405 | // return 406 | // } 407 | // s[i] = v 408 | // } 409 | // s = s[0:i] 410 | // return 411 | // } 412 | 413 | // ENUMERATED 414 | 415 | // An Enumerated is represented as a plain int. 416 | type Enumerated int 417 | 418 | // FLAG 419 | 420 | // A Flag accepts any data and is set to true if present. 421 | type Flag bool 422 | 423 | // parseBase128Int parses a base-128 encoded int from the given offset in the 424 | // given byte slice. It returns the value and the new offset. 425 | func parseBase128Int(bytes []byte, initOffset int) (ret, offset int, err error) { 426 | offset = initOffset 427 | for shifted := 0; offset < len(bytes); shifted++ { 428 | if shifted > 4 { 429 | err = StructuralError{"base 128 integer too large"} 430 | return 431 | } 432 | ret <<= 7 433 | b := bytes[offset] 434 | ret |= int(b & 0x7f) 435 | offset++ 436 | if b&0x80 == 0 { 437 | return 438 | } 439 | } 440 | err = SyntaxError{"truncated base 128 integer"} 441 | return 442 | } 443 | 444 | func sizeBase128Int(value int) (size int) { 445 | for i := value; i > 0; i >>= 7 { 446 | size++ 447 | } 448 | return 449 | } 450 | 451 | // Write start as the end of the slice and goes back 452 | // We assume we have enough size 453 | func writeBase128Int(bytes *Bytes, value int) (size int) { 454 | for ; value > 0 || size == 0; value >>= 7 { // Write at least one byte even if the value is 0 455 | // Get the 7 lowest bits 456 | b := byte(value) & 0x7f 457 | if value < 128 { 458 | b |= 0x80 459 | } 460 | bytes.writeBytes([]byte{b}) 461 | size++ 462 | } 463 | return 464 | } 465 | 466 | // // UTCTime 467 | 468 | // func parseUTCTime(bytes []byte) (ret time.Time, err error) { 469 | // s := string(bytes) 470 | // ret, err = time.Parse("0601021504Z0700", s) 471 | // if err != nil { 472 | // ret, err = time.Parse("060102150405Z0700", s) 473 | // } 474 | // if err == nil && ret.Year() >= 2050 { 475 | // // UTCTime only encodes times prior to 2050. See https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 476 | // ret = ret.AddDate(-100, 0, 0) 477 | // } 478 | 479 | // return 480 | // } 481 | 482 | // // parseGeneralizedTime parses the GeneralizedTime from the given byte slice 483 | // // and returns the resulting time. 484 | // func parseGeneralizedTime(bytes []byte) (ret time.Time, err error) { 485 | // return time.Parse("20060102150405Z0700", string(bytes)) 486 | // } 487 | 488 | // // PrintableString 489 | 490 | // // parsePrintableString parses a ASN.1 PrintableString from the given byte 491 | // // array and returns it. 492 | // func parsePrintableString(bytes []byte) (ret string, err error) { 493 | // for _, b := range bytes { 494 | // if !isPrintable(b) { 495 | // err = SyntaxError{"PrintableString contains invalid character"} 496 | // return 497 | // } 498 | // } 499 | // ret = string(bytes) 500 | // return 501 | // } 502 | 503 | // // isPrintable returns true iff the given b is in the ASN.1 PrintableString set. 504 | // func isPrintable(b byte) bool { 505 | // return 'a' <= b && b <= 'z' || 506 | // 'A' <= b && b <= 'Z' || 507 | // '0' <= b && b <= '9' || 508 | // '\'' <= b && b <= ')' || 509 | // '+' <= b && b <= '/' || 510 | // b == ' ' || 511 | // b == ':' || 512 | // b == '=' || 513 | // b == '?' || 514 | // // This is technically not allowed in a PrintableString. 515 | // // However, x509 certificates with wildcard strings don't 516 | // // always use the correct string type so we permit it. 517 | // b == '*' 518 | // } 519 | 520 | // // IA5String 521 | 522 | // // parseIA5String parses a ASN.1 IA5String (ASCII string) from the given 523 | // // byte slice and returns it. 524 | // func parseIA5String(bytes []byte) (ret string, err error) { 525 | // for _, b := range bytes { 526 | // if b >= 0x80 { 527 | // err = SyntaxError{"IA5String contains invalid character"} 528 | // return 529 | // } 530 | // } 531 | // ret = string(bytes) 532 | // return 533 | // } 534 | 535 | // // T61String 536 | 537 | // // parseT61String parses a ASN.1 T61String (8-bit clean string) from the given 538 | // // byte slice and returns it. 539 | // func parseT61String(bytes []byte) (ret string, err error) { 540 | // return string(bytes), nil 541 | // } 542 | 543 | // UTF8String 544 | 545 | // parseUTF8String parses a ASN.1 UTF8String (raw UTF-8) from the given byte 546 | // array and returns it. 547 | // func parseUTF8String(bytes []byte) (ret string, err error) { 548 | // return string(bytes), nil 549 | // } 550 | // func sizeUTF8String(s string) int { 551 | // return len(s) 552 | // } 553 | // func writeUTF8String(bytes *Bytes, s string) int { 554 | // return bytes.writeString(s) 555 | // } 556 | 557 | // Octet string 558 | func parseOctetString(bytes []byte) (ret []byte, err error) { 559 | return bytes, nil 560 | } 561 | func sizeOctetString(s []byte) int { 562 | return len(s) 563 | } 564 | func writeOctetString(bytes *Bytes, s []byte) int { 565 | return bytes.writeBytes(s) 566 | } 567 | 568 | // A RawValue represents an undecoded ASN.1 object. 569 | type RawValue struct { 570 | Class, Tag int 571 | IsCompound bool 572 | Bytes []byte 573 | FullBytes []byte // includes the tag and length 574 | } 575 | 576 | // RawContent is used to signal that the undecoded, DER data needs to be 577 | // preserved for a struct. To use it, the first field of the struct must have 578 | // this type. It's an error for any of the other fields to have this type. 579 | type RawContent []byte 580 | 581 | // Tagging 582 | 583 | // parseTagAndLength parses an ASN.1 tag and length pair from the given offset 584 | // into a byte slice. It returns the parsed data and the new offset. SET and 585 | // SET OF (tag 17) are mapped to SEQUENCE and SEQUENCE OF (tag 16) since we 586 | // don't distinguish between ordered and unordered objects in this code. 587 | func parseTagAndLength(bytes []byte, initOffset int) (ret TagAndLength, offset int, err error) { 588 | offset = initOffset 589 | b := bytes[offset] 590 | offset++ 591 | ret.Class = int(b >> 6) 592 | ret.IsCompound = b&0x20 == 0x20 593 | ret.Tag = int(b & 0x1f) 594 | 595 | // If the bottom five bits are set, then the tag number is actually base 128 596 | // encoded afterwards 597 | if ret.Tag == 0x1f { 598 | ret.Tag, offset, err = parseBase128Int(bytes, offset) 599 | if err != nil { 600 | return 601 | } 602 | } 603 | if offset >= len(bytes) { 604 | err = SyntaxError{"truncated tag or length"} 605 | return 606 | } 607 | b = bytes[offset] 608 | offset++ 609 | if b&0x80 == 0 { 610 | // The length is encoded in the bottom 7 bits. 611 | ret.Length = int(b & 0x7f) 612 | } else { 613 | // Bottom 7 bits give the number of length bytes to follow. 614 | numBytes := int(b & 0x7f) 615 | if numBytes == 0 { 616 | err = SyntaxError{"indefinite length found (not DER)"} 617 | return 618 | } 619 | ret.Length = 0 620 | for i := 0; i < numBytes; i++ { 621 | if offset >= len(bytes) { 622 | err = SyntaxError{"truncated tag or length"} 623 | return 624 | } 625 | b = bytes[offset] 626 | offset++ 627 | if ret.Length >= 1<<23 { 628 | // We can't shift ret.length up without 629 | // overflowing. 630 | err = StructuralError{"length too large"} 631 | return 632 | } 633 | ret.Length <<= 8 634 | ret.Length |= int(b) 635 | if ret.Length == 0 { 636 | // DER requires that lengths be minimal. 637 | err = StructuralError{"superfluous leading zeros in length"} 638 | return 639 | } 640 | } 641 | } 642 | 643 | return 644 | } 645 | 646 | // func writeTagAndLength(out *forkableWriter, t tagAndLength) (err error) { 647 | // b := uint8(t.class) << 6 648 | // if t.isCompound { 649 | // b |= 0x20 650 | // } 651 | // if t.tag >= 31 { 652 | // b |= 0x1f 653 | // err = out.WriteByte(b) 654 | // if err != nil { 655 | // return 656 | // } 657 | // err = marshalBase128Int(out, int64(t.tag)) 658 | // if err != nil { 659 | // return 660 | // } 661 | // } else { 662 | // b |= uint8(t.tag) 663 | // err = out.WriteByte(b) 664 | // if err != nil { 665 | // return 666 | // } 667 | // } 668 | 669 | // if t.length >= 128 { 670 | // l := lengthLength(t.length) 671 | // err = out.WriteByte(0x80 | byte(l)) 672 | // if err != nil { 673 | // return 674 | // } 675 | // err = marshalLength(out, t.length) 676 | // if err != nil { 677 | // return 678 | // } 679 | // } else { 680 | // err = out.WriteByte(byte(t.length)) 681 | // if err != nil { 682 | // return 683 | // } 684 | // } 685 | 686 | // return nil 687 | // } 688 | 689 | func sizeTagAndLength(tag int, length int) (size int) { 690 | // Compute the size of the tag 691 | size = 1 692 | if tag >= 31 { 693 | // Long-form identifier if the tag is greater than 30 694 | // http://en.wikipedia.org/wiki/X.690#Identifier_tags_greater_than_30 695 | size += sizeBase128Int(tag) 696 | } 697 | // Compute the size of the length using the definite form 698 | // http://en.wikipedia.org/wiki/X.690#The_definite_form 699 | size += 1 700 | if length >= 128 { 701 | size += 1 702 | for length > 255 { 703 | size++ 704 | length >>= 8 705 | } 706 | } 707 | return 708 | } 709 | 710 | func writeTagAndLength(bytes *Bytes, t TagAndLength) (size int) { 711 | // We are writing backward, so write the length bytes first 712 | if t.Length < 0 { 713 | panic("Can't have a negative length") 714 | 715 | } else if t.Length >= 128 { 716 | lengthBytes := writeInt64(bytes, int64(t.Length)) 717 | bytes.writeBytes([]byte{byte(0x80 | byte(lengthBytes))}) 718 | size += lengthBytes + 1 719 | 720 | } else if t.Length < 128 { 721 | size += bytes.writeBytes([]byte{byte(t.Length)}) 722 | } 723 | // Then write the tag 724 | b := uint8(t.Class) << 6 725 | if t.IsCompound { 726 | b |= 0x20 727 | } 728 | if t.Tag >= 31 { 729 | b |= 0x1f 730 | size += writeBase128Int(bytes, t.Tag) 731 | } else { 732 | b |= uint8(t.Tag) 733 | } 734 | size += bytes.writeBytes([]byte{byte(b)}) 735 | return 736 | } 737 | 738 | // 739 | // END encoding/asn1/asn1.go 740 | // 741 | -------------------------------------------------------------------------------- /message/assertion_value.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AssertionValue ::= OCTET STRING 7 | 8 | func readAssertionValue(bytes *Bytes) (assertionvalue AssertionValue, err error) { 9 | var octetstring OCTETSTRING 10 | octetstring, err = readOCTETSTRING(bytes) 11 | if err != nil { 12 | err = LdapError{fmt.Sprintf("readAssertionValue:\n%s", err.Error())} 13 | return 14 | } 15 | assertionvalue = AssertionValue(octetstring) 16 | return 17 | } 18 | 19 | func readTaggedAssertionValue(bytes *Bytes, class int, tag int) (assertionvalue AssertionValue, err error) { 20 | var octetstring OCTETSTRING 21 | octetstring, err = readTaggedOCTETSTRING(bytes, class, tag) 22 | if err != nil { 23 | err = LdapError{fmt.Sprintf("readTaggedAssertionValue:\n%s", err.Error())} 24 | return 25 | } 26 | assertionvalue = AssertionValue(octetstring) 27 | return 28 | } 29 | 30 | func (assertion AssertionValue) size() int { 31 | return OCTETSTRING(assertion).size() 32 | } 33 | 34 | func (assertion AssertionValue) sizeTagged(tag int) int { 35 | return OCTETSTRING(assertion).sizeTagged(tag) 36 | } 37 | 38 | func (assertion AssertionValue) write(bytes *Bytes) int { 39 | return OCTETSTRING(assertion).write(bytes) 40 | } 41 | 42 | func (assertion AssertionValue) writeTagged(bytes *Bytes, class int, tag int) int { 43 | return OCTETSTRING(assertion).writeTagged(bytes, class, tag) 44 | } 45 | -------------------------------------------------------------------------------- /message/attribute.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // Attribute ::= PartialAttribute(WITH COMPONENTS { 7 | // ..., 8 | // vals (SIZE(1..MAX))}) 9 | 10 | func (attribute *Attribute) Type_() AttributeDescription { 11 | return attribute.type_ 12 | } 13 | 14 | func (attribute *Attribute) Vals() []AttributeValue { 15 | return attribute.vals 16 | } 17 | 18 | func readAttribute(bytes *Bytes) (ret Attribute, err error) { 19 | var par PartialAttribute 20 | par, err = readPartialAttribute(bytes) 21 | if err != nil { 22 | err = LdapError{fmt.Sprintf("readAttribute:\n%s", err.Error())} 23 | return 24 | } 25 | if len(par.vals) == 0 { 26 | err = LdapError{"readAttribute: expecting at least one value"} 27 | return 28 | } 29 | ret = Attribute(par) 30 | return 31 | 32 | } 33 | 34 | func (attribute Attribute) size() (size int) { 35 | return PartialAttribute(attribute).size() 36 | } 37 | 38 | func (attribute Attribute) write(bytes *Bytes) (size int) { 39 | return PartialAttribute(attribute).write(bytes) 40 | } 41 | -------------------------------------------------------------------------------- /message/attribute_description.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AttributeDescription ::= LDAPString 7 | // -- Constrained to 8 | // -- [RFC4512] 9 | 10 | func (description AttributeDescription) Pointer() *AttributeDescription { return &description } 11 | 12 | func readAttributeDescription(bytes *Bytes) (ret AttributeDescription, err error) { 13 | var ldapstring LDAPString 14 | ldapstring, err = readLDAPString(bytes) 15 | if err != nil { 16 | err = LdapError{fmt.Sprintf("readAttributeDescription:\n%s", err.Error())} 17 | return 18 | } 19 | // @TODO: check RFC4512 20 | ret = AttributeDescription(ldapstring) 21 | return 22 | } 23 | 24 | func readTaggedAttributeDescription(bytes *Bytes, class int, tag int) (ret AttributeDescription, err error) { 25 | var ldapstring LDAPString 26 | ldapstring, err = readTaggedLDAPString(bytes, class, tag) 27 | // @TODO: check RFC4512 28 | if err != nil { 29 | err = LdapError{fmt.Sprintf("readTaggedAttributeDescription:\n%s", err.Error())} 30 | return 31 | } 32 | ret = AttributeDescription(ldapstring) 33 | return 34 | } 35 | 36 | func (description AttributeDescription) size() int { 37 | return LDAPString(description).size() 38 | } 39 | 40 | func (description AttributeDescription) sizeTagged(tag int) int { 41 | return LDAPString(description).sizeTagged(tag) 42 | } 43 | 44 | func (description AttributeDescription) write(bytes *Bytes) int { 45 | return LDAPString(description).write(bytes) 46 | } 47 | 48 | func (description AttributeDescription) writeTagged(bytes *Bytes, class int, tag int) int { 49 | return LDAPString(description).writeTagged(bytes, class, tag) 50 | } 51 | -------------------------------------------------------------------------------- /message/attribute_list.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AttributeList ::= SEQUENCE OF attribute Attribute 7 | 8 | func readAttributeList(bytes *Bytes) (ret AttributeList, err error) { 9 | err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readAttributeList:\n%s", err.Error())} 12 | return 13 | } 14 | return 15 | } 16 | func (list *AttributeList) readComponents(bytes *Bytes) (err error) { 17 | for bytes.HasMoreData() { 18 | var attr Attribute 19 | attr, err = readAttribute(bytes) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 22 | return 23 | } 24 | *list = append(*list, attr) 25 | } 26 | return 27 | } 28 | 29 | func (list AttributeList) size() (size int) { 30 | for _, att := range list { 31 | size += att.size() 32 | } 33 | size += sizeTagAndLength(tagSequence, size) 34 | return 35 | } 36 | 37 | func (list AttributeList) write(bytes *Bytes) (size int) { 38 | for i := len(list) - 1; i >= 0; i-- { 39 | size += list[i].write(bytes) 40 | } 41 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 42 | return 43 | } 44 | -------------------------------------------------------------------------------- /message/attribute_selection.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AttributeSelection ::= SEQUENCE OF selector LDAPString 7 | // -- The LDAPString is constrained to 8 | // -- in Section 4.5.1.8 9 | 10 | func readAttributeSelection(bytes *Bytes) (attributeSelection AttributeSelection, err error) { 11 | err = bytes.ReadSubBytes(classUniversal, tagSequence, attributeSelection.readComponents) 12 | if err != nil { 13 | err = LdapError{fmt.Sprintf("readAttributeSelection:\n%s", err.Error())} 14 | return 15 | } 16 | return 17 | } 18 | func (selection *AttributeSelection) readComponents(bytes *Bytes) (err error) { 19 | for bytes.HasMoreData() { 20 | var ldapstring LDAPString 21 | ldapstring, err = readLDAPString(bytes) 22 | // @TOTO: check in Section 4.5.1.8 23 | if err != nil { 24 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 25 | return 26 | } 27 | *selection = append(*selection, ldapstring) 28 | } 29 | return 30 | } 31 | 32 | func (selection AttributeSelection) write(bytes *Bytes) (size int) { 33 | for i := len(selection) - 1; i >= 0; i-- { 34 | size += selection[i].write(bytes) 35 | } 36 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 37 | return 38 | } 39 | 40 | func (selection AttributeSelection) size() (size int) { 41 | for _, selector := range selection { 42 | size += selector.size() 43 | } 44 | size += sizeTagAndLength(tagSequence, size) 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /message/attribute_value.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AttributeValue ::= OCTET STRING 7 | 8 | func readAttributeValue(bytes *Bytes) (ret AttributeValue, err error) { 9 | octetstring, err := readOCTETSTRING(bytes) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readAttributeValue:\n%s", err.Error())} 12 | return 13 | } 14 | ret = AttributeValue(octetstring) 15 | return 16 | } 17 | 18 | func (value AttributeValue) write(bytes *Bytes) int { 19 | return OCTETSTRING(value).write(bytes) 20 | } 21 | 22 | func (value AttributeValue) size() int { 23 | return OCTETSTRING(value).size() 24 | } 25 | -------------------------------------------------------------------------------- /message/attribute_value_assertion.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AttributeValueAssertion ::= SEQUENCE { 7 | // attributeDesc AttributeDescription, 8 | // assertionValue AssertionValue } 9 | 10 | func (assertion *AttributeValueAssertion) AttributeDesc() AttributeDescription { 11 | return assertion.attributeDesc 12 | } 13 | 14 | func (assertion *AttributeValueAssertion) AssertionValue() AssertionValue { 15 | return assertion.assertionValue 16 | } 17 | 18 | func readAttributeValueAssertion(bytes *Bytes) (ret AttributeValueAssertion, err error) { 19 | err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readAttributeValueAssertion:\n%s", err.Error())} 22 | return 23 | } 24 | return 25 | 26 | } 27 | 28 | func readTaggedAttributeValueAssertion(bytes *Bytes, class int, tag int) (ret AttributeValueAssertion, err error) { 29 | err = bytes.ReadSubBytes(class, tag, ret.readComponents) 30 | if err != nil { 31 | err = LdapError{fmt.Sprintf("readTaggedAttributeValueAssertion:\n%s", err.Error())} 32 | return 33 | } 34 | return 35 | } 36 | 37 | func (assertion *AttributeValueAssertion) readComponents(bytes *Bytes) (err error) { 38 | assertion.attributeDesc, err = readAttributeDescription(bytes) 39 | if err != nil { 40 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 41 | return 42 | } 43 | assertion.assertionValue, err = readAssertionValue(bytes) 44 | if err != nil { 45 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 46 | return 47 | } 48 | return 49 | } 50 | 51 | func (assertion AttributeValueAssertion) write(bytes *Bytes) (size int) { 52 | size += assertion.assertionValue.write(bytes) 53 | size += assertion.attributeDesc.write(bytes) 54 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 55 | return 56 | } 57 | 58 | func (assertion AttributeValueAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) { 59 | size += assertion.assertionValue.write(bytes) 60 | size += assertion.attributeDesc.write(bytes) 61 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 62 | return 63 | } 64 | 65 | func (assertion AttributeValueAssertion) size() (size int) { 66 | size += assertion.attributeDesc.size() 67 | size += assertion.assertionValue.size() 68 | size += sizeTagAndLength(tagSequence, size) 69 | return 70 | } 71 | 72 | func (assertion AttributeValueAssertion) sizeTagged(tag int) (size int) { 73 | size += assertion.attributeDesc.size() 74 | size += assertion.assertionValue.size() 75 | size += sizeTagAndLength(tag, size) 76 | return 77 | } 78 | -------------------------------------------------------------------------------- /message/authentication_choice.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // AuthenticationChoice ::= CHOICE { 7 | // simple [0] OCTET STRING, 8 | // -- 1 and 2 reserved 9 | // sasl [3] SaslCredentials, 10 | // ... } 11 | 12 | func readAuthenticationChoice(bytes *Bytes) (ret AuthenticationChoice, err error) { 13 | tagAndLength, err := bytes.PreviewTagAndLength() 14 | if err != nil { 15 | err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())} 16 | return 17 | } 18 | err = tagAndLength.ExpectClass(classContextSpecific) 19 | if err != nil { 20 | err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())} 21 | return 22 | } 23 | switch tagAndLength.Tag { 24 | case TagAuthenticationChoiceSimple: 25 | ret, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagAuthenticationChoiceSimple) 26 | case TagAuthenticationChoiceSaslCredentials: 27 | ret, err = readSaslCredentials(bytes) 28 | default: 29 | err = LdapError{fmt.Sprintf("readAuthenticationChoice: invalid tag value %d for AuthenticationChoice", tagAndLength.Tag)} 30 | return 31 | } 32 | if err != nil { 33 | err = LdapError{fmt.Sprintf("readAuthenticationChoice:\n%s", err.Error())} 34 | return 35 | } 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /message/bind_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // BindRequest ::= [APPLICATION 0] SEQUENCE { 6 | // version INTEGER (1 .. 127), 7 | // name LDAPDN, 8 | // authentication AuthenticationChoice } 9 | 10 | func (request *BindRequest) Name() LDAPDN { 11 | return request.name 12 | } 13 | 14 | func (request *BindRequest) Authentication() AuthenticationChoice { 15 | return request.authentication 16 | } 17 | 18 | func (request *BindRequest) AuthenticationSimple() OCTETSTRING { 19 | return request.Authentication().(OCTETSTRING) 20 | } 21 | 22 | func (request *BindRequest) AuthenticationChoice() string { 23 | switch request.Authentication().(type) { 24 | case OCTETSTRING: 25 | return "simple" 26 | case SaslCredentials: 27 | return "sasl" 28 | } 29 | return "" 30 | } 31 | 32 | func readBindRequest(bytes *Bytes) (bindrequest BindRequest, err error) { 33 | err = bytes.ReadSubBytes(classApplication, TagBindRequest, bindrequest.readComponents) 34 | if err != nil { 35 | err = LdapError{fmt.Sprintf("readBindRequest:\n%s", err.Error())} 36 | return 37 | } 38 | return 39 | } 40 | 41 | func (request *BindRequest) readComponents(bytes *Bytes) (err error) { 42 | request.version, err = readINTEGER(bytes) 43 | if err != nil { 44 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 45 | return 46 | } 47 | if !(request.version >= BindRequestVersionMin && request.version <= BindRequestVersionMax) { 48 | err = LdapError{fmt.Sprintf("readComponents: invalid version %d, must be between %d and %d", request.version, BindRequestVersionMin, BindRequestVersionMax)} 49 | return 50 | } 51 | request.name, err = readLDAPDN(bytes) 52 | if err != nil { 53 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 54 | return 55 | } 56 | request.authentication, err = readAuthenticationChoice(bytes) 57 | if err != nil { 58 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 59 | return 60 | } 61 | return 62 | } 63 | 64 | func (request BindRequest) write(bytes *Bytes) (size int) { 65 | switch request.authentication.(type) { 66 | case OCTETSTRING: 67 | size += request.authentication.(OCTETSTRING).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSimple) 68 | case SaslCredentials: 69 | size += request.authentication.(SaslCredentials).writeTagged(bytes, classContextSpecific, TagAuthenticationChoiceSaslCredentials) 70 | default: 71 | panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication)) 72 | } 73 | size += request.name.write(bytes) 74 | size += request.version.write(bytes) 75 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindRequest, size) 76 | return 77 | } 78 | 79 | func (request BindRequest) size() (size int) { 80 | size += request.version.size() 81 | size += request.name.size() 82 | switch request.authentication.(type) { 83 | case OCTETSTRING: 84 | size += request.authentication.(OCTETSTRING).sizeTagged(TagAuthenticationChoiceSimple) 85 | case SaslCredentials: 86 | size += request.authentication.(SaslCredentials).sizeTagged(TagAuthenticationChoiceSaslCredentials) 87 | default: 88 | panic(fmt.Sprintf("Unknown authentication choice: %#v", request.authentication)) 89 | } 90 | 91 | size += sizeTagAndLength(TagBindRequest, size) 92 | return 93 | } 94 | -------------------------------------------------------------------------------- /message/bind_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // BindResponse ::= [APPLICATION 1] SEQUENCE { 6 | // COMPONENTS OF LDAPResult, 7 | // serverSaslCreds [7] OCTET STRING OPTIONAL } 8 | 9 | func readBindResponse(bytes *Bytes) (bindresponse BindResponse, err error) { 10 | err = bytes.ReadSubBytes(classApplication, TagBindResponse, bindresponse.readComponents) 11 | if err != nil { 12 | err = LdapError{fmt.Sprintf("readBindResponse:\n%s", err.Error())} 13 | return 14 | } 15 | return 16 | } 17 | 18 | func (response *BindResponse) readComponents(bytes *Bytes) (err error) { 19 | response.LDAPResult.readComponents(bytes) 20 | if bytes.HasMoreData() { 21 | var tag TagAndLength 22 | tag, err = bytes.PreviewTagAndLength() 23 | if err != nil { 24 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 25 | return 26 | } 27 | if tag.Tag == TagBindResponseServerSaslCreds { 28 | var serverSaslCreds OCTETSTRING 29 | serverSaslCreds, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagBindResponseServerSaslCreds) 30 | if err != nil { 31 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 32 | return 33 | } 34 | response.serverSaslCreds = serverSaslCreds.Pointer() 35 | } 36 | } 37 | return 38 | } 39 | 40 | func (response BindResponse) write(bytes *Bytes) (size int) { 41 | if response.serverSaslCreds != nil { 42 | size += response.serverSaslCreds.writeTagged(bytes, classContextSpecific, TagBindResponseServerSaslCreds) 43 | } 44 | size += response.LDAPResult.writeComponents(bytes) 45 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagBindResponse, size) 46 | return 47 | } 48 | 49 | func (response BindResponse) size() (size int) { 50 | if response.serverSaslCreds != nil { 51 | size += response.serverSaslCreds.sizeTagged(TagBindResponseServerSaslCreds) 52 | } 53 | size += response.LDAPResult.sizeComponents() 54 | size += sizeTagAndLength(TagBindResponse, size) 55 | return 56 | } 57 | -------------------------------------------------------------------------------- /message/boolean.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readBOOLEAN(bytes *Bytes) (ret BOOLEAN, err error) { 6 | var value interface{} 7 | value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagBoolean, tagBoolean) 8 | if err != nil { 9 | err = LdapError{fmt.Sprintf("readBOOLEAN:\n%s", err.Error())} 10 | return 11 | } 12 | ret = BOOLEAN(value.(bool)) 13 | return 14 | } 15 | 16 | func (boolean BOOLEAN) write(bytes *Bytes) int { 17 | return bytes.WritePrimitiveSubBytes(classUniversal, tagBoolean, boolean) 18 | } 19 | 20 | func (boolean BOOLEAN) writeTagged(bytes *Bytes, class int, tag int) int { 21 | return bytes.WritePrimitiveSubBytes(class, tag, boolean) 22 | } 23 | 24 | func readTaggedBOOLEAN(bytes *Bytes, class int, tag int) (ret BOOLEAN, err error) { 25 | var value interface{} 26 | value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagBoolean) 27 | if err != nil { 28 | err = LdapError{fmt.Sprintf("readTaggedBOOLEAN:\n%s", err.Error())} 29 | return 30 | } 31 | ret = BOOLEAN(value.(bool)) 32 | return 33 | } 34 | 35 | func SizePrimitiveSubBytes(tag int, value interface{}) (size int) { 36 | switch value.(type) { 37 | case BOOLEAN: 38 | size = sizeBool(bool(value.(BOOLEAN))) 39 | case INTEGER: 40 | size = sizeInt32(int32(value.(INTEGER))) 41 | case ENUMERATED: 42 | size = sizeInt32(int32(value.(ENUMERATED))) 43 | case OCTETSTRING: 44 | size = sizeOctetString([]byte(string(value.(OCTETSTRING)))) 45 | default: 46 | panic(fmt.Sprintf("SizePrimitiveSubBytes: invalid value type %v", value)) 47 | } 48 | size += sizeTagAndLength(tag, size) 49 | return 50 | } 51 | 52 | func (boolean BOOLEAN) size() int { 53 | return SizePrimitiveSubBytes(tagBoolean, boolean) 54 | } 55 | 56 | func (boolean BOOLEAN) sizeTagged(tag int) int { 57 | return SizePrimitiveSubBytes(tag, boolean) 58 | } 59 | 60 | func (boolean BOOLEAN) Bool() bool { 61 | return bool(boolean) 62 | } 63 | -------------------------------------------------------------------------------- /message/bytes.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Bytes struct { 8 | offset int 9 | bytes []byte 10 | } 11 | 12 | func (bytes *Bytes) getBytes() []byte { 13 | return bytes.bytes 14 | } 15 | func NewBytes(offset int, bytes []byte) (ret *Bytes) { 16 | return &Bytes{offset: offset, bytes: bytes} 17 | } 18 | 19 | func (bytes Bytes) Debug() { 20 | fmt.Printf("Offset: %d, Bytes: %+v\n", bytes.offset, bytes.bytes) 21 | } 22 | 23 | // Return a string with the hex dump of the bytes around the current offset 24 | // The current offset byte is put in brackets 25 | // Example: 0x01, [0x02], 0x03 26 | func (bytes *Bytes) DumpCurrentBytes() (ret string) { 27 | var strings [3]string 28 | for i := -1; i <= 1; i++ { 29 | if bytes.offset+i >= 0 && bytes.offset+i < len(bytes.bytes) { 30 | strings[i+1] = fmt.Sprintf("%#x", bytes.bytes[bytes.offset+i]) 31 | } 32 | } 33 | ret = fmt.Sprintf("%s, [%s], %s", strings[0], strings[1], strings[2]) 34 | return 35 | } 36 | 37 | func (bytes *Bytes) HasMoreData() bool { 38 | return bytes.offset < len(bytes.bytes) 39 | } 40 | 41 | func (bytes *Bytes) ParseTagAndLength() (ret TagAndLength, err error) { 42 | var offset int 43 | ret, offset, err = ParseTagAndLength(bytes.bytes, bytes.offset) 44 | if err != nil { 45 | err = LdapError{fmt.Sprintf("ParseTagAndLength: %s", err.Error())} 46 | return 47 | } else { 48 | bytes.offset = offset 49 | } 50 | return 51 | } 52 | 53 | func (bytes *Bytes) ReadSubBytes(class int, tag int, callback func(bytes *Bytes) error) (err error) { 54 | // Check tag 55 | tagAndLength, err := bytes.ParseTagAndLength() 56 | if err != nil { 57 | return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} 58 | } 59 | err = tagAndLength.Expect(class, tag, isCompound) 60 | if err != nil { 61 | return LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} 62 | } 63 | 64 | start := bytes.offset 65 | end := bytes.offset + tagAndLength.Length 66 | 67 | // Check we got enough bytes to process 68 | if end > len(bytes.bytes) { 69 | return LdapError{fmt.Sprintf("ReadSubBytes: data truncated: expecting %d bytes at offset %d", tagAndLength.Length, bytes.offset)} 70 | } 71 | // Process sub-bytes 72 | subBytes := Bytes{offset: 0, bytes: bytes.bytes[start:end]} 73 | err = callback(&subBytes) 74 | if err != nil { 75 | bytes.offset += subBytes.offset 76 | err = LdapError{fmt.Sprintf("ReadSubBytes:\n%s", err.Error())} 77 | return 78 | } 79 | // Check we got no more bytes to process 80 | if subBytes.HasMoreData() { 81 | return LdapError{fmt.Sprintf("ReadSubBytes: data too long: %d more bytes to read at offset %d", end-bytes.offset, bytes.offset)} 82 | } 83 | // Move offset 84 | bytes.offset = end 85 | return 86 | } 87 | 88 | func SizeSubBytes(tag int, callback func() int) (size int) { 89 | size = callback() 90 | size += sizeTagAndLength(tag, size) 91 | return 92 | } 93 | 94 | func (bytes *Bytes) WritePrimitiveSubBytes(class int, tag int, value interface{}) (size int) { 95 | switch value.(type) { 96 | case BOOLEAN: 97 | size = writeBool(bytes, bool(value.(BOOLEAN))) 98 | case INTEGER: 99 | size = writeInt32(bytes, int32(value.(INTEGER))) 100 | case ENUMERATED: 101 | size = writeInt32(bytes, int32(value.(ENUMERATED))) 102 | case OCTETSTRING: 103 | size = writeOctetString(bytes, []byte(string(value.(OCTETSTRING)))) 104 | default: 105 | panic(fmt.Sprintf("WritePrimitiveSubBytes: invalid value type %v", value)) 106 | } 107 | size += bytes.WriteTagAndLength(class, isNotCompound, tag, size) 108 | return 109 | } 110 | 111 | func (bytes *Bytes) WriteTagAndLength(class int, compound bool, tag int, length int) int { 112 | return writeTagAndLength(bytes, TagAndLength{Class: class, IsCompound: compound, Tag: tag, Length: length}) 113 | } 114 | 115 | func (bytes *Bytes) writeString(s string) (size int) { 116 | size = len(s) 117 | start := bytes.offset - size 118 | if start < 0 { 119 | panic("Not enough space for string") 120 | } 121 | copy(bytes.bytes[start:], s) 122 | bytes.offset = start 123 | return 124 | } 125 | 126 | func (bytes *Bytes) writeBytes(b []byte) (size int) { 127 | size = len(b) 128 | start := bytes.offset - size 129 | if start < 0 { 130 | panic("Not enough space for bytes") 131 | } 132 | copy(bytes.bytes[start:], b) 133 | bytes.offset = start 134 | return 135 | } 136 | 137 | // 138 | // Parse tag, length and read the a primitive value 139 | // Supported types are: 140 | // - boolean 141 | // - integer (parsed as int32) 142 | // - enumerated (parsed as int32) 143 | // - UTF8 string 144 | // - Octet string 145 | // 146 | // Parameters: 147 | // - class: the expected class value(classUniversal, classApplication, classContextSpecific) 148 | // - tag: the expected tag value 149 | // - typeTag: the real primitive type to parse (tagBoolean, tagInteger, tagEnym, tagUTF8String, tagOctetString) 150 | // 151 | func (bytes *Bytes) ReadPrimitiveSubBytes(class int, tag int, typeTag int) (value interface{}, err error) { 152 | // Check tag 153 | tagAndLength, err := bytes.ParseTagAndLength() 154 | if err != nil { 155 | err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} 156 | return 157 | } 158 | err = tagAndLength.Expect(class, tag, isNotCompound) 159 | if err != nil { 160 | err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} 161 | return 162 | } 163 | 164 | start := bytes.offset 165 | end := bytes.offset + tagAndLength.Length 166 | 167 | // Check we got enough bytes to process 168 | if end > len(bytes.bytes) { 169 | // 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)} 170 | 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)} 171 | return 172 | } 173 | // Process sub-bytes 174 | subBytes := bytes.bytes[start:end] 175 | switch typeTag { 176 | case tagBoolean: 177 | value, err = parseBool(subBytes) 178 | case tagInteger: 179 | value, err = parseInt32(subBytes) 180 | case tagEnum: 181 | value, err = parseInt32(subBytes) 182 | case tagOctetString: 183 | value, err = parseOctetString(subBytes) 184 | default: 185 | err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes: invalid type tag value %d", typeTag)} 186 | return 187 | } 188 | if err != nil { 189 | err = LdapError{fmt.Sprintf("ReadPrimitiveSubBytes:\n%s", err.Error())} 190 | return 191 | } 192 | // Move offset 193 | bytes.offset = end 194 | return 195 | } 196 | 197 | func (bytes *Bytes) Bytes() []byte { 198 | return bytes.bytes 199 | } 200 | -------------------------------------------------------------------------------- /message/bytes_test.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestReadPrimitiveSubBytesTestData(t *testing.T) { 9 | for i, test := range PrimitiveSubBytesTestData() { 10 | value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag) 11 | if err != nil { 12 | t.Errorf("#%d failed: %s", i+1, err) 13 | } else if !reflect.DeepEqual(test.value, value) { 14 | t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value) 15 | } else if test.offset != test.bytes.offset { 16 | t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset) 17 | } 18 | } 19 | } 20 | 21 | func TestSizePrimitiveSubBytesTestData(t *testing.T) { 22 | for i, test := range PrimitiveSubBytesTestData() { 23 | value, err := test.bytes.ReadPrimitiveSubBytes(test.class, test.tag, test.typeTag) 24 | if err != nil { 25 | t.Errorf("#%d failed: %s", i+1, err) 26 | } else if !reflect.DeepEqual(test.value, value) { 27 | t.Errorf("#%d: Wrong value %#v, got %#v", i+1, test.value, value) 28 | } else if test.offset != test.bytes.offset { 29 | t.Errorf("#%d: Wrong Offset, value %#v, got %#v", i+1, test.offset, test.bytes.offset) 30 | } 31 | } 32 | } 33 | 34 | func NewInt(value int) (ret *int) { 35 | ret = &value 36 | return 37 | } 38 | 39 | type PrimitiveSubBytesTestSingleData struct { 40 | bytes Bytes // Input 41 | class int // Expected class 42 | tag int // Expected tag 43 | typeTag int // Expected type 44 | value interface{} // Expected output 45 | offset int // Expected offset after processing 46 | } 47 | 48 | func PrimitiveSubBytesTestData() []PrimitiveSubBytesTestSingleData { 49 | 50 | return []PrimitiveSubBytesTestSingleData{ 51 | // Test 1 52 | { 53 | bytes: Bytes{ 54 | offset: 0, 55 | bytes: []byte{0x02, 0x01, 0x09}, 56 | }, 57 | class: classUniversal, 58 | tag: tagInteger, 59 | typeTag: tagInteger, 60 | value: int32(0x09), 61 | offset: 3, 62 | }, 63 | // Test 2 64 | { 65 | bytes: Bytes{ 66 | offset: 0, 67 | bytes: []byte{0x02, 0x02, 0x09, 0x87}, 68 | }, 69 | class: classUniversal, 70 | tag: tagInteger, 71 | typeTag: tagInteger, 72 | value: int32(0x0987), 73 | offset: 4, 74 | }, 75 | // Test 3 76 | { 77 | bytes: Bytes{ 78 | offset: 0, 79 | bytes: []byte{0x02, 0x03, 0x09, 0x87, 0x65}, 80 | }, 81 | class: classUniversal, 82 | tag: tagInteger, 83 | typeTag: tagInteger, 84 | value: int32(0x098765), 85 | offset: 5, 86 | }, 87 | // Test 4 88 | { 89 | bytes: Bytes{ 90 | offset: 0, 91 | bytes: []byte{0x02, 0x04, 0x09, 0x87, 0x65, 0x43}, 92 | }, 93 | class: classUniversal, 94 | tag: tagInteger, 95 | typeTag: tagInteger, 96 | value: int32(0x09876543), 97 | offset: 6, 98 | }, 99 | // Test 5 100 | { 101 | bytes: Bytes{ 102 | offset: 2, 103 | bytes: []byte{0x30, 0x03, 0x02, 0x01, 0x0f}, 104 | }, 105 | class: classUniversal, 106 | tag: tagInteger, 107 | typeTag: tagInteger, 108 | value: int32(0x0f), 109 | offset: 5, 110 | }, 111 | // Test 6 112 | { 113 | bytes: Bytes{ 114 | offset: 2, 115 | 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}, 116 | }, 117 | class: classUniversal, 118 | tag: tagInteger, 119 | typeTag: tagInteger, 120 | value: int32(0x0f), 121 | offset: 5, 122 | }, 123 | // Test 7 124 | { 125 | bytes: Bytes{ 126 | offset: 2, 127 | 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}, 128 | }, 129 | class: classUniversal, 130 | tag: tagInteger, 131 | typeTag: tagInteger, 132 | value: int32(0x07fffffff), 133 | offset: 8, 134 | }, 135 | // Test 8 136 | { 137 | bytes: Bytes{ 138 | offset: 0, 139 | bytes: []byte{0x04, 0x08, 0x43, 0x52, 0x41, 0x4d, 0x2d, 0x4d, 0x44, 0x35}, 140 | }, 141 | class: classUniversal, 142 | tag: tagOctetString, 143 | typeTag: tagOctetString, 144 | value: []byte("CRAM-MD5"), 145 | offset: 10, 146 | }, 147 | // Test 9 148 | { 149 | bytes: Bytes{ 150 | offset: 0, 151 | bytes: []byte{0x04, 0x0d, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c}, 152 | }, 153 | class: classUniversal, 154 | tag: tagOctetString, 155 | typeTag: tagOctetString, 156 | value: []byte("Hello, 世界"), 157 | offset: 15, 158 | }, 159 | // Test 10 160 | { 161 | bytes: Bytes{ 162 | offset: 10, 163 | 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}, 164 | }, 165 | class: classUniversal, 166 | tag: tagOctetString, 167 | typeTag: tagOctetString, 168 | value: []byte("myLogin"), 169 | offset: 19, 170 | }, 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /message/compare_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // CompareRequest ::= [APPLICATION 14] SEQUENCE { 7 | // entry LDAPDN, 8 | // ava AttributeValueAssertion } 9 | 10 | func (request *CompareRequest) Entry() LDAPDN { 11 | return request.entry 12 | } 13 | 14 | func (request *CompareRequest) Ava() *AttributeValueAssertion { 15 | return &request.ava 16 | } 17 | 18 | func readCompareRequest(bytes *Bytes) (ret CompareRequest, err error) { 19 | err = bytes.ReadSubBytes(classApplication, TagCompareRequest, ret.readComponents) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readCompareRequest:\n%s", err.Error())} 22 | return 23 | } 24 | return 25 | } 26 | 27 | func (request *CompareRequest) readComponents(bytes *Bytes) (err error) { 28 | request.entry, err = readLDAPDN(bytes) 29 | if err != nil { 30 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 31 | return 32 | } 33 | request.ava, err = readAttributeValueAssertion(bytes) 34 | if err != nil { 35 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 36 | return 37 | } 38 | return 39 | } 40 | 41 | func (request CompareRequest) write(bytes *Bytes) (size int) { 42 | size += request.ava.write(bytes) 43 | size += request.entry.write(bytes) 44 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagCompareRequest, size) 45 | return 46 | } 47 | 48 | func (request CompareRequest) size() (size int) { 49 | size += request.entry.size() 50 | size += request.ava.size() 51 | size += sizeTagAndLength(TagCompareRequest, size) 52 | return 53 | } 54 | -------------------------------------------------------------------------------- /message/compare_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // CompareResponse ::= [APPLICATION 15] LDAPResult 7 | 8 | func (response *CompareResponse) SetResultCode(code int) { 9 | response.resultCode = ENUMERATED(code) 10 | } 11 | 12 | func readCompareResponse(bytes *Bytes) (ret CompareResponse, err error) { 13 | var res LDAPResult 14 | res, err = readTaggedLDAPResult(bytes, classApplication, TagCompareResponse) 15 | if err != nil { 16 | err = LdapError{fmt.Sprintf("readCompareResponse:\n%s", err.Error())} 17 | return 18 | } 19 | ret = CompareResponse(res) 20 | return 21 | } 22 | 23 | func (response CompareResponse) write(bytes *Bytes) int { 24 | return LDAPResult(response).writeTagged(bytes, classApplication, TagCompareResponse) 25 | } 26 | 27 | func (response CompareResponse) size() int { 28 | return LDAPResult(response).sizeTagged(TagCompareResponse) 29 | } 30 | -------------------------------------------------------------------------------- /message/control.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | // 9 | // Control ::= SEQUENCE { 10 | // controlType LDAPOID, 11 | // criticality BOOLEAN DEFAULT FALSE, 12 | // controlValue OCTET STRING OPTIONAL } 13 | 14 | func (control *Control) ControlType() LDAPOID { 15 | return control.controlType 16 | } 17 | 18 | func (control *Control) Criticality() BOOLEAN { 19 | return control.criticality 20 | } 21 | 22 | func (control *Control) ControlValue() *OCTETSTRING { 23 | return control.controlValue 24 | } 25 | 26 | func readControl(bytes *Bytes) (control Control, err error) { 27 | err = bytes.ReadSubBytes(classUniversal, tagSequence, control.readComponents) 28 | if err != nil { 29 | err = LdapError{fmt.Sprintf("readControl:\n%s", err.Error())} 30 | return 31 | } 32 | return 33 | } 34 | 35 | func (control *Control) readComponents(bytes *Bytes) (err error) { 36 | control.controlType, err = readLDAPOID(bytes) 37 | if err != nil { 38 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 39 | return 40 | } 41 | if bytes.HasMoreData() { 42 | var tag TagAndLength 43 | tag, err = bytes.PreviewTagAndLength() 44 | if err != nil { 45 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 46 | return 47 | } 48 | if tag.Tag == tagBoolean { 49 | control.criticality, err = readBOOLEAN(bytes) 50 | if err != nil { 51 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 52 | return 53 | } 54 | if control.criticality == false { 55 | err = errors.New(fmt.Sprintf("readComponents: criticality default value FALSE should not be specified")) 56 | return 57 | } 58 | } 59 | } 60 | if bytes.HasMoreData() { 61 | var octetstring OCTETSTRING 62 | octetstring, err = readOCTETSTRING(bytes) 63 | if err != nil { 64 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 65 | return 66 | } 67 | control.controlValue = octetstring.Pointer() 68 | } 69 | return 70 | } 71 | 72 | func (control Control) write(bytes *Bytes) (size int) { 73 | if control.controlValue != nil { 74 | size += control.controlValue.write(bytes) 75 | } 76 | if control.criticality != BOOLEAN(false) { 77 | size += control.criticality.write(bytes) 78 | } 79 | size += control.controlType.write(bytes) 80 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 81 | return 82 | } 83 | 84 | func (control Control) size() (size int) { 85 | if control.controlValue != nil { 86 | size += control.controlValue.size() 87 | } 88 | if control.criticality != BOOLEAN(false) { 89 | size += control.criticality.size() 90 | } 91 | size += control.controlType.size() 92 | size += sizeTagAndLength(tagSequence, size) 93 | return 94 | } 95 | -------------------------------------------------------------------------------- /message/controls.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // Controls ::= SEQUENCE OF control Control 7 | 8 | func readTaggedControls(bytes *Bytes, class int, tag int) (controls Controls, err error) { 9 | err = bytes.ReadSubBytes(class, tag, controls.readComponents) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readTaggedControls:\n%s", err.Error())} 12 | return 13 | } 14 | return 15 | } 16 | func (controls *Controls) readComponents(bytes *Bytes) (err error) { 17 | for bytes.HasMoreData() { 18 | var control Control 19 | control, err = readControl(bytes) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 22 | return 23 | } 24 | *controls = append(*controls, control) 25 | } 26 | return 27 | } 28 | func (controls Controls) Pointer() *Controls { return &controls } 29 | 30 | func (controls Controls) writeTagged(bytes *Bytes, class int, tag int) (size int) { 31 | for i := len(controls) - 1; i >= 0; i-- { 32 | size += controls[i].write(bytes) 33 | } 34 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 35 | return 36 | } 37 | 38 | func (controls Controls) sizeTagged(tag int) (size int) { 39 | for _, control := range controls { 40 | size += control.size() 41 | } 42 | size += sizeTagAndLength(tag, size) 43 | return 44 | } 45 | -------------------------------------------------------------------------------- /message/del_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // DelRequest ::= [APPLICATION 10] LDAPDN 7 | func readDelRequest(bytes *Bytes) (ret DelRequest, err error) { 8 | var res LDAPDN 9 | res, err = readTaggedLDAPDN(bytes, classApplication, TagDelRequest) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readDelRequest:\n%s", err.Error())} 12 | return 13 | } 14 | ret = DelRequest(res) 15 | return 16 | } 17 | 18 | func (del DelRequest) write(bytes *Bytes) int { 19 | return LDAPDN(del).writeTagged(bytes, classApplication, TagDelRequest) 20 | } 21 | 22 | func (del DelRequest) size() int { 23 | return LDAPDN(del).sizeTagged(TagDelRequest) 24 | } 25 | -------------------------------------------------------------------------------- /message/del_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // DelResponse ::= [APPLICATION 11] LDAPResult 7 | 8 | func (del *DelResponse) SetResultCode(code int) { 9 | del.resultCode = ENUMERATED(code) 10 | } 11 | 12 | func readDelResponse(bytes *Bytes) (ret DelResponse, err error) { 13 | var res LDAPResult 14 | res, err = readTaggedLDAPResult(bytes, classApplication, TagDelResponse) 15 | if err != nil { 16 | err = LdapError{fmt.Sprintf("readDelResponse:\n%s", err.Error())} 17 | return 18 | } 19 | ret = DelResponse(res) 20 | return 21 | } 22 | 23 | func (del DelResponse) write(bytes *Bytes) int { 24 | return LDAPResult(del).writeTagged(bytes, classApplication, TagDelResponse) 25 | } 26 | 27 | func (del DelResponse) size() int { 28 | return LDAPResult(del).sizeTagged(TagDelResponse) 29 | } 30 | -------------------------------------------------------------------------------- /message/dn.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // LDAPDN ::= LDAPString -- Constrained to 7 | // -- [RFC4514] 8 | 9 | func readLDAPDN(bytes *Bytes) (ret LDAPDN, err error) { 10 | var str LDAPString 11 | str, err = readLDAPString(bytes) 12 | if err != nil { 13 | err = LdapError{fmt.Sprintf("readLDAPDN:\n%s", err.Error())} 14 | return 15 | } 16 | ret = LDAPDN(str) 17 | return 18 | } 19 | 20 | func readTaggedLDAPDN(bytes *Bytes, class int, tag int) (ret LDAPDN, err error) { 21 | var ldapstring LDAPString 22 | ldapstring, err = readTaggedLDAPString(bytes, class, tag) 23 | if err != nil { 24 | err = LdapError{fmt.Sprintf("readTaggedLDAPDN:\n%s", err.Error())} 25 | return 26 | } 27 | // @TODO: check RFC4514 28 | ret = LDAPDN(ldapstring) 29 | return 30 | } 31 | 32 | func (l LDAPDN) Pointer() *LDAPDN { return &l } 33 | 34 | func readRelativeLDAPDN(bytes *Bytes) (ret RelativeLDAPDN, err error) { 35 | var ldapstring LDAPString 36 | ldapstring, err = readLDAPString(bytes) 37 | if err != nil { 38 | err = LdapError{fmt.Sprintf("readRelativeLDAPDN:\n%s", err.Error())} 39 | return 40 | } 41 | // @TODO: check RFC4514 42 | ret = RelativeLDAPDN(ldapstring) 43 | return 44 | } 45 | 46 | func (l LDAPDN) write(bytes *Bytes) int { 47 | return LDAPString(l).write(bytes) 48 | } 49 | 50 | func (l LDAPDN) writeTagged(bytes *Bytes, class int, tag int) int { 51 | return LDAPString(l).writeTagged(bytes, class, tag) 52 | } 53 | 54 | func (l LDAPDN) size() int { 55 | return LDAPString(l).size() 56 | } 57 | 58 | func (l LDAPDN) sizeTagged(tag int) int { 59 | return LDAPString(l).sizeTagged(tag) 60 | } 61 | -------------------------------------------------------------------------------- /message/enumerated.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func (enum ENUMERATED) Int() int { 6 | return int(enum) 7 | } 8 | 9 | func readENUMERATED(bytes *Bytes, allowedValues map[ENUMERATED]string) (ret ENUMERATED, err error) { 10 | var value interface{} 11 | value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagEnum, tagEnum) 12 | if err != nil { 13 | err = LdapError{fmt.Sprintf("readENUMERATED:\n%s", err.Error())} 14 | return 15 | } 16 | ret = ENUMERATED(value.(int32)) 17 | if _, ok := allowedValues[ret]; !ok { 18 | err = LdapError{fmt.Sprintf("readENUMERATED: Invalid ENUMERATED VALUE %d", ret)} 19 | return 20 | } 21 | return 22 | } 23 | 24 | func (enum ENUMERATED) write(bytes *Bytes) int { 25 | return bytes.WritePrimitiveSubBytes(classUniversal, tagEnum, enum) 26 | } 27 | 28 | func (enum ENUMERATED) writeTagged(bytes *Bytes, class int, tag int) int { 29 | return bytes.WritePrimitiveSubBytes(class, tag, enum) 30 | } 31 | 32 | func (enum ENUMERATED) size() int { 33 | return SizePrimitiveSubBytes(tagEnum, enum) 34 | } 35 | -------------------------------------------------------------------------------- /message/error.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | type LdapError struct { 4 | Msg string 5 | } 6 | 7 | func (err LdapError) Error() string { 8 | return err.Msg 9 | } 10 | -------------------------------------------------------------------------------- /message/extended_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // ExtendedRequest ::= [APPLICATION 23] SEQUENCE { 7 | // requestName [0] LDAPOID, 8 | // requestValue [1] OCTET STRING OPTIONAL } 9 | 10 | func (extended *ExtendedRequest) RequestName() LDAPOID { 11 | return extended.requestName 12 | } 13 | 14 | func (extended *ExtendedRequest) RequestValue() *OCTETSTRING { 15 | return extended.requestValue 16 | } 17 | 18 | func readExtendedRequest(bytes *Bytes) (ret ExtendedRequest, err error) { 19 | err = bytes.ReadSubBytes(classApplication, TagExtendedRequest, ret.readComponents) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readExtendedRequest:\n%s", err.Error())} 22 | return 23 | } 24 | return 25 | } 26 | 27 | func (extended *ExtendedRequest) readComponents(bytes *Bytes) (err error) { 28 | extended.requestName, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedRequestName) 29 | if err != nil { 30 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 31 | return 32 | } 33 | if bytes.HasMoreData() { 34 | var tag TagAndLength 35 | tag, err = bytes.PreviewTagAndLength() 36 | if err != nil { 37 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 38 | return 39 | } 40 | if tag.Tag == TagExtendedRequestValue { 41 | var requestValue OCTETSTRING 42 | requestValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedRequestValue) 43 | if err != nil { 44 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 45 | return 46 | } 47 | extended.requestValue = requestValue.Pointer() 48 | } 49 | } 50 | return 51 | } 52 | 53 | func (extended ExtendedRequest) write(bytes *Bytes) (size int) { 54 | if extended.requestValue != nil { 55 | size += extended.requestValue.writeTagged(bytes, classContextSpecific, TagExtendedRequestValue) 56 | } 57 | size += extended.requestName.writeTagged(bytes, classContextSpecific, TagExtendedRequestName) 58 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedRequest, size) 59 | return 60 | } 61 | 62 | func (extended ExtendedRequest) size() (size int) { 63 | size += extended.requestName.sizeTagged(TagExtendedRequestName) 64 | if extended.requestValue != nil { 65 | size += extended.requestValue.sizeTagged(TagExtendedRequestValue) 66 | } 67 | size += sizeTagAndLength(TagExtendedRequest, size) 68 | return 69 | } 70 | -------------------------------------------------------------------------------- /message/extended_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // ExtendedResponse ::= [APPLICATION 24] SEQUENCE { 7 | // COMPONENTS OF LDAPResult, 8 | // responseName [10] LDAPOID OPTIONAL, 9 | // responseValue [11] OCTET STRING OPTIONAL } 10 | 11 | func (extended *ExtendedResponse) SetResponseName(name LDAPOID) { 12 | extended.responseName = &name 13 | } 14 | 15 | func readExtendedResponse(bytes *Bytes) (ret ExtendedResponse, err error) { 16 | err = bytes.ReadSubBytes(classApplication, TagExtendedResponse, ret.readComponents) 17 | if err != nil { 18 | err = LdapError{fmt.Sprintf("readExtendedResponse:\n%s", err.Error())} 19 | return 20 | } 21 | return 22 | } 23 | 24 | func (extended *ExtendedResponse) readComponents(bytes *Bytes) (err error) { 25 | extended.LDAPResult.readComponents(bytes) 26 | if bytes.HasMoreData() { 27 | var tag TagAndLength 28 | tag, err = bytes.PreviewTagAndLength() 29 | if err != nil { 30 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 31 | return 32 | } 33 | if tag.Tag == TagExtendedResponseName { 34 | var oid LDAPOID 35 | oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagExtendedResponseName) 36 | if err != nil { 37 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 38 | return 39 | } 40 | extended.responseName = oid.Pointer() 41 | } 42 | } 43 | if bytes.HasMoreData() { 44 | var tag TagAndLength 45 | tag, err = bytes.PreviewTagAndLength() 46 | if err != nil { 47 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 48 | return 49 | } 50 | if tag.Tag == TagExtendedResponseValue { 51 | var responseValue OCTETSTRING 52 | responseValue, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagExtendedResponseValue) 53 | if err != nil { 54 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 55 | return 56 | } 57 | extended.responseValue = responseValue.Pointer() 58 | } 59 | } 60 | return 61 | } 62 | 63 | func (extended ExtendedResponse) write(bytes *Bytes) (size int) { 64 | if extended.responseValue != nil { 65 | size += extended.responseValue.writeTagged(bytes, classContextSpecific, TagExtendedResponseValue) 66 | } 67 | if extended.responseName != nil { 68 | size += extended.responseName.writeTagged(bytes, classContextSpecific, TagExtendedResponseName) 69 | } 70 | size += extended.LDAPResult.writeComponents(bytes) 71 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagExtendedResponse, size) 72 | return 73 | } 74 | 75 | func (extended ExtendedResponse) size() (size int) { 76 | size += extended.LDAPResult.sizeComponents() 77 | if extended.responseName != nil { 78 | size += extended.responseName.sizeTagged(TagExtendedResponseName) 79 | } 80 | if extended.responseValue != nil { 81 | size += extended.responseValue.sizeTagged(TagExtendedResponseValue) 82 | } 83 | size += sizeTagAndLength(TagExtendedResponse, size) 84 | return 85 | } 86 | -------------------------------------------------------------------------------- /message/filter.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // Filter ::= CHOICE { 7 | // and [0] SET SIZE (1..MAX) OF filter Filter, 8 | // or [1] SET SIZE (1..MAX) OF filter Filter, 9 | // not [2] Filter, 10 | // equalityMatch [3] AttributeValueAssertion, 11 | // 12 | // 13 | // 14 | //Sermersheim Standards Track [Page 57] 15 | // 16 | // 17 | //RFC 4511 LDAPv3 June 2006 18 | // 19 | // 20 | // substrings [4] SubstringFilter, 21 | // greaterOrEqual [5] AttributeValueAssertion, 22 | // lessOrEqual [6] AttributeValueAssertion, 23 | // present [7] AttributeDescription, 24 | // approxMatch [8] AttributeValueAssertion, 25 | // extensibleMatch [9] MatchingRuleAssertion, 26 | // ... } 27 | 28 | func readFilter(bytes *Bytes) (filter Filter, err error) { 29 | var tagAndLength TagAndLength 30 | tagAndLength, err = bytes.PreviewTagAndLength() 31 | if err != nil { 32 | err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())} 33 | return 34 | } 35 | err = tagAndLength.ExpectClass(classContextSpecific) 36 | if err != nil { 37 | err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())} 38 | return 39 | } 40 | switch tagAndLength.Tag { 41 | case TagFilterAnd: 42 | filter, err = readFilterAnd(bytes) 43 | case TagFilterOr: 44 | filter, err = readFilterOr(bytes) 45 | case TagFilterNot: 46 | filter, err = readFilterNot(bytes) 47 | case TagFilterEqualityMatch: 48 | filter, err = readFilterEqualityMatch(bytes) 49 | case TagFilterSubstrings: 50 | filter, err = readFilterSubstrings(bytes) 51 | case TagFilterGreaterOrEqual: 52 | filter, err = readFilterGreaterOrEqual(bytes) 53 | case TagFilterLessOrEqual: 54 | filter, err = readFilterLessOrEqual(bytes) 55 | case TagFilterPresent: 56 | filter, err = readFilterPresent(bytes) 57 | case TagFilterApproxMatch: 58 | filter, err = readFilterApproxMatch(bytes) 59 | case TagFilterExtensibleMatch: 60 | filter, err = readFilterExtensibleMatch(bytes) 61 | default: 62 | err = LdapError{fmt.Sprintf("readFilter: invalid tag value %d for filter", tagAndLength.Tag)} 63 | return 64 | } 65 | if err != nil { 66 | err = LdapError{fmt.Sprintf("readFilter:\n%s", err.Error())} 67 | return 68 | } 69 | return 70 | } 71 | -------------------------------------------------------------------------------- /message/filter_and.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // and [0] SET SIZE (1..MAX) OF filter Filter, 6 | 7 | func (filterAnd FilterAnd) getFilterTag() int { 8 | return TagFilterAnd 9 | } 10 | 11 | func (filterAnd FilterAnd) size() (size int) { 12 | for _, filter := range filterAnd { 13 | size += filter.size() 14 | } 15 | size += sizeTagAndLength(TagFilterAnd, size) 16 | return 17 | } 18 | 19 | func (filterAnd *FilterAnd) readComponents(bytes *Bytes) (err error) { 20 | count := 0 21 | for bytes.HasMoreData() { 22 | count++ 23 | var filter Filter 24 | filter, err = readFilter(bytes) 25 | if err != nil { 26 | err = LdapError{fmt.Sprintf("readComponents (filter %d):\n%s", count, err.Error())} 27 | return 28 | } 29 | *filterAnd = append(*filterAnd, filter) 30 | } 31 | if len(*filterAnd) == 0 { 32 | err = LdapError{"readComponents: expecting at least one Filter"} 33 | return 34 | } 35 | return 36 | } 37 | 38 | func (filterAnd FilterAnd) write(bytes *Bytes) (size int) { 39 | 40 | for i := len(filterAnd) - 1; i >= 0; i-- { 41 | size += filterAnd[i].write(bytes) 42 | } 43 | size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterAnd, size) 44 | return 45 | } 46 | 47 | func readFilterAnd(bytes *Bytes) (filterand FilterAnd, err error) { 48 | err = bytes.ReadSubBytes(classContextSpecific, TagFilterAnd, filterand.readComponents) 49 | if err != nil { 50 | err = LdapError{fmt.Sprintf("readFilterAnd:\n%s", err.Error())} 51 | return 52 | } 53 | return 54 | } 55 | -------------------------------------------------------------------------------- /message/filter_approx_match.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // approxMatch [8] AttributeValueAssertion, 6 | func readFilterApproxMatch(bytes *Bytes) (ret FilterApproxMatch, err error) { 7 | var attributevalueassertion AttributeValueAssertion 8 | attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterApproxMatch) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterApproxMatch:\n%s", err.Error())} 11 | return 12 | } 13 | ret = FilterApproxMatch(attributevalueassertion) 14 | return 15 | } 16 | 17 | // approxMatch [8] AttributeValueAssertion, 18 | func (f FilterApproxMatch) write(bytes *Bytes) int { 19 | return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterApproxMatch) 20 | } 21 | func (filterAnd FilterApproxMatch) getFilterTag() int { 22 | return TagFilterApproxMatch 23 | } 24 | 25 | // approxMatch [8] AttributeValueAssertion, 26 | func (f FilterApproxMatch) size() int { 27 | return AttributeValueAssertion(f).sizeTagged(TagFilterApproxMatch) 28 | } 29 | func (a *FilterApproxMatch) AttributeDesc() AttributeDescription { 30 | return a.attributeDesc 31 | } 32 | func (a *FilterApproxMatch) AssertionValue() AssertionValue { 33 | return a.assertionValue 34 | } 35 | -------------------------------------------------------------------------------- /message/filter_equality_match.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // equalityMatch [3] AttributeValueAssertion, 6 | func readFilterEqualityMatch(bytes *Bytes) (ret FilterEqualityMatch, err error) { 7 | var attributevalueassertion AttributeValueAssertion 8 | attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterEqualityMatch) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterEqualityMatch:\n%s", err.Error())} 11 | return 12 | } 13 | ret = FilterEqualityMatch(attributevalueassertion) 14 | return 15 | } 16 | 17 | // equalityMatch [3] AttributeValueAssertion, 18 | func (f FilterEqualityMatch) write(bytes *Bytes) int { 19 | return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterEqualityMatch) 20 | } 21 | func (filter FilterEqualityMatch) getFilterTag() int { 22 | return TagFilterEqualityMatch 23 | } 24 | 25 | // equalityMatch [3] AttributeValueAssertion, 26 | func (f FilterEqualityMatch) size() int { 27 | return AttributeValueAssertion(f).sizeTagged(TagFilterEqualityMatch) 28 | } 29 | func (a *FilterEqualityMatch) AttributeDesc() AttributeDescription { 30 | return a.attributeDesc 31 | } 32 | func (a *FilterEqualityMatch) AssertionValue() AssertionValue { 33 | return a.assertionValue 34 | } 35 | -------------------------------------------------------------------------------- /message/filter_extensible_match.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // extensibleMatch [9] MatchingRuleAssertion, 6 | func readFilterExtensibleMatch(bytes *Bytes) (filterextensiblematch FilterExtensibleMatch, err error) { 7 | var matchingruleassertion MatchingRuleAssertion 8 | matchingruleassertion, err = readTaggedMatchingRuleAssertion(bytes, classContextSpecific, TagFilterExtensibleMatch) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterExtensibleMatch:\n%s", err.Error())} 11 | return 12 | } 13 | filterextensiblematch = FilterExtensibleMatch(matchingruleassertion) 14 | return 15 | } 16 | 17 | // extensibleMatch [9] MatchingRuleAssertion, 18 | func (f FilterExtensibleMatch) write(bytes *Bytes) int { 19 | return MatchingRuleAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterExtensibleMatch) 20 | } 21 | func (filterAnd FilterExtensibleMatch) getFilterTag() int { 22 | return TagFilterExtensibleMatch 23 | } 24 | 25 | // extensibleMatch [9] MatchingRuleAssertion, 26 | func (f FilterExtensibleMatch) size() int { 27 | return MatchingRuleAssertion(f).sizeTagged(TagFilterExtensibleMatch) 28 | } 29 | -------------------------------------------------------------------------------- /message/filter_greater_or_equal.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // greaterOrEqual [5] AttributeValueAssertion, 6 | func readFilterGreaterOrEqual(bytes *Bytes) (ret FilterGreaterOrEqual, err error) { 7 | var attributevalueassertion AttributeValueAssertion 8 | attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterGreaterOrEqual) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterGreaterOrEqual:\n%s", err.Error())} 11 | return 12 | } 13 | ret = FilterGreaterOrEqual(attributevalueassertion) 14 | return 15 | } 16 | 17 | // greaterOrEqual [5] AttributeValueAssertion, 18 | func (filter FilterGreaterOrEqual) write(bytes *Bytes) int { 19 | return AttributeValueAssertion(filter).writeTagged(bytes, classContextSpecific, TagFilterGreaterOrEqual) 20 | } 21 | func (filter FilterGreaterOrEqual) getFilterTag() int { 22 | return TagFilterGreaterOrEqual 23 | } 24 | 25 | // greaterOrEqual [5] AttributeValueAssertion, 26 | func (filter FilterGreaterOrEqual) size() int { 27 | return AttributeValueAssertion(filter).sizeTagged(TagFilterGreaterOrEqual) 28 | } 29 | func (filter *FilterGreaterOrEqual) AttributeDesc() AttributeDescription { 30 | return filter.attributeDesc 31 | } 32 | func (filter *FilterGreaterOrEqual) AssertionValue() AssertionValue { 33 | return filter.assertionValue 34 | } 35 | -------------------------------------------------------------------------------- /message/filter_less_or_equal.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // lessOrEqual [6] AttributeValueAssertion, 6 | func readFilterLessOrEqual(bytes *Bytes) (ret FilterLessOrEqual, err error) { 7 | var attributevalueassertion AttributeValueAssertion 8 | attributevalueassertion, err = readTaggedAttributeValueAssertion(bytes, classContextSpecific, TagFilterLessOrEqual) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterLessOrEqual:\n%s", err.Error())} 11 | return 12 | } 13 | ret = FilterLessOrEqual(attributevalueassertion) 14 | return 15 | } 16 | 17 | // lessOrEqual [6] AttributeValueAssertion, 18 | func (f FilterLessOrEqual) write(bytes *Bytes) int { 19 | return AttributeValueAssertion(f).writeTagged(bytes, classContextSpecific, TagFilterLessOrEqual) 20 | } 21 | func (filterAnd FilterLessOrEqual) getFilterTag() int { 22 | return TagFilterLessOrEqual 23 | } 24 | 25 | // lessOrEqual [6] AttributeValueAssertion, 26 | func (f FilterLessOrEqual) size() int { 27 | return AttributeValueAssertion(f).sizeTagged(TagFilterLessOrEqual) 28 | } 29 | func (a *FilterLessOrEqual) AttributeDesc() AttributeDescription { 30 | return a.attributeDesc 31 | } 32 | func (a *FilterLessOrEqual) AssertionValue() AssertionValue { 33 | return a.assertionValue 34 | } 35 | -------------------------------------------------------------------------------- /message/filter_not.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func (filterNot FilterNot) getFilterTag() int { 6 | return TagFilterNot 7 | } 8 | 9 | // not [2] Filter, 10 | func (filterNot FilterNot) size() (size int) { 11 | size = filterNot.Filter.size() 12 | size += sizeTagAndLength(tagSequence, size) 13 | return 14 | } 15 | 16 | func (filterNot *FilterNot) readComponents(bytes *Bytes) (err error) { 17 | filterNot.Filter, err = readFilter(bytes) 18 | if err != nil { 19 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 20 | return 21 | } 22 | return 23 | } 24 | 25 | // not [2] Filter, 26 | func (filterNot FilterNot) write(bytes *Bytes) (size int) { 27 | size = filterNot.Filter.write(bytes) 28 | size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterNot, size) 29 | return 30 | } 31 | 32 | // not [2] Filter, 33 | func readFilterNot(bytes *Bytes) (filternot FilterNot, err error) { 34 | err = bytes.ReadSubBytes(classContextSpecific, TagFilterNot, filternot.readComponents) 35 | if err != nil { 36 | err = LdapError{fmt.Sprintf("readFilterNot:\n%s", err.Error())} 37 | return 38 | } 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /message/filter_or.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // or [1] SET SIZE (1..MAX) OF filter Filter, 6 | func readFilterOr(bytes *Bytes) (filteror FilterOr, err error) { 7 | err = bytes.ReadSubBytes(classContextSpecific, TagFilterOr, filteror.readComponents) 8 | if err != nil { 9 | err = LdapError{fmt.Sprintf("readFilterOr:\n%s", err.Error())} 10 | return 11 | } 12 | return 13 | } 14 | func (filteror *FilterOr) readComponents(bytes *Bytes) (err error) { 15 | count := 0 16 | for bytes.HasMoreData() { 17 | count++ 18 | var filter Filter 19 | filter, err = readFilter(bytes) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readComponents (filter %d): %s", count, err.Error())} 22 | return 23 | } 24 | *filteror = append(*filteror, filter) 25 | } 26 | if len(*filteror) == 0 { 27 | err = LdapError{"readComponents: expecting at least one Filter"} 28 | return 29 | } 30 | return 31 | } 32 | 33 | // or [1] SET SIZE (1..MAX) OF filter Filter, 34 | func (f FilterOr) write(bytes *Bytes) (size int) { 35 | for i := len(f) - 1; i >= 0; i-- { 36 | size += f[i].write(bytes) 37 | } 38 | size += bytes.WriteTagAndLength(classContextSpecific, isCompound, TagFilterOr, size) 39 | return 40 | } 41 | func (filter FilterOr) getFilterTag() int { 42 | return TagFilterOr 43 | } 44 | 45 | // or [1] SET SIZE (1..MAX) OF filter Filter, 46 | func (f FilterOr) size() (size int) { 47 | for _, filter := range f { 48 | size += filter.size() 49 | } 50 | size += sizeTagAndLength(TagFilterOr, size) 51 | return 52 | } 53 | -------------------------------------------------------------------------------- /message/filter_present.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // present [7] AttributeDescription, 6 | func readFilterPresent(bytes *Bytes) (ret FilterPresent, err error) { 7 | var attributedescription AttributeDescription 8 | attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagFilterPresent) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterPresent:\n%s", err.Error())} 11 | return 12 | } 13 | ret = FilterPresent(attributedescription) 14 | return 15 | } 16 | 17 | // present [7] AttributeDescription, 18 | func (f FilterPresent) write(bytes *Bytes) int { 19 | return AttributeDescription(f).writeTagged(bytes, classContextSpecific, TagFilterPresent) 20 | } 21 | func (filterAnd FilterPresent) getFilterTag() int { 22 | return TagFilterPresent 23 | } 24 | 25 | // present [7] AttributeDescription, 26 | func (f FilterPresent) size() int { 27 | return AttributeDescription(f).sizeTagged(TagFilterPresent) 28 | } 29 | -------------------------------------------------------------------------------- /message/filter_substring.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // substrings [4] SubstringFilter, 6 | func readFilterSubstrings(bytes *Bytes) (filtersubstrings FilterSubstrings, err error) { 7 | var substringfilter SubstringFilter 8 | substringfilter, err = readTaggedSubstringFilter(bytes, classContextSpecific, TagFilterSubstrings) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readFilterSubstrings:\n%s", err.Error())} 11 | return 12 | } 13 | filtersubstrings = FilterSubstrings(substringfilter) 14 | return 15 | } 16 | 17 | // 18 | // SubstringFilter ::= SEQUENCE { 19 | // type AttributeDescription, 20 | // substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { 21 | // initial [0] AssertionValue, -- can occur at most once 22 | // any [1] AssertionValue, 23 | // final [2] AssertionValue } -- can occur at most once 24 | // } 25 | func readTaggedSubstringFilter(bytes *Bytes, class int, tag int) (substringfilter SubstringFilter, err error) { 26 | err = bytes.ReadSubBytes(class, tag, substringfilter.readComponents) 27 | if err != nil { 28 | err = LdapError{fmt.Sprintf("readTaggedSubstringFilter:\n%s", err.Error())} 29 | return 30 | } 31 | return 32 | } 33 | func (substringfilter *SubstringFilter) readComponents(bytes *Bytes) (err error) { 34 | substringfilter.type_, err = readAttributeDescription(bytes) 35 | if err != nil { 36 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 37 | return 38 | } 39 | err = substringfilter.readSubstrings(bytes) 40 | if err != nil { 41 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 42 | return 43 | } 44 | return 45 | } 46 | func (substringfilter *SubstringFilter) readSubstrings(bytes *Bytes) (err error) { 47 | err = bytes.ReadSubBytes(classUniversal, tagSequence, substringfilter.readSubstringsComponents) 48 | if err != nil { 49 | err = LdapError{fmt.Sprintf("readSubstrings:\n%s", err.Error())} 50 | return 51 | } 52 | return 53 | } 54 | func (substringfilter *SubstringFilter) readSubstringsComponents(bytes *Bytes) (err error) { 55 | var foundInitial = 0 56 | var foundFinal = 0 57 | var tagAndLength TagAndLength 58 | for bytes.HasMoreData() { 59 | tagAndLength, err = bytes.PreviewTagAndLength() 60 | if err != nil { 61 | err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} 62 | return 63 | } 64 | var assertionvalue AssertionValue 65 | switch tagAndLength.Tag { 66 | case TagSubstringInitial: 67 | foundInitial++ 68 | if foundInitial > 1 { 69 | err = LdapError{"readSubstringsComponents: initial can occur at most once"} 70 | return 71 | } 72 | assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringInitial) 73 | if err != nil { 74 | err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} 75 | return 76 | } 77 | substringfilter.substrings = append(substringfilter.substrings, SubstringInitial(assertionvalue)) 78 | case TagSubstringAny: 79 | assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringAny) 80 | if err != nil { 81 | err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} 82 | return 83 | } 84 | substringfilter.substrings = append(substringfilter.substrings, SubstringAny(assertionvalue)) 85 | case TagSubstringFinal: 86 | foundFinal++ 87 | if foundFinal > 1 { 88 | err = LdapError{"readSubstringsComponents: final can occur at most once"} 89 | return 90 | } 91 | assertionvalue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagSubstringFinal) 92 | if err != nil { 93 | err = LdapError{fmt.Sprintf("readSubstringsComponents:\n%s", err.Error())} 94 | return 95 | } 96 | substringfilter.substrings = append(substringfilter.substrings, SubstringFinal(assertionvalue)) 97 | default: 98 | err = LdapError{fmt.Sprintf("readSubstringsComponents: invalid tag %d", tagAndLength.Tag)} 99 | return 100 | } 101 | } 102 | if len(substringfilter.substrings) == 0 { 103 | err = LdapError{"readSubstringsComponents: expecting at least one substring"} 104 | return 105 | } 106 | return 107 | } 108 | 109 | // substrings [4] SubstringFilter, 110 | func (f FilterSubstrings) write(bytes *Bytes) int { 111 | return SubstringFilter(f).writeTagged(bytes, classContextSpecific, TagFilterSubstrings) 112 | } 113 | func (s SubstringFilter) writeTagged(bytes *Bytes, class int, tag int) (size int) { 114 | for i := len(s.substrings) - 1; i >= 0; i-- { 115 | substring := s.substrings[i] 116 | switch substring.(type) { 117 | case SubstringInitial: 118 | size += AssertionValue(substring.(SubstringInitial)).writeTagged(bytes, classContextSpecific, TagSubstringInitial) 119 | case SubstringAny: 120 | size += AssertionValue(substring.(SubstringAny)).writeTagged(bytes, classContextSpecific, TagSubstringAny) 121 | case SubstringFinal: 122 | size += AssertionValue(substring.(SubstringFinal)).writeTagged(bytes, classContextSpecific, TagSubstringFinal) 123 | default: 124 | panic("Unknown type for SubstringFilter substring") 125 | } 126 | } 127 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 128 | size += s.type_.write(bytes) 129 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 130 | return 131 | } 132 | 133 | // 134 | // SubstringFilter ::= SEQUENCE { 135 | // type AttributeDescription, 136 | // substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { 137 | // initial [0] AssertionValue, -- can occur at most once 138 | // any [1] AssertionValue, 139 | // final [2] AssertionValue } -- can occur at most once 140 | // } 141 | func (s SubstringFilter) write(bytes *Bytes) (size int) { 142 | return s.writeTagged(bytes, classUniversal, tagSequence) 143 | } 144 | func (filter FilterSubstrings) getFilterTag() int { 145 | return TagFilterSubstrings 146 | } 147 | 148 | // substrings [4] SubstringFilter, 149 | func (f FilterSubstrings) size() int { 150 | return SubstringFilter(f).sizeTagged(TagFilterSubstrings) 151 | } 152 | 153 | // 154 | // SubstringFilter ::= SEQUENCE { 155 | // type AttributeDescription, 156 | // substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { 157 | // initial [0] AssertionValue, -- can occur at most once 158 | // any [1] AssertionValue, 159 | // final [2] AssertionValue } -- can occur at most once 160 | // } 161 | func (s SubstringFilter) size() (size int) { 162 | return s.sizeTagged(tagSequence) 163 | } 164 | func (s SubstringFilter) sizeTagged(tag int) (size int) { 165 | for _, substring := range s.substrings { 166 | switch substring.(type) { 167 | case SubstringInitial: 168 | size += AssertionValue(substring.(SubstringInitial)).sizeTagged(TagSubstringInitial) 169 | case SubstringAny: 170 | size += AssertionValue(substring.(SubstringAny)).sizeTagged(TagSubstringAny) 171 | case SubstringFinal: 172 | size += AssertionValue(substring.(SubstringFinal)).sizeTagged(TagSubstringFinal) 173 | default: 174 | panic("Unknown type for SubstringFilter substring") 175 | } 176 | } 177 | size += sizeTagAndLength(tagSequence, size) 178 | size += s.type_.size() 179 | size += sizeTagAndLength(tag, size) 180 | return 181 | } 182 | func (s *FilterSubstrings) Type_() AttributeDescription { 183 | return s.type_ 184 | } 185 | func (s *FilterSubstrings) Substrings() []Substring { 186 | return s.substrings 187 | } 188 | -------------------------------------------------------------------------------- /message/integer.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readINTEGER(bytes *Bytes) (ret INTEGER, err error) { 6 | var value interface{} 7 | value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagInteger, tagInteger) 8 | if err != nil { 9 | err = LdapError{fmt.Sprintf("readINTEGER:\n%s", err.Error())} 10 | return 11 | } 12 | ret = INTEGER(value.(int32)) 13 | return 14 | } 15 | func readTaggedINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) { 16 | var value interface{} 17 | value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagInteger) 18 | if err != nil { 19 | err = LdapError{fmt.Sprintf("readTaggedINTEGER:\n%s", err.Error())} 20 | return 21 | } 22 | ret = INTEGER(value.(int32)) 23 | return 24 | } 25 | func readTaggedPositiveINTEGER(bytes *Bytes, class int, tag int) (ret INTEGER, err error) { 26 | ret, err = readTaggedINTEGER(bytes, class, tag) 27 | if err != nil { 28 | err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER:\n%s", err.Error())} 29 | return 30 | } 31 | if !(ret >= 0 && ret <= maxInt) { 32 | err = LdapError{fmt.Sprintf("readTaggedPositiveINTEGER: Invalid INTEGER value %d ! Expected value between 0 and %d", ret, maxInt)} 33 | } 34 | return 35 | } 36 | func readPositiveINTEGER(bytes *Bytes) (ret INTEGER, err error) { 37 | return readTaggedPositiveINTEGER(bytes, classUniversal, tagInteger) 38 | } 39 | func (i INTEGER) write(bytes *Bytes) int { 40 | return bytes.WritePrimitiveSubBytes(classUniversal, tagInteger, i) 41 | } 42 | func (i INTEGER) writeTagged(bytes *Bytes, class int, tag int) int { 43 | return bytes.WritePrimitiveSubBytes(class, tag, i) 44 | } 45 | func (i INTEGER) size() int { 46 | return SizePrimitiveSubBytes(tagInteger, i) 47 | } 48 | func (i INTEGER) sizeTagged(tag int) int { 49 | return SizePrimitiveSubBytes(tag, i) 50 | } 51 | func (l INTEGER) Int() int { 52 | return int(l) 53 | } 54 | -------------------------------------------------------------------------------- /message/intermediate_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // IntermediateResponse ::= [APPLICATION 25] SEQUENCE { 7 | // responseName [0] LDAPOID OPTIONAL, 8 | // responseValue [1] OCTET STRING OPTIONAL } 9 | func readIntermediateResponse(bytes *Bytes) (ret IntermediateResponse, err error) { 10 | err = bytes.ReadSubBytes(classApplication, TagIntermediateResponse, ret.readComponents) 11 | if err != nil { 12 | err = LdapError{fmt.Sprintf("readIntermediateResponse:\n%s", err.Error())} 13 | return 14 | } 15 | return 16 | } 17 | func (bytes *Bytes) PreviewTagAndLength() (tagAndLength TagAndLength, err error) { 18 | previousOffset := bytes.offset // Save offset 19 | tagAndLength, err = bytes.ParseTagAndLength() 20 | bytes.offset = previousOffset // Restore offset 21 | return 22 | } 23 | func (res *IntermediateResponse) readComponents(bytes *Bytes) (err error) { 24 | if bytes.HasMoreData() { 25 | var tag TagAndLength 26 | tag, err = bytes.PreviewTagAndLength() 27 | if err != nil { 28 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 29 | return 30 | } 31 | if tag.Tag == TagIntermediateResponseName { 32 | var oid LDAPOID 33 | oid, err = readTaggedLDAPOID(bytes, classContextSpecific, TagIntermediateResponseName) 34 | if err != nil { 35 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 36 | return 37 | } 38 | res.responseName = oid.Pointer() 39 | } 40 | } 41 | if bytes.HasMoreData() { 42 | var tag TagAndLength 43 | tag, err = bytes.PreviewTagAndLength() 44 | if err != nil { 45 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 46 | return 47 | } 48 | if tag.Tag == TagIntermediateResponseValue { 49 | var str OCTETSTRING 50 | str, err = readTaggedOCTETSTRING(bytes, classContextSpecific, TagIntermediateResponseValue) 51 | if err != nil { 52 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 53 | return 54 | } 55 | res.responseValue = str.Pointer() 56 | } 57 | } 58 | return 59 | } 60 | 61 | // 62 | // IntermediateResponse ::= [APPLICATION 25] SEQUENCE { 63 | // responseName [0] LDAPOID OPTIONAL, 64 | // responseValue [1] OCTET STRING OPTIONAL } 65 | func (i IntermediateResponse) write(bytes *Bytes) (size int) { 66 | if i.responseValue != nil { 67 | size += i.responseValue.writeTagged(bytes, classContextSpecific, TagIntermediateResponseValue) 68 | } 69 | if i.responseName != nil { 70 | size += i.responseName.writeTagged(bytes, classContextSpecific, TagIntermediateResponseName) 71 | } 72 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagIntermediateResponse, size) 73 | return 74 | } 75 | 76 | // 77 | // IntermediateResponse ::= [APPLICATION 25] SEQUENCE { 78 | // responseName [0] LDAPOID OPTIONAL, 79 | // responseValue [1] OCTET STRING OPTIONAL } 80 | func (i IntermediateResponse) size() (size int) { 81 | if i.responseName != nil { 82 | size += i.responseName.sizeTagged(TagIntermediateResponseName) 83 | } 84 | if i.responseValue != nil { 85 | size += i.responseValue.sizeTagged(TagIntermediateResponseValue) 86 | } 87 | size += sizeTagAndLength(TagIntermediateResponse, size) 88 | return 89 | } 90 | -------------------------------------------------------------------------------- /message/matching_rule_assertion.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // MatchingRuleAssertion ::= SEQUENCE { 7 | // matchingRule [1] MatchingRuleId OPTIONAL, 8 | // type [2] AttributeDescription OPTIONAL, 9 | // matchValue [3] AssertionValue, 10 | // dnAttributes [4] BOOLEAN DEFAULT FALSE } 11 | func readTaggedMatchingRuleAssertion(bytes *Bytes, class int, tag int) (ret MatchingRuleAssertion, err error) { 12 | err = bytes.ReadSubBytes(class, tag, ret.readComponents) 13 | if err != nil { 14 | err = LdapError{fmt.Sprintf("readTaggedMatchingRuleAssertion:\n%s", err.Error())} 15 | return 16 | } 17 | return 18 | } 19 | func (matchingruleassertion *MatchingRuleAssertion) readComponents(bytes *Bytes) (err error) { 20 | err = matchingruleassertion.readMatchingRule(bytes) 21 | if err != nil { 22 | return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} 23 | } 24 | err = matchingruleassertion.readType(bytes) 25 | if err != nil { 26 | return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} 27 | } 28 | matchingruleassertion.matchValue, err = readTaggedAssertionValue(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue) 29 | if err != nil { 30 | return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} 31 | } 32 | matchingruleassertion.dnAttributes, err = readTaggedBOOLEAN(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes) 33 | if err != nil { 34 | return LdapError{fmt.Sprintf("readComponents: %s", err.Error())} 35 | } 36 | return 37 | } 38 | func (matchingruleassertion *MatchingRuleAssertion) readMatchingRule(bytes *Bytes) (err error) { 39 | var tagAndLength TagAndLength 40 | tagAndLength, err = bytes.PreviewTagAndLength() 41 | if err != nil { 42 | return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())} 43 | } 44 | if tagAndLength.Tag == TagMatchingRuleAssertionMatchingRule { 45 | var matchingRule MatchingRuleId 46 | matchingRule, err = readTaggedMatchingRuleId(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule) 47 | if err != nil { 48 | return LdapError{fmt.Sprintf("readMatchingRule: %s", err.Error())} 49 | } 50 | matchingruleassertion.matchingRule = matchingRule.Pointer() 51 | } 52 | return 53 | } 54 | func (matchingruleassertion *MatchingRuleAssertion) readType(bytes *Bytes) (err error) { 55 | var tagAndLength TagAndLength 56 | tagAndLength, err = bytes.PreviewTagAndLength() 57 | if err != nil { 58 | return LdapError{fmt.Sprintf("readType: %s", err.Error())} 59 | } 60 | if tagAndLength.Tag == TagMatchingRuleAssertionType { 61 | var attributedescription AttributeDescription 62 | attributedescription, err = readTaggedAttributeDescription(bytes, classContextSpecific, TagMatchingRuleAssertionType) 63 | if err != nil { 64 | return LdapError{fmt.Sprintf("readType: %s", err.Error())} 65 | } 66 | matchingruleassertion.type_ = &attributedescription 67 | } 68 | return 69 | } 70 | func (m MatchingRuleAssertion) writeTagged(bytes *Bytes, class int, tag int) (size int) { 71 | if m.dnAttributes != BOOLEAN(false) { 72 | size += m.dnAttributes.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionDnAttributes) 73 | } 74 | size += m.matchValue.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchValue) 75 | if m.type_ != nil { 76 | size += m.type_.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionType) 77 | } 78 | if m.matchingRule != nil { 79 | size += m.matchingRule.writeTagged(bytes, classContextSpecific, TagMatchingRuleAssertionMatchingRule) 80 | } 81 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 82 | return 83 | } 84 | 85 | // 86 | // MatchingRuleAssertion ::= SEQUENCE { 87 | // matchingRule [1] MatchingRuleId OPTIONAL, 88 | // type [2] AttributeDescription OPTIONAL, 89 | // matchValue [3] AssertionValue, 90 | // dnAttributes [4] BOOLEAN DEFAULT FALSE } 91 | func (m MatchingRuleAssertion) write(bytes *Bytes) (size int) { 92 | return m.writeTagged(bytes, classUniversal, tagSequence) 93 | } 94 | 95 | // 96 | // MatchingRuleAssertion ::= SEQUENCE { 97 | // matchingRule [1] MatchingRuleId OPTIONAL, 98 | // type [2] AttributeDescription OPTIONAL, 99 | // matchValue [3] AssertionValue, 100 | // dnAttributes [4] BOOLEAN DEFAULT FALSE } 101 | func (m MatchingRuleAssertion) size() (size int) { 102 | return m.sizeTagged(tagSequence) 103 | } 104 | func (m MatchingRuleAssertion) sizeTagged(tag int) (size int) { 105 | if m.matchingRule != nil { 106 | size += m.matchingRule.sizeTagged(TagMatchingRuleAssertionMatchingRule) 107 | } 108 | if m.type_ != nil { 109 | size += m.type_.sizeTagged(TagMatchingRuleAssertionType) 110 | } 111 | size += m.matchValue.sizeTagged(TagMatchingRuleAssertionMatchValue) 112 | if m.dnAttributes != BOOLEAN(false) { 113 | size += m.dnAttributes.sizeTagged(TagMatchingRuleAssertionDnAttributes) 114 | } 115 | size += sizeTagAndLength(tag, size) 116 | return 117 | } 118 | -------------------------------------------------------------------------------- /message/matching_rule_id.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // MatchingRuleId ::= LDAPString 7 | func readTaggedMatchingRuleId(bytes *Bytes, class int, tag int) (matchingruleid MatchingRuleId, err error) { 8 | var ldapstring LDAPString 9 | ldapstring, err = readTaggedLDAPString(bytes, class, tag) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readTaggedMatchingRuleId:\n%s", err.Error())} 12 | return 13 | } 14 | matchingruleid = MatchingRuleId(ldapstring) 15 | return 16 | } 17 | func (m MatchingRuleId) Pointer() *MatchingRuleId { return &m } 18 | 19 | // 20 | // MatchingRuleId ::= LDAPString 21 | func (m MatchingRuleId) writeTagged(bytes *Bytes, class int, tag int) int { 22 | return LDAPString(m).writeTagged(bytes, class, tag) 23 | } 24 | 25 | // 26 | // MatchingRuleId ::= LDAPString 27 | func (m MatchingRuleId) sizeTagged(tag int) int { 28 | return LDAPString(m).sizeTagged(tag) 29 | } 30 | -------------------------------------------------------------------------------- /message/message.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | // This appendix is normative. 9 | // 10 | // Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18} 11 | // -- Copyright (C) The Internet Society (2006). This version of 12 | // -- this ASN.1 module is part of RFC 4511; see the RFC itself 13 | // -- for full legal notices. 14 | // DEFINITIONS 15 | // IMPLICIT TAGS 16 | // EXTENSIBILITY IMPLIED ::= 17 | // 18 | // BEGIN 19 | // 20 | // LDAPMessage ::= SEQUENCE { 21 | // messageID MessageID, 22 | // protocolOp CHOICE { 23 | // bindRequest BindRequest, 24 | // bindResponse BindResponse, 25 | // unbindRequest UnbindRequest, 26 | // searchRequest SearchRequest, 27 | // searchResEntry SearchResultEntry, 28 | // searchResDone SearchResultDone, 29 | // searchResRef SearchResultReference, 30 | // modifyRequest ModifyRequest, 31 | // modifyResponse ModifyResponse, 32 | // addRequest AddRequest, 33 | // addResponse AddResponse, 34 | // delRequest DelRequest, 35 | // delResponse DelResponse, 36 | // modDNRequest ModifyDNRequest, 37 | // modDNResponse ModifyDNResponse, 38 | // compareRequest CompareRequest, 39 | // compareResponse CompareResponse, 40 | // abandonRequest AbandonRequest, 41 | // extendedReq ExtendedRequest, 42 | // extendedResp ExtendedResponse, 43 | // ..., 44 | // intermediateResponse IntermediateResponse }, 45 | // controls [0] Controls OPTIONAL } 46 | // 47 | 48 | func NewLDAPMessage() *LDAPMessage { return &LDAPMessage{} } 49 | 50 | func (message *LDAPMessage) readComponents(bytes *Bytes) (err error) { 51 | message.messageID, err = readMessageID(bytes) 52 | if err != nil { 53 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 54 | return 55 | } 56 | message.protocolOp, err = readProtocolOp(bytes) 57 | if err != nil { 58 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 59 | return 60 | } 61 | if bytes.HasMoreData() { 62 | var tag TagAndLength 63 | tag, err = bytes.PreviewTagAndLength() 64 | if err != nil { 65 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 66 | return 67 | } 68 | if tag.Tag == TagLDAPMessageControls { 69 | var controls Controls 70 | controls, err = readTaggedControls(bytes, classContextSpecific, TagLDAPMessageControls) 71 | if err != nil { 72 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 73 | return 74 | } 75 | message.controls = controls.Pointer() 76 | } 77 | } 78 | return 79 | } 80 | 81 | func (m *LDAPMessage) Write() (bytes *Bytes, err error) { 82 | defer func() { 83 | if e := recover(); e != nil { 84 | err = LdapError{fmt.Sprintf("Error in LDAPMessage.Write: %s", e)} 85 | } 86 | }() 87 | // Compute the needed size 88 | totalSize := m.size() 89 | // Initialize the structure 90 | bytes = &Bytes{ 91 | bytes: make([]byte, totalSize), 92 | offset: totalSize, 93 | } 94 | 95 | // Go ! 96 | size := 0 97 | if m.controls != nil { 98 | size += m.controls.writeTagged(bytes, classContextSpecific, TagLDAPMessageControls) 99 | } 100 | size += m.protocolOp.write(bytes) 101 | size += m.messageID.write(bytes) 102 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 103 | // Check 104 | if size != totalSize || bytes.offset != 0 { 105 | 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)} 106 | } 107 | return 108 | } 109 | func (m *LDAPMessage) size() (size int) { 110 | size += m.messageID.size() 111 | size += m.protocolOp.size() 112 | if m.controls != nil { 113 | size += m.controls.sizeTagged(TagLDAPMessageControls) 114 | } 115 | size += sizeTagAndLength(tagSequence, size) 116 | return 117 | } 118 | func (l *LDAPMessage) MessageID() MessageID { 119 | return l.messageID 120 | } 121 | func (l *LDAPMessage) SetMessageID(ID int) { 122 | l.messageID = MessageID(ID) 123 | } 124 | func (l *LDAPMessage) Controls() *Controls { 125 | return l.controls 126 | } 127 | func (l *LDAPMessage) ProtocolOp() ProtocolOp { 128 | return l.protocolOp 129 | } 130 | func (l *LDAPMessage) ProtocolOpName() string { 131 | return reflect.TypeOf(l.ProtocolOp()).Name() 132 | } 133 | func (l *LDAPMessage) ProtocolOpType() int { 134 | switch l.protocolOp.(type) { 135 | case BindRequest: 136 | return TagBindRequest 137 | } 138 | return 0 139 | } 140 | -------------------------------------------------------------------------------- /message/message_id.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readTaggedMessageID(bytes *Bytes, class int, tag int) (ret MessageID, err error) { 6 | var integer INTEGER 7 | integer, err = readTaggedPositiveINTEGER(bytes, class, tag) 8 | if err != nil { 9 | err = LdapError{fmt.Sprintf("readTaggedMessageID:\n%s", err.Error())} 10 | return 11 | } 12 | return MessageID(integer), err 13 | } 14 | 15 | // MessageID ::= INTEGER (0 .. maxInt) 16 | // 17 | // maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- 18 | // 19 | func readMessageID(bytes *Bytes) (ret MessageID, err error) { 20 | return readTaggedMessageID(bytes, classUniversal, tagInteger) 21 | } 22 | 23 | // MessageID ::= INTEGER (0 .. maxInt) 24 | // 25 | // maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- 26 | // 27 | func (m MessageID) write(bytes *Bytes) int { 28 | return INTEGER(m).write(bytes) 29 | } 30 | func (m MessageID) writeTagged(bytes *Bytes, class int, tag int) int { 31 | return INTEGER(m).writeTagged(bytes, class, tag) 32 | } 33 | 34 | // MessageID ::= INTEGER (0 .. maxInt) 35 | // 36 | // maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- 37 | // 38 | func (m MessageID) size() int { 39 | return INTEGER(m).size() 40 | } 41 | func (m MessageID) sizeTagged(tag int) int { 42 | return INTEGER(m).sizeTagged(tag) 43 | } 44 | func (l MessageID) Int() int { 45 | return int(l) 46 | } 47 | -------------------------------------------------------------------------------- /message/modify_dn_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { 7 | // entry LDAPDN, 8 | // newrdn RelativeLDAPDN, 9 | // deleteoldrdn BOOLEAN, 10 | // newSuperior [0] LDAPDN OPTIONAL } 11 | func readModifyDNRequest(bytes *Bytes) (ret ModifyDNRequest, err error) { 12 | err = bytes.ReadSubBytes(classApplication, TagModifyDNRequest, ret.readComponents) 13 | if err != nil { 14 | err = LdapError{fmt.Sprintf("readModifyDNRequest:\n%s", err.Error())} 15 | return 16 | } 17 | return 18 | } 19 | func (req *ModifyDNRequest) readComponents(bytes *Bytes) (err error) { 20 | req.entry, err = readLDAPDN(bytes) 21 | if err != nil { 22 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 23 | return 24 | } 25 | req.newrdn, err = readRelativeLDAPDN(bytes) 26 | if err != nil { 27 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 28 | return 29 | } 30 | req.deleteoldrdn, err = readBOOLEAN(bytes) 31 | if err != nil { 32 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 33 | return 34 | } 35 | if bytes.HasMoreData() { 36 | var tag TagAndLength 37 | tag, err = bytes.PreviewTagAndLength() 38 | if err != nil { 39 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 40 | return 41 | } 42 | if tag.Tag == TagModifyDNRequestNewSuperior { 43 | var ldapdn LDAPDN 44 | ldapdn, err = readTaggedLDAPDN(bytes, classContextSpecific, TagModifyDNRequestNewSuperior) 45 | if err != nil { 46 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 47 | return 48 | } 49 | req.newSuperior = ldapdn.Pointer() 50 | } 51 | } 52 | return 53 | } 54 | 55 | // 56 | // ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { 57 | // entry LDAPDN, 58 | // newrdn RelativeLDAPDN, 59 | // deleteoldrdn BOOLEAN, 60 | // newSuperior [0] LDAPDN OPTIONAL } 61 | func (m ModifyDNRequest) write(bytes *Bytes) (size int) { 62 | if m.newSuperior != nil { 63 | size += m.newSuperior.writeTagged(bytes, classContextSpecific, TagModifyDNRequestNewSuperior) 64 | } 65 | size += m.deleteoldrdn.write(bytes) 66 | size += m.newrdn.write(bytes) 67 | size += m.entry.write(bytes) 68 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyDNRequest, size) 69 | return 70 | } 71 | 72 | // 73 | // ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { 74 | // entry LDAPDN, 75 | // newrdn RelativeLDAPDN, 76 | // deleteoldrdn BOOLEAN, 77 | // newSuperior [0] LDAPDN OPTIONAL } 78 | func (m ModifyDNRequest) size() (size int) { 79 | size += m.entry.size() 80 | size += m.newrdn.size() 81 | size += m.deleteoldrdn.size() 82 | if m.newSuperior != nil { 83 | size += m.newSuperior.sizeTagged(TagModifyDNRequestNewSuperior) 84 | } 85 | size += sizeTagAndLength(TagModifyDNRequest, size) 86 | return 87 | } 88 | -------------------------------------------------------------------------------- /message/modify_dn_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // ModifyDNResponse ::= [APPLICATION 13] LDAPResult 7 | func readModifyDNResponse(bytes *Bytes) (ret ModifyDNResponse, err error) { 8 | var res LDAPResult 9 | res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyDNResponse) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readModifyDNResponse:\n%s", err.Error())} 12 | return 13 | } 14 | ret = ModifyDNResponse(res) 15 | return 16 | } 17 | 18 | // 19 | // ModifyDNResponse ::= [APPLICATION 13] LDAPResult 20 | func (m ModifyDNResponse) write(bytes *Bytes) int { 21 | return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyDNResponse) 22 | } 23 | 24 | // 25 | // ModifyDNResponse ::= [APPLICATION 13] LDAPResult 26 | func (m ModifyDNResponse) size() int { 27 | return LDAPResult(m).sizeTagged(TagModifyDNResponse) 28 | } 29 | -------------------------------------------------------------------------------- /message/modify_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // ModifyRequest ::= [APPLICATION 6] SEQUENCE { 7 | // object LDAPDN, 8 | // changes SEQUENCE OF change SEQUENCE { 9 | // operation ENUMERATED { 10 | // add (0), 11 | // delete (1), 12 | // replace (2), 13 | // ... }, 14 | // modification PartialAttribute } } 15 | func readModifyRequest(bytes *Bytes) (ret ModifyRequest, err error) { 16 | err = bytes.ReadSubBytes(classApplication, TagModifyRequest, ret.readComponents) 17 | if err != nil { 18 | err = LdapError{fmt.Sprintf("readModifyRequest:\n%s", err.Error())} 19 | return 20 | } 21 | return 22 | } 23 | func (m *ModifyRequest) readComponents(bytes *Bytes) (err error) { 24 | m.object, err = readLDAPDN(bytes) 25 | if err != nil { 26 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 27 | return 28 | } 29 | err = bytes.ReadSubBytes(classUniversal, tagSequence, m.readChanges) 30 | return 31 | } 32 | func (m *ModifyRequest) readChanges(bytes *Bytes) (err error) { 33 | for bytes.HasMoreData() { 34 | var c ModifyRequestChange 35 | c, err = readModifyRequestChange(bytes) 36 | if err != nil { 37 | err = LdapError{fmt.Sprintf("readChanges:\n%s", err.Error())} 38 | return 39 | } 40 | m.changes = append(m.changes, c) 41 | } 42 | return 43 | } 44 | 45 | // 46 | // ModifyRequest ::= [APPLICATION 6] SEQUENCE { 47 | // object LDAPDN, 48 | // changes SEQUENCE OF change SEQUENCE { 49 | // operation ENUMERATED { 50 | // add (0), 51 | // delete (1), 52 | // replace (2), 53 | // ... }, 54 | // modification PartialAttribute } } 55 | func (m ModifyRequest) write(bytes *Bytes) (size int) { 56 | for i := len(m.changes) - 1; i >= 0; i-- { 57 | size += m.changes[i].write(bytes) 58 | } 59 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 60 | size += m.object.write(bytes) 61 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagModifyRequest, size) 62 | return 63 | } 64 | 65 | // 66 | // ModifyRequest ::= [APPLICATION 6] SEQUENCE { 67 | // object LDAPDN, 68 | // changes SEQUENCE OF change SEQUENCE { 69 | // operation ENUMERATED { 70 | // add (0), 71 | // delete (1), 72 | // replace (2), 73 | // ... }, 74 | // modification PartialAttribute } } 75 | func (m ModifyRequest) size() (size int) { 76 | for _, change := range m.changes { 77 | size += change.size() 78 | } 79 | size += sizeTagAndLength(tagSequence, size) 80 | size += m.object.size() 81 | size += sizeTagAndLength(TagModifyRequest, size) 82 | return 83 | } 84 | func (m *ModifyRequest) Object() LDAPDN { 85 | return m.object 86 | } 87 | func (m *ModifyRequest) Changes() []ModifyRequestChange { 88 | return m.changes 89 | } 90 | -------------------------------------------------------------------------------- /message/modify_request_change.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readModifyRequestChange(bytes *Bytes) (ret ModifyRequestChange, err error) { 6 | err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) 7 | if err != nil { 8 | err = LdapError{fmt.Sprintf("readModifyRequestChange:\n%s", err.Error())} 9 | return 10 | } 11 | return 12 | } 13 | func (m *ModifyRequestChange) readComponents(bytes *Bytes) (err error) { 14 | m.operation, err = readENUMERATED(bytes, EnumeratedModifyRequestChangeOperation) 15 | if err != nil { 16 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 17 | return 18 | } 19 | m.modification, err = readPartialAttribute(bytes) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 22 | return 23 | } 24 | return 25 | } 26 | func (m ModifyRequestChange) write(bytes *Bytes) (size int) { 27 | size += m.modification.write(bytes) 28 | size += m.operation.write(bytes) 29 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 30 | return 31 | } 32 | func (m ModifyRequestChange) size() (size int) { 33 | size += m.operation.size() 34 | size += m.modification.size() 35 | size += sizeTagAndLength(tagSequence, size) 36 | return 37 | } 38 | func (m *ModifyRequestChange) Operation() ENUMERATED { 39 | return m.operation 40 | } 41 | func (m *ModifyRequestChange) Modification() *PartialAttribute { 42 | return &m.modification 43 | } 44 | -------------------------------------------------------------------------------- /message/modify_response.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // ModifyResponse ::= [APPLICATION 7] LDAPResult 7 | func readModifyResponse(bytes *Bytes) (ret ModifyResponse, err error) { 8 | var res LDAPResult 9 | res, err = readTaggedLDAPResult(bytes, classApplication, TagModifyResponse) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readModifyResponse:\n%s", err.Error())} 12 | return 13 | } 14 | ret = ModifyResponse(res) 15 | return 16 | } 17 | func (l LDAPResult) writeTagged(bytes *Bytes, class int, tag int) (size int) { 18 | size += l.writeComponents(bytes) 19 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 20 | return 21 | } 22 | 23 | // 24 | // ModifyResponse ::= [APPLICATION 7] LDAPResult 25 | func (m ModifyResponse) write(bytes *Bytes) int { 26 | return LDAPResult(m).writeTagged(bytes, classApplication, TagModifyResponse) 27 | } 28 | 29 | // 30 | // ModifyResponse ::= [APPLICATION 7] LDAPResult 31 | func (m ModifyResponse) size() int { 32 | return LDAPResult(m).sizeTagged(TagModifyResponse) 33 | } 34 | func (l *ModifyResponse) SetResultCode(code int) { 35 | l.resultCode = ENUMERATED(code) 36 | } 37 | -------------------------------------------------------------------------------- /message/octetstring.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readOCTETSTRING(bytes *Bytes) (ret OCTETSTRING, err error) { 6 | var value interface{} 7 | value, err = bytes.ReadPrimitiveSubBytes(classUniversal, tagOctetString, tagOctetString) 8 | if err != nil { 9 | err = LdapError{fmt.Sprintf("readOCTETSTRING:\n%s", err.Error())} 10 | return 11 | } 12 | ret = OCTETSTRING(value.([]byte)) 13 | return 14 | } 15 | 16 | func readTaggedOCTETSTRING(bytes *Bytes, class int, tag int) (ret OCTETSTRING, err error) { 17 | var value interface{} 18 | value, err = bytes.ReadPrimitiveSubBytes(class, tag, tagOctetString) 19 | if err != nil { 20 | err = LdapError{fmt.Sprintf("readTaggedOCTETSTRING:\n%s", err.Error())} 21 | return 22 | } 23 | ret = OCTETSTRING(value.([]byte)) 24 | return 25 | } 26 | func (o OCTETSTRING) Pointer() *OCTETSTRING { return &o } 27 | func (o OCTETSTRING) write(bytes *Bytes) int { 28 | return bytes.WritePrimitiveSubBytes(classUniversal, tagOctetString, o) 29 | } 30 | func (o OCTETSTRING) writeTagged(bytes *Bytes, class int, tag int) int { 31 | return bytes.WritePrimitiveSubBytes(class, tag, o) 32 | } 33 | func (o OCTETSTRING) size() int { 34 | return SizePrimitiveSubBytes(tagOctetString, o) 35 | } 36 | func (o OCTETSTRING) sizeTagged(tag int) int { 37 | return SizePrimitiveSubBytes(tag, o) 38 | } 39 | func (l OCTETSTRING) String() string { 40 | return string(l) 41 | } 42 | func (l OCTETSTRING) Bytes() []byte { 43 | return []byte(l) 44 | } 45 | -------------------------------------------------------------------------------- /message/oid.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // 7 | // LDAPOID ::= OCTET STRING -- Constrained to 8 | // -- [RFC4512] 9 | 10 | func (l LDAPOID) String() string { 11 | return string(l) 12 | } 13 | 14 | func (l LDAPOID) Bytes() []byte { 15 | return []byte(l) 16 | } 17 | 18 | func (l LDAPOID) Pointer() *LDAPOID { return &l } 19 | 20 | func readTaggedLDAPOID(bytes *Bytes, class int, tag int) (ret LDAPOID, err error) { 21 | var octetstring OCTETSTRING 22 | octetstring, err = readTaggedOCTETSTRING(bytes, class, tag) 23 | if err != nil { 24 | err = LdapError{fmt.Sprintf("readTaggedLDAPOID:\n%s", err.Error())} 25 | return 26 | } 27 | // @TODO: check RFC4512 for 28 | ret = LDAPOID(octetstring) 29 | return 30 | } 31 | 32 | func readLDAPOID(bytes *Bytes) (ret LDAPOID, err error) { 33 | return readTaggedLDAPOID(bytes, classUniversal, tagOctetString) 34 | } 35 | 36 | func (l LDAPOID) write(bytes *Bytes) int { 37 | return OCTETSTRING(l).write(bytes) 38 | } 39 | 40 | func (l LDAPOID) writeTagged(bytes *Bytes, class int, tag int) int { 41 | return OCTETSTRING(l).writeTagged(bytes, class, tag) 42 | } 43 | 44 | func (l LDAPOID) size() int { 45 | return OCTETSTRING(l).size() 46 | } 47 | 48 | func (l LDAPOID) sizeTagged(tag int) int { 49 | return OCTETSTRING(l).sizeTagged(tag) 50 | } 51 | -------------------------------------------------------------------------------- /message/partial_attribute.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // PartialAttribute ::= SEQUENCE { 7 | // type AttributeDescription, 8 | // vals SET OF value AttributeValue } 9 | func readPartialAttribute(bytes *Bytes) (ret PartialAttribute, err error) { 10 | ret = PartialAttribute{vals: make([]AttributeValue, 0, 10)} 11 | err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) 12 | if err != nil { 13 | err = LdapError{fmt.Sprintf("readPartialAttribute:\n%s", err.Error())} 14 | return 15 | } 16 | return 17 | } 18 | func (partialattribute *PartialAttribute) readComponents(bytes *Bytes) (err error) { 19 | partialattribute.type_, err = readAttributeDescription(bytes) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 22 | return 23 | } 24 | err = bytes.ReadSubBytes(classUniversal, tagSet, partialattribute.readValsComponents) 25 | if err != nil { 26 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 27 | return 28 | } 29 | return 30 | } 31 | func (partialattribute *PartialAttribute) readValsComponents(bytes *Bytes) (err error) { 32 | for bytes.HasMoreData() { 33 | var attributevalue AttributeValue 34 | attributevalue, err = readAttributeValue(bytes) 35 | if err != nil { 36 | err = LdapError{fmt.Sprintf("readValsComponents:\n%s", err.Error())} 37 | return 38 | } 39 | partialattribute.vals = append(partialattribute.vals, attributevalue) 40 | } 41 | return 42 | } 43 | 44 | // 45 | // PartialAttribute ::= SEQUENCE { 46 | // type AttributeDescription, 47 | // vals SET OF value AttributeValue } 48 | func (p PartialAttribute) write(bytes *Bytes) (size int) { 49 | for i := len(p.vals) - 1; i >= 0; i-- { 50 | size += p.vals[i].write(bytes) 51 | } 52 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSet, size) 53 | size += p.type_.write(bytes) 54 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 55 | return 56 | } 57 | 58 | // 59 | // PartialAttribute ::= SEQUENCE { 60 | // type AttributeDescription, 61 | // vals SET OF value AttributeValue } 62 | func (p PartialAttribute) size() (size int) { 63 | for _, value := range p.vals { 64 | size += value.size() 65 | } 66 | size += sizeTagAndLength(tagSet, size) 67 | size += p.type_.size() 68 | size += sizeTagAndLength(tagSequence, size) 69 | return 70 | } 71 | func (p *PartialAttribute) Type_() AttributeDescription { 72 | return p.type_ 73 | } 74 | func (p *PartialAttribute) Vals() []AttributeValue { 75 | return p.vals 76 | } 77 | -------------------------------------------------------------------------------- /message/partial_attribute_list.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // PartialAttributeList ::= SEQUENCE OF 7 | // partialAttribute PartialAttribute 8 | func readPartialAttributeList(bytes *Bytes) (ret PartialAttributeList, err error) { 9 | ret = PartialAttributeList(make([]PartialAttribute, 0, 10)) 10 | err = bytes.ReadSubBytes(classUniversal, tagSequence, ret.readComponents) 11 | if err != nil { 12 | err = LdapError{fmt.Sprintf("readPartialAttributeList:\n%s", err.Error())} 13 | return 14 | } 15 | return 16 | } 17 | func (partialattributelist *PartialAttributeList) readComponents(bytes *Bytes) (err error) { 18 | for bytes.HasMoreData() { 19 | var partialattribute PartialAttribute 20 | partialattribute, err = readPartialAttribute(bytes) 21 | if err != nil { 22 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 23 | return 24 | } 25 | *partialattributelist = append(*partialattributelist, partialattribute) 26 | } 27 | return 28 | } 29 | 30 | // 31 | // PartialAttributeList ::= SEQUENCE OF 32 | // partialAttribute PartialAttribute 33 | func (p PartialAttributeList) write(bytes *Bytes) (size int) { 34 | for i := len(p) - 1; i >= 0; i-- { 35 | size += p[i].write(bytes) 36 | } 37 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 38 | return 39 | } 40 | 41 | // 42 | // PartialAttributeList ::= SEQUENCE OF 43 | // partialAttribute PartialAttribute 44 | func (p PartialAttributeList) size() (size int) { 45 | for _, att := range p { 46 | size += att.size() 47 | } 48 | size += sizeTagAndLength(tagSequence, size) 49 | return 50 | } 51 | func (p *PartialAttributeList) add(a PartialAttribute) { 52 | *p = append(*p, a) 53 | } 54 | -------------------------------------------------------------------------------- /message/protocol_op.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readProtocolOp(bytes *Bytes) (ret ProtocolOp, err error) { 6 | tagAndLength, err := bytes.PreviewTagAndLength() 7 | if err != nil { 8 | err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())} 9 | return 10 | } 11 | switch tagAndLength.Tag { 12 | case TagBindRequest: 13 | ret, err = readBindRequest(bytes) 14 | case TagBindResponse: 15 | ret, err = readBindResponse(bytes) 16 | case TagUnbindRequest: 17 | ret, err = readUnbindRequest(bytes) 18 | case TagSearchRequest: 19 | ret, err = readSearchRequest(bytes) 20 | case TagSearchResultEntry: 21 | ret, err = readSearchResultEntry(bytes) 22 | case TagSearchResultDone: 23 | ret, err = readSearchResultDone(bytes) 24 | case TagSearchResultReference: 25 | ret, err = readSearchResultReference(bytes) 26 | case TagModifyRequest: 27 | ret, err = readModifyRequest(bytes) 28 | case TagModifyResponse: 29 | ret, err = readModifyResponse(bytes) 30 | case TagAddRequest: 31 | ret, err = readAddRequest(bytes) 32 | case TagAddResponse: 33 | ret, err = readAddResponse(bytes) 34 | case TagDelRequest: 35 | ret, err = readDelRequest(bytes) 36 | case TagDelResponse: 37 | ret, err = readDelResponse(bytes) 38 | case TagModifyDNRequest: 39 | ret, err = readModifyDNRequest(bytes) 40 | case TagModifyDNResponse: 41 | ret, err = readModifyDNResponse(bytes) 42 | case TagCompareRequest: 43 | ret, err = readCompareRequest(bytes) 44 | case TagCompareResponse: 45 | ret, err = readCompareResponse(bytes) 46 | case TagAbandonRequest: 47 | ret, err = readAbandonRequest(bytes) 48 | case TagExtendedRequest: 49 | ret, err = readExtendedRequest(bytes) 50 | case TagExtendedResponse: 51 | ret, err = readExtendedResponse(bytes) 52 | case TagIntermediateResponse: 53 | ret, err = readIntermediateResponse(bytes) 54 | default: 55 | err = LdapError{fmt.Sprintf("readProtocolOp: invalid tag value %d for protocolOp", tagAndLength.Tag)} 56 | return 57 | } 58 | if err != nil { 59 | err = LdapError{fmt.Sprintf("readProtocolOp:\n%s", err.Error())} 60 | return 61 | } 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /message/read.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ReadLDAPMessage(bytes *Bytes) (message LDAPMessage, err error) { 8 | err = bytes.ReadSubBytes(classUniversal, tagSequence, message.readComponents) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("ReadLDAPMessage:\n%s", err.Error())} 11 | return 12 | } 13 | return 14 | } 15 | 16 | // 17 | // END 18 | // 19 | -------------------------------------------------------------------------------- /message/referral.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI 7 | func readTaggedReferral(bytes *Bytes, class int, tag int) (referral Referral, err error) { 8 | err = bytes.ReadSubBytes(class, tag, referral.readComponents) 9 | if err != nil { 10 | err = LdapError{fmt.Sprintf("readTaggedReferral:\n%s", err.Error())} 11 | return 12 | } 13 | return 14 | } 15 | func (referral *Referral) readComponents(bytes *Bytes) (err error) { 16 | for bytes.HasMoreData() { 17 | var uri URI 18 | uri, err = readURI(bytes) 19 | if err != nil { 20 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 21 | return 22 | } 23 | *referral = append(*referral, uri) 24 | } 25 | if len(*referral) == 0 { 26 | return LdapError{"readComponents: expecting at least one URI"} 27 | } 28 | return 29 | } 30 | func (referral Referral) Pointer() *Referral { return &referral } 31 | 32 | // 33 | // Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI 34 | func (r Referral) writeTagged(bytes *Bytes, class int, tag int) (size int) { 35 | for i := len(r) - 1; i >= 0; i-- { 36 | size += r[i].write(bytes) 37 | } 38 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 39 | return 40 | } 41 | 42 | // 43 | // Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI 44 | func (r Referral) sizeTagged(tag int) (size int) { 45 | for _, uri := range r { 46 | size += uri.size() 47 | } 48 | size += sizeTagAndLength(tag, size) 49 | return 50 | } 51 | -------------------------------------------------------------------------------- /message/relative_ldap_dn.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | // 4 | // RelativeLDAPDN ::= LDAPString -- Constrained to 5 | // -- [RFC4514] 6 | func (r RelativeLDAPDN) write(bytes *Bytes) int { 7 | return LDAPString(r).write(bytes) 8 | } 9 | 10 | // 11 | // RelativeLDAPDN ::= LDAPString -- Constrained to 12 | // -- [RFC4514] 13 | func (r RelativeLDAPDN) size() int { 14 | return LDAPString(r).size() 15 | } 16 | -------------------------------------------------------------------------------- /message/result.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // LDAPResult ::= SEQUENCE { 7 | // resultCode ENUMERATED { 8 | // success (0), 9 | // operationsError (1), 10 | // protocolError (2), 11 | // timeLimitExceeded (3), 12 | // sizeLimitExceeded (4), 13 | // compareFalse (5), 14 | // compareTrue (6), 15 | // authMethodNotSupported (7), 16 | // strongerAuthRequired (8), 17 | // -- 9 reserved -- 18 | // referral (10), 19 | // adminLimitExceeded (11), 20 | // unavailableCriticalExtension (12), 21 | // confidentialityRequired (13), 22 | // saslBindInProgress (14), 23 | // 24 | // 25 | // 26 | //Sermersheim Standards Track [Page 55] 27 | // 28 | // 29 | //RFC 4511 LDAPv3 June 2006 30 | // 31 | // 32 | // noSuchAttribute (16), 33 | // undefinedAttributeType (17), 34 | // inappropriateMatching (18), 35 | // constraintViolation (19), 36 | // attributeOrValueExists (20), 37 | // invalidAttributeSyntax (21), 38 | // -- 22-31 unused -- 39 | // noSuchObject (32), 40 | // aliasProblem (33), 41 | // invalidDNSyntax (34), 42 | // -- 35 reserved for undefined isLeaf -- 43 | // aliasDereferencingProblem (36), 44 | // -- 37-47 unused -- 45 | // inappropriateAuthentication (48), 46 | // invalidCredentials (49), 47 | // insufficientAccessRights (50), 48 | // busy (51), 49 | // unavailable (52), 50 | // unwillingToPerform (53), 51 | // loopDetect (54), 52 | // -- 55-63 unused -- 53 | // namingViolation (64), 54 | // objectClassViolation (65), 55 | // notAllowedOnNonLeaf (66), 56 | // notAllowedOnRDN (67), 57 | // entryAlreadyExists (68), 58 | // objectClassModsProhibited (69), 59 | // -- 70 reserved for CLDAP -- 60 | // affectsMultipleDSAs (71), 61 | // -- 72-79 unused -- 62 | // other (80), 63 | // ... }, 64 | // matchedDN LDAPDN, 65 | // diagnosticMessage LDAPString, 66 | // referral [3] Referral OPTIONAL } 67 | func readTaggedLDAPResult(bytes *Bytes, class int, tag int) (ret LDAPResult, err error) { 68 | err = bytes.ReadSubBytes(class, tag, ret.readComponents) 69 | if err != nil { 70 | err = LdapError{fmt.Sprintf("readTaggedLDAPResult:\n%s", err.Error())} 71 | } 72 | return 73 | } 74 | func readLDAPResult(bytes *Bytes) (ldapresult LDAPResult, err error) { 75 | return readTaggedLDAPResult(bytes, classUniversal, tagSequence) 76 | } 77 | func (ldapresult *LDAPResult) readComponents(bytes *Bytes) (err error) { 78 | ldapresult.resultCode, err = readENUMERATED(bytes, EnumeratedLDAPResultCode) 79 | if err != nil { 80 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 81 | return 82 | } 83 | ldapresult.matchedDN, err = readLDAPDN(bytes) 84 | if err != nil { 85 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 86 | return 87 | } 88 | ldapresult.diagnosticMessage, err = readLDAPString(bytes) 89 | if err != nil { 90 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 91 | return 92 | } 93 | if bytes.HasMoreData() { 94 | var tag TagAndLength 95 | tag, err = bytes.PreviewTagAndLength() 96 | if err != nil { 97 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 98 | return 99 | } 100 | if tag.Tag == TagLDAPResultReferral { 101 | var referral Referral 102 | referral, err = readTaggedReferral(bytes, classContextSpecific, TagLDAPResultReferral) 103 | if err != nil { 104 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 105 | return 106 | } 107 | ldapresult.referral = referral.Pointer() 108 | } 109 | } 110 | return 111 | } 112 | 113 | // 114 | // LDAPResult ::= SEQUENCE { 115 | // resultCode ENUMERATED { 116 | // success (0), 117 | // operationsError (1), 118 | // protocolError (2), 119 | // timeLimitExceeded (3), 120 | // sizeLimitExceeded (4), 121 | // compareFalse (5), 122 | // compareTrue (6), 123 | // authMethodNotSupported (7), 124 | // strongerAuthRequired (8), 125 | // -- 9 reserved -- 126 | // referral (10), 127 | // adminLimitExceeded (11), 128 | // unavailableCriticalExtension (12), 129 | // confidentialityRequired (13), 130 | // saslBindInProgress (14), 131 | // 132 | // 133 | // 134 | //Sermersheim Standards Track [Page 55] 135 | // 136 | // 137 | //RFC 4511 LDAPv3 June 2006 138 | // 139 | // 140 | // noSuchAttribute (16), 141 | // undefinedAttributeType (17), 142 | // inappropriateMatching (18), 143 | // constraintViolation (19), 144 | // attributeOrValueExists (20), 145 | // invalidAttributeSyntax (21), 146 | // -- 22-31 unused -- 147 | // noSuchObject (32), 148 | // aliasProblem (33), 149 | // invalidDNSyntax (34), 150 | // -- 35 reserved for undefined isLeaf -- 151 | // aliasDereferencingProblem (36), 152 | // -- 37-47 unused -- 153 | // inappropriateAuthentication (48), 154 | // invalidCredentials (49), 155 | // insufficientAccessRights (50), 156 | // busy (51), 157 | // unavailable (52), 158 | // unwillingToPerform (53), 159 | // loopDetect (54), 160 | // -- 55-63 unused -- 161 | // namingViolation (64), 162 | // objectClassViolation (65), 163 | // notAllowedOnNonLeaf (66), 164 | // notAllowedOnRDN (67), 165 | // entryAlreadyExists (68), 166 | // objectClassModsProhibited (69), 167 | // -- 70 reserved for CLDAP -- 168 | // affectsMultipleDSAs (71), 169 | // -- 72-79 unused -- 170 | // other (80), 171 | // ... }, 172 | // matchedDN LDAPDN, 173 | // diagnosticMessage LDAPString, 174 | // referral [3] Referral OPTIONAL } 175 | func (l LDAPResult) write(bytes *Bytes) (size int) { 176 | size += l.writeComponents(bytes) 177 | size += bytes.WriteTagAndLength(classUniversal, isCompound, tagSequence, size) 178 | return 179 | } 180 | func (l LDAPResult) writeComponents(bytes *Bytes) (size int) { 181 | if l.referral != nil { 182 | size += l.referral.writeTagged(bytes, classContextSpecific, TagLDAPResultReferral) 183 | } 184 | size += l.diagnosticMessage.write(bytes) 185 | size += l.matchedDN.write(bytes) 186 | size += l.resultCode.write(bytes) 187 | return 188 | } 189 | 190 | // 191 | // LDAPResult ::= SEQUENCE { 192 | // resultCode ENUMERATED { 193 | // success (0), 194 | // operationsError (1), 195 | // protocolError (2), 196 | // timeLimitExceeded (3), 197 | // sizeLimitExceeded (4), 198 | // compareFalse (5), 199 | // compareTrue (6), 200 | // authMethodNotSupported (7), 201 | // strongerAuthRequired (8), 202 | // -- 9 reserved -- 203 | // referral (10), 204 | // adminLimitExceeded (11), 205 | // unavailableCriticalExtension (12), 206 | // confidentialityRequired (13), 207 | // saslBindInProgress (14), 208 | // 209 | // 210 | // 211 | //Sermersheim Standards Track [Page 55] 212 | // 213 | // 214 | //RFC 4511 LDAPv3 June 2006 215 | // 216 | // 217 | // noSuchAttribute (16), 218 | // undefinedAttributeType (17), 219 | // inappropriateMatching (18), 220 | // constraintViolation (19), 221 | // attributeOrValueExists (20), 222 | // invalidAttributeSyntax (21), 223 | // -- 22-31 unused -- 224 | // noSuchObject (32), 225 | // aliasProblem (33), 226 | // invalidDNSyntax (34), 227 | // -- 35 reserved for undefined isLeaf -- 228 | // aliasDereferencingProblem (36), 229 | // -- 37-47 unused -- 230 | // inappropriateAuthentication (48), 231 | // invalidCredentials (49), 232 | // insufficientAccessRights (50), 233 | // busy (51), 234 | // unavailable (52), 235 | // unwillingToPerform (53), 236 | // loopDetect (54), 237 | // -- 55-63 unused -- 238 | // namingViolation (64), 239 | // objectClassViolation (65), 240 | // notAllowedOnNonLeaf (66), 241 | // notAllowedOnRDN (67), 242 | // entryAlreadyExists (68), 243 | // objectClassModsProhibited (69), 244 | // -- 70 reserved for CLDAP -- 245 | // affectsMultipleDSAs (71), 246 | // -- 72-79 unused -- 247 | // other (80), 248 | // ... }, 249 | // matchedDN LDAPDN, 250 | // diagnosticMessage LDAPString, 251 | // referral [3] Referral OPTIONAL } 252 | func (l LDAPResult) size() (size int) { 253 | size += l.sizeComponents() 254 | size += sizeTagAndLength(tagSequence, size) 255 | return 256 | } 257 | func (l LDAPResult) sizeTagged(tag int) (size int) { 258 | size += l.sizeComponents() 259 | size += sizeTagAndLength(tag, size) 260 | return 261 | } 262 | func (l LDAPResult) sizeComponents() (size int) { 263 | if l.referral != nil { 264 | size += l.referral.sizeTagged(TagLDAPResultReferral) 265 | } 266 | size += l.diagnosticMessage.size() 267 | size += l.matchedDN.size() 268 | size += l.resultCode.size() 269 | return 270 | } 271 | func (l *LDAPResult) SetResultCode(code int) { 272 | l.resultCode = ENUMERATED(code) 273 | } 274 | func (l *LDAPResult) SeMatchedDN(code string) { 275 | l.matchedDN = LDAPDN(code) 276 | } 277 | func (l *LDAPResult) SetDiagnosticMessage(code string) { 278 | l.diagnosticMessage = LDAPString(code) 279 | } 280 | func (l *LDAPResult) SetReferral(r *Referral) { 281 | l.referral = r 282 | } 283 | -------------------------------------------------------------------------------- /message/sasl_credentials.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // SaslCredentials ::= SEQUENCE { 7 | // mechanism LDAPString, 8 | // credentials OCTET STRING OPTIONAL } 9 | // 10 | func readSaslCredentials(bytes *Bytes) (authentication SaslCredentials, err error) { 11 | authentication = SaslCredentials{} 12 | err = bytes.ReadSubBytes(classContextSpecific, TagAuthenticationChoiceSaslCredentials, authentication.readComponents) 13 | if err != nil { 14 | err = LdapError{fmt.Sprintf("readSaslCredentials:\n%s", err.Error())} 15 | return 16 | } 17 | return 18 | } 19 | func (authentication *SaslCredentials) readComponents(bytes *Bytes) (err error) { 20 | authentication.mechanism, err = readLDAPString(bytes) 21 | if err != nil { 22 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 23 | return 24 | } 25 | if bytes.HasMoreData() { 26 | var credentials OCTETSTRING 27 | credentials, err = readOCTETSTRING(bytes) 28 | if err != nil { 29 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 30 | return 31 | } 32 | authentication.credentials = credentials.Pointer() 33 | } 34 | return 35 | } 36 | 37 | // 38 | // SaslCredentials ::= SEQUENCE { 39 | // mechanism LDAPString, 40 | // credentials OCTET STRING OPTIONAL } 41 | // 42 | func (s SaslCredentials) writeTagged(bytes *Bytes, class int, tag int) (size int) { 43 | if s.credentials != nil { 44 | size += s.credentials.write(bytes) 45 | } 46 | size += s.mechanism.write(bytes) 47 | size += bytes.WriteTagAndLength(class, isCompound, tag, size) 48 | return 49 | } 50 | 51 | // 52 | // SaslCredentials ::= SEQUENCE { 53 | // mechanism LDAPString, 54 | // credentials OCTET STRING OPTIONAL } 55 | // 56 | func (s SaslCredentials) sizeTagged(tag int) (size int) { 57 | if s.credentials != nil { 58 | size += s.credentials.size() 59 | } 60 | size += s.mechanism.size() 61 | size += sizeTagAndLength(tag, size) 62 | return 63 | } 64 | -------------------------------------------------------------------------------- /message/search_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | // 9 | // SearchRequest ::= [APPLICATION 3] SEQUENCE { 10 | // baseObject LDAPDN, 11 | // scope ENUMERATED { 12 | // baseObject (0), 13 | // singleLevel (1), 14 | // wholeSubtree (2), 15 | // ... }, 16 | // derefAliases ENUMERATED { 17 | // neverDerefAliases (0), 18 | // derefInSearching (1), 19 | // derefFindingBaseObj (2), 20 | // derefAlways (3) }, 21 | // sizeLimit INTEGER (0 .. maxInt), 22 | // timeLimit INTEGER (0 .. maxInt), 23 | // typesOnly BOOLEAN, 24 | // filter Filter, 25 | // attributes AttributeSelection } 26 | func readSearchRequest(bytes *Bytes) (searchrequest SearchRequest, err error) { 27 | err = bytes.ReadSubBytes(classApplication, TagSearchRequest, searchrequest.readComponents) 28 | if err != nil { 29 | err = LdapError{fmt.Sprintf("readSearchRequest:\n%s", err.Error())} 30 | return 31 | } 32 | return 33 | } 34 | func (searchrequest *SearchRequest) readComponents(bytes *Bytes) (err error) { 35 | searchrequest.baseObject, err = readLDAPDN(bytes) 36 | if err != nil { 37 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 38 | return 39 | } 40 | searchrequest.scope, err = readENUMERATED(bytes, EnumeratedSearchRequestScope) 41 | if err != nil { 42 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 43 | return 44 | } 45 | searchrequest.derefAliases, err = readENUMERATED(bytes, EnumeratedSearchRequestDerefAliases) 46 | if err != nil { 47 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 48 | return 49 | } 50 | searchrequest.sizeLimit, err = readPositiveINTEGER(bytes) 51 | if err != nil { 52 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 53 | return 54 | } 55 | searchrequest.timeLimit, err = readPositiveINTEGER(bytes) 56 | if err != nil { 57 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 58 | return 59 | } 60 | searchrequest.typesOnly, err = readBOOLEAN(bytes) 61 | if err != nil { 62 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 63 | return 64 | } 65 | searchrequest.filter, err = readFilter(bytes) 66 | if err != nil { 67 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 68 | return 69 | } 70 | searchrequest.attributes, err = readAttributeSelection(bytes) 71 | if err != nil { 72 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 73 | return 74 | } 75 | return 76 | } 77 | 78 | // 79 | // SearchRequest ::= [APPLICATION 3] SEQUENCE { 80 | // baseObject LDAPDN, 81 | // scope ENUMERATED { 82 | // baseObject (0), 83 | // singleLevel (1), 84 | // wholeSubtree (2), 85 | // ... }, 86 | // derefAliases ENUMERATED { 87 | // neverDerefAliases (0), 88 | // derefInSearching (1), 89 | // derefFindingBaseObj (2), 90 | // derefAlways (3) }, 91 | // sizeLimit INTEGER (0 .. maxInt), 92 | // timeLimit INTEGER (0 .. maxInt), 93 | // typesOnly BOOLEAN, 94 | // filter Filter, 95 | // attributes AttributeSelection } 96 | func (s SearchRequest) write(bytes *Bytes) (size int) { 97 | size += s.attributes.write(bytes) 98 | size += s.filter.write(bytes) 99 | size += s.typesOnly.write(bytes) 100 | size += s.timeLimit.write(bytes) 101 | size += s.sizeLimit.write(bytes) 102 | size += s.derefAliases.write(bytes) 103 | size += s.scope.write(bytes) 104 | size += s.baseObject.write(bytes) 105 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchRequest, size) 106 | return 107 | } 108 | 109 | // 110 | // SearchRequest ::= [APPLICATION 3] SEQUENCE { 111 | // baseObject LDAPDN, 112 | // scope ENUMERATED { 113 | // baseObject (0), 114 | // singleLevel (1), 115 | // wholeSubtree (2), 116 | // ... }, 117 | // derefAliases ENUMERATED { 118 | // neverDerefAliases (0), 119 | // derefInSearching (1), 120 | // derefFindingBaseObj (2), 121 | // derefAlways (3) }, 122 | // sizeLimit INTEGER (0 .. maxInt), 123 | // timeLimit INTEGER (0 .. maxInt), 124 | // typesOnly BOOLEAN, 125 | // filter Filter, 126 | // attributes AttributeSelection } 127 | func (s SearchRequest) size() (size int) { 128 | size += s.baseObject.size() 129 | size += s.scope.size() 130 | size += s.derefAliases.size() 131 | size += s.sizeLimit.size() 132 | size += s.timeLimit.size() 133 | size += s.typesOnly.size() 134 | size += s.filter.size() 135 | size += s.attributes.size() 136 | size += sizeTagAndLength(TagSearchRequest, size) 137 | return 138 | } 139 | func (s *SearchRequest) BaseObject() LDAPDN { 140 | return s.baseObject 141 | } 142 | func (s *SearchRequest) Scope() ENUMERATED { 143 | return s.scope 144 | } 145 | func (s *SearchRequest) DerefAliases() ENUMERATED { 146 | return s.derefAliases 147 | } 148 | func (s *SearchRequest) SizeLimit() INTEGER { 149 | return s.sizeLimit 150 | } 151 | func (s *SearchRequest) TimeLimit() INTEGER { 152 | return s.timeLimit 153 | } 154 | func (s *SearchRequest) TypesOnly() BOOLEAN { 155 | return s.typesOnly 156 | } 157 | func (s *SearchRequest) Attributes() AttributeSelection { 158 | return s.attributes 159 | } 160 | func (s *SearchRequest) Filter() Filter { 161 | return s.filter 162 | } 163 | func (s *SearchRequest) FilterString() string { 164 | str, _ := s.decompileFilter(s.Filter()) 165 | return str 166 | } 167 | func (s *SearchRequest) decompileFilter(packet Filter) (ret string, err error) { 168 | defer func() { 169 | if r := recover(); r != nil { 170 | err = errors.New("error decompiling filter") 171 | } 172 | }() 173 | 174 | ret = "(" 175 | err = nil 176 | childStr := "" 177 | 178 | switch f := packet.(type) { 179 | case FilterAnd: 180 | ret += "&" 181 | for _, child := range f { 182 | childStr, err = s.decompileFilter(child) 183 | if err != nil { 184 | return 185 | } 186 | ret += childStr 187 | } 188 | case FilterOr: 189 | ret += "|" 190 | for _, child := range f { 191 | childStr, err = s.decompileFilter(child) 192 | if err != nil { 193 | return 194 | } 195 | ret += childStr 196 | } 197 | case FilterNot: 198 | ret += "!" 199 | childStr, err = s.decompileFilter(f.Filter) 200 | if err != nil { 201 | return 202 | } 203 | ret += childStr 204 | 205 | case FilterSubstrings: 206 | ret += string(f.Type_()) 207 | ret += "=" 208 | for _, fs := range f.Substrings() { 209 | switch fsv := fs.(type) { 210 | case SubstringInitial: 211 | ret += string(fsv) + "*" 212 | case SubstringAny: 213 | ret += "*" + string(fsv) + "*" 214 | case SubstringFinal: 215 | ret += "*" + string(fsv) 216 | } 217 | } 218 | case FilterEqualityMatch: 219 | ret += string(f.AttributeDesc()) 220 | ret += "=" 221 | ret += string(f.AssertionValue()) 222 | case FilterGreaterOrEqual: 223 | ret += string(f.AttributeDesc()) 224 | ret += ">=" 225 | ret += string(f.AssertionValue()) 226 | case FilterLessOrEqual: 227 | ret += string(f.AttributeDesc()) 228 | ret += "<=" 229 | ret += string(f.AssertionValue()) 230 | case FilterPresent: 231 | // if 0 == len(packet.Children) { 232 | // ret += ber.DecodeString(packet.Data.Bytes()) 233 | // } else { 234 | // ret += ber.DecodeString(packet.Children[0].Data.Bytes()) 235 | // } 236 | ret += string(f) 237 | ret += "=*" 238 | case FilterApproxMatch: 239 | ret += string(f.AttributeDesc()) 240 | ret += "~=" 241 | ret += string(f.AssertionValue()) 242 | } 243 | 244 | ret += ")" 245 | return 246 | } 247 | -------------------------------------------------------------------------------- /message/search_result_done.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // SearchResultDone ::= [APPLICATION 5] LDAPResult 7 | func readSearchResultDone(bytes *Bytes) (ret SearchResultDone, err error) { 8 | var ldapresult LDAPResult 9 | ldapresult, err = readTaggedLDAPResult(bytes, classApplication, TagSearchResultDone) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readSearchResultDone:\n%s", err.Error())} 12 | return 13 | } 14 | ret = SearchResultDone(ldapresult) 15 | return 16 | } 17 | 18 | // 19 | // SearchResultDone ::= [APPLICATION 5] LDAPResult 20 | func (s SearchResultDone) write(bytes *Bytes) int { 21 | return LDAPResult(s).writeTagged(bytes, classApplication, TagSearchResultDone) 22 | } 23 | 24 | // 25 | // SearchResultDone ::= [APPLICATION 5] LDAPResult 26 | func (s SearchResultDone) size() int { 27 | return LDAPResult(s).sizeTagged(TagSearchResultDone) 28 | } 29 | func (l *SearchResultDone) SetResultCode(code int) { 30 | l.resultCode = ENUMERATED(code) 31 | } 32 | -------------------------------------------------------------------------------- /message/search_result_entry.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { 7 | // objectName LDAPDN, 8 | // attributes PartialAttributeList } 9 | func readSearchResultEntry(bytes *Bytes) (searchresultentry SearchResultEntry, err error) { 10 | err = bytes.ReadSubBytes(classApplication, TagSearchResultEntry, searchresultentry.readComponents) 11 | if err != nil { 12 | err = LdapError{fmt.Sprintf("readSearchResultEntry:\n%s", err.Error())} 13 | return 14 | } 15 | return 16 | } 17 | func (searchresultentry *SearchResultEntry) readComponents(bytes *Bytes) (err error) { 18 | searchresultentry.objectName, err = readLDAPDN(bytes) 19 | if err != nil { 20 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 21 | return 22 | } 23 | searchresultentry.attributes, err = readPartialAttributeList(bytes) 24 | if err != nil { 25 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 26 | return 27 | } 28 | return 29 | } 30 | 31 | // 32 | // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { 33 | // objectName LDAPDN, 34 | // attributes PartialAttributeList } 35 | func (s SearchResultEntry) write(bytes *Bytes) (size int) { 36 | size += s.attributes.write(bytes) 37 | size += s.objectName.write(bytes) 38 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultEntry, size) 39 | return 40 | } 41 | 42 | // 43 | // SearchResultEntry ::= [APPLICATION 4] SEQUENCE { 44 | // objectName LDAPDN, 45 | // attributes PartialAttributeList } 46 | func (s SearchResultEntry) size() (size int) { 47 | size += s.objectName.size() 48 | size += s.attributes.size() 49 | size += sizeTagAndLength(tagSequence, size) 50 | return 51 | } 52 | func (s *SearchResultEntry) SetObjectName(on string) { 53 | s.objectName = LDAPDN(on) 54 | } 55 | func (s *SearchResultEntry) AddAttribute(name AttributeDescription, values ...AttributeValue) { 56 | var ea = PartialAttribute{type_: name, vals: values} 57 | s.attributes.add(ea) 58 | } 59 | -------------------------------------------------------------------------------- /message/search_result_reference.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // SearchResultReference ::= [APPLICATION 19] SEQUENCE 7 | // SIZE (1..MAX) OF uri URI 8 | func readSearchResultReference(bytes *Bytes) (ret SearchResultReference, err error) { 9 | err = bytes.ReadSubBytes(classApplication, TagSearchResultReference, ret.readComponents) 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readSearchResultReference:\n%s", err.Error())} 12 | return 13 | } 14 | return 15 | } 16 | func (s *SearchResultReference) readComponents(bytes *Bytes) (err error) { 17 | for bytes.HasMoreData() { 18 | var uri URI 19 | uri, err = readURI(bytes) 20 | if err != nil { 21 | err = LdapError{fmt.Sprintf("readComponents:\n%s", err.Error())} 22 | return 23 | } 24 | *s = append(*s, uri) 25 | } 26 | if len(*s) == 0 { 27 | err = LdapError{"readComponents: expecting at least one URI"} 28 | return 29 | } 30 | return 31 | } 32 | 33 | // 34 | // SearchResultReference ::= [APPLICATION 19] SEQUENCE 35 | // SIZE (1..MAX) OF uri URI 36 | func (s SearchResultReference) write(bytes *Bytes) (size int) { 37 | for i := len(s) - 1; i >= 0; i-- { 38 | size += s[i].write(bytes) 39 | } 40 | size += bytes.WriteTagAndLength(classApplication, isCompound, TagSearchResultReference, size) 41 | return 42 | } 43 | 44 | // 45 | // SearchResultReference ::= [APPLICATION 19] SEQUENCE 46 | // SIZE (1..MAX) OF uri URI 47 | func (s SearchResultReference) size() (size int) { 48 | for _, uri := range s { 49 | size += uri.size() 50 | } 51 | size += sizeTagAndLength(tagSequence, size) 52 | return 53 | } 54 | -------------------------------------------------------------------------------- /message/size_test.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestSizeLDAPMessage(t *testing.T) { 8 | 9 | var testData = getLDAPMessageTestData() 10 | for i, test := range testData { 11 | message, err := ReadLDAPMessage(&test.bytes) 12 | if err != nil { 13 | t.Errorf("#%d error at offset %d (%s): %s", i, test.bytes.offset, test.bytes.DumpCurrentBytes(), err) 14 | } 15 | size := message.size() 16 | expected := len(test.bytes.bytes) 17 | if size != expected { 18 | t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, expected) 19 | } 20 | } 21 | } 22 | 23 | type tagAndLengthTestData struct { 24 | tag int 25 | length int 26 | expectedSize int 27 | } 28 | 29 | func getSizeTagAndLengthTestData() (ret []tagAndLengthTestData) { 30 | return []tagAndLengthTestData{ 31 | // Length between 0 and 127 are encoded on one byte 32 | { 33 | tag: tagSequence, 34 | length: 0, 35 | expectedSize: 2, 36 | }, 37 | { 38 | tag: tagSequence, 39 | length: 127, 40 | expectedSize: 2, 41 | }, 42 | // Length between 128 and 255 are encoded on two bytes 43 | { 44 | tag: tagSequence, 45 | length: 128, 46 | expectedSize: 3, 47 | }, 48 | { 49 | tag: tagSequence, 50 | length: 255, 51 | expectedSize: 3, 52 | }, 53 | // Length between 256 (2^8) and 65535 (2^16-1) are encoded on three bytes 54 | { 55 | tag: tagSequence, 56 | length: 256, 57 | expectedSize: 4, 58 | }, 59 | { 60 | tag: tagSequence, 61 | length: 65535, 62 | expectedSize: 4, 63 | }, 64 | // Length between 65536 (2^16) and 16777215 (2^24-1) are encoded on four bytes 65 | { 66 | tag: tagSequence, 67 | length: 65536, 68 | expectedSize: 5, 69 | }, 70 | { 71 | tag: tagSequence, 72 | length: 16777215, 73 | expectedSize: 5, 74 | }, 75 | } 76 | } 77 | func TestSizeTagAndLength(t *testing.T) { 78 | for i, test := range getSizeTagAndLengthTestData() { 79 | size := sizeTagAndLength(test.tag, test.length) 80 | if test.expectedSize != size { 81 | t.Errorf("#%d: wrong size, GOT: %d, EXPECTED: %d", i, size, test.expectedSize) 82 | } 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /message/string.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | func readTaggedLDAPString(bytes *Bytes, class int, tag int) (ldapstring LDAPString, err error) { 6 | var octetstring OCTETSTRING 7 | octetstring, err = readTaggedOCTETSTRING(bytes, class, tag) 8 | if err != nil { 9 | err = LdapError{fmt.Sprintf("readTaggedLDAPString:\n%s", err.Error())} 10 | return 11 | } 12 | ldapstring = LDAPString(octetstring) 13 | return 14 | } 15 | 16 | // LDAPString ::= OCTET STRING -- UTF-8 encoded, 17 | // -- [ISO10646] characters 18 | func readLDAPString(bytes *Bytes) (ldapstring LDAPString, err error) { 19 | return readTaggedLDAPString(bytes, classUniversal, tagOctetString) 20 | } 21 | 22 | // LDAPString ::= OCTET STRING -- UTF-8 encoded, 23 | // -- [ISO10646] characters 24 | func (s LDAPString) write(bytes *Bytes) int { 25 | return OCTETSTRING(s).write(bytes) 26 | } 27 | func (s LDAPString) writeTagged(bytes *Bytes, class int, tag int) int { 28 | return OCTETSTRING(s).writeTagged(bytes, class, tag) 29 | } 30 | 31 | // LDAPString ::= OCTET STRING -- UTF-8 encoded, 32 | // -- [ISO10646] characters 33 | func (s LDAPString) size() int { 34 | return OCTETSTRING(s).size() 35 | } 36 | func (s LDAPString) sizeTagged(tag int) int { 37 | return OCTETSTRING(s).sizeTagged(tag) 38 | } 39 | -------------------------------------------------------------------------------- /message/struct_methods.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | func NewLDAPMessageWithProtocolOp(po ProtocolOp) *LDAPMessage { 4 | m := NewLDAPMessage() 5 | m.protocolOp = po 6 | return m 7 | } 8 | -------------------------------------------------------------------------------- /message/unbind_request.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // UnbindRequest ::= [APPLICATION 2] NULL 7 | func readUnbindRequest(bytes *Bytes) (unbindrequest UnbindRequest, err error) { 8 | var tagAndLength TagAndLength 9 | tagAndLength, err = bytes.ParseTagAndLength() 10 | if err != nil { 11 | err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())} 12 | return 13 | } 14 | err = tagAndLength.Expect(classApplication, TagUnbindRequest, isNotCompound) 15 | if err != nil { 16 | err = LdapError{fmt.Sprintf("readUnbindRequest:\n%s", err.Error())} 17 | return 18 | } 19 | if tagAndLength.Length != 0 { 20 | err = LdapError{"readUnbindRequest: expecting NULL"} 21 | return 22 | } 23 | return 24 | } 25 | 26 | // 27 | // UnbindRequest ::= [APPLICATION 2] NULL 28 | func (u UnbindRequest) write(bytes *Bytes) (size int) { 29 | size += bytes.WriteTagAndLength(classApplication, isNotCompound, TagUnbindRequest, 0) 30 | return 31 | } 32 | 33 | // 34 | // UnbindRequest ::= [APPLICATION 2] NULL 35 | func (u UnbindRequest) size() (size int) { 36 | size = sizeTagAndLength(TagUnbindRequest, 0) 37 | return 38 | } 39 | -------------------------------------------------------------------------------- /message/uri.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import "fmt" 4 | 5 | // 6 | // URI ::= LDAPString -- limited to characters permitted in 7 | // -- URIs 8 | func readURI(bytes *Bytes) (uri URI, err error) { 9 | var ldapstring LDAPString 10 | ldapstring, err = readLDAPString(bytes) 11 | // @TODO: check permitted chars in URI 12 | if err != nil { 13 | err = LdapError{fmt.Sprintf("readURI:\n%s", err.Error())} 14 | return 15 | } 16 | uri = URI(ldapstring) 17 | return 18 | } 19 | 20 | // 21 | // URI ::= LDAPString -- limited to characters permitted in 22 | // -- URIs 23 | func (u URI) write(bytes *Bytes) int { 24 | return LDAPString(u).write(bytes) 25 | } 26 | 27 | // 28 | // URI ::= LDAPString -- limited to characters permitted in 29 | // -- URIs 30 | func (u URI) size() int { 31 | return LDAPString(u).size() 32 | } 33 | -------------------------------------------------------------------------------- /message/write.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | type Writable interface { 4 | write(bytes *Bytes) int 5 | writeTagged(bytes *Bytes, class int, tag int) int 6 | } 7 | -------------------------------------------------------------------------------- /message/write_test.go: -------------------------------------------------------------------------------- 1 | package message 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestWriteLDAPMessage(t *testing.T) { 9 | 10 | var testData = getLDAPMessageTestData() 11 | for i, test := range testData { 12 | bytes, err := test.out.Write() 13 | if err != nil { 14 | 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()) 15 | } else if !reflect.DeepEqual(bytes.getBytes(), test.bytes.getBytes()) { 16 | t.Errorf("#%d:\nGOT:\n%#+v\nEXPECTED:\n%#+v", i, bytes.getBytes(), test.bytes.getBytes()) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /perf/cpu_ondemand.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in /sys/devices/system/cpu/cpu[0-7] 3 | do 4 | echo ondemand > $i/cpufreq/scaling_governor 5 | done 6 | -------------------------------------------------------------------------------- /perf/cpu_performance.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in /sys/devices/system/cpu/cpu[0-7] 3 | do 4 | echo performance > $i/cpufreq/scaling_governor 5 | done 6 | -------------------------------------------------------------------------------- /perf/cpu_powersave.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in /sys/devices/system/cpu/cpu[0-7] 3 | do 4 | echo powersave > $i/cpufreq/scaling_governor 5 | done 6 | -------------------------------------------------------------------------------- /perf/perf.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | MAIN=$1 3 | if [ "$1" = "" ] 4 | then 5 | echo "Please specify a version name. Ex ./perf main1" 6 | exit 1 7 | fi 8 | 9 | echo "Compiling..." 10 | go build main.go 11 | mv main $MAIN 12 | echo "Perf..." 13 | ./xtime.sh ./$MAIN 14 | echo "Profiling..." 15 | ./$MAIN -cpuprofile=${MAIN}.prof 16 | echo "OK now launch: go tool pprof $MAIN ${MAIN}.prof" 17 | # go tool pprof ./$MAIN ${MAIN}.prof 18 | -------------------------------------------------------------------------------- /perf/xtime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | /usr/bin/time -f '%Uu %Ss %er %MkB %C' "$@" 3 | --------------------------------------------------------------------------------