RaspberryIO/Unosquare.Swan/Networking/Ldap/RfcLdap.cs
2019-12-03 18:44:25 +01:00

238 lines
8.8 KiB
C#

using System;
using System.IO;
namespace Unosquare.Swan.Networking.Ldap {
/// <summary>
/// Encapsulates a single search result that is in response to an asynchronous
/// search operation.
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.LdapMessage" />
internal class LdapSearchResult : LdapMessage {
private LdapEntry _entry;
internal LdapSearchResult(RfcLdapMessage message)
: base(message) {
}
public LdapEntry Entry {
get {
if(this._entry != null) {
return this._entry;
}
LdapAttributeSet attrs = new LdapAttributeSet();
RfcSearchResultEntry entry = (RfcSearchResultEntry)this.Message.Response;
foreach(Asn1Object o in entry.Attributes.ToArray()) {
Asn1Sequence seq = (Asn1Sequence)o;
LdapAttribute attr = new LdapAttribute(((Asn1OctetString)seq.Get(0)).StringValue());
Asn1Set set = (Asn1Set)seq.Get(1);
foreach(Asn1Object t in set.ToArray()) {
attr.AddValue(((Asn1OctetString)t).ByteValue());
}
_ = attrs.Add(attr);
}
this._entry = new LdapEntry(entry.ObjectName, attrs);
return this._entry;
}
}
public override String ToString() => this._entry?.ToString() ?? base.ToString();
}
/// <summary>
/// Represents an Ldap Search Result Reference.
/// <pre>
/// SearchResultReference ::= [APPLICATION 19] SEQUENCE OF LdapURL
/// </pre>
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1SequenceOf" />
internal class RfcSearchResultReference : Asn1SequenceOf {
/// <summary>
/// Initializes a new instance of the <see cref="RfcSearchResultReference"/> class.
/// The only time a client will create a SearchResultReference is when it is
/// decoding it from an Stream.
/// </summary>
/// <param name="stream">The streab.</param>
/// <param name="len">The length.</param>
public RfcSearchResultReference(Stream stream, Int32 len)
: base(stream, len) {
}
public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.SearchResultReference);
}
/// <summary>
/// Represents an Ldap Extended Response.
/// <pre>
/// ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
/// COMPONENTS OF LdapResult,
/// responseName [10] LdapOID OPTIONAL,
/// response [11] OCTET STRING OPTIONAL }
/// </pre>
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
/// <seealso cref="Unosquare.Swan.Networking.Ldap.IRfcResponse" />
internal class RfcExtendedResponse : Asn1Sequence, IRfcResponse {
public const Int32 ResponseNameCode = 10;
public const Int32 ResponseCode = 11;
private readonly Int32 _referralIndex;
private readonly Int32 _responseNameIndex;
private readonly Int32 _responseIndex;
/// <summary>
/// Initializes a new instance of the <see cref="RfcExtendedResponse"/> class.
/// The only time a client will create a ExtendedResponse is when it is
/// decoding it from an stream.
/// </summary>
/// <param name="stream">The stream.</param>
/// <param name="len">The length.</param>
public RfcExtendedResponse(Stream stream, Int32 len)
: base(stream, len) {
if(this.Size() <= 3) {
return;
}
for(Int32 i = 3; i < this.Size(); i++) {
Asn1Tagged obj = (Asn1Tagged)this.Get(i);
Asn1Identifier id = obj.GetIdentifier();
switch(id.Tag) {
case RfcLdapResult.Referral:
SByte[] content = ((Asn1OctetString)obj.TaggedValue).ByteValue();
using(MemoryStream bais = new MemoryStream(content.ToByteArray())) {
this.Set(i, new Asn1SequenceOf(bais, content.Length));
}
this._referralIndex = i;
break;
case ResponseNameCode:
this.Set(i, new Asn1OctetString(((Asn1OctetString)obj.TaggedValue).ByteValue()));
this._responseNameIndex = i;
break;
case ResponseCode:
this.Set(i, obj.TaggedValue);
this._responseIndex = i;
break;
}
}
}
public Asn1OctetString ResponseName => this._responseNameIndex != 0 ? (Asn1OctetString)this.Get(this._responseNameIndex) : null;
public Asn1OctetString Response => this._responseIndex != 0 ? (Asn1OctetString)this.Get(this._responseIndex) : null;
public Asn1Enumerated GetResultCode() => (Asn1Enumerated)this.Get(0);
public Asn1OctetString GetMatchedDN() => new Asn1OctetString(((Asn1OctetString)this.Get(1)).ByteValue());
public Asn1OctetString GetErrorMessage() => new Asn1OctetString(((Asn1OctetString)this.Get(2)).ByteValue());
public Asn1SequenceOf GetReferral()
=> this._referralIndex != 0 ? (Asn1SequenceOf)this.Get(this._referralIndex) : null;
public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.ExtendedResponse);
}
/// <summary>
/// Represents and Ldap Bind Response.
/// <pre>
/// BindResponse ::= [APPLICATION 1] SEQUENCE {
/// COMPONENTS OF LdapResult,
/// serverSaslCreds [7] OCTET STRING OPTIONAL }
/// </pre>
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
/// <seealso cref="Unosquare.Swan.Networking.Ldap.IRfcResponse" />
internal class RfcBindResponse : Asn1Sequence, IRfcResponse {
/// <summary>
/// Initializes a new instance of the <see cref="RfcBindResponse"/> class.
/// The only time a client will create a BindResponse is when it is
/// decoding it from an InputStream
/// Note: If serverSaslCreds is included in the BindResponse, it does not
/// need to be decoded since it is already an OCTET STRING.
/// </summary>
/// <param name="stream">The in renamed.</param>
/// <param name="len">The length.</param>
public RfcBindResponse(Stream stream, Int32 len)
: base(stream, len) {
// Decode optional referral from Asn1OctetString to Referral.
if(this.Size() <= 3) {
return;
}
Asn1Tagged obj = (Asn1Tagged)this.Get(3);
if(obj.GetIdentifier().Tag != RfcLdapResult.Referral) {
return;
}
SByte[] content = ((Asn1OctetString)obj.TaggedValue).ByteValue();
using(MemoryStream bais = new MemoryStream(content.ToByteArray())) {
this.Set(3, new Asn1SequenceOf(bais, content.Length));
}
}
public Asn1Enumerated GetResultCode() => (Asn1Enumerated)this.Get(0);
public Asn1OctetString GetMatchedDN() => new Asn1OctetString(((Asn1OctetString)this.Get(1)).ByteValue());
public Asn1OctetString GetErrorMessage() => new Asn1OctetString(((Asn1OctetString)this.Get(2)).ByteValue());
public Asn1SequenceOf GetReferral() => this.Size() > 3 && this.Get(3) is Asn1SequenceOf ? (Asn1SequenceOf)this.Get(3) : null;
public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.BindResponse);
}
/// <summary>
/// Represents an LDAP Intermediate Response.
/// IntermediateResponse ::= [APPLICATION 25] SEQUENCE {
/// COMPONENTS OF LDAPResult, note: only present on incorrectly
/// encoded response from pre Falcon-sp1 server
/// responseName [10] LDAPOID OPTIONAL,
/// responseValue [11] OCTET STRING OPTIONAL }.
/// </summary>
/// <seealso cref="Unosquare.Swan.Networking.Ldap.Asn1Sequence" />
/// <seealso cref="Unosquare.Swan.Networking.Ldap.IRfcResponse" />
internal class RfcIntermediateResponse : Asn1Sequence, IRfcResponse {
public const Int32 TagResponseName = 0;
public const Int32 TagResponse = 1;
public RfcIntermediateResponse(Stream stream, Int32 len)
: base(stream, len) {
Int32 i = this.Size() >= 3 ? 3 : 0;
for(; i < this.Size(); i++) {
Asn1Tagged obj = (Asn1Tagged)this.Get(i);
switch(obj.GetIdentifier().Tag) {
case TagResponseName:
this.Set(i, new Asn1OctetString(((Asn1OctetString)obj.TaggedValue).ByteValue()));
break;
case TagResponse:
this.Set(i, obj.TaggedValue);
break;
}
}
}
public Asn1Enumerated GetResultCode() => this.Size() > 3 ? (Asn1Enumerated)this.Get(0) : null;
public Asn1OctetString GetMatchedDN() => this.Size() > 3 ? new Asn1OctetString(((Asn1OctetString)this.Get(1)).ByteValue()) : null;
public Asn1OctetString GetErrorMessage() =>
this.Size() > 3 ? new Asn1OctetString(((Asn1OctetString)this.Get(2)).ByteValue()) : null;
public Asn1SequenceOf GetReferral() => this.Size() > 3 ? (Asn1SequenceOf)this.Get(3) : null;
public override Asn1Identifier GetIdentifier() => new Asn1Identifier(LdapOperation.IntermediateResponse);
}
}