|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
namespace System.DirectoryServices
{
[StructLayout(LayoutKind.Sequential)]
internal struct SystemTime
{
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
[StructLayout(LayoutKind.Sequential)]
internal sealed class DnWithBinary
{
public int dwLength;
public IntPtr lpBinaryValue; // GUID of directory object
public IntPtr pszDNString; // Distinguished Name
}
[StructLayout(LayoutKind.Sequential)]
internal sealed class DnWithString
{
public IntPtr pszStringValue; // associated value
public IntPtr pszDNString; // Distinguished Name
}
/// <summary>
/// Helper class for dealing with struct AdsValue.
/// </summary>
internal sealed class AdsValueHelper
{
public AdsValue adsvalue;
private GCHandle _pinnedHandle;
public AdsValueHelper(AdsValue adsvalue)
{
this.adsvalue = adsvalue;
}
public AdsValueHelper(object managedValue)
{
AdsType adsType = GetAdsTypeForManagedType(managedValue.GetType());
SetValue(managedValue, adsType);
}
public AdsValueHelper(object managedValue, AdsType adsType)
{
SetValue(managedValue, adsType);
}
public long LowInt64
{
get => (uint)adsvalue.generic.a + (((long)adsvalue.generic.b) << 32);
set
{
adsvalue.generic.a = (int)(value & 0xFFFFFFFF);
adsvalue.generic.b = (int)(value >> 32);
}
}
~AdsValueHelper()
{
if (_pinnedHandle.IsAllocated)
{
_pinnedHandle.Free();
}
}
private AdsType GetAdsTypeForManagedType(Type type)
{
// Consider this code is only exercised by DirectorySearcher
// it just translates the types needed by such a component, if more managed
// types are to be used in the future, this function needs to be expanded.
if (type == typeof(int))
{
return AdsType.ADSTYPE_INTEGER;
}
if (type == typeof(long))
{
return AdsType.ADSTYPE_LARGE_INTEGER;
}
if (type == typeof(bool))
{
return AdsType.ADSTYPE_BOOLEAN;
}
return AdsType.ADSTYPE_UNKNOWN;
}
public AdsValue GetStruct() => adsvalue;
private static ushort LowOfInt(int i) => unchecked((ushort)(i & 0xFFFF));
private static ushort HighOfInt(int i) => unchecked((ushort)((i >> 16) & 0xFFFF));
public object GetValue()
{
switch ((AdsType)adsvalue.dwType)
{
// Common for DNS and LDAP.
case AdsType.ADSTYPE_UTC_TIME:
{
var st = new SystemTime()
{
wYear = LowOfInt(adsvalue.generic.a),
wMonth = HighOfInt(adsvalue.generic.a),
wDayOfWeek = LowOfInt(adsvalue.generic.b),
wDay = HighOfInt(adsvalue.generic.b),
wHour = LowOfInt(adsvalue.generic.c),
wMinute = HighOfInt(adsvalue.generic.c),
wSecond = LowOfInt(adsvalue.generic.d),
wMilliseconds = HighOfInt(adsvalue.generic.d)
};
return new DateTime(st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
}
case AdsType.ADSTYPE_DN_WITH_BINARY:
{
var dnb = new DnWithBinary();
Marshal.PtrToStructure(adsvalue.pointer.value, dnb);
byte[] bytes = new byte[dnb.dwLength];
Marshal.Copy(dnb.lpBinaryValue, bytes, 0, dnb.dwLength);
var strb = new StringBuilder();
var binaryPart = new StringBuilder();
for (int i = 0; i < bytes.Length; i++)
{
string s = bytes[i].ToString("X", CultureInfo.InvariantCulture);
if (s.Length == 1)
{
binaryPart.Append('0');
}
binaryPart.Append(s);
}
strb.Append("B:");
strb.Append(binaryPart.Length);
strb.Append(':');
strb.Append(binaryPart);
strb.Append(':');
strb.Append(Marshal.PtrToStringUni(dnb.pszDNString));
return strb.ToString();
}
case AdsType.ADSTYPE_DN_WITH_STRING:
{
var dns = new DnWithString();
Marshal.PtrToStructure(adsvalue.pointer.value, dns);
string strValue = Marshal.PtrToStringUni(dns.pszStringValue) ?? string.Empty;
return $"S:{strValue.Length}:{strValue}:{Marshal.PtrToStringUni(dns.pszDNString)}";
}
case AdsType.ADSTYPE_DN_STRING:
case AdsType.ADSTYPE_CASE_EXACT_STRING:
case AdsType.ADSTYPE_CASE_IGNORE_STRING:
case AdsType.ADSTYPE_PRINTABLE_STRING:
case AdsType.ADSTYPE_NUMERIC_STRING:
case AdsType.ADSTYPE_OBJECT_CLASS:
// The value is a String.
return Marshal.PtrToStringUni(adsvalue.pointer.value)!;
case AdsType.ADSTYPE_BOOLEAN:
// The value is a bool.
return adsvalue.generic.a != 0;
case AdsType.ADSTYPE_INTEGER:
// The value is an int.
return adsvalue.generic.a;
case AdsType.ADSTYPE_NT_SECURITY_DESCRIPTOR:
case AdsType.ADSTYPE_OCTET_STRING:
case AdsType.ADSTYPE_PROV_SPECIFIC:
// The value is a byte[].
int len = adsvalue.octetString.length;
byte[] value = new byte[len];
Marshal.Copy(adsvalue.octetString.value, value, 0, len);
return value;
case AdsType.ADSTYPE_INVALID:
throw new InvalidOperationException(SR.DSConvertTypeInvalid);
case AdsType.ADSTYPE_LARGE_INTEGER:
return LowInt64;
// Not used in LDAP
case AdsType.ADSTYPE_CASEIGNORE_LIST:
case AdsType.ADSTYPE_OCTET_LIST:
case AdsType.ADSTYPE_PATH:
case AdsType.ADSTYPE_POSTALADDRESS:
case AdsType.ADSTYPE_TIMESTAMP:
case AdsType.ADSTYPE_NETADDRESS:
case AdsType.ADSTYPE_FAXNUMBER:
case AdsType.ADSTYPE_EMAIL:
case AdsType.ADSTYPE_BACKLINK:
case AdsType.ADSTYPE_HOLD:
case AdsType.ADSTYPE_TYPEDNAME:
case AdsType.ADSTYPE_REPLICAPOINTER:
case AdsType.ADSTYPE_UNKNOWN:
return new NotImplementedException(SR.Format(SR.DSAdsvalueTypeNYI, "0x" + Convert.ToString(adsvalue.dwType, 16)));
default:
return new ArgumentException(SR.Format(SR.DSConvertFailed, "0x" + Convert.ToString(LowInt64, 16), "0x" + Convert.ToString(adsvalue.dwType, 16)));
}
}
public object GetVlvValue()
{
var vlv = new AdsVLV();
Marshal.PtrToStructure(adsvalue.octetString.value, vlv);
byte[]? bytes = null;
if (vlv.contextID != (IntPtr)0 && vlv.contextIDlength != 0)
{
bytes = new byte[vlv.contextIDlength];
Marshal.Copy(vlv.contextID, bytes, 0, vlv.contextIDlength);
}
return new DirectoryVirtualListView
{
Offset = vlv.offset,
ApproximateTotal = vlv.contentCount,
DirectoryVirtualListViewContext = new DirectoryVirtualListViewContext(bytes)
};
}
private void SetValue(object managedValue, AdsType adsType)
{
adsvalue = new AdsValue()
{
dwType = (int)adsType
};
switch (adsType)
{
case AdsType.ADSTYPE_INTEGER:
adsvalue.generic.a = (int)managedValue;
adsvalue.generic.b = 0;
break;
case AdsType.ADSTYPE_LARGE_INTEGER:
LowInt64 = (long)managedValue;
break;
case AdsType.ADSTYPE_BOOLEAN:
LowInt64 = (bool)managedValue ? -1 : 0;
break;
case AdsType.ADSTYPE_CASE_IGNORE_STRING:
_pinnedHandle = GCHandle.Alloc(managedValue, GCHandleType.Pinned);
adsvalue.pointer.value = _pinnedHandle.AddrOfPinnedObject();
break;
case AdsType.ADSTYPE_PROV_SPECIFIC:
byte[] bytes = (byte[])managedValue;
// Filling in an ADS_PROV_SPECIFIC struct.
// 1st dword (our member a) is DWORD dwLength.
// 2nd dword (our member b) is byte *lpValue.
adsvalue.octetString.length = bytes.Length;
_pinnedHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
adsvalue.octetString.value = _pinnedHandle.AddrOfPinnedObject();
break;
default:
throw new NotImplementedException(SR.Format(SR.DSAdsvalueTypeNYI, "0x" + Convert.ToString((int)adsType, 16)));
}
}
}
}
|