263 lines
10 KiB
C#
263 lines
10 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="TdsParserSafeHandles.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
// <owner current="true" primary="true">[....]</owner>
|
||
|
// <owner current="true" primary="false">[....]</owner>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
namespace System.Data.SqlClient {
|
||
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Data.Common;
|
||
|
using System.Diagnostics;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Security;
|
||
|
using System.Security.Permissions;
|
||
|
using System.Threading;
|
||
|
using System.Runtime.ConstrainedExecution;
|
||
|
|
||
|
internal sealed class SNILoadHandle : SafeHandle {
|
||
|
internal static readonly SNILoadHandle SingletonInstance = new SNILoadHandle();
|
||
|
|
||
|
internal readonly SNINativeMethodWrapper.SqlAsyncCallbackDelegate ReadAsyncCallbackDispatcher = new SNINativeMethodWrapper.SqlAsyncCallbackDelegate(ReadDispatcher);
|
||
|
internal readonly SNINativeMethodWrapper.SqlAsyncCallbackDelegate WriteAsyncCallbackDispatcher = new SNINativeMethodWrapper.SqlAsyncCallbackDelegate(WriteDispatcher);
|
||
|
|
||
|
private readonly UInt32 _sniStatus = TdsEnums.SNI_UNINITIALIZED;
|
||
|
private readonly EncryptionOptions _encryptionOption;
|
||
|
|
||
|
private SNILoadHandle() : base(IntPtr.Zero, true) {
|
||
|
// SQL BU DT 346588 - from security review - SafeHandle guarantees this is only called once.
|
||
|
// The reason for the safehandle is guaranteed initialization and termination of SNI to
|
||
|
// ensure SNI terminates and cleans up properly.
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {} finally {
|
||
|
|
||
|
_sniStatus = SNINativeMethodWrapper.SNIInitialize();
|
||
|
|
||
|
UInt32 value = 0;
|
||
|
|
||
|
// VSDevDiv 479597: If initialize fails, don't call QueryInfo.
|
||
|
if (TdsEnums.SNI_SUCCESS == _sniStatus) {
|
||
|
// Query OS to find out whether encryption is supported.
|
||
|
SNINativeMethodWrapper.SNIQueryInfo(SNINativeMethodWrapper.QTypes.SNI_QUERY_CLIENT_ENCRYPT_POSSIBLE, ref value);
|
||
|
}
|
||
|
|
||
|
_encryptionOption = (value == 0) ? EncryptionOptions.NOT_SUP : EncryptionOptions.OFF;
|
||
|
|
||
|
base.handle = (IntPtr) 1; // Initialize to non-zero dummy variable.
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override bool IsInvalid {
|
||
|
get {
|
||
|
return (IntPtr.Zero == base.handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override protected bool ReleaseHandle() {
|
||
|
if (base.handle != IntPtr.Zero) {
|
||
|
if (TdsEnums.SNI_SUCCESS == _sniStatus) {
|
||
|
LocalDBAPI.ReleaseDLLHandles();
|
||
|
SNINativeMethodWrapper.SNITerminate();
|
||
|
}
|
||
|
base.handle = IntPtr.Zero;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public UInt32 SNIStatus {
|
||
|
get {
|
||
|
return _sniStatus;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public EncryptionOptions Options {
|
||
|
get {
|
||
|
return _encryptionOption;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static private void ReadDispatcher(IntPtr key, IntPtr packet, UInt32 error) {
|
||
|
// This is the app-domain dispatcher for all async read callbacks, It
|
||
|
// simply gets the state object from the key that it is passed, and
|
||
|
// calls the state object's read callback.
|
||
|
Debug.Assert(IntPtr.Zero != key, "no key passed to read callback dispatcher?");
|
||
|
if (IntPtr.Zero != key) {
|
||
|
// NOTE: we will get a null ref here if we don't get a key that
|
||
|
// contains a GCHandle to TDSParserStateObject; that is
|
||
|
// very bad, and we want that to occur so we can catch it.
|
||
|
GCHandle gcHandle = (GCHandle)key;
|
||
|
TdsParserStateObject stateObj = (TdsParserStateObject)gcHandle.Target;
|
||
|
|
||
|
if (null != stateObj) {
|
||
|
stateObj.ReadAsyncCallback(IntPtr.Zero, packet, error);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static private void WriteDispatcher(IntPtr key, IntPtr packet, UInt32 error) {
|
||
|
// This is the app-domain dispatcher for all async write callbacks, It
|
||
|
// simply gets the state object from the key that it is passed, and
|
||
|
// calls the state object's write callback.
|
||
|
Debug.Assert(IntPtr.Zero != key, "no key passed to write callback dispatcher?");
|
||
|
if (IntPtr.Zero != key) {
|
||
|
// NOTE: we will get a null ref here if we don't get a key that
|
||
|
// contains a GCHandle to TDSParserStateObject; that is
|
||
|
// very bad, and we want that to occur so we can catch it.
|
||
|
GCHandle gcHandle = (GCHandle)key;
|
||
|
TdsParserStateObject stateObj = (TdsParserStateObject)gcHandle.Target;
|
||
|
|
||
|
if (null != stateObj) {
|
||
|
stateObj.WriteAsyncCallback(IntPtr.Zero, packet, error);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class SNIHandle : SafeHandle {
|
||
|
private readonly UInt32 _status = TdsEnums.SNI_UNINITIALIZED;
|
||
|
private readonly bool _fSync = false;
|
||
|
|
||
|
// creates a physical connection
|
||
|
internal SNIHandle(
|
||
|
SNINativeMethodWrapper.ConsumerInfo myInfo,
|
||
|
string serverName,
|
||
|
byte[] spnBuffer,
|
||
|
bool ignoreSniOpenTimeout,
|
||
|
int timeout,
|
||
|
out byte[] instanceName,
|
||
|
bool flushCache,
|
||
|
bool fSync,
|
||
|
bool fParallel,
|
||
|
TransparentNetworkResolutionState transparentNetworkResolutionState,
|
||
|
int totalTimeout)
|
||
|
: base(IntPtr.Zero, true) {
|
||
|
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {} finally {
|
||
|
_fSync = fSync;
|
||
|
instanceName = new byte[256]; // Size as specified by netlibs.
|
||
|
if (ignoreSniOpenTimeout) {
|
||
|
//
|
||
|
|
||
|
|
||
|
timeout = Timeout.Infinite; // -1 == native SNIOPEN_TIMEOUT_VALUE / INFINITE
|
||
|
}
|
||
|
|
||
|
int transparentNetworkResolutionStateNo = (int)transparentNetworkResolutionState;
|
||
|
_status = SNINativeMethodWrapper.SNIOpenSyncEx(myInfo, serverName, ref base.handle,
|
||
|
spnBuffer, instanceName, flushCache, fSync, timeout, fParallel, transparentNetworkResolutionStateNo, totalTimeout);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// constructs SNI Handle for MARS session
|
||
|
internal SNIHandle(SNINativeMethodWrapper.ConsumerInfo myInfo, SNIHandle parent) : base(IntPtr.Zero, true) {
|
||
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||
|
try {} finally {
|
||
|
_status = SNINativeMethodWrapper.SNIOpenMarsSession(myInfo, parent, ref base.handle, parent._fSync);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override bool IsInvalid {
|
||
|
get {
|
||
|
return (IntPtr.Zero == base.handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override protected bool ReleaseHandle() {
|
||
|
// NOTE: The SafeHandle class guarantees this will be called exactly once.
|
||
|
IntPtr ptr = base.handle;
|
||
|
base.handle = IntPtr.Zero;
|
||
|
if (IntPtr.Zero != ptr) {
|
||
|
if (0 != SNINativeMethodWrapper.SNIClose(ptr)) {
|
||
|
return false; // SNIClose should never fail.
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
internal UInt32 Status {
|
||
|
get {
|
||
|
return _status;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class SNIPacket : SafeHandle {
|
||
|
|
||
|
internal SNIPacket(SafeHandle sniHandle) : base(IntPtr.Zero, true) {
|
||
|
SNINativeMethodWrapper.SNIPacketAllocate(sniHandle, SNINativeMethodWrapper.IOType.WRITE, ref base.handle);
|
||
|
if (IntPtr.Zero == base.handle) {
|
||
|
throw SQL.SNIPacketAllocationFailure();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override bool IsInvalid {
|
||
|
get {
|
||
|
return (IntPtr.Zero == base.handle);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
override protected bool ReleaseHandle() {
|
||
|
// NOTE: The SafeHandle class guarantees this will be called exactly once.
|
||
|
IntPtr ptr = base.handle;
|
||
|
base.handle = IntPtr.Zero;
|
||
|
if (IntPtr.Zero != ptr) {
|
||
|
SNINativeMethodWrapper.SNIPacketRelease(ptr);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal sealed class WritePacketCache : IDisposable {
|
||
|
private bool _disposed;
|
||
|
private Stack<SNIPacket> _packets;
|
||
|
|
||
|
public WritePacketCache() {
|
||
|
_disposed = false;
|
||
|
_packets = new Stack<SNIPacket>();
|
||
|
}
|
||
|
|
||
|
public SNIPacket Take(SNIHandle sniHandle) {
|
||
|
SNIPacket packet;
|
||
|
if (_packets.Count > 0) {
|
||
|
// Success - reset the packet
|
||
|
packet = _packets.Pop();
|
||
|
SNINativeMethodWrapper.SNIPacketReset(sniHandle, SNINativeMethodWrapper.IOType.WRITE, packet, SNINativeMethodWrapper.ConsumerNumber.SNI_Consumer_SNI);
|
||
|
}
|
||
|
else {
|
||
|
// Failed to take a packet - create a new one
|
||
|
packet = new SNIPacket(sniHandle);
|
||
|
}
|
||
|
return packet;
|
||
|
}
|
||
|
|
||
|
public void Add(SNIPacket packet) {
|
||
|
if (!_disposed) {
|
||
|
_packets.Push(packet);
|
||
|
}
|
||
|
else {
|
||
|
// If we're disposed, then get rid of any packets added to us
|
||
|
packet.Dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Clear() {
|
||
|
while (_packets.Count > 0) {
|
||
|
_packets.Pop().Dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Dispose() {
|
||
|
if (!_disposed) {
|
||
|
_disposed = true;
|
||
|
Clear();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|