namespace Unosquare.Swan.Networking.Ldap { using System; using System.IO; /// /// Represents an Ldap Message. ///
    /// LdapMessage ::= SEQUENCE {
    /// messageID       MessageID,
    /// protocolOp      CHOICE {
    /// bindRequest     BindRequest,
    /// bindResponse    BindResponse,
    /// unbindRequest   UnbindRequest,
    /// searchRequest   SearchRequest,
    /// searchResEntry  SearchResultEntry,
    /// searchResDone   SearchResultDone,
    /// searchResRef    SearchResultReference,
    /// modifyRequest   ModifyRequest,
    /// modifyResponse  ModifyResponse,
    /// addRequest      AddRequest,
    /// addResponse     AddResponse,
    /// delRequest      DelRequest,
    /// delResponse     DelResponse,
    /// modDNRequest    ModifyDNRequest,
    /// modDNResponse   ModifyDNResponse,
    /// compareRequest  CompareRequest,
    /// compareResponse CompareResponse,
    /// abandonRequest  AbandonRequest,
    /// extendedReq     ExtendedRequest,
    /// extendedResp    ExtendedResponse },
    /// controls       [0] Controls OPTIONAL }
    /// 
/// Note: The creation of a MessageID should be hidden within the creation of /// an RfcLdapMessage. The MessageID needs to be in sequence, and has an /// upper and lower limit. There is never a case when a user should be /// able to specify the MessageID for an RfcLdapMessage. The MessageID() /// constructor should be package protected. (So the MessageID value /// isn't arbitrarily run up.). ///
/// internal sealed class RfcLdapMessage : Asn1Sequence { private readonly Asn1Object _op; /// /// Initializes a new instance of the class. /// Create an RfcLdapMessage request from input parameters. /// /// The op. /// The controls. public RfcLdapMessage(IRfcRequest op, RfcControls controls) : base(3) { _op = (Asn1Object) op; Add(new RfcMessageID()); // MessageID has static counter Add((Asn1Object) op); if (controls != null) { Add(controls); } } /// /// Initializes a new instance of the class. /// Will decode an RfcLdapMessage directly from an InputStream. /// /// The stream. /// The length. /// RfcLdapMessage: Invalid tag: " + protocolOpId.Tag. public RfcLdapMessage(Stream stream, int len) : base(stream, len) { // Decode implicitly tagged protocol operation from an Asn1Tagged type // to its appropriate application type. var protocolOp = (Asn1Tagged) Get(1); var protocolOpId = protocolOp.GetIdentifier(); var content = ((Asn1OctetString) protocolOp.TaggedValue).ByteValue(); var bais = new MemoryStream(content.ToByteArray()); switch ((LdapOperation) protocolOpId.Tag) { case LdapOperation.SearchResponse: Set(1, new RfcSearchResultEntry(bais, content.Length)); break; case LdapOperation.SearchResult: Set(1, new RfcSearchResultDone(bais, content.Length)); break; case LdapOperation.SearchResultReference: Set(1, new RfcSearchResultReference(bais, content.Length)); break; case LdapOperation.BindResponse: Set(1, new RfcBindResponse(bais, content.Length)); break; case LdapOperation.ExtendedResponse: Set(1, new RfcExtendedResponse(bais, content.Length)); break; case LdapOperation.IntermediateResponse: Set(1, new RfcIntermediateResponse(bais, content.Length)); break; case LdapOperation.ModifyResponse: Set(1, new RfcModifyResponse(bais, content.Length)); break; default: throw new InvalidOperationException($"RfcLdapMessage: Invalid tag: {protocolOpId.Tag}"); } // decode optional implicitly tagged controls from Asn1Tagged type to // to RFC 2251 types. if (Size() <= 2) return; var controls = (Asn1Tagged) Get(2); content = ((Asn1OctetString) controls.TaggedValue).ByteValue(); using (var ms = new MemoryStream(content.ToByteArray())) Set(2, new RfcControls(ms, content.Length)); } public int MessageId => ((Asn1Integer) Get(0)).IntValue(); /// Returns this RfcLdapMessage's message type. public LdapOperation Type => (LdapOperation) Get(1).GetIdentifier().Tag; public Asn1Object Response => Get(1); public string RequestDn => ((IRfcRequest) _op).GetRequestDN(); public LdapMessage RequestingMessage { get; set; } public IRfcRequest GetRequest() => (IRfcRequest) Get(1); public bool IsRequest() => Get(1) is IRfcRequest; } /// /// Represents Ldap Controls. ///
    /// Controls ::= SEQUENCE OF Control
    /// 
///
/// internal class RfcControls : Asn1SequenceOf { public const int Controls = 0; public RfcControls() : base(5) { } public RfcControls(Stream stream, int len) : base(stream, len) { // Convert each SEQUENCE element to a Control for (var i = 0; i < Size(); i++) { var tempControl = new RfcControl((Asn1Sequence) Get(i)); Set(i, tempControl); } } public void Add(RfcControl control) => base.Add(control); public void Set(int index, RfcControl control) => base.Set(index, control); public override Asn1Identifier GetIdentifier() => new Asn1Identifier(Controls, true); } /// /// This interface represents RfcLdapMessages that contain a response from a /// server. /// If the protocol operation of the RfcLdapMessage is of this type, /// it contains at least an RfcLdapResult. /// internal interface IRfcResponse { /// /// Gets the result code. /// /// Asn1Enumerated. Asn1Enumerated GetResultCode(); /// /// Gets the matched dn. /// /// RfcLdapDN. Asn1OctetString GetMatchedDN(); /// /// Gets the error message. /// /// RfcLdapString. Asn1OctetString GetErrorMessage(); /// /// Gets the referral. /// /// Asn1SequenceOf. Asn1SequenceOf GetReferral(); } /// /// This interface represents Protocol Operations that are requests from a /// client. /// internal interface IRfcRequest { /// /// Builds a new request using the data from the this object. /// /// A . string GetRequestDN(); } /// /// Represents an LdapResult. ///
    /// LdapResult ::= SEQUENCE {
    /// resultCode      ENUMERATED {
    /// success                      (0),
    /// operationsError              (1),
    /// protocolError                (2),
    /// timeLimitExceeded            (3),
    /// sizeLimitExceeded            (4),
    /// compareFalse                 (5),
    /// compareTrue                  (6),
    /// authMethodNotSupported       (7),
    /// strongAuthRequired           (8),
    /// -- 9 reserved --
    /// referral                     (10),  -- new
    /// adminLimitExceeded           (11),  -- new
    /// unavailableCriticalExtension (12),  -- new
    /// confidentialityRequired      (13),  -- new
    /// saslBindInProgress           (14),  -- new
    /// noSuchAttribute              (16),
    /// undefinedAttributeType       (17),
    /// inappropriateMatching        (18),
    /// constraintViolation          (19),
    /// attributeOrValueExists       (20),
    /// invalidAttributeSyntax       (21),
    /// -- 22-31 unused --
    /// noSuchObject                 (32),
    /// aliasProblem                 (33),
    /// invalidDNSyntax              (34),
    /// -- 35 reserved for undefined isLeaf --
    /// aliasDereferencingProblem    (36),
    /// -- 37-47 unused --
    /// inappropriateAuthentication  (48),
    /// invalidCredentials           (49),
    /// insufficientAccessRights     (50),
    /// busy                         (51),
    /// unavailable                  (52),
    /// unwillingToPerform           (53),
    /// loopDetect                   (54),
    /// -- 55-63 unused --
    /// namingViolation              (64),
    /// objectClassViolation         (65),
    /// notAllowedOnNonLeaf          (66),
    /// notAllowedOnRDN              (67),
    /// entryAlreadyExists           (68),
    /// objectClassModsProhibited    (69),
    /// -- 70 reserved for CLdap --
    /// affectsMultipleDSAs          (71), -- new
    /// -- 72-79 unused --
    /// other                        (80) },
    /// -- 81-90 reserved for APIs --
    /// matchedDN       LdapDN,
    /// errorMessage    LdapString,
    /// referral        [3] Referral OPTIONAL }
    /// 
///
/// /// internal class RfcLdapResult : Asn1Sequence, IRfcResponse { public const int Referral = 3; public RfcLdapResult(Stream stream, int len) : base(stream, len) { // Decode optional referral from Asn1OctetString to Referral. if (Size() <= 3) return; var obj = (Asn1Tagged) Get(3); var id = obj.GetIdentifier(); if (id.Tag != Referral) return; var content = ((Asn1OctetString) obj.TaggedValue).ByteValue(); Set(3, new Asn1SequenceOf(new MemoryStream(content.ToByteArray()), content.Length)); } public Asn1Enumerated GetResultCode() => (Asn1Enumerated) Get(0); public Asn1OctetString GetMatchedDN() => new Asn1OctetString(((Asn1OctetString) Get(1)).ByteValue()); public Asn1OctetString GetErrorMessage() => new Asn1OctetString(((Asn1OctetString) Get(2)).ByteValue()); public Asn1SequenceOf GetReferral() => Size() > 3 ? (Asn1SequenceOf) Get(3) : null; } /// /// Represents an Ldap Search Result Done Response. ///
    /// SearchResultDone ::= [APPLICATION 5] LdapResult
    /// 
///
/// internal class RfcSearchResultDone : RfcLdapResult { public RfcSearchResultDone(Stream stream, int len) : base(stream, len) { } public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.SearchResult); } /// /// Represents an Ldap Search Result Entry. ///
    /// SearchResultEntry ::= [APPLICATION 4] SEQUENCE {
    /// objectName      LdapDN,
    /// attributes      PartialAttributeList }
    /// 
///
/// internal sealed class RfcSearchResultEntry : Asn1Sequence { public RfcSearchResultEntry(Stream stream, int len) : base(stream, len) { } public string ObjectName => ((Asn1OctetString) Get(0)).StringValue(); public Asn1Sequence Attributes => (Asn1Sequence) Get(1); public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.SearchResponse); } /// /// Represents an Ldap Message ID. ///
    /// MessageID ::= INTEGER (0 .. maxInt)
    /// maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) --
    /// Note: The creation of a MessageID should be hidden within the creation of
    /// an RfcLdapMessage. The MessageID needs to be in sequence, and has an
    /// upper and lower limit. There is never a case when a user should be
    /// able to specify the MessageID for an RfcLdapMessage. The MessageID()
    /// class should be package protected. (So the MessageID value isn't
    /// arbitrarily run up.)
    /// 
/// internal class RfcMessageID : Asn1Integer { private static int _messageId; private static readonly object SyncRoot = new object(); /// /// Initializes a new instance of the class. /// Creates a MessageID with an auto incremented Asn1Integer value. /// Bounds: (0 .. 2,147,483,647) (2^^31 - 1 or Integer.MAX_VALUE) /// MessageID zero is never used in this implementation. Always /// start the messages with one. /// protected internal RfcMessageID() : base(MessageId) { } private static int MessageId { get { lock (SyncRoot) { return _messageId < int.MaxValue ? ++_messageId : (_messageId = 1); } } } } }