// Copyright (c) Microsoft Open Technologies, Inc. // All Rights Reserved // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING // WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF // TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR // NON-INFRINGEMENT. // See the Apache 2 License for the specific language governing // permissions and limitations under the License. //------------------------------------------------------------------------------ // // Copyright (c) Microsoft Corporation. All rights reserved. // //------------------------------------------------------------------------------ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; namespace Microsoft.Net.Server { internal static class UnsafeNclNativeMethods { private const string KERNEL32 = "kernel32.dll"; private const string SECUR32 = "secur32.dll"; private const string HTTPAPI = "httpapi.dll"; // CONSIDER: Make this an enum, requires changing a lot of types from uint to ErrorCodes. internal static class ErrorCodes { internal const uint ERROR_SUCCESS = 0; internal const uint ERROR_HANDLE_EOF = 38; internal const uint ERROR_NOT_SUPPORTED = 50; internal const uint ERROR_INVALID_PARAMETER = 87; internal const uint ERROR_ALREADY_EXISTS = 183; internal const uint ERROR_MORE_DATA = 234; internal const uint ERROR_OPERATION_ABORTED = 995; internal const uint ERROR_IO_PENDING = 997; internal const uint ERROR_NOT_FOUND = 1168; internal const uint ERROR_CONNECTION_INVALID = 1229; } [DllImport(KERNEL32, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint GetCurrentThreadId(); [DllImport(KERNEL32, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static unsafe extern uint CancelIoEx(SafeHandle handle, SafeNativeOverlapped overlapped); [DllImport(KERNEL32, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static unsafe extern bool SetFileCompletionNotificationModes(SafeHandle handle, FileCompletionNotificationModes modes); [Flags] internal enum FileCompletionNotificationModes : byte { None = 0, SkipCompletionPortOnSuccess = 1, SkipSetEventOnHandle = 2 } internal static class SafeNetHandles { [DllImport(SECUR32, ExactSpelling = true, SetLastError = true)] internal static extern int FreeContextBuffer( [In] IntPtr contextBuffer); [DllImport(SECUR32, ExactSpelling = true, SetLastError = true)] internal static unsafe extern int QueryContextAttributesW( ref SSPIHandle contextHandle, [In] ContextAttribute attribute, [In] void* buffer); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe uint HttpCreateRequestQueue(HttpApi.HTTPAPI_VERSION version, string pName, Microsoft.Net.Server.UnsafeNclNativeMethods.SECURITY_ATTRIBUTES pSecurityAttributes, uint flags, out HttpRequestQueueV2Handle pReqQueueHandle); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern unsafe uint HttpCloseRequestQueue(IntPtr pReqQueueHandle); [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] internal static extern bool CloseHandle(IntPtr handle); [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] internal static extern SafeLocalFree LocalAlloc(int uFlags, UIntPtr sizetdwBytes); [DllImport(KERNEL32, EntryPoint = "LocalAlloc", SetLastError = true)] internal static extern SafeLocalFreeChannelBinding LocalAllocChannelBinding(int uFlags, UIntPtr sizetdwBytes); [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] internal static extern IntPtr LocalFree(IntPtr handle); [DllImport(KERNEL32, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern unsafe SafeLoadLibrary LoadLibraryExW([In] string lpwLibFileName, [In] void* hFile, [In] uint dwFlags); [DllImport(KERNEL32, ExactSpelling = true, SetLastError = true)] internal static extern unsafe bool FreeLibrary([In] IntPtr hModule); } internal static unsafe class HttpApi { [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpInitialize(HTTPAPI_VERSION version, uint flags, void* pReserved); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpReceiveRequestEntityBody(SafeHandle requestQueueHandle, ulong requestId, uint flags, IntPtr pEntityBuffer, uint entityBufferLength, out uint bytesReturned, SafeNativeOverlapped pOverlapped); [DllImport(HTTPAPI, EntryPoint = "HttpReceiveRequestEntityBody", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpReceiveRequestEntityBody2(SafeHandle requestQueueHandle, ulong requestId, uint flags, void* pEntityBuffer, uint entityBufferLength, out uint bytesReturned, [In] SafeHandle pOverlapped); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpReceiveClientCertificate(SafeHandle requestQueueHandle, ulong connectionId, uint flags, HTTP_SSL_CLIENT_CERT_INFO* pSslClientCertInfo, uint sslClientCertInfoSize, uint* pBytesReceived, SafeNativeOverlapped pOverlapped); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpReceiveClientCertificate(SafeHandle requestQueueHandle, ulong connectionId, uint flags, byte* pSslClientCertInfo, uint sslClientCertInfoSize, uint* pBytesReceived, SafeNativeOverlapped pOverlapped); [SuppressMessage("Microsoft.Interoperability", "CA1415:DeclarePInvokesCorrectly", Justification = "NativeOverlapped is now wrapped by SafeNativeOverlapped")] [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpReceiveHttpRequest(SafeHandle requestQueueHandle, ulong requestId, uint flags, HTTP_REQUEST* pRequestBuffer, uint requestBufferLength, uint* pBytesReturned, SafeNativeOverlapped pOverlapped); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpSendHttpResponse(SafeHandle requestQueueHandle, ulong requestId, uint flags, HTTP_RESPONSE_V2* pHttpResponse, void* pCachePolicy, uint* pBytesSent, SafeLocalFree pRequestBuffer, uint requestBufferLength, SafeNativeOverlapped pOverlapped, IntPtr pLogData); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpSendResponseEntityBody(SafeHandle requestQueueHandle, ulong requestId, uint flags, ushort entityChunkCount, HTTP_DATA_CHUNK* pEntityChunks, uint* pBytesSent, SafeLocalFree pRequestBuffer, uint requestBufferLength, SafeNativeOverlapped pOverlapped, IntPtr pLogData); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpCancelHttpRequest(SafeHandle requestQueueHandle, ulong requestId, IntPtr pOverlapped); [DllImport(HTTPAPI, EntryPoint = "HttpSendResponseEntityBody", ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpSendResponseEntityBody2(SafeHandle requestQueueHandle, ulong requestId, uint flags, ushort entityChunkCount, IntPtr pEntityChunks, out uint pBytesSent, SafeLocalFree pRequestBuffer, uint requestBufferLength, SafeHandle pOverlapped, IntPtr pLogData); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpWaitForDisconnect(SafeHandle requestQueueHandle, ulong connectionId, SafeNativeOverlapped pOverlapped); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpCreateServerSession(HTTPAPI_VERSION version, ulong* serverSessionId, uint reserved); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpCreateUrlGroup(ulong serverSessionId, ulong* urlGroupId, uint reserved); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern uint HttpAddUrlToUrlGroup(ulong urlGroupId, string pFullyQualifiedUrl, ulong context, uint pReserved); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpSetUrlGroupProperty(ulong urlGroupId, HTTP_SERVER_PROPERTY serverProperty, IntPtr pPropertyInfo, uint propertyInfoLength); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern uint HttpRemoveUrlFromUrlGroup(ulong urlGroupId, string pFullyQualifiedUrl, uint flags); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpCloseServerSession(ulong serverSessionId); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpCloseUrlGroup(ulong urlGroupId); [DllImport(HTTPAPI, ExactSpelling = true, CallingConvention = CallingConvention.StdCall, SetLastError = true)] internal static extern uint HttpSetRequestQueueProperty(SafeHandle requestQueueHandle, HTTP_SERVER_PROPERTY serverProperty, IntPtr pPropertyInfo, uint propertyInfoLength, uint reserved, IntPtr pReserved); internal enum HTTP_API_VERSION { Invalid, Version10, Version20, } // see http.w for definitions internal enum HTTP_SERVER_PROPERTY { HttpServerAuthenticationProperty, HttpServerLoggingProperty, HttpServerQosProperty, HttpServerTimeoutsProperty, HttpServerQueueLengthProperty, HttpServerStateProperty, HttpServer503VerbosityProperty, HttpServerBindingProperty, HttpServerExtendedAuthenticationProperty, HttpServerListenEndpointProperty, HttpServerChannelBindProperty, HttpServerProtectionLevelProperty, } // Currently only one request info type is supported but the enum is for future extensibility. internal enum HTTP_REQUEST_INFO_TYPE { HttpRequestInfoTypeAuth, } internal enum HTTP_RESPONSE_INFO_TYPE { HttpResponseInfoTypeMultipleKnownHeaders, HttpResponseInfoTypeAuthenticationProperty, HttpResponseInfoTypeQosProperty, } internal enum HTTP_TIMEOUT_TYPE { EntityBody, DrainEntityBody, RequestQueue, IdleConnection, HeaderWait, MinSendRate, } internal const int MaxTimeout = 6; [StructLayout(LayoutKind.Sequential)] internal struct HTTP_VERSION { internal ushort MajorVersion; internal ushort MinorVersion; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_KNOWN_HEADER { internal ushort RawValueLength; internal sbyte* pRawValue; } [StructLayout(LayoutKind.Explicit)] internal struct HTTP_DATA_CHUNK { [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields", Justification = "Used natively")] [FieldOffset(0)] internal HTTP_DATA_CHUNK_TYPE DataChunkType; [FieldOffset(8)] internal FromMemory fromMemory; [FieldOffset(8)] internal FromFileHandle fromFile; } [SuppressMessage("Microsoft.Design", "CA1049:TypesThatOwnNativeResourcesShouldBeDisposable", Justification = "This type does not own the native buffer")] [StructLayout(LayoutKind.Sequential)] internal struct FromMemory { // 4 bytes for 32bit, 8 bytes for 64bit internal IntPtr pBuffer; internal uint BufferLength; } [StructLayout(LayoutKind.Sequential)] internal struct FromFileHandle { internal ulong offset; internal ulong count; internal IntPtr fileHandle; } [StructLayout(LayoutKind.Sequential)] internal struct HTTPAPI_VERSION { internal ushort HttpApiMajorVersion; internal ushort HttpApiMinorVersion; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_COOKED_URL { internal ushort FullUrlLength; internal ushort HostLength; internal ushort AbsPathLength; internal ushort QueryStringLength; internal ushort* pFullUrl; internal ushort* pHost; internal ushort* pAbsPath; internal ushort* pQueryString; } [StructLayout(LayoutKind.Sequential)] internal struct SOCKADDR { internal ushort sa_family; internal byte sa_data; internal byte sa_data_02; internal byte sa_data_03; internal byte sa_data_04; internal byte sa_data_05; internal byte sa_data_06; internal byte sa_data_07; internal byte sa_data_08; internal byte sa_data_09; internal byte sa_data_10; internal byte sa_data_11; internal byte sa_data_12; internal byte sa_data_13; internal byte sa_data_14; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_TRANSPORT_ADDRESS { internal SOCKADDR* pRemoteAddress; internal SOCKADDR* pLocalAddress; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_SSL_CLIENT_CERT_INFO { internal uint CertFlags; internal uint CertEncodedSize; internal byte* pCertEncoded; internal void* Token; internal byte CertDeniedByMapper; } internal enum HTTP_SERVICE_BINDING_TYPE : uint { HttpServiceBindingTypeNone = 0, HttpServiceBindingTypeW, HttpServiceBindingTypeA } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_SERVICE_BINDING_BASE { internal HTTP_SERVICE_BINDING_TYPE Type; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_REQUEST_CHANNEL_BIND_STATUS { internal IntPtr ServiceName; internal IntPtr ChannelToken; internal uint ChannelTokenSize; internal uint Flags; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_UNKNOWN_HEADER { internal ushort NameLength; internal ushort RawValueLength; internal sbyte* pName; internal sbyte* pRawValue; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_SSL_INFO { internal ushort ServerCertKeySize; internal ushort ConnectionKeySize; internal uint ServerCertIssuerSize; internal uint ServerCertSubjectSize; internal sbyte* pServerCertIssuer; internal sbyte* pServerCertSubject; internal HTTP_SSL_CLIENT_CERT_INFO* pClientCertInfo; internal uint SslClientCertNegotiated; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_RESPONSE_HEADERS { internal ushort UnknownHeaderCount; internal HTTP_UNKNOWN_HEADER* pUnknownHeaders; internal ushort TrailerCount; internal HTTP_UNKNOWN_HEADER* pTrailers; internal HTTP_KNOWN_HEADER KnownHeaders; internal HTTP_KNOWN_HEADER KnownHeaders_02; internal HTTP_KNOWN_HEADER KnownHeaders_03; internal HTTP_KNOWN_HEADER KnownHeaders_04; internal HTTP_KNOWN_HEADER KnownHeaders_05; internal HTTP_KNOWN_HEADER KnownHeaders_06; internal HTTP_KNOWN_HEADER KnownHeaders_07; internal HTTP_KNOWN_HEADER KnownHeaders_08; internal HTTP_KNOWN_HEADER KnownHeaders_09; internal HTTP_KNOWN_HEADER KnownHeaders_10; internal HTTP_KNOWN_HEADER KnownHeaders_11; internal HTTP_KNOWN_HEADER KnownHeaders_12; internal HTTP_KNOWN_HEADER KnownHeaders_13; internal HTTP_KNOWN_HEADER KnownHeaders_14; internal HTTP_KNOWN_HEADER KnownHeaders_15; internal HTTP_KNOWN_HEADER KnownHeaders_16; internal HTTP_KNOWN_HEADER KnownHeaders_17; internal HTTP_KNOWN_HEADER KnownHeaders_18; internal HTTP_KNOWN_HEADER KnownHeaders_19; internal HTTP_KNOWN_HEADER KnownHeaders_20; internal HTTP_KNOWN_HEADER KnownHeaders_21; internal HTTP_KNOWN_HEADER KnownHeaders_22; internal HTTP_KNOWN_HEADER KnownHeaders_23; internal HTTP_KNOWN_HEADER KnownHeaders_24; internal HTTP_KNOWN_HEADER KnownHeaders_25; internal HTTP_KNOWN_HEADER KnownHeaders_26; internal HTTP_KNOWN_HEADER KnownHeaders_27; internal HTTP_KNOWN_HEADER KnownHeaders_28; internal HTTP_KNOWN_HEADER KnownHeaders_29; internal HTTP_KNOWN_HEADER KnownHeaders_30; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_REQUEST_HEADERS { internal ushort UnknownHeaderCount; internal HTTP_UNKNOWN_HEADER* pUnknownHeaders; internal ushort TrailerCount; internal HTTP_UNKNOWN_HEADER* pTrailers; internal HTTP_KNOWN_HEADER KnownHeaders; internal HTTP_KNOWN_HEADER KnownHeaders_02; internal HTTP_KNOWN_HEADER KnownHeaders_03; internal HTTP_KNOWN_HEADER KnownHeaders_04; internal HTTP_KNOWN_HEADER KnownHeaders_05; internal HTTP_KNOWN_HEADER KnownHeaders_06; internal HTTP_KNOWN_HEADER KnownHeaders_07; internal HTTP_KNOWN_HEADER KnownHeaders_08; internal HTTP_KNOWN_HEADER KnownHeaders_09; internal HTTP_KNOWN_HEADER KnownHeaders_10; internal HTTP_KNOWN_HEADER KnownHeaders_11; internal HTTP_KNOWN_HEADER KnownHeaders_12; internal HTTP_KNOWN_HEADER KnownHeaders_13; internal HTTP_KNOWN_HEADER KnownHeaders_14; internal HTTP_KNOWN_HEADER KnownHeaders_15; internal HTTP_KNOWN_HEADER KnownHeaders_16; internal HTTP_KNOWN_HEADER KnownHeaders_17; internal HTTP_KNOWN_HEADER KnownHeaders_18; internal HTTP_KNOWN_HEADER KnownHeaders_19; internal HTTP_KNOWN_HEADER KnownHeaders_20; internal HTTP_KNOWN_HEADER KnownHeaders_21; internal HTTP_KNOWN_HEADER KnownHeaders_22; internal HTTP_KNOWN_HEADER KnownHeaders_23; internal HTTP_KNOWN_HEADER KnownHeaders_24; internal HTTP_KNOWN_HEADER KnownHeaders_25; internal HTTP_KNOWN_HEADER KnownHeaders_26; internal HTTP_KNOWN_HEADER KnownHeaders_27; internal HTTP_KNOWN_HEADER KnownHeaders_28; internal HTTP_KNOWN_HEADER KnownHeaders_29; internal HTTP_KNOWN_HEADER KnownHeaders_30; internal HTTP_KNOWN_HEADER KnownHeaders_31; internal HTTP_KNOWN_HEADER KnownHeaders_32; internal HTTP_KNOWN_HEADER KnownHeaders_33; internal HTTP_KNOWN_HEADER KnownHeaders_34; internal HTTP_KNOWN_HEADER KnownHeaders_35; internal HTTP_KNOWN_HEADER KnownHeaders_36; internal HTTP_KNOWN_HEADER KnownHeaders_37; internal HTTP_KNOWN_HEADER KnownHeaders_38; internal HTTP_KNOWN_HEADER KnownHeaders_39; internal HTTP_KNOWN_HEADER KnownHeaders_40; internal HTTP_KNOWN_HEADER KnownHeaders_41; } internal enum HTTP_VERB : int { HttpVerbUnparsed = 0, HttpVerbUnknown = 1, HttpVerbInvalid = 2, HttpVerbOPTIONS = 3, HttpVerbGET = 4, HttpVerbHEAD = 5, HttpVerbPOST = 6, HttpVerbPUT = 7, HttpVerbDELETE = 8, HttpVerbTRACE = 9, HttpVerbCONNECT = 10, HttpVerbTRACK = 11, HttpVerbMOVE = 12, HttpVerbCOPY = 13, HttpVerbPROPFIND = 14, HttpVerbPROPPATCH = 15, HttpVerbMKCOL = 16, HttpVerbLOCK = 17, HttpVerbUNLOCK = 18, HttpVerbSEARCH = 19, HttpVerbMaximum = 20, } internal static readonly string[] HttpVerbs = new string[] { null, "Unknown", "Invalid", "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE", "TRACE", "CONNECT", "TRACK", "MOVE", "COPY", "PROPFIND", "PROPPATCH", "MKCOL", "LOCK", "UNLOCK", "SEARCH", }; internal enum HTTP_DATA_CHUNK_TYPE : int { HttpDataChunkFromMemory = 0, HttpDataChunkFromFileHandle = 1, HttpDataChunkFromFragmentCache = 2, HttpDataChunkMaximum = 3, } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_RESPONSE_INFO { internal HTTP_RESPONSE_INFO_TYPE Type; internal uint Length; internal HTTP_MULTIPLE_KNOWN_HEADERS* pInfo; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_RESPONSE { internal uint Flags; internal HTTP_VERSION Version; internal ushort StatusCode; internal ushort ReasonLength; internal sbyte* pReason; internal HTTP_RESPONSE_HEADERS Headers; internal ushort EntityChunkCount; internal HTTP_DATA_CHUNK* pEntityChunks; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_RESPONSE_V2 { internal HTTP_RESPONSE Response_V1; internal ushort ResponseInfoCount; internal HTTP_RESPONSE_INFO* pResponseInfo; } internal enum HTTP_RESPONSE_INFO_FLAGS : uint { None = 0, PreserveOrder = 1, } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_MULTIPLE_KNOWN_HEADERS { internal HTTP_RESPONSE_HEADER_ID.Enum HeaderId; internal HTTP_RESPONSE_INFO_FLAGS Flags; internal ushort KnownHeaderCount; internal HTTP_KNOWN_HEADER* KnownHeaders; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_REQUEST_AUTH_INFO { internal HTTP_AUTH_STATUS AuthStatus; internal uint SecStatus; internal uint Flags; internal HTTP_REQUEST_AUTH_TYPE AuthType; internal IntPtr AccessToken; internal uint ContextAttributes; internal uint PackedContextLength; internal uint PackedContextType; internal IntPtr PackedContext; internal uint MutualAuthDataLength; internal char* pMutualAuthData; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_REQUEST_INFO { internal HTTP_REQUEST_INFO_TYPE InfoType; internal uint InfoLength; internal HTTP_REQUEST_AUTH_INFO* pInfo; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_REQUEST { internal uint Flags; internal ulong ConnectionId; internal ulong RequestId; internal ulong UrlContext; internal HTTP_VERSION Version; internal HTTP_VERB Verb; internal ushort UnknownVerbLength; internal ushort RawUrlLength; internal sbyte* pUnknownVerb; internal sbyte* pRawUrl; internal HTTP_COOKED_URL CookedUrl; internal HTTP_TRANSPORT_ADDRESS Address; internal HTTP_REQUEST_HEADERS Headers; internal ulong BytesReceived; internal ushort EntityChunkCount; internal HTTP_DATA_CHUNK* pEntityChunks; internal ulong RawConnectionId; internal HTTP_SSL_INFO* pSslInfo; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_REQUEST_V2 { internal HTTP_REQUEST Request; internal ushort RequestInfoCount; internal HTTP_REQUEST_INFO* pRequestInfo; } internal enum HTTP_AUTH_STATUS { HttpAuthStatusSuccess, HttpAuthStatusNotAuthenticated, HttpAuthStatusFailure, } internal enum HTTP_REQUEST_AUTH_TYPE { HttpRequestAuthTypeNone = 0, HttpRequestAuthTypeBasic, HttpRequestAuthTypeDigest, HttpRequestAuthTypeNTLM, HttpRequestAuthTypeNegotiate, HttpRequestAuthTypeKerberos } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_SERVER_AUTHENTICATION_INFO { internal HTTP_FLAGS Flags; internal HTTP_AUTH_TYPES AuthSchemes; internal bool ReceiveMutualAuth; internal bool ReceiveContextHandle; internal bool DisableNTLMCredentialCaching; internal ulong ExFlags; HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS DigestParams; HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS BasicParams; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_SERVER_AUTHENTICATION_DIGEST_PARAMS { internal ushort DomainNameLength; internal char* DomainName; internal ushort RealmLength; internal char* Realm; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_SERVER_AUTHENTICATION_BASIC_PARAMS { ushort RealmLength; char* Realm; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_TIMEOUT_LIMIT_INFO { internal HTTP_FLAGS Flags; internal ushort EntityBody; internal ushort DrainEntityBody; internal ushort RequestQueue; internal ushort IdleConnection; internal ushort HeaderWait; internal uint MinSendRate; } [StructLayout(LayoutKind.Sequential)] internal struct HTTP_BINDING_INFO { internal HTTP_FLAGS Flags; internal IntPtr RequestQueueHandle; } // see http.w for definitions [Flags] internal enum HTTP_FLAGS : uint { NONE = 0x00000000, HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY = 0x00000001, HTTP_RECEIVE_SECURE_CHANNEL_TOKEN = 0x00000001, HTTP_SEND_RESPONSE_FLAG_DISCONNECT = 0x00000001, HTTP_SEND_RESPONSE_FLAG_MORE_DATA = 0x00000002, HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA = 0x00000004, HTTP_SEND_RESPONSE_FLAG_RAW_HEADER = 0x00000004, HTTP_SEND_REQUEST_FLAG_MORE_DATA = 0x00000001, HTTP_PROPERTY_FLAG_PRESENT = 0x00000001, HTTP_INITIALIZE_SERVER = 0x00000001, HTTP_INITIALIZE_CBT = 0x00000004, HTTP_SEND_RESPONSE_FLAG_OPAQUE = 0x00000040, } [Flags] internal enum HTTP_AUTH_TYPES : uint { NONE = 0x00000000, HTTP_AUTH_ENABLE_BASIC = 0x00000001, HTTP_AUTH_ENABLE_DIGEST = 0x00000002, HTTP_AUTH_ENABLE_NTLM = 0x00000004, HTTP_AUTH_ENABLE_NEGOTIATE = 0x00000008, HTTP_AUTH_ENABLE_KERBEROS = 0x00000010, } private const int HttpHeaderRequestMaximum = (int)HttpSysRequestHeader.UserAgent + 1; private const int HttpHeaderResponseMaximum = (int)HttpSysResponseHeader.WwwAuthenticate + 1; internal static class HTTP_REQUEST_HEADER_ID { internal static string ToString(int position) { return _strings[position]; } private static string[] _strings = { "Cache-Control", "Connection", "Date", "Keep-Alive", "Pragma", "Trailer", "Transfer-Encoding", "Upgrade", "Via", "Warning", "Allow", "Content-Length", "Content-Type", "Content-Encoding", "Content-Language", "Content-Location", "Content-MD5", "Content-Range", "Expires", "Last-Modified", "Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language", "Authorization", "Cookie", "Expect", "From", "Host", "If-Match", "If-Modified-Since", "If-None-Match", "If-Range", "If-Unmodified-Since", "Max-Forwards", "Proxy-Authorization", "Referer", "Range", "Te", "Translate", "User-Agent", }; } internal static class HTTP_RESPONSE_HEADER_ID { private static string[] _strings = { "Cache-Control", "Connection", "Date", "Keep-Alive", "Pragma", "Trailer", "Transfer-Encoding", "Upgrade", "Via", "Warning", "Allow", "Content-Length", "Content-Type", "Content-Encoding", "Content-Language", "Content-Location", "Content-MD5", "Content-Range", "Expires", "Last-Modified", "Accept-Ranges", "Age", "ETag", "Location", "Proxy-Authenticate", "Retry-After", "Server", "Set-Cookie", "Vary", "WWW-Authenticate", }; private static Dictionary _lookupTable = CreateLookupTable(); private static Dictionary CreateLookupTable() { Dictionary lookupTable = new Dictionary((int)Enum.HttpHeaderResponseMaximum, StringComparer.OrdinalIgnoreCase); for (int i = 0; i < (int)Enum.HttpHeaderResponseMaximum; i++) { lookupTable.Add(_strings[i], i); } return lookupTable; } internal static int IndexOfKnownHeader(string HeaderName) { int index; return _lookupTable.TryGetValue(HeaderName, out index) ? index : -1; } internal static string ToString(int position) { return _strings[position]; } internal enum Enum { HttpHeaderCacheControl = 0, // general-header [section 4.5] HttpHeaderConnection = 1, // general-header [section 4.5] HttpHeaderDate = 2, // general-header [section 4.5] HttpHeaderKeepAlive = 3, // general-header [not in rfc] HttpHeaderPragma = 4, // general-header [section 4.5] HttpHeaderTrailer = 5, // general-header [section 4.5] HttpHeaderTransferEncoding = 6, // general-header [section 4.5] HttpHeaderUpgrade = 7, // general-header [section 4.5] HttpHeaderVia = 8, // general-header [section 4.5] HttpHeaderWarning = 9, // general-header [section 4.5] HttpHeaderAllow = 10, // entity-header [section 7.1] HttpHeaderContentLength = 11, // entity-header [section 7.1] HttpHeaderContentType = 12, // entity-header [section 7.1] HttpHeaderContentEncoding = 13, // entity-header [section 7.1] HttpHeaderContentLanguage = 14, // entity-header [section 7.1] HttpHeaderContentLocation = 15, // entity-header [section 7.1] HttpHeaderContentMd5 = 16, // entity-header [section 7.1] HttpHeaderContentRange = 17, // entity-header [section 7.1] HttpHeaderExpires = 18, // entity-header [section 7.1] HttpHeaderLastModified = 19, // entity-header [section 7.1] // Response Headers HttpHeaderAcceptRanges = 20, // response-header [section 6.2] HttpHeaderAge = 21, // response-header [section 6.2] HttpHeaderEtag = 22, // response-header [section 6.2] HttpHeaderLocation = 23, // response-header [section 6.2] HttpHeaderProxyAuthenticate = 24, // response-header [section 6.2] HttpHeaderRetryAfter = 25, // response-header [section 6.2] HttpHeaderServer = 26, // response-header [section 6.2] HttpHeaderSetCookie = 27, // response-header [not in rfc] HttpHeaderVary = 28, // response-header [section 6.2] HttpHeaderWwwAuthenticate = 29, // response-header [section 6.2] HttpHeaderResponseMaximum = 30, HttpHeaderMaximum = 41 } } private static HTTPAPI_VERSION version; // This property is used by HttpListener to pass the version structure to the native layer in API // calls. internal static HTTPAPI_VERSION Version { get { return version; } } // This property is used by HttpListener to get the Api version in use so that it uses appropriate // Http APIs. internal static HTTP_API_VERSION ApiVersion { get { if (version.HttpApiMajorVersion == 2 && version.HttpApiMinorVersion == 0) { return HTTP_API_VERSION.Version20; } else if (version.HttpApiMajorVersion == 1 && version.HttpApiMinorVersion == 0) { return HTTP_API_VERSION.Version10; } else { return HTTP_API_VERSION.Invalid; } } } static HttpApi() { InitHttpApi(2, 0); } private static void InitHttpApi(ushort majorVersion, ushort minorVersion) { version.HttpApiMajorVersion = majorVersion; version.HttpApiMinorVersion = minorVersion; // For pre-Win7 OS versions, we need to check whether http.sys contains the CBT patch. // We do so by passing HTTP_INITIALIZE_CBT flag to HttpInitialize. If the flag is not // supported, http.sys is not patched. Note that http.sys will return invalid parameter // also on Win7, even though it shipped with CBT support. Therefore we must not pass // the flag on Win7 and later. uint statusCode = ErrorCodes.ERROR_SUCCESS; // on Win7 and later, we don't pass the CBT flag. CBT is always supported. statusCode = HttpApi.HttpInitialize(version, (uint)HTTP_FLAGS.HTTP_INITIALIZE_SERVER, null); supported = statusCode == ErrorCodes.ERROR_SUCCESS; } private static volatile bool supported; internal static bool Supported { get { return supported; } } // Server API internal static void GetUnknownHeaders(IDictionary unknownHeaders, byte[] memoryBlob, IntPtr originalAddress) { // Return value. fixed (byte* pMemoryBlob = memoryBlob) { HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; long fixup = pMemoryBlob - (byte*)originalAddress; int index; // unknown headers if (request->Headers.UnknownHeaderCount != 0) { HTTP_UNKNOWN_HEADER* pUnknownHeader = (HTTP_UNKNOWN_HEADER*)(fixup + (byte*)request->Headers.pUnknownHeaders); for (index = 0; index < request->Headers.UnknownHeaderCount; index++) { // For unknown headers, when header value is empty, RawValueLength will be 0 and // pRawValue will be null. if (pUnknownHeader->pName != null && pUnknownHeader->NameLength > 0) { string headerName = HeaderEncoding.GetString(pUnknownHeader->pName + fixup, pUnknownHeader->NameLength); string headerValue; if (pUnknownHeader->pRawValue != null && pUnknownHeader->RawValueLength > 0) { headerValue = HeaderEncoding.GetString(pUnknownHeader->pRawValue + fixup, pUnknownHeader->RawValueLength); } else { headerValue = string.Empty; } // Note that Http.Sys currently collapses all headers of the same name to a single coma seperated string, // so we can just call Set. unknownHeaders[headerName] = new[] { headerValue }; } pUnknownHeader++; } } } } private static string GetKnownHeader(HTTP_REQUEST* request, long fixup, int headerIndex) { string header = null; HTTP_KNOWN_HEADER* pKnownHeader = (&request->Headers.KnownHeaders) + headerIndex; // For known headers, when header value is empty, RawValueLength will be 0 and // pRawValue will point to empty string ("\0") if (pKnownHeader->pRawValue != null) { header = HeaderEncoding.GetString(pKnownHeader->pRawValue + fixup, pKnownHeader->RawValueLength); } return header; } internal static string GetKnownHeader(byte[] memoryBlob, IntPtr originalAddress, int headerIndex) { fixed (byte* pMemoryBlob = memoryBlob) { return GetKnownHeader((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress, headerIndex); } } private static unsafe string GetVerb(HTTP_REQUEST* request, long fixup) { string verb = null; if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnknown && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum) { verb = HttpVerbs[(int)request->Verb]; } else if (request->Verb == HTTP_VERB.HttpVerbUnknown && request->pUnknownVerb != null) { verb = HeaderEncoding.GetString(request->pUnknownVerb + fixup, request->UnknownVerbLength); } return verb; } internal static unsafe string GetVerb(byte[] memoryBlob, IntPtr originalAddress) { fixed (byte* pMemoryBlob = memoryBlob) { return GetVerb((HTTP_REQUEST*)pMemoryBlob, pMemoryBlob - (byte*)originalAddress); } } internal static HTTP_VERB GetKnownVerb(byte[] memoryBlob, IntPtr originalAddress) { // Return value. HTTP_VERB verb = HTTP_VERB.HttpVerbUnknown; fixed (byte* pMemoryBlob = memoryBlob) { HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; if ((int)request->Verb > (int)HTTP_VERB.HttpVerbUnparsed && (int)request->Verb < (int)HTTP_VERB.HttpVerbMaximum) { verb = request->Verb; } } return verb; } internal static uint GetChunks(byte[] memoryBlob, IntPtr originalAddress, ref int dataChunkIndex, ref uint dataChunkOffset, byte[] buffer, int offset, int size) { // Return value. uint dataRead = 0; fixed (byte* pMemoryBlob = memoryBlob) { HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; long fixup = pMemoryBlob - (byte*)originalAddress; if (request->EntityChunkCount > 0 && dataChunkIndex < request->EntityChunkCount && dataChunkIndex != -1) { HTTP_DATA_CHUNK* pDataChunk = (HTTP_DATA_CHUNK*)(fixup + (byte*)&request->pEntityChunks[dataChunkIndex]); fixed (byte* pReadBuffer = buffer) { byte* pTo = &pReadBuffer[offset]; while (dataChunkIndex < request->EntityChunkCount && dataRead < size) { if (dataChunkOffset >= pDataChunk->fromMemory.BufferLength) { dataChunkOffset = 0; dataChunkIndex++; pDataChunk++; } else { byte* pFrom = (byte*)pDataChunk->fromMemory.pBuffer + dataChunkOffset + fixup; uint bytesToRead = pDataChunk->fromMemory.BufferLength - (uint)dataChunkOffset; if (bytesToRead > (uint)size) { bytesToRead = (uint)size; } for (uint i = 0; i < bytesToRead; i++) { *(pTo++) = *(pFrom++); } dataRead += bytesToRead; dataChunkOffset += bytesToRead; } } } } // we're finished. if (dataChunkIndex == request->EntityChunkCount) { dataChunkIndex = -1; } } return dataRead; } internal static SocketAddress GetRemoteEndPoint(byte[] memoryBlob, IntPtr originalAddress) { fixed (byte* pMemoryBlob = memoryBlob) { HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; return GetEndPoint(memoryBlob, originalAddress, (byte*)request->Address.pRemoteAddress); } } internal static SocketAddress GetLocalEndPoint(byte[] memoryBlob, IntPtr originalAddress) { fixed (byte* pMemoryBlob = memoryBlob) { HTTP_REQUEST* request = (HTTP_REQUEST*)pMemoryBlob; return GetEndPoint(memoryBlob, originalAddress, (byte*)request->Address.pLocalAddress); } } internal static SocketAddress GetEndPoint(byte[] memoryBlob, IntPtr originalAddress, byte* source) { fixed (byte* pMemoryBlob = memoryBlob) { IntPtr address = source != null ? (IntPtr)(pMemoryBlob - (byte*)originalAddress + source) : IntPtr.Zero; return CopyOutAddress(address); } } private static SocketAddress CopyOutAddress(IntPtr address) { if (address != IntPtr.Zero) { ushort addressFamily = *((ushort*)address); if (addressFamily == (ushort)AddressFamily.InterNetwork) { SocketAddress v4address = new SocketAddress(AddressFamily.InterNetwork, SocketAddress.IPv4AddressSize); fixed (byte* pBuffer = v4address.Buffer) { for (int index = 2; index < SocketAddress.IPv4AddressSize; index++) { pBuffer[index] = ((byte*)address)[index]; } } return v4address; } if (addressFamily == (ushort)AddressFamily.InterNetworkV6) { SocketAddress v6address = new SocketAddress(AddressFamily.InterNetworkV6, SocketAddress.IPv6AddressSize); fixed (byte* pBuffer = v6address.Buffer) { for (int index = 2; index < SocketAddress.IPv6AddressSize; index++) { pBuffer[index] = ((byte*)address)[index]; } } return v6address; } } return null; } } // DACL related stuff [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Instantiated natively")] [SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable", Justification = "Does not own the resource.")] [StructLayout(LayoutKind.Sequential)] internal class SECURITY_ATTRIBUTES { public int nLength = 12; public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false); public bool bInheritHandle = false; } } }