diff --git a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs index cdb419fa001f1..a910d35edad5c 100644 --- a/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Linux/OpenLdap/Interop.Ldap.cs @@ -221,9 +221,6 @@ internal static partial int ldap_sasl_interactive_bind( [LibraryImport(Libraries.OpenLdap, EntryPoint = "ldap_first_reference")] public static partial IntPtr ldap_first_reference(ConnectionHandle ldapHandle, IntPtr result); - [LibraryImport(Libraries.OpenLdap, EntryPoint = "ldap_create_sort_control")] - public static partial int ldap_create_sort_control(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control); - [LibraryImport(Libraries.OpenLdap, EntryPoint = "ldap_control_free")] public static partial int ldap_control_free(IntPtr control); diff --git a/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs b/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs index 8af474fe948ba..299149a309f3d 100644 --- a/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs +++ b/src/libraries/Common/src/Interop/Windows/Wldap32/Interop.Ldap.cs @@ -186,10 +186,6 @@ internal static partial class Ldap [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial int ldap_parse_reference(ConnectionHandle ldapHandle, IntPtr result, ref IntPtr referrals); - [LibraryImport(Libraries.Wldap32, EntryPoint = "ldap_create_sort_controlW")] - [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] - public static partial int ldap_create_sort_control(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control); - [LibraryImport(Libraries.Wldap32, EntryPoint = "ldap_control_freeW")] [UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])] public static partial int ldap_control_free(IntPtr control); diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj b/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj index 60b7742c23b61..a14b7fa4d14b8 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj +++ b/src/libraries/System.DirectoryServices.Protocols/src/System.DirectoryServices.Protocols.csproj @@ -26,6 +26,7 @@ + @@ -51,7 +52,6 @@ - @@ -64,7 +64,6 @@ - @@ -79,7 +78,6 @@ - diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs index f364b8e44e19e..a74bff6853512 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Linux.cs @@ -19,8 +19,6 @@ internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string internal static void FreeDirectoryControls(IntPtr value) => Interop.Ldap.ldap_controls_free(value); - internal static int CreateDirectorySortControl(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control) => Interop.Ldap.ldap_create_sort_control(handle, keys, critical, ref control); - internal static int DeleteDirectoryEntry(ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_delete_ext(ldapHandle, dn, servercontrol, clientcontrol, ref messageNumber); internal static int ExtendedDirectoryOperation(ConnectionHandle ldapHandle, string oid, BerVal data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs index a0f6133f66bbb..d7d74d5c735ba 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/LdapPal.Windows.cs @@ -19,8 +19,6 @@ internal static int CompareDirectoryEntries(ConnectionHandle ldapHandle, string internal static void FreeDirectoryControls(IntPtr value) => Interop.Ldap.ldap_controls_free(value); - internal static int CreateDirectorySortControl(ConnectionHandle handle, IntPtr keys, byte critical, ref IntPtr control) => Interop.Ldap.ldap_create_sort_control(handle, keys, critical, ref control); - internal static int DeleteDirectoryEntry(ConnectionHandle ldapHandle, string dn, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => Interop.Ldap.ldap_delete_ext(ldapHandle, dn, servercontrol, clientcontrol, ref messageNumber); internal static int ExtendedDirectoryOperation(ConnectionHandle ldapHandle, string oid, BerVal data, IntPtr servercontrol, IntPtr clientcontrol, ref int messageNumber) => diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.Linux.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.Linux.cs deleted file mode 100644 index 5fdffa0ff82ee..0000000000000 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.Linux.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Security.Principal; -using System.Text; - -namespace System.DirectoryServices.Protocols -{ - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] - internal partial struct SortKeyInterop - { - } -} diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.Windows.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.Windows.cs deleted file mode 100644 index 8d5ae5420309e..0000000000000 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.Windows.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Security.Principal; -using System.Text; - -namespace System.DirectoryServices.Protocols -{ - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal partial struct SortKeyInterop - { - } -} diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.cs deleted file mode 100644 index 61852e8a7c6d6..0000000000000 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/Interop/SortKeyInterop.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Collections; -using System.ComponentModel; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; -using System.Security.Principal; -using System.Text; - -namespace System.DirectoryServices.Protocols -{ - // Declared as partial in order to be able to set the different StructLayout - // attributes in the Windows and Linux specific files. - // This is a layout-controlled struct, do not alter property ordering. - internal partial struct SortKeyInterop - { - public SortKeyInterop(SortKey sortKey) - { - if (sortKey == null) - throw new ArgumentNullException(nameof(sortKey)); - - AttributeName = sortKey.AttributeName; - MatchingRule = sortKey.MatchingRule; - ReverseOrder = sortKey.ReverseOrder; - } - - internal string AttributeName { get; set; } - - internal string MatchingRule { get; set; } - - internal bool ReverseOrder { get; set; } - } -} diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/AsnWriterExtensions.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/AsnWriterExtensions.cs new file mode 100644 index 0000000000000..4891d9e587513 --- /dev/null +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/AsnWriterExtensions.cs @@ -0,0 +1,36 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Formats.Asn1; +using System.Text; + +namespace System.DirectoryServices.Protocols +{ + internal static class AsnWriterExtensions + { + public static void WriteLdapString(this AsnWriter writer, string value, Encoding stringEncoding, bool mandatory = true, Asn1Tag? tag = null) + { + // A typical stack allocation threshold would be 256 bytes. A higher threshold has been chosen because an LdapString can be + // used to serialize server names. A server name is defined by RF1035, which specifies that a label in a domain name should + // be < 64 characters. If a server name is specified as an FQDN, this will be at least three labels in an AD environment - + // up to 192 characters. Doubling this to allow for Unicode encoding, then rounding to the nearest power of two yields 512. + const int StackAllocationThreshold = 512; + + if (!string.IsNullOrEmpty(value)) + { + int octetStringLength = stringEncoding.GetByteCount(value); + // Allocate space on the stack. There's a modest codegen advantage to a constant-value stackalloc. + Span tmpValue = octetStringLength <= StackAllocationThreshold + ? stackalloc byte[StackAllocationThreshold].Slice(0, octetStringLength) + : new byte[octetStringLength]; + + stringEncoding.GetBytes(value, tmpValue); + writer.WriteOctetString(tmpValue, tag); + } + else if (mandatory) + { + writer.WriteOctetString([], tag); + } + } + } +} diff --git a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs index ba0f714b2b02f..85aa27a5d7986 100644 --- a/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs +++ b/src/libraries/System.DirectoryServices.Protocols/src/System/DirectoryServices/Protocols/common/DirectoryControl.cs @@ -4,6 +4,9 @@ using System.Collections; using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Formats.Asn1; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Principal; @@ -87,10 +90,29 @@ public bool ReverseOrder } } + // The encoding and decoding of LDAP directory controls interprets BER INTEGER values as 32-bit signed integers. + // Although BER INTEGERS can exceed this data type's maximum value, previous versions of DirectoryControl (and + // its derived classes) encoded and decoded these types by passing the "i" format specifier to BerConverter. The + // .NET Framework continues to do so. There is therefore historical precedent to justify limiting BER INTEGER + // values to this data type when using AsnDecoder and AsnWriter. public class DirectoryControl { + // Scratch buffer allocations with sizes which are below this threshold should be made on the stack. + // This is based on the Active Directory schema. The largest attribute name here is msDS-FailedInteractiveLogonCountAtLastSuccessfulLogon, + // which is 53 characters long. This is rounded up to the nearest power of two. + internal const int AttributeNameStackAllocationThreshold = 64; + + internal static readonly UTF8Encoding s_utf8Encoding = new(false, true); + internal byte[] _directoryControlValue; + [ThreadStatic] + private static AsnWriter? t_writer; + + [MemberNotNull(nameof(t_writer))] + internal static AsnWriter GetWriter() + => t_writer ??= new AsnWriter(AsnEncodingRules.BER); + public DirectoryControl(string type, byte[] value, bool isCritical, bool serverSide) { ArgumentNullException.ThrowIfNull(type); @@ -99,11 +121,7 @@ public DirectoryControl(string type, byte[] value, bool isCritical, bool serverS if (value != null) { - _directoryControlValue = new byte[value.Length]; - for (int i = 0; i < value.Length; i++) - { - _directoryControlValue[i] = value[i]; - } + _directoryControlValue = value.AsSpan().ToArray(); } IsCritical = isCritical; ServerSide = serverSide; @@ -116,12 +134,7 @@ public virtual byte[] GetValue() return Array.Empty(); } - byte[] tempValue = new byte[_directoryControlValue.Length]; - for (int i = 0; i < _directoryControlValue.Length; i++) - { - tempValue[i] = _directoryControlValue[i]; - } - return tempValue; + return _directoryControlValue.AsSpan().ToArray(); } public string Type { get; } @@ -132,102 +145,174 @@ public virtual byte[] GetValue() internal static void TransformControls(DirectoryControl[] controls) { - for (int i = 0; i < controls.Length; i++) + Span attributeNameScratchSpace = stackalloc byte[AttributeNameStackAllocationThreshold]; + + try { - Debug.Assert(controls[i] != null); - byte[] value = controls[i].GetValue(); - if (controls[i].Type == "1.2.840.113556.1.4.319") + for (int i = 0; i < controls.Length; i++) { - // The control is a PageControl. - object[] result = BerConverter.Decode("{iO}", value); - Debug.Assert((result != null) && (result.Length == 2)); + Debug.Assert(controls[i] != null); + byte[] value = controls[i]._directoryControlValue ?? Array.Empty(); + Span asnSpan = value; + bool asnReadSuccessful; - int size = (int)result[0]; - // user expects cookie with length 0 as paged search is done. - byte[] cookie = (byte[])result[1] ?? Array.Empty(); + if (controls[i].Type == "1.2.840.113556.1.4.319") + { + // The control is a PageResultResponseControl. The structure of its value is described as a realSearchControlValue structure in RFC 2696. + byte[] cookie; + + AsnDecoder.ReadSequence(asnSpan, AsnEncodingRules.BER, out int sequenceContentOffset, out int sequenceContentLength, out int bytesConsumed); + ThrowUnless(sequenceContentLength > 0); + ThrowUnless(bytesConsumed == asnSpan.Length); + + asnSpan = asnSpan.Slice(sequenceContentOffset, sequenceContentLength); + asnReadSuccessful = AsnDecoder.TryReadInt32(asnSpan, AsnEncodingRules.BER, out int size, out bytesConsumed); + ThrowUnless(asnReadSuccessful); + asnSpan = asnSpan.Slice(bytesConsumed); + + // The remaining bytes in the control are expected to be the cookie (an octet string.) + // A cookie with length 0 will be sent when paged search is done. In this situation, the ASN.1 tag will still consume two bytes. + cookie = AsnDecoder.ReadOctetString(asnSpan, AsnEncodingRules.BER, out bytesConsumed); + asnSpan = asnSpan.Slice(bytesConsumed); + ThrowUnless(asnSpan.IsEmpty); + + PageResultResponseControl pageControl = new PageResultResponseControl(size, cookie, controls[i].IsCritical, value); + controls[i] = pageControl; + } + else if (controls[i].Type == "1.2.840.113556.1.4.1504") + { + // The control is an AsqResponseControl. The structure of its value is described as an ASQResponseValue in MS-ADTS section 3.1.1.3.4.1.18. + ResultCode result; - PageResultResponseControl pageControl = new PageResultResponseControl(size, cookie, controls[i].IsCritical, controls[i].GetValue()); - controls[i] = pageControl; - } - else if (controls[i].Type == "1.2.840.113556.1.4.1504") - { - // The control is an AsqControl. - object[] o = BerConverter.Decode("{e}", value); - Debug.Assert((o != null) && (o.Length == 1)); + AsnDecoder.ReadSequence(asnSpan, AsnEncodingRules.BER, out int sequenceContentOffset, out int sequenceContentLength, out int bytesConsumed); + ThrowUnless(sequenceContentLength > 0); + ThrowUnless(bytesConsumed == asnSpan.Length); - int result = (int)o[0]; - AsqResponseControl asq = new AsqResponseControl(result, controls[i].IsCritical, controls[i].GetValue()); - controls[i] = asq; - } - else if (controls[i].Type == "1.2.840.113556.1.4.841") - { - // The control is a DirSyncControl. - object[] o = BerConverter.Decode("{iiO}", value); - Debug.Assert(o != null && o.Length == 3); + result = AsnDecoder.ReadEnumeratedValue(asnSpan.Slice(sequenceContentOffset, sequenceContentLength), AsnEncodingRules.BER, out bytesConsumed); + ThrowUnless(bytesConsumed == sequenceContentLength); - int moreData = (int)o[0]; - int count = (int)o[1]; - byte[] dirsyncCookie = (byte[])o[2]; + AsqResponseControl asq = new AsqResponseControl(result, controls[i].IsCritical, value); + controls[i] = asq; + } + else if (controls[i].Type == "1.2.840.113556.1.4.841") + { + // The control is a DirSyncResponseControl. The structure of its value is described as a DirSyncResponseValue in MS-ADTS section 3.1.1.3.4.1.3. + byte[] dirsyncCookie; - DirSyncResponseControl dirsync = new DirSyncResponseControl(dirsyncCookie, (moreData == 0 ? false : true), count, controls[i].IsCritical, controls[i].GetValue()); - controls[i] = dirsync; - } - else if (controls[i].Type == "1.2.840.113556.1.4.474") - { - // The control is a SortControl. - int result; - string attribute = null; - object[] o = BerConverter.TryDecode("{ea}", value, out bool decodeSucceeded); + AsnDecoder.ReadSequence(asnSpan, AsnEncodingRules.BER, out int sequenceContentOffset, out int sequenceContentLength, out int bytesConsumed); + ThrowUnless(sequenceContentLength > 0); + ThrowUnless(bytesConsumed == asnSpan.Length); + asnSpan = asnSpan.Slice(sequenceContentOffset, sequenceContentLength); - // decode might fail as AD for example never returns attribute name, we don't want to unnecessarily throw and catch exception - if (decodeSucceeded) - { - Debug.Assert(o != null && o.Length == 2); - result = (int)o[0]; - attribute = (string)o[1]; + asnReadSuccessful = AsnDecoder.TryReadInt32(asnSpan, AsnEncodingRules.BER, out int moreResults, out bytesConsumed); + ThrowUnless(asnReadSuccessful); + asnSpan = asnSpan.Slice(bytesConsumed); + + asnReadSuccessful = AsnDecoder.TryReadInt32(asnSpan, AsnEncodingRules.BER, out int count, out bytesConsumed); + ThrowUnless(asnReadSuccessful); + asnSpan = asnSpan.Slice(bytesConsumed); + + dirsyncCookie = AsnDecoder.ReadOctetString(asnSpan, AsnEncodingRules.BER, out bytesConsumed); + ThrowUnless(asnSpan.Length == bytesConsumed); + + DirSyncResponseControl dirsync = new DirSyncResponseControl(dirsyncCookie, moreResults != 0, count, controls[i].IsCritical, value); + controls[i] = dirsync; } - else + else if (controls[i].Type == "1.2.840.113556.1.4.474") { - // decoding might fail as attribute is optional - o = BerConverter.Decode("{e}", value); - Debug.Assert(o != null && o.Length == 1); + // The control is a SortResponseControl. The structure of its value is described as a SortResult in RFC 2891. + ResultCode result; + string attribute = null; - result = (int)o[0]; - } + AsnDecoder.ReadSequence(asnSpan, AsnEncodingRules.BER, out int sequenceContentOffset, out int sequenceContentLength, out int bytesConsumed); + ThrowUnless(sequenceContentLength > 0); + ThrowUnless(bytesConsumed == asnSpan.Length); + asnSpan = asnSpan.Slice(sequenceContentOffset, sequenceContentLength); - SortResponseControl sort = new SortResponseControl((ResultCode)result, attribute, controls[i].IsCritical, controls[i].GetValue()); - controls[i] = sort; - } - else if (controls[i].Type == "2.16.840.1.113730.3.4.10") - { - // The control is a VlvResponseControl. - int position; - int count; - int result; - byte[] context = null; - object[] o = BerConverter.TryDecode("{iieO}", value, out bool decodeSucceeded); - - if (decodeSucceeded) - { - Debug.Assert(o != null && o.Length == 4); - position = (int)o[0]; - count = (int)o[1]; - result = (int)o[2]; - context = (byte[])o[3]; + result = AsnDecoder.ReadEnumeratedValue(asnSpan, AsnEncodingRules.BER, out bytesConsumed); + asnSpan = asnSpan.Slice(bytesConsumed); + + // If present, the remaining bytes in the control are expected to be an octet string. + if (!asnSpan.IsEmpty) + { + // Attribute name is optional: AD for example never returns attribute name + scoped Span attributeNameBuffer; + + if (asnSpan.Length <= AttributeNameStackAllocationThreshold) + { + asnReadSuccessful = AsnDecoder.TryReadOctetString(asnSpan, attributeNameScratchSpace, + AsnEncodingRules.BER, out bytesConsumed, out int octetStringLength, SortResponseControl.AttributeNameTag); + Debug.Assert(asnReadSuccessful); + attributeNameBuffer = attributeNameScratchSpace.Slice(0, octetStringLength); + } + else + { + attributeNameBuffer = AsnDecoder.ReadOctetString(asnSpan, AsnEncodingRules.BER, out bytesConsumed); + } + asnSpan = asnSpan.Slice(bytesConsumed); + + attribute = s_utf8Encoding.GetString(attributeNameBuffer); + } + + ThrowUnless(asnSpan.IsEmpty); + + SortResponseControl sort = new SortResponseControl(result, attribute, controls[i].IsCritical, value); + controls[i] = sort; } - else + else if (controls[i].Type == "2.16.840.1.113730.3.4.10") { - o = BerConverter.Decode("{iie}", value); - Debug.Assert(o != null && o.Length == 3); - position = (int)o[0]; - count = (int)o[1]; - result = (int)o[2]; - } + // The control is a VlvResponseControl. The structure of its value is described as a VLVResponseValue in MS-ADTS 3.1.1.3.4.1.17. + ResultCode result; + byte[] context = null; + + AsnDecoder.ReadSequence(asnSpan, AsnEncodingRules.BER, out int sequenceContentOffset, out int sequenceContentLength, out int bytesConsumed); + ThrowUnless(sequenceContentLength > 0); + ThrowUnless(bytesConsumed == asnSpan.Length); + asnSpan = asnSpan.Slice(sequenceContentOffset, sequenceContentLength); + + asnReadSuccessful = AsnDecoder.TryReadInt32(asnSpan, AsnEncodingRules.BER, out int position, out bytesConsumed); + ThrowUnless(asnReadSuccessful); + asnSpan = asnSpan.Slice(bytesConsumed); + + asnReadSuccessful = AsnDecoder.TryReadInt32(asnSpan, AsnEncodingRules.BER, out int count, out bytesConsumed); + ThrowUnless(asnReadSuccessful); + asnSpan = asnSpan.Slice(bytesConsumed); + + result = AsnDecoder.ReadEnumeratedValue(asnSpan, AsnEncodingRules.BER, out bytesConsumed); + asnSpan = asnSpan.Slice(bytesConsumed); - VlvResponseControl vlv = new VlvResponseControl(position, count, context, (ResultCode)result, controls[i].IsCritical, controls[i].GetValue()); - controls[i] = vlv; + // If present, the remaining bytes in the control are expected to be an octet string. + if (!asnSpan.IsEmpty) + { + // The user expects cookie with length 0 as paged search is done. In this situation, there'll still be two bytes + // for the ASN.1 tag. + context = AsnDecoder.ReadOctetString(asnSpan, AsnEncodingRules.BER, out bytesConsumed); + asnSpan = asnSpan.Slice(bytesConsumed); + } + ThrowUnless(asnSpan.IsEmpty); + + VlvResponseControl vlv = new VlvResponseControl(position, count, context, result, controls[i].IsCritical, value); + controls[i] = vlv; + } } } + catch (AsnContentException asnEx) + { + throw new BerConversionException(SR.BerConversionError, asnEx); + } + catch (DecoderFallbackException decEx) + { + throw new BerConversionException(SR.BerConversionError, decEx); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void ThrowUnless(bool condition) + { + if (!condition) + { + throw new BerConversionException(); + } } } @@ -246,16 +331,29 @@ public AsqRequestControl(string attributeName) : this() public override byte[] GetValue() { - _directoryControlValue = BerConverter.Encode("{s}", new object[] { AttributeName }); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.18. + * ASQRequestValue ::= SEQUENCE { + * sourceAttribute OCTET STRING } + */ + using (writer.PushSequence()) + { + writer.WriteLdapString(AttributeName, s_utf8Encoding); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } public class AsqResponseControl : DirectoryControl { - internal AsqResponseControl(int result, bool criticality, byte[] controlValue) : base("1.2.840.113556.1.4.1504", controlValue, criticality, true) + internal AsqResponseControl(ResultCode result, bool criticality, byte[] controlValue) : base("1.2.840.113556.1.4.1504", controlValue, criticality, true) { - Result = (ResultCode)result; + Result = result; } public ResultCode Result { get; } @@ -276,18 +374,20 @@ public CrossDomainMoveControl(string targetDomainController) : this() public override byte[] GetValue() { + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.2. + * "When sending this control to the DC, the controlValue field is set to a UTF-8 string + * containing the fully qualified domain name of a DC in the domain to which the object + * is to be moved. The string is not BER-encoded." + */ if (TargetDomainController != null) { - UTF8Encoding encoder = new UTF8Encoding(); - byte[] bytes = encoder.GetBytes(TargetDomainController); + int byteCount = s_utf8Encoding.GetByteCount(TargetDomainController); // Allocate large enough space for the '\0' character. - _directoryControlValue = new byte[bytes.Length + 2]; - for (int i = 0; i < bytes.Length; i++) - { - _directoryControlValue[i] = bytes[i]; - } + _directoryControlValue = new byte[byteCount + 2]; + s_utf8Encoding.GetBytes(TargetDomainController, _directoryControlValue); } + return base.GetValue(); } } @@ -318,14 +418,29 @@ public ExtendedDNFlag Flag set { if (value < ExtendedDNFlag.HexString || value > ExtendedDNFlag.StandardString) + { throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(ExtendedDNFlag)); + } _flag = value; } } public override byte[] GetValue() { - _directoryControlValue = BerConverter.Encode("{i}", new object[] { (int)Flag }); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.5. + * ExtendedDNRequestValue ::= SEQUENCE { + * Flag INTEGER } + */ + using (writer.PushSequence()) + { + writer.WriteInteger((int)Flag); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } @@ -360,7 +475,20 @@ public SecurityDescriptorFlagControl(SecurityMasks masks) : this() public override byte[] GetValue() { - _directoryControlValue = BerConverter.Encode("{i}", new object[] { (int)SecurityMasks }); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.11. + * SDFlagsRequestValue ::= SEQUENCE { + * Flags INTEGER } + */ + using (writer.PushSequence()) + { + writer.WriteInteger((int)SecurityMasks); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } @@ -381,7 +509,9 @@ public SearchOption SearchOption set { if (value < SearchOption.DomainScope || value > SearchOption.PhantomRoot) + { throw new InvalidEnumArgumentException(nameof(value), (int)value, typeof(SearchOption)); + } _searchOption = value; } @@ -389,7 +519,20 @@ public SearchOption SearchOption public override byte[] GetValue() { - _directoryControlValue = BerConverter.Encode("{i}", new object[] { (int)SearchOption }); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.12. + * SearchOptionsRequestValue ::= SEQUENCE { + * Flags INTEGER } + */ + using (writer.PushSequence()) + { + writer.WriteInteger((int)SearchOption); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } @@ -407,7 +550,6 @@ public TreeDeleteControl() : base("1.2.840.113556.1.4.805", null, true, true) { public class VerifyNameControl : DirectoryControl { private string _serverName; - public VerifyNameControl() : base("1.2.840.113556.1.4.1338", null, true, true) { } public VerifyNameControl(string serverName) : this() @@ -432,14 +574,23 @@ public string ServerName public override byte[] GetValue() { - byte[] tmpValue = null; - if (ServerName != null) + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.16. + * VerifyNameRequestValue ::= SEQUENCE { + * Flags INTEGER, + * ServerName OCTET STRING } + */ + using (writer.PushSequence()) { - UnicodeEncoding unicode = new UnicodeEncoding(); - tmpValue = unicode.GetBytes(ServerName); + writer.WriteInteger(Flag); + + writer.WriteLdapString(ServerName, Encoding.Unicode); } + _directoryControlValue = writer.Encode(); + writer.Reset(); - _directoryControlValue = BerConverter.Encode("{io}", new object[] { Flag, tmpValue }); return base.GetValue(); } } @@ -474,13 +625,7 @@ public byte[] Cookie return Array.Empty(); } - byte[] tempCookie = new byte[_dirsyncCookie.Length]; - for (int i = 0; i < tempCookie.Length; i++) - { - tempCookie[i] = _dirsyncCookie[i]; - } - - return tempCookie; + return _dirsyncCookie.AsSpan().ToArray(); } set => _dirsyncCookie = value; } @@ -505,8 +650,24 @@ public int AttributeCount public override byte[] GetValue() { - object[] o = new object[] { (int)Option, AttributeCount, _dirsyncCookie }; - _directoryControlValue = BerConverter.Encode("{iio}", o); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.3. + * DirSyncRequestValue ::= SEQUENCE { + * Flags INTEGER, + * MaxBytes INTEGER, + * Cookie OCTET STRING } + */ + using (writer.PushSequence()) + { + writer.WriteInteger((int)Option); + writer.WriteInteger(AttributeCount); + writer.WriteOctetString(_dirsyncCookie ?? []); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } @@ -531,13 +692,7 @@ public byte[] Cookie return Array.Empty(); } - byte[] tempCookie = new byte[_dirsyncCookie.Length]; - for (int i = 0; i < tempCookie.Length; i++) - { - tempCookie[i] = _dirsyncCookie[i]; - } - - return tempCookie; + return _dirsyncCookie.AsSpan().ToArray(); } } @@ -586,21 +741,29 @@ public byte[] Cookie return Array.Empty(); } - byte[] tempCookie = new byte[_pageCookie.Length]; - for (int i = 0; i < _pageCookie.Length; i++) - { - tempCookie[i] = _pageCookie[i]; - } - - return tempCookie; + return _pageCookie.AsSpan().ToArray(); } set => _pageCookie = value; } public override byte[] GetValue() { - object[] o = new object[] { PageSize, _pageCookie }; - _directoryControlValue = BerConverter.Encode("{io}", o); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in RFC2696. + * realSearchControlValue ::= SEQUENCE { + * size INTEGER, + * cookie OCTET STRING } + */ + using (writer.PushSequence()) + { + writer.WriteInteger(PageSize); + writer.WriteOctetString(_pageCookie); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } @@ -624,12 +787,7 @@ public byte[] Cookie return Array.Empty(); } - byte[] tempCookie = new byte[_pageCookie.Length]; - for (int i = 0; i < _pageCookie.Length; i++) - { - tempCookie[i] = _pageCookie[i]; - } - return tempCookie; + return _pageCookie.AsSpan().ToArray(); } } @@ -638,6 +796,9 @@ public byte[] Cookie public class SortRequestControl : DirectoryControl { + private static readonly Asn1Tag s_orderingRuleTag = new(TagClass.ContextSpecific, 0); + private static readonly Asn1Tag s_reverseOrderTag = new(TagClass.ContextSpecific, 1); + private SortKey[] _keys = Array.Empty(); public SortRequestControl(params SortKey[] sortKeys) : base("1.2.840.113556.1.4.473", null, true, true) { @@ -707,93 +868,37 @@ public SortKey[] SortKeys } } - public override unsafe byte[] GetValue() + public override byte[] GetValue() { - SortKeyInterop[] nativeSortKeys = new SortKeyInterop[_keys.Length]; - for (int i = 0; i < _keys.Length; ++i) - { - nativeSortKeys[i] = new SortKeyInterop(_keys[i]); - } - - IntPtr control = IntPtr.Zero; - int structSize = Marshal.SizeOf(); - int keyCount = nativeSortKeys.Length; - IntPtr memHandle = Utility.AllocHGlobalIntPtrArray(keyCount + 1); + AsnWriter writer = GetWriter(); - try + writer.Reset(); + /* This is as laid out in RFC2891. + * SortKeyList ::= SEQUENCE OF SEQUENCE { + * attributeType AttributeDescription, + * orderingRule [0] MatchingRuleId OPTIONAL, + * reverseOrder [1] BOOLEAN DEFAULT FALSE } + * */ + using (writer.PushSequence()) { - void** pMemHandle = (void**)memHandle; - IntPtr sortPtr = IntPtr.Zero; - int i = 0; - for (i = 0; i < keyCount; i++) + for (int i = 0; i < _keys.Length; i++) { - sortPtr = Marshal.AllocHGlobal(structSize); - Marshal.StructureToPtr(nativeSortKeys[i], sortPtr, false); - pMemHandle[i] = (void*)sortPtr; - } - pMemHandle[i] = null; - - bool critical = IsCritical; - int error = LdapPal.CreateDirectorySortControl(UtilityHandle.GetHandle(), memHandle, critical ? (byte)1 : (byte)0, ref control); + SortKey key = _keys[i]; - if (error != 0) - { - if (LdapErrorMappings.IsLdapError(error)) - { - string errorMessage = LdapErrorMappings.MapResultCode(error); - throw new LdapException(error, errorMessage); - } - else + using (writer.PushSequence()) { - throw new LdapException(error); - } - } + writer.WriteLdapString(key.AttributeName, s_utf8Encoding, mandatory: true); + writer.WriteLdapString(key.MatchingRule, s_utf8Encoding, mandatory: false, tag: s_orderingRuleTag); - LdapControl managedControl = new LdapControl(); - Marshal.PtrToStructure(control, managedControl); - BerVal value = managedControl.ldctl_value; - // reinitialize the value - _directoryControlValue = null; - if (value != null) - { - _directoryControlValue = new byte[value.bv_len.Value]; - Marshal.Copy(value.bv_val, _directoryControlValue, 0, (int)value.bv_len.Value); - } - } - finally - { - if (control != IntPtr.Zero) - { - LdapPal.FreeDirectoryControl(control); - } - - if (memHandle != IntPtr.Zero) - { - //release the memory from the heap - for (int i = 0; i < keyCount; i++) - { - IntPtr tempPtr = Marshal.ReadIntPtr(memHandle, IntPtr.Size * i); - if (tempPtr != IntPtr.Zero) + if (key.ReverseOrder) { - // free the marshalled name - IntPtr ptr = Marshal.ReadIntPtr(tempPtr); - if (ptr != IntPtr.Zero) - { - Marshal.FreeHGlobal(ptr); - } - // free the marshalled rule - ptr = Marshal.ReadIntPtr(tempPtr, IntPtr.Size); - if (ptr != IntPtr.Zero) - { - Marshal.FreeHGlobal(ptr); - } - - Marshal.FreeHGlobal(tempPtr); + writer.WriteBoolean(key.ReverseOrder, s_reverseOrderTag); } } - Marshal.FreeHGlobal(memHandle); } } + _directoryControlValue = writer.Encode(); + writer.Reset(); return base.GetValue(); } @@ -801,6 +906,8 @@ public override unsafe byte[] GetValue() public class SortResponseControl : DirectoryControl { + internal static readonly Asn1Tag AttributeNameTag = new(TagClass.ContextSpecific, 0); + internal SortResponseControl(ResultCode result, string attributeName, bool critical, byte[] value) : base("1.2.840.113556.1.4.474", value, critical, true) { Result = result; @@ -814,6 +921,9 @@ internal SortResponseControl(ResultCode result, string attributeName, bool criti public class VlvRequestControl : DirectoryControl { + private static readonly Asn1Tag s_byOffsetChoiceTag = new(TagClass.ContextSpecific, 0, true); + private static readonly Asn1Tag s_greaterThanOrEqualChoiceTag = new(TagClass.ContextSpecific, 1, false); + private int _before; private int _after; private int _offset; @@ -836,8 +946,7 @@ public VlvRequestControl(int beforeCount, int afterCount, string target) : this( AfterCount = afterCount; if (target != null) { - UTF8Encoding encoder = new UTF8Encoding(); - _target = encoder.GetBytes(target); + _target = s_utf8Encoding.GetBytes(target); } } @@ -913,12 +1022,7 @@ public byte[] Target return Array.Empty(); } - byte[] tempContext = new byte[_target.Length]; - for (int i = 0; i < tempContext.Length; i++) - { - tempContext[i] = _target[i]; - } - return tempContext; + return _target.AsSpan().ToArray(); } set => _target = value; } @@ -932,59 +1036,57 @@ public byte[] ContextId return Array.Empty(); } - byte[] tempContext = new byte[_context.Length]; - for (int i = 0; i < tempContext.Length; i++) - { - tempContext[i] = _context[i]; - } - return tempContext; + return _context.AsSpan().ToArray(); } set => _context = value; } public override byte[] GetValue() { - var seq = new StringBuilder(10); - var objList = new ArrayList(); - - // first encode the before and the after count. - seq.Append("{ii"); - objList.Add(BeforeCount); - objList.Add(AfterCount); - - // encode Target if it is not null - if (Target.Length != 0) - { - seq.Append('t'); - objList.Add(0x80 | 0x1); - seq.Append('o'); - objList.Add(Target); - } - else + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.17. + * VLVRequestValue ::= SEQUENCE { + * beforeCount INTEGER, + * afterCount INTEGER, + * CHOICE { + * byoffset [0] SEQUENCE { + * offset INTEGER, + * contentCount INTEGER }, + * greaterThanOrEqual [1] AssertionValue }, + * contextID OCTET STRING OPTIONAL } + */ + using (writer.PushSequence()) { - seq.Append("t{"); - objList.Add(0xa0); - seq.Append("ii"); - objList.Add(Offset); - objList.Add(EstimateCount); - seq.Append('}'); - } + // first encode the before and the after count. + writer.WriteInteger(BeforeCount); + writer.WriteInteger(AfterCount); - // encode the contextID if present - if (ContextId.Length != 0) - { - seq.Append('o'); - objList.Add(ContextId); - } + // encode Target if it is not null + if (_target != null && _target.Length > 0) + { + writer.WriteOctetString(_target, s_greaterThanOrEqualChoiceTag); + } + else + { + using (writer.PushSequence(s_byOffsetChoiceTag)) + { + writer.WriteInteger(Offset); + writer.WriteInteger(EstimateCount); + } + } + + // encode the contextID if present + if (_context != null && _context.Length > 0) + { + writer.WriteOctetString(_context); + } - seq.Append('}'); - object[] values = new object[objList.Count]; - for (int i = 0; i < objList.Count; i++) - { - values[i] = objList[i]; } + _directoryControlValue = writer.Encode(); + writer.Reset(); - _directoryControlValue = BerConverter.Encode(seq.ToString(), values); return base.GetValue(); } } @@ -1014,12 +1116,7 @@ public byte[] ContextId return Array.Empty(); } - byte[] tempContext = new byte[_context.Length]; - for (int i = 0; i < tempContext.Length; i++) - { - tempContext[i] = _context[i]; - } - return tempContext; + return _context.AsSpan().ToArray(); } } @@ -1040,7 +1137,20 @@ public QuotaControl(SecurityIdentifier querySid) : this() public override byte[] GetValue() { - _directoryControlValue = BerConverter.Encode("{o}", new object[] { _sid }); + AsnWriter writer = GetWriter(); + + writer.Reset(); + /* This is as laid out in MS-ADTS, 3.1.1.3.4.1.19. + * QuotaRequestValue ::= SEQUENCE { + * querySID OCTET STRING } + */ + using (writer.PushSequence()) + { + writer.WriteOctetString(_sid); + } + _directoryControlValue = writer.Encode(); + writer.Reset(); + return base.GetValue(); } } diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/AsqRequestControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/AsqRequestControlTests.cs index d2704307152d5..3aa7f6097e810 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/AsqRequestControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/AsqRequestControlTests.cs @@ -19,25 +19,26 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.1504", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } : new byte[] { 48, 2, 4, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 }; +#else + var expected = new byte[] { 48, 2, 4, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_String_Test_data() { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - yield return new object[] { null, new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } }; - yield return new object[] { "", new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } }; - yield return new object[] { "A", new byte[] { 48, 132, 0, 0, 0, 3, 4, 1, 65 } }; - } - else - { - yield return new object[] { null, new byte[] { 48, 2, 4, 0 } }; - yield return new object[] { "", new byte[] { 48, 2, 4, 0 } }; - yield return new object[] { "A", new byte[] { 48, 3, 4, 1, 65 } }; - } +#if NETFRAMEWORK + yield return new object[] { null, new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } }; + yield return new object[] { "", new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } }; + yield return new object[] { "A", new byte[] { 48, 132, 0, 0, 0, 3, 4, 1, 65 } }; +#else + yield return new object[] { null, new byte[] { 48, 2, 4, 0 } }; + yield return new object[] { "", new byte[] { 48, 2, 4, 0 } }; + yield return new object[] { "A", new byte[] { 48, 3, 4, 1, 65 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/AsqResponseControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/AsqResponseControlTests.cs index 4a2a0c86fda5d..a033eaacf8a1b 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/AsqResponseControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/AsqResponseControlTests.cs @@ -36,6 +36,7 @@ public static IEnumerable ConformantControlValues() public static IEnumerable NonconformantControlValues() { +#if NETFRAMEWORK // {i}, single-byte length. ASN.1 type of INTEGER rather than ENUMERATED yield return new object[] { new byte[] { 0x30, 0x03, 0x02, 0x01, 0x00 @@ -91,6 +92,9 @@ public static IEnumerable NonconformantControlValues() 0x0A, 0x01, 0x7F, 0x80, 0x80, 0x80, 0x80 }, (ResultCode)0x7F }; +#else + yield break; +#endif } public static IEnumerable InvalidControlValues() @@ -98,6 +102,19 @@ public static IEnumerable InvalidControlValues() // e, not wrapped in an ASN.1 SEQUENCE yield return new object[] { new byte[] { 0x02, 0x01, 0x00 } }; +#if NET + // {i}, single-byte length. ASN.1 type of INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x03, + 0x02, 0x01, 0x00 } }; + yield return new object[] { new byte[] { 0x30, 0x03, + 0x02, 0x01, 0x7F } }; + + // {i}, four-byte length. ASN.1 type of INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x01, 0x00 } }; + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x01, 0x7F } }; +#endif // {e}, single-byte length, sequence length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x04, 0x0A, 0x01, 0x00 } }; @@ -105,6 +122,40 @@ public static IEnumerable InvalidControlValues() // {e}, four-byte length, sequence length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x04, 0x0A, 0x01, 0x00 } }; + +#if NET + // {e}, single-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x03, + 0x0A, 0x01, 0x00, + 0x80, 0x80, 0x80, 0x80 } }; + yield return new object[] { new byte[] { 0x30, 0x03, + 0x0A, 0x01, 0x7F, + 0x80, 0x80, 0x80, 0x80 } }; + + // {e}, four-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x03, + 0x0A, 0x01, 0x00, + 0x80, 0x80, 0x80, 0x80 } }; + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x03, + 0x0A, 0x01, 0x7F, + 0x80, 0x80, 0x80, 0x80 } }; + + // {e}, single-byte length. Trailing data within the sequence + yield return new object[] { new byte[] { 0x30, 0x07, + 0x0A, 0x01, 0x00, + 0x80, 0x80, 0x80, 0x80 } }; + yield return new object[] { new byte[] { 0x30, 0x07, + 0x0A, 0x01, 0x7F, + 0x80, 0x80, 0x80, 0x80 } }; + + // {e}, four-byte length. Trailing data within the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x07, + 0x0A, 0x01, 0x00, + 0x80, 0x80, 0x80, 0x80 } }; + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x07, + 0x0A, 0x01, 0x7F, + 0x80, 0x80, 0x80, 0x80 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncRequestControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncRequestControlTests.cs index da6bcfbdf5b69..4699d1e008fed 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncRequestControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncRequestControlTests.cs @@ -22,15 +22,25 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.841", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } : new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 }; +#else + var expected = new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_Cookie_Data() { - yield return new object[] { null, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } : new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; - yield return new object[] { new byte[0], (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } : new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; - yield return new object[] { new byte[] { 97, 98, 99 }, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 13, 2, 1, 0, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } : new byte[] { 48, 13, 2, 1, 0, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } }; +#if NETFRAMEWORK + yield return new object[] { null, new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[0], new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[] { 97, 98, 99 }, new byte[] { 48, 132, 0, 0, 0, 13, 2, 1, 0, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } }; +#else + yield return new object[] { null, new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[0], new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[] { 97, 98, 99 }, new byte[] { 48, 13, 2, 1, 0, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } }; +#endif } [Theory] @@ -51,9 +61,15 @@ public void Ctor_Cookie(byte[] cookie, byte[] expectedValue) public static IEnumerable Ctor_Cookie_Options_Data() { - yield return new object[] { null, DirectorySynchronizationOptions.None, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } : new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; - yield return new object[] { new byte[0], DirectorySynchronizationOptions.None - 1, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 13, 2, 4, 255, 255, 255, 255, 2, 3, 16, 0, 0, 4, 0 } : new byte[] { 48, 10, 2, 1, 255, 2, 3, 16, 0, 0, 4, 0 } }; - yield return new object[] { new byte[] { 97, 98, 99 }, DirectorySynchronizationOptions.ObjectSecurity, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 13, 2, 1, 1, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } : new byte[] { 48, 13, 2, 1, 1, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } }; +#if NETFRAMEWORK + yield return new object[] { null, DirectorySynchronizationOptions.None, new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[0], DirectorySynchronizationOptions.None - 1, new byte[] { 48, 132, 0, 0, 0, 13, 2, 4, 255, 255, 255, 255, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[] { 97, 98, 99 }, DirectorySynchronizationOptions.ObjectSecurity, new byte[] { 48, 132, 0, 0, 0, 13, 2, 1, 1, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } }; +#else + yield return new object[] { null, DirectorySynchronizationOptions.None, new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[0], DirectorySynchronizationOptions.None - 1, new byte[] { 48, 10, 2, 1, 255, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[] { 97, 98, 99 }, DirectorySynchronizationOptions.ObjectSecurity, new byte[] { 48, 13, 2, 1, 1, 2, 3, 16, 0, 0, 4, 3, 97, 98, 99 } }; +#endif } [Theory] @@ -74,9 +90,15 @@ public void Ctor_Cookie_Options(byte[] cookie, DirectorySynchronizationOptions o public static IEnumerable Ctor_Cookie_Options_AttributeCount_Data() { - yield return new object[] { null, DirectorySynchronizationOptions.None, 1048576, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } : new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; - yield return new object[] { new byte[0], DirectorySynchronizationOptions.None - 1, 0, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 11, 2, 4, 255, 255, 255, 255, 2, 1, 0, 4, 0 } : new byte[] { 48, 8, 2, 1, 255, 2, 1, 0, 4, 0 } }; - yield return new object[] { new byte[] { 97, 98, 99 }, DirectorySynchronizationOptions.ObjectSecurity, 10, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 11, 2, 1, 1, 2, 1, 10, 4, 3, 97, 98, 99 } : new byte[] { 48, 11, 2, 1, 1, 2, 1, 10, 4, 3, 97, 98, 99 } }; +#if NETFRAMEWORK + yield return new object[] { null, DirectorySynchronizationOptions.None, 1048576, new byte[] { 48, 132, 0, 0, 0, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[0], DirectorySynchronizationOptions.None - 1, 0, new byte[] { 48, 132, 0, 0, 0, 11, 2, 4, 255, 255, 255, 255, 2, 1, 0, 4, 0 } }; + yield return new object[] { new byte[] { 97, 98, 99 }, DirectorySynchronizationOptions.ObjectSecurity, 10, new byte[] { 48, 132, 0, 0, 0, 11, 2, 1, 1, 2, 1, 10, 4, 3, 97, 98, 99 } }; +#else + yield return new object[] { null, DirectorySynchronizationOptions.None, 1048576, new byte[] { 48, 10, 2, 1, 0, 2, 3, 16, 0, 0, 4, 0 } }; + yield return new object[] { new byte[0], DirectorySynchronizationOptions.None - 1, 0, new byte[] { 48, 8, 2, 1, 255, 2, 1, 0, 4, 0 } }; + yield return new object[] { new byte[] { 97, 98, 99 }, DirectorySynchronizationOptions.ObjectSecurity, 10, new byte[] { 48, 11, 2, 1, 1, 2, 1, 10, 4, 3, 97, 98, 99 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncResponseControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncResponseControlTests.cs index b670477c499e4..c970705fb49f2 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncResponseControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/DirSyncResponseControlTests.cs @@ -67,6 +67,7 @@ public static IEnumerable ConformantControlValues() public static IEnumerable NonconformantControlValues() { +#if NETFRAMEWORK // {eeO}, single-byte length. ASN.1 type of ENUMERATED rather than INTEGER yield return new object[] { new byte[] { 0x30, 0x0D, 0x0A, 0x01, 0xFF, @@ -112,26 +113,9 @@ public static IEnumerable NonconformantControlValues() 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, 0x80, 0x80, 0x80 }, true, 0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; - - // Windows will treat these values as invalid. OpenLDAP has slightly looser parsing rules around octet string lengths. - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // {iiO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x0D, - 0x02, 0x01, 0x00, - 0x02, 0x01, 0x40, - 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 - }, false, 0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80 } }; - - // {iiO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x11, - 0x02, 0x01, 0x00, - 0x02, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 - }, false, 0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80 } }; - } +#else + yield break; +#endif } public static IEnumerable InvalidControlValues() @@ -153,6 +137,20 @@ public static IEnumerable InvalidControlValues() 0x02, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4} }; +#if NET + // {eeO}, single-byte length. ASN.1 type of ENUMERATED rather than INTEGER + yield return new object[] { new byte[] { 0x30, 0x0D, + 0x0A, 0x01, 0xFF, + 0x0A, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + + // {eeO}, four-byte length. ASN.1 type of ENUMERATED rather than INTEGER + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x11, + 0x0A, 0x01, 0xFF, + 0x0A, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; +#endif + // {iiO}, single-byte length, sequence length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x09, 0x02, 0x01, 0x00, @@ -165,24 +163,19 @@ public static IEnumerable InvalidControlValues() 0x02, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00} }; - // Only Windows treats these values as invalid. These values are present in NonconformantControlValues to prove - // the OpenLDAP behavior. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // {iiO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x0D, - 0x02, 0x01, 0x00, - 0x02, 0x01, 0x40, - 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 } }; - - // {iiO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x11, - 0x02, 0x01, 0x00, - 0x02, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 } }; - } + // {iiO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x0D, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x40, + 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, + 0x80, 0x80, 0x80 } }; + + // {iiO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x11, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, + 0x80, 0x80, 0x80 } }; // {iiO}, single-byte length. Octet string length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x0D, @@ -195,6 +188,36 @@ public static IEnumerable InvalidControlValues() 0x02, 0x01, 0x00, 0x02, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + +#if NET + // {iiO}, single-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x0D, + 0x02, 0x01, 0xFF, + 0x02, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iiO}, four-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x11, + 0x02, 0x01, 0xFF, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iiO}, single-byte length. Trailing data within the sequence (after the octet string) + yield return new object[] { new byte[] { 0x30, 0x11, + 0x02, 0x01, 0xFF, + 0x02, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iiO}, four-byte length. Trailing data within the sequence (after the octet string) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x15, + 0x02, 0x01, 0xFF, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs index 00433bae9875c..64c8487c7b481 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/DirectoryServicesProtocolsTests.cs @@ -669,7 +669,7 @@ public void TestSortedSearch() Assert.Equal(1, searchResponse.Controls.Length); Assert.True(searchResponse.Controls[0] is SortResponseControl); Assert.True(searchResponse.Entries.Count > 0); - Assert.Equal("ou=ProtocolsSubGroup10.9," + dn + "," + LdapConfiguration.Configuration.SearchDn, searchResponse.Entries[0].DistinguishedName); + Assert.Equal("ou=ProtocolsSubGroup10.9," + dn + "," + LdapConfiguration.Configuration.SearchDn, searchResponse.Entries[0].DistinguishedName, true); } finally { diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/ExtendedDNControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/ExtendedDNControlTests.cs index 284d916613b23..d7fe7a9aca68c 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/ExtendedDNControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/ExtendedDNControlTests.cs @@ -19,7 +19,11 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.529", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 0 } : new byte[] { 48, 3, 2, 1, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 0 }; +#else + var expected = new byte[] { 48, 3, 2, 1, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } @@ -32,7 +36,11 @@ public void Ctor_Flag() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.529", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 1 } : new byte[] { 48, 3, 2, 1, 1 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 1 }; +#else + var expected = new byte[] { 48, 3, 2, 1, 1 }; +#endif Assert.Equal(expected, control.GetValue()); } diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/PageResultRequestControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/PageResultRequestControlTests.cs index 537874c7f4fbd..5fab45ff1cb0e 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/PageResultRequestControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/PageResultRequestControlTests.cs @@ -20,14 +20,23 @@ public void Ctor_Default() Assert.Equal(512, control.PageSize); Assert.Equal("1.2.840.113556.1.4.319", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 6, 2, 2, 2, 0, 4, 0 } : new byte[] { 48, 6, 2, 2, 2, 0, 4, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 6, 2, 2, 2, 0, 4, 0 }; +#else + var expected = new byte[] { 48, 6, 2, 2, 2, 0, 4, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_PageSize_Data() { - yield return new object[] { 0, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 0, 4, 0 } : new byte[] { 48, 5, 2, 1, 0, 4, 0 } }; - yield return new object[] { 10, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 10, 4, 0 } : new byte[] { 48, 5, 2, 1, 10, 4, 0 } }; +#if NETFRAMEWORK + yield return new object[] { 0, new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 0, 4, 0 } }; + yield return new object[] { 10, new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 10, 4, 0 } }; +#else + yield return new object[] { 0, new byte[] { 48, 5, 2, 1, 0, 4, 0 } }; + yield return new object[] { 10, new byte[] { 48, 5, 2, 1, 10, 4, 0 } }; +#endif } [Theory] @@ -52,9 +61,15 @@ public void Ctor_NegativePageSize_ThrowsArgumentException() public static IEnumerable Ctor_Cookie_Data() { - yield return new object[] { null, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 6, 2, 2, 2, 0, 4, 0 } : new byte[] { 48, 6, 2, 2, 2, 0, 4, 0 } }; - yield return new object[] { new byte[0], (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 6, 2, 2, 2, 0, 4, 0 } : new byte[] { 48, 6, 2, 2, 2, 0, 4, 0 } }; - yield return new object[] { new byte[] { 1, 2, 3, }, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 9, 2, 2, 2, 0, 4, 3, 1, 2, 3 } : new byte[] { 48, 9, 2, 2, 2, 0, 4, 3, 1, 2, 3 } }; +#if NETFRAMEWORK + yield return new object[] { null, new byte[] { 48, 132, 0, 0, 0, 6, 2, 2, 2, 0, 4, 0 } }; + yield return new object[] { new byte[0], new byte[] { 48, 132, 0, 0, 0, 6, 2, 2, 2, 0, 4, 0 } }; + yield return new object[] { new byte[] { 1, 2, 3, }, new byte[] { 48, 132, 0, 0, 0, 9, 2, 2, 2, 0, 4, 3, 1, 2, 3 } }; +#else + yield return new object[] { null, new byte[] { 48, 6, 2, 2, 2, 0, 4, 0 } }; + yield return new object[] { new byte[0], new byte[] { 48, 6, 2, 2, 2, 0, 4, 0 } }; + yield return new object[] { new byte[] { 1, 2, 3, }, new byte[] { 48, 9, 2, 2, 2, 0, 4, 3, 1, 2, 3 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/PageResultResponseControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/PageResultResponseControlTests.cs index bb02931f17c48..b7e62c7579041 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/PageResultResponseControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/PageResultResponseControlTests.cs @@ -59,6 +59,7 @@ public static IEnumerable ConformantControlValues() public static IEnumerable NonconformantControlValues() { +#if NETFRAMEWORK // {eO}, single-byte length. ASN.1 type of ENUMERATED rather than INTEGER yield return new object[] { new byte[] { 0x30, 0x0A, 0x0A, 0x01, 0x40, @@ -98,24 +99,9 @@ public static IEnumerable NonconformantControlValues() 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, 0x80, 0x80, 0x80 }, 0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; - - // Windows will treat these values as invalid. OpenLDAP has slightly looser parsing rules around octet string lengths. - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // {iO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x0A, - 0x02, 0x01, 0x40, - 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 - }, 0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80 } }; - - // {iO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, - 0x02, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 - }, 0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80 } }; - } +#else + yield break; +#endif } public static IEnumerable InvalidControlValues() @@ -131,6 +117,18 @@ public static IEnumerable InvalidControlValues() yield return new object[] { new byte[] { 0x02, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4} }; +#if NET + // {eO}, single-byte length. ASN.1 type of ENUMERATED rather than INTEGER + yield return new object[] { new byte[] { 0x30, 0x0A, + 0x0A, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + + // {eO}, four-byte length. ASN.1 type of ENUMERATED rather than INTEGER + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, + 0x0A, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; +#endif + // {iO}, single-byte length, sequence length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x06, 0x02, 0x01, 0x40, @@ -141,23 +139,18 @@ public static IEnumerable InvalidControlValues() 0x02, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x00} }; - // Only Windows treats these values as invalid. These values are present in NonconformantControlValues to prove - // the OpenLDAP behavior. - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - // {iO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x0A, - 0x02, 0x01, 0x40, - 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 } }; - - // {iO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) - yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, - 0x02, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, - 0x80, 0x80, 0x80 } }; - } + // {iO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x0A, + 0x02, 0x01, 0x40, + 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, + 0x80, 0x80, 0x80 } }; + // {iO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, + 0x80, 0x80, 0x80 } }; + // {iO}, single-byte length. Octet string length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x0A, 0x02, 0x01, 0x40, @@ -167,6 +160,32 @@ public static IEnumerable InvalidControlValues() yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, 0x02, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + +#if NET + // {iO}, single-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x0A, + 0x02, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iO}, four-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iO}, single-byte length. Trailing data within the sequence (after the octet string) + yield return new object[] { new byte[] { 0x30, 0x0E, + 0x02, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iO}, four-byte length. Trailing data within the sequence (after the octet string) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x12, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/QuotaControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/QuotaControlTests.cs index b1ddaa9efa8e8..c89086f4066f5 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/QuotaControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/QuotaControlTests.cs @@ -20,14 +20,23 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.1852", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } : new byte[] { 48, 2, 4, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 }; +#else + var expected = new byte[] { 48, 2, 4, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_QuerySid_TestData() { +#if NETFRAMEWORK yield return new object[] { new SecurityIdentifier("S-1-5-32-544"), new byte[] { 48, 132, 0, 0, 0, 18, 4, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0 } }; yield return new object[] { null, new byte[] { 48, 132, 0, 0, 0, 2, 4, 0 } }; +#else + yield return new object[] { new SecurityIdentifier("S-1-5-32-544"), new byte[] { 48, 18, 4, 16, 1, 2, 0, 0, 0, 0, 0, 5, 32, 0, 0, 0, 32, 2, 0, 0 } }; + yield return new object[] { null, new byte[] { 48, 2, 4, 0 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/SearchOptionsControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/SearchOptionsControlTests.cs index 2a30d7dc3a522..f2e2b1b6c46d9 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/SearchOptionsControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/SearchOptionsControlTests.cs @@ -19,7 +19,11 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.1340", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 1 } : new byte[] { 48, 3, 2, 1, 1 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 1 }; +#else + var expected = new byte[] { 48, 3, 2, 1, 1 }; +#endif Assert.Equal(expected, control.GetValue()); } @@ -32,7 +36,11 @@ public void Ctor_Flags() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.1340", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 2 } : new byte[] { 48, 3, 2, 1, 2 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 2 }; +#else + var expected = new byte[] { 48, 3, 2, 1, 2 }; +#endif Assert.Equal(expected, control.GetValue()); } diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/SecurityDescriptorFlagControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/SecurityDescriptorFlagControlTests.cs index 6ec7955faa782..a973cfd141af9 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/SecurityDescriptorFlagControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/SecurityDescriptorFlagControlTests.cs @@ -19,14 +19,23 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.801", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 0 } : new byte[] { 48, 3, 2, 1, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 0 }; +#else + var expected = new byte[] { 48, 3, 2, 1, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_Flags_Data() { - yield return new object[] { SecurityMasks.Group, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 2 } : new byte[] { 48, 3, 2, 1, 2 } }; - yield return new object[] { SecurityMasks.None - 1, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 6, 2, 4, 255, 255, 255, 255 } : new byte[] { 48, 3, 2, 1, 255 } }; +#if NETFRAMEWORK + yield return new object[] { SecurityMasks.Group, new byte[] { 48, 132, 0, 0, 0, 3, 2, 1, 2 } }; + yield return new object[] { SecurityMasks.None - 1, new byte[] { 48, 132, 0, 0, 0, 6, 2, 4, 255, 255, 255, 255 } }; +#else + yield return new object[] { SecurityMasks.Group, new byte[] { 48, 3, 2, 1, 2 } }; + yield return new object[] { SecurityMasks.None - 1, new byte[] { 48, 3, 2, 1, 255 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs index 50185cf8d52ef..aeed0e772e198 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/SortRequestControlTests.cs @@ -11,7 +11,6 @@ namespace System.DirectoryServices.Protocols.Tests public class SortRequestControlTests { [Theory] - [ActiveIssue("https://github.com/dotnet/runtime/issues/34679", TestPlatforms.Windows, TargetFrameworkMonikers.Netcoreapp, TestRuntimes.Mono)] [InlineData(true)] [InlineData(false)] public void Ctor_SortKeys(bool critical) @@ -31,16 +30,16 @@ public void Ctor_SortKeys(bool critical) } control.IsCritical = critical; - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? - // WLDAP formatted ASN.1 - new byte[] { 48, 132, 0, 0, 0, 43, 48, 132, 0, 0, 0, 17, 4, 5,110, +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 43, 48, 132, 0, 0, 0, 17, 4, 5,110, 97, 109, 101, 49, 128, 5, 114, 117, 108, 101, 49, 129, 1, 255, 48, 132, 0, 0, 0, 14, 4, 5, 110, 97, 109, 101, - 50, 128, 5, 114, 117, 108, 101, 50 } : - // OpenLdap formatted ASN.1 - new byte[] { 48, 35, 48, 17, 4, 5, 110, 97, 109, 101, 49, 128, 5, + 50, 128, 5, 114, 117, 108, 101, 50 }; +#else + var expected = new byte[] { 48, 35, 48, 17, 4, 5, 110, 97, 109, 101, 49, 128, 5, 114, 117, 108, 101, 49, 129, 1, 255, 48, 14, 4, 5, 110, 97, 109, 101, 50, 128, 5, 114, 117, 108, 101, 50 }; +#endif Assert.Equal(expected, control.GetValue()); } diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/SortResponseControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/SortResponseControlTests.cs index 0a17dfb27a338..2b476bec893bd 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/SortResponseControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/SortResponseControlTests.cs @@ -39,58 +39,43 @@ public static IEnumerable ConformantControlValues() // {ea}, single-byte length yield return new object[] { new byte[] { 0x30, 0x0A, 0x0A, 0x01, 0x40, - 0x04, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 + 0x80, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 }, (ResultCode)0x40, "name1" }; yield return new object[] { new byte[] { 0x30, 0x0A, 0x0A, 0x01, 0x7F, - 0x04, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 + 0x80, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 }, (ResultCode)0x7F, "name1" }; yield return new object[] { new byte[] { 0x30, 0x05, 0x0A, 0x01, 0x40, - 0x04, 0x00 -#if NET - }, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major < 10 ? null : string.Empty }; -#else + 0x80, 0x00 }, (ResultCode)0x40, string.Empty }; -#endif yield return new object[] { new byte[] { 0x30, 0x05, 0x0A, 0x01, 0x7F, - 0x04, 0x00 -#if NET - }, (ResultCode)0x7F, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major < 10 ? null : string.Empty }; -#else + 0x80, 0x00 }, (ResultCode)0x7F, string.Empty }; -#endif // {ea}, four-byte length yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, 0x0A, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 + 0x80, 0x84, 0x00, 0x00, 0x00, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 }, (ResultCode)0x40, "name1" }; yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0E, 0x0A, 0x01, 0x7F, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 + 0x80, 0x84, 0x00, 0x00, 0x00, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31 }, (ResultCode)0x7F, "name1" }; yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x09, 0x0A, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x00 -#if NET - }, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major < 10 ? null : string.Empty }; -#else + 0x80, 0x84, 0x00, 0x00, 0x00, 0x00 }, (ResultCode)0x40, string.Empty }; -#endif yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x09, 0x0A, 0x01, 0x7F, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x00 -#if NET - }, (ResultCode)0x7F, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.OSVersion.Version.Major < 10 ? null : string.Empty }; -#else + 0x80, 0x84, 0x00, 0x00, 0x00, 0x00 }, (ResultCode)0x7F, string.Empty }; -#endif } public static IEnumerable NonconformantControlValues() { +#if NETFRAMEWORK // {i}, single-byte length. ASN.1 type of INTEGER rather than ENUMERATED yield return new object[] { new byte[] { 0x30, 0x03, 0x02, 0x01, 0x40 @@ -113,36 +98,33 @@ public static IEnumerable NonconformantControlValues() 0x80, 0x80, 0x80, 0x80 }, (ResultCode)0x40, null }; - // {e}, single-byte length. Trailing data within the sequence is interpreted as an empty string by Windows, null by OpenLDAP + // {e}, single-byte length. Trailing data within the sequence is interpreted as an empty string by Windows yield return new object[] { new byte[] { 0x30, 0x07, 0x0A, 0x01, 0x40, 0x80, 0x80, 0x80, 0x80 - }, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? string.Empty : null }; + }, (ResultCode)0x40, string.Empty }; - // {e}, four-byte length. Trailing data within the sequence is interpreted as an empty string by Windows, null by OpenLDAP + // {e}, four-byte length. Trailing data within the sequence is interpreted as an empty string by Windows yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x07, 0x0A, 0x01, 0x40, 0x80, 0x80, 0x80, 0x80 - }, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? string.Empty : null }; + }, (ResultCode)0x40, string.Empty }; // {ea}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer.) - // The result of the "a" format specifier is null on Windows, but any OS platform which uses OpenLDAP will return - // the out-of-sequence contents. This is also why the first trailing data byte is 0x31 rather than 0x80 - 0x80 is - // not a valid Unicode character, so we change it to 0x31 to avoid encountering a DecoderFallbackException before - // we can verify the results + // The result of the "a" format specifier is null on Windows yield return new object[] { new byte[] { 0x30, 0x0A, 0x0A, 0x01, 0x40, 0x04, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x31, 0x80, 0x80, 0x80 - }, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? null : "name11" }; + }, (ResultCode)0x40, null }; - // {ea}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer.) Result of the "a" format specifier is null - // The comment on the test case above also applies here + // {ea}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer.) + // The result of the "a" format specifier is null on Windows yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x31, 0x80, 0x80, 0x80 - }, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? null : "name11" }; + }, (ResultCode)0x40, null }; // {ea}, single-byte length. Octet string length extending beyond the end of the buffer. Result of the "a" format specifier is null yield return new object[] { new byte[] { 0x30, 0x0A, @@ -169,6 +151,9 @@ public static IEnumerable NonconformantControlValues() 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x80, 0x80, 0x80, 0x80 }, (ResultCode)0x40, "name1" }; +#else + yield break; +#endif } public static IEnumerable InvalidControlValues() @@ -183,6 +168,70 @@ public static IEnumerable InvalidControlValues() // {e}, four-byte length, sequence length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x04, 0x0A, 0x01, 0x40 } }; + +#if NET + // {i}, single-byte length. ASN.1 type of INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x03, + 0x02, 0x01, 0x40 } }; + + // {i}, four-byte length. ASN.1 type of INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x03, + 0x02, 0x01, 0x40 } }; + + // {e}, single-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x03, + 0x0A, 0x01, 0x40, + 0x80, 0x80, 0x80, 0x80 } }; + + // {e}, four-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x03, + 0x0A, 0x01, 0x40, + 0x80, 0x80, 0x80, 0x80 } }; + + // {e}, single-byte length. Trailing data within the sequence + yield return new object[] { new byte[] { 0x30, 0x07, + 0x0A, 0x01, 0x40, + 0x80, 0x80, 0x80, 0x80 } }; + + // {e}, four-byte length. Trailing data within the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x07, + 0x0A, 0x01, 0x40, + 0x80, 0x80, 0x80, 0x80 } }; + + // {ea}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x0A, + 0x0A, 0x01, 0x40, + 0x80, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x31, + 0x80, 0x80, 0x80 } }; + + // {ea}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0A, + 0x0A, 0x01, 0x40, + 0x80, 0x84, 0x00, 0x00, 0x00, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x31, + 0x80, 0x80, 0x80 } }; + + // {ea}, single-byte length. Octet string length extending beyond the end of the buffer + yield return new object[] { new byte[] { 0x30, 0x0A, + 0x0A, 0x01, 0x40, + 0x80, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31 } }; + + // {ea}, four-byte length. Octet string length extending beyond the end of the buffer + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0A, + 0x0A, 0x01, 0x40, + 0x80, 0x84, 0x00, 0x00, 0x00, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31 } }; + + // {ea}, single-byte length. Trailing data within the sequence (after the octet string) + yield return new object[] { new byte[] { 0x30, 0x0E, + 0x0A, 0x01, 0x40, + 0x80, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31, + 0x80, 0x80, 0x80, 0x80 } }; + + // {ea}, four-byte length. Trailing data within the sequence (after the octet string) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x12, + 0x0A, 0x01, 0x40, + 0x80, 0x84, 0x00, 0x00, 0x00, 0x05, 0x6E, 0x61, 0x6D, 0x65, 0x31, + 0x80, 0x80, 0x80, 0x80 } }; +#endif } public static IEnumerable InvalidUnicodeText() @@ -190,13 +239,13 @@ public static IEnumerable InvalidUnicodeText() // {ea}, single-byte length. Octet string contains a trailing 0x80 (which is invalid Unicode) yield return new object[] { new byte[] { 0x30, 0x0B, 0x0A, 0x01, 0x40, - 0x04, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x80 + 0x80, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x80 } }; // {ea}, four-byte length. Octet string contains a trailing 0x80 (which is invalid Unicode) yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x0F, 0x0A, 0x01, 0x40, - 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x80 + 0x80, 0x84, 0x00, 0x00, 0x00, 0x06, 0x6E, 0x61, 0x6D, 0x65, 0x31, 0x80 } }; } @@ -219,14 +268,27 @@ public void InvalidResponseControlThrowsException(byte[] value) Assert.Throws(() => TransformResponseControl(control, false)); } +#if NETFRAMEWORK [Theory] [MemberData(nameof(InvalidUnicodeText))] - public void InvalidUnicodeTextThrowsException(byte[] value) + public void InvalidUnicodeTextThrowsDecoderFallbackException(byte[] value) { DirectoryControl control = new(ControlOid, value, true, true); Assert.Throws(() => TransformResponseControl(control, false)); } +#else + [Theory] + [MemberData(nameof(InvalidUnicodeText))] + public void InvalidUnicodeTextThrowsBerConversionException(byte[] value) + { + DirectoryControl control = new(ControlOid, value, true, true); + + BerConversionException conversionException = Assert.Throws(() => TransformResponseControl(control, false)); + + Assert.IsType(conversionException.InnerException); + } +#endif private static void VerifyResponseControl(byte[] value, ResultCode expectedResultCode, string expectedAttribute) { diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/VerifyNameControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/VerifyNameControlTests.cs index 5e7469d27ed01..c967963483387 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/VerifyNameControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/VerifyNameControlTests.cs @@ -20,14 +20,23 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("1.2.840.113556.1.4.1338", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 0, 4, 0 } : new byte[] { 48, 5, 2, 1, 0, 4, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 0, 4, 0 }; +#else + var expected = new byte[] { 48, 5, 2, 1, 0, 4, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_ServerName_Data() { - yield return new object[] { "", (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 0, 4, 0 } : new byte[] { 48, 5, 2, 1, 0, 4, 0 } }; - yield return new object[] { "S", (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 7, 2, 1, 0, 4, 2, 83, 0 } : new byte[] { 48, 7, 2, 1, 0, 4, 2, 83, 0 } }; +#if NETFRAMEWORK + yield return new object[] { "", new byte[] { 48, 132, 0, 0, 0, 5, 2, 1, 0, 4, 0 } }; + yield return new object[] { "S", new byte[] { 48, 132, 0, 0, 0, 7, 2, 1, 0, 4, 2, 83, 0 } }; +#else + yield return new object[] { "", new byte[] { 48, 5, 2, 1, 0, 4, 0 } }; + yield return new object[] { "S", new byte[] { 48, 7, 2, 1, 0, 4, 2, 83, 0 } }; +#endif } [Theory] @@ -46,8 +55,13 @@ public void Ctor_ServerName(string serverName, byte[] expectedValue) public static IEnumerable Ctor_ServerName_Flag_Data() { - yield return new object[] { "", -1, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 8, 2, 4, 255, 255, 255, 255, 4, 0 } : new byte[] { 48, 5, 2, 1, 255, 4, 0 } }; - yield return new object[] { "S", 10, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 7, 2, 1, 10, 4, 2, 83, 0 } : new byte[] { 48, 7, 2, 1, 10, 4, 2, 83, 0 } }; +#if NETFRAMEWORK + yield return new object[] { "", -1, new byte[] { 48, 132, 0, 0, 0, 8, 2, 4, 255, 255, 255, 255, 4, 0 } }; + yield return new object[] { "S", 10, new byte[] { 48, 132, 0, 0, 0, 7, 2, 1, 10, 4, 2, 83, 0 } }; +#else + yield return new object[] { "", -1, new byte[] { 48, 5, 2, 1, 255, 4, 0 } }; + yield return new object[] { "S", 10, new byte[] { 48, 7, 2, 1, 10, 4, 2, 83, 0 } }; +#endif } [Theory] diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/VlvRequestControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/VlvRequestControlTests.cs index a259bd11c7e8a..00f964200a9d9 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/VlvRequestControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/VlvRequestControlTests.cs @@ -24,14 +24,23 @@ public void Ctor_Default() Assert.True(control.ServerSide); Assert.Equal("2.16.840.1.113730.3.4.9", control.Type); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } : new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 }; +#else + var expected = new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 }; +#endif Assert.Equal(expected, control.GetValue()); } public static IEnumerable Ctor_BeforeCount_AfterCount_Offset_Data() { - yield return new object[] { 0, 0, 0, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } : new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 } }; - yield return new object[] { 10, 10, 10, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 10, 2, 1, 10, 160, 132, 0, 0, 0, 6, 2, 1, 10, 2, 1, 0 } : new byte[] { 48, 14, 2, 1, 10, 2, 1, 10, 160, 6, 2, 1, 10, 2, 1, 0 } }; +#if NETFRAMEWORK + yield return new object[] { 0, 0, 0, new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } }; + yield return new object[] { 10, 10, 10, new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 10, 2, 1, 10, 160, 132, 0, 0, 0, 6, 2, 1, 10, 2, 1, 0 } }; +#else + yield return new object[] { 0, 0, 0, new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 } }; + yield return new object[] { 10, 10, 10, new byte[] { 48, 14, 2, 1, 10, 2, 1, 10, 160, 6, 2, 1, 10, 2, 1, 0 } }; +#endif } [Theory] @@ -54,8 +63,13 @@ public void Ctor_BeforeCount_AfterCount_Offset(int beforeCount, int afterCount, public static IEnumerable Ctor_BeforeCount_AfterCount_StringTarget_Data() { - yield return new object[] { 0, 0, null, new byte[0], (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } : new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 } }; - yield return new object[] { 10, 10, "abc", new byte[] { 97, 98, 99 }, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 11, 2, 1, 10, 2, 1, 10, 129, 3, 97, 98, 99 } : new byte[] { 48, 11, 2, 1, 10, 2, 1, 10, 129, 3, 97, 98, 99 } }; +#if NETFRAMEWORK + yield return new object[] { 0, 0, null, new byte[0], new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } }; + yield return new object[] { 10, 10, "abc", new byte[] { 97, 98, 99 }, new byte[] { 48, 132, 0, 0, 0, 11, 2, 1, 10, 2, 1, 10, 129, 3, 97, 98, 99 } }; +#else + yield return new object[] { 0, 0, null, new byte[0], new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 } }; + yield return new object[] { 10, 10, "abc", new byte[] { 97, 98, 99 }, new byte[] { 48, 11, 2, 1, 10, 2, 1, 10, 129, 3, 97, 98, 99 } }; +#endif } [Theory] @@ -79,8 +93,13 @@ public void Ctor_BeforeCount_AfterCount_StringTarget(int beforeCount, int afterC public static IEnumerable Ctor_BeforeCount_AfterCount_ByteArrayTarget_Data() { - yield return new object[] { 0, 0, null, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } : new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 } }; - yield return new object[] { 10, 10, new byte[] { 1, 2, 3 }, (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 11, 2, 1, 10, 2, 1, 10, 129, 3, 1, 2, 3 } : new byte[] { 48, 11, 2, 1, 10, 2, 1, 10, 129, 3, 1, 2, 3 } }; +#if NETFRAMEWORK + yield return new object[] { 0, 0, null, new byte[] { 48, 132, 0, 0, 0, 18, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0 } }; + yield return new object[] { 10, 10, new byte[] { 1, 2, 3 }, new byte[] { 48, 132, 0, 0, 0, 11, 2, 1, 10, 2, 1, 10, 129, 3, 1, 2, 3 } }; +#else + yield return new object[] { 0, 0, null, new byte[] { 48, 14, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0 } }; + yield return new object[] { 10, 10, new byte[] { 1, 2, 3 }, new byte[] { 48, 11, 2, 1, 10, 2, 1, 10, 129, 3, 1, 2, 3 } }; +#endif } [Theory] @@ -146,7 +165,11 @@ public void ContextId_Set_GetReturnsExpected() Assert.NotSame(contextId, control.ContextId); Assert.Equal(contextId, control.ContextId); - var expected = (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) ? new byte[] { 48, 132, 0, 0, 0, 23, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0, 4, 3, 1, 2, 3 } : new byte[] { 48, 19, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0, 4, 3, 1, 2, 3 }; +#if NETFRAMEWORK + var expected = new byte[] { 48, 132, 0, 0, 0, 23, 2, 1, 0, 2, 1, 0, 160, 132, 0, 0, 0, 6, 2, 1, 0, 2, 1, 0, 4, 3, 1, 2, 3 }; +#else + var expected = new byte[] { 48, 19, 2, 1, 0, 2, 1, 0, 160, 6, 2, 1, 0, 2, 1, 0, 4, 3, 1, 2, 3 }; +#endif Assert.Equal(expected, control.GetValue()); } } diff --git a/src/libraries/System.DirectoryServices.Protocols/tests/VlvResponseControlTests.cs b/src/libraries/System.DirectoryServices.Protocols/tests/VlvResponseControlTests.cs index 07283a7cc6a0d..acfa6adfd39b2 100644 --- a/src/libraries/System.DirectoryServices.Protocols/tests/VlvResponseControlTests.cs +++ b/src/libraries/System.DirectoryServices.Protocols/tests/VlvResponseControlTests.cs @@ -63,6 +63,7 @@ public static IEnumerable ConformantControlValues() public static IEnumerable NonconformantControlValues() { +#if NETFRAMEWORK // {eei}, single-byte length. ASN.1 types of ENUMERATED rather than INTEGER, and INTEGER rather than ENUMERATED yield return new object[] { new byte[] { 0x30, 0x09, 0x0A, 0x01, 0x00, @@ -129,10 +130,6 @@ public static IEnumerable NonconformantControlValues() 0x80, 0x80, 0x80, 0x80 }, 0x00, 0x20, (ResultCode)0x40, new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; - // These cases would normally fail, but TransformControls will fall back to ignore the octet string. - // This behavior is inconsistent with other tests which cover the parsing of octet strings (which would - // throw an exception rather than return an empty array.) It is also inconsistent between Windows and - // non-Windows platforms. // {iieO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) yield return new object[] { new byte[] { 0x30, 0x10, 0x02, 0x01, 0x00, @@ -140,7 +137,7 @@ public static IEnumerable NonconformantControlValues() 0x0A, 0x01, 0x40, 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, 0x80, 0x80, 0x80 - }, 0x00, 0x20, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Array.Empty() : new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80 } }; + }, 0x00, 0x20, (ResultCode)0x40, Array.Empty() }; // {iieO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x14, @@ -149,7 +146,7 @@ public static IEnumerable NonconformantControlValues() 0x0A, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, 0x80, 0x80, 0x80 - }, 0x00, 0x20, (ResultCode)0x40, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? Array.Empty() : new byte[] { 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80 } }; + }, 0x00, 0x20, (ResultCode)0x40, Array.Empty() }; // {iieO}, single-byte length. Octet string length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x10, @@ -166,6 +163,9 @@ public static IEnumerable NonconformantControlValues() 0x0A, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 }, 0x00, 0x20, (ResultCode)0x40, Array.Empty() }; +#else + yield break; +#endif } public static IEnumerable InvalidControlValues() @@ -194,6 +194,96 @@ public static IEnumerable InvalidControlValues() 0x0A, 0x01, 0x40, 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; +#if NET + // {eei}, single-byte length. ASN.1 types of ENUMERATED rather than INTEGER, and INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x09, + 0x0A, 0x01, 0x00, + 0x0A, 0x01, 0x20, + 0x02, 0x01, 0x40 } }; + + // {eei}, four-byte length. ASN.1 types of ENUMERATED rather than INTEGER, and INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x09, + 0x0A, 0x01, 0x00, + 0x0A, 0x01, 0x20, + 0x02, 0x01, 0x40 } }; + + // {eeiO}, single-byte length. ASN.1 types of ENUMERATED rather than INTEGER, and INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x10, + 0x0A, 0x01, 0x00, + 0x0A, 0x01, 0x20, + 0x02, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + + // {eeiO}, four-byte length. ASN.1 types of ENUMERATED rather than INTEGER, and INTEGER rather than ENUMERATED + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x14, + 0x0A, 0x01, 0x00, + 0x0A, 0x01, 0x20, + 0x02, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + + // {iieO}, single-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x10, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iieO}, four-byte length. Trailing data after the end of the sequence + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x14, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iieO}, single-byte length. Trailing data within the sequence (after the end of the OCTET STRING) + yield return new object[] { new byte[] { 0x30, 0x14, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iieO}, four-byte length. Trailing data within the sequence (after the end of the OCTET STRING) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x18, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x05, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, + 0x80, 0x80, 0x80, 0x80 } }; + + // {iieO}, single-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x10, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, + 0x80, 0x80, 0x80 } }; + + // {iieO}, four-byte length. Octet string length extending beyond the end of the sequence (but within the buffer) + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x14, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0x80, + 0x80, 0x80, 0x80 } }; + + // {iieO}, single-byte length. Octet string length extending beyond the end of the buffer + yield return new object[] { new byte[] { 0x30, 0x10, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; + + // {iieO}, four-byte length. Octet string length extending beyond the end of the buffer + yield return new object[] { new byte[] { 0x30, 0x84, 0x00, 0x00, 0x00, 0x14, + 0x02, 0x01, 0x00, + 0x02, 0x01, 0x20, + 0x0A, 0x01, 0x40, + 0x04, 0x84, 0x00, 0x00, 0x00, 0x06, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4 } }; +#endif + // {iieO}, single-byte length, sequence length extending beyond the end of the buffer yield return new object[] { new byte[] { 0x30, 0x0C, 0x02, 0x01, 0x00,