HTTPS TLS with AFD.sys, WinSocks not necessary

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>

typedef LONG NTSTATUS;

#define AF_INET 2
#define AUTHTYPE_SERVER 1

#define DEFAULT_BUFFER_ALLOCATION_SIZE 16384
#define DEFAULT_HTTPHEADER_GET_SIZE 68
#define DEFAULT_HTTPHEADER_POST_OCTETSTREAM_SIZE 104

#define InlineGetMinimum(a,b) (((a) < (b)) ? (a) : (b))

#define HTTP_CR '\r'
#define HTTP_LF '\n'
#define HTTP_SP ' '
#define HTTP_TB '\t'
#define HTTP_CO ':'
#define HTTP_DELIMITED_LENGTH 3
#define HTTP_DELIMITER "\r\n"

#define SECURITY_NATIVE_DREP 0x00000010
#define SECPKG_ATTR_STREAM_SIZES 4

#define SECBUFFER_VERSION 0
#define SECBUFFER_EMPTY 0
#define SECBUFFER_DATA 1
#define SECBUFFER_TOKEN  2
#define SECBUFFER_EXTRA 5
#define SECBUFFER_STREAM_TRAILER 6
#define SECBUFFER_STREAM_HEADER 7

#define ISC_REQ_REPLAY_DETECT 0x00000004
#define ISC_REQ_SEQUENCE_DETECT 0x00000008
#define ISC_REQ_CONFIDENTIALITY 0x00000010
#define ISC_REQ_ALLOCATE_MEMORY 0x00000100
#define ISC_REQ_EXTENDED_ERROR 0x00004000
#define ISC_REQ_STREAM 0x00008000

#define SCHANNEL_CRED_VERSION 0x00000004
#define SCHANNEL_SHUTDOWN 0x00000001
#define SCH_CRED_NO_DEFAULT_CREDS 0x00000010
#define SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x00000400
#define SECPKG_ATTR_REMOTE_CERT_CONTEXT 0x53

#define SOCK_STREAM 1
#define IPPROTO_TCP 6

#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
#define STATUS_UNSUCCESSFUL ((NTSTATUS)0xC0000001L)
#define STATUS_CONNECTION_RESET ((NTSTATUS)0xC000020DL)

#define OBJ_CASE_INSENSITIVE 0x00000040
#define FILE_OPEN_IF 0x00000003
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020


typedef struct _LSA_UNICODE_STRING {
    USHORT Length;
    USHORT MaximumLength;
    PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

typedef struct _OBJECT_ATTRIBUTES {
    ULONG Length;
    HANDLE RootDirectory;
    PUNICODE_STRING ObjectName;
    ULONG Attributes;
    PVOID SecurityDescriptor;
    PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;

typedef struct _IO_STATUS_BLOCK {
    union {
        NTSTATUS Status;
        PVOID Pointer;
    };
    ULONG_PTR Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef enum _EVENT_TYPE {
    NotificationEvent = 0,
    SynchronizationEvent = 1
} EVENT_TYPE;

typedef PVOID HCERTSTORE;
typedef PVOID HCRYPTMSG;
typedef PVOID HCERTCHAINENGINE;

typedef struct _CERT_CONTEXT CERT_CONTEXT;
typedef struct _CERT_CHAIN_CONTEXT CERT_CHAIN_CONTEXT;
typedef struct _CTL_CONTEXT CTL_CONTEXT;

typedef const CERT_CONTEXT* PCCERT_CONTEXT;
typedef const CERT_CHAIN_CONTEXT* PCCERT_CHAIN_CONTEXT;
typedef const CTL_CONTEXT* PCCTL_CONTEXT;

typedef struct _CRYPTOAPI_BLOB {
    DWORD cbData;
    BYTE* pbData;
} CRYPT_DATA_BLOB, *PCRYPT_DATA_BLOB;

typedef CRYPT_DATA_BLOB CRYPT_INTEGER_BLOB, *PCRYPT_INTEGER_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_UINT_BLOB, *PCRYPT_UINT_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_OBJID_BLOB, *PCRYPT_OBJID_BLOB;
typedef CRYPT_DATA_BLOB CERT_NAME_BLOB, *PCERT_NAME_BLOB;
typedef CRYPT_DATA_BLOB CERT_RDN_VALUE_BLOB, *PCERT_RDN_VALUE_BLOB;
typedef CRYPT_DATA_BLOB CERT_BLOB, *PCERT_BLOB;
typedef CRYPT_DATA_BLOB CRL_BLOB, *PCRL_BLOB;
typedef CRYPT_DATA_BLOB DATA_BLOB, *PDATA_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_HASH_BLOB, *PCRYPT_HASH_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_DIGEST_BLOB, *PCRYPT_DIGEST_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_DER_BLOB, *PCRYPT_DER_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_ATTR_BLOB, *PCRYPT_ATTR_BLOB;
typedef CRYPT_DATA_BLOB CRYPT_DATA_BLOB_ALIAS, *PCRYPT_DATA_BLOB_ALIAS;

typedef struct _CERT_TRUST_STATUS {
    DWORD dwErrorStatus;
    DWORD dwInfoStatus;
} CERT_TRUST_STATUS, *PCERT_TRUST_STATUS;

typedef struct _CRYPT_ALGORITHM_IDENTIFIER {
    LPSTR pszObjId;
    CRYPT_OBJID_BLOB Parameters;
} CRYPT_ALGORITHM_IDENTIFIER, *PCRYPT_ALGORITHM_IDENTIFIER;

typedef struct _CRYPT_BIT_BLOB {
    DWORD cbData;
    BYTE* pbData;
    DWORD cUnusedBits;
} CRYPT_BIT_BLOB, *PCRYPT_BIT_BLOB;

typedef struct _CERT_PUBLIC_KEY_INFO {
    CRYPT_ALGORITHM_IDENTIFIER Algorithm;
    CRYPT_BIT_BLOB PublicKey;
} CERT_PUBLIC_KEY_INFO, *PCERT_PUBLIC_KEY_INFO;

typedef struct _CERT_EXTENSION {
    LPSTR pszObjId;
    BOOL fCritical;
    CRYPT_OBJID_BLOB Value;
} CERT_EXTENSION, *PCERT_EXTENSION;

typedef struct _CERT_EXTENSIONS {
    DWORD cExtension;
    PCERT_EXTENSION rgExtension;
} CERT_EXTENSIONS, *PCERT_EXTENSIONS;

typedef struct _CERT_INFO {
    DWORD dwVersion;
    CRYPT_INTEGER_BLOB SerialNumber;
    CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
    CERT_NAME_BLOB Issuer;
    FILETIME NotBefore;
    FILETIME NotAfter;
    CERT_NAME_BLOB Subject;
    CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo;
    CRYPT_BIT_BLOB IssuerUniqueId;
    CRYPT_BIT_BLOB SubjectUniqueId;
    DWORD cExtension;
    PCERT_EXTENSION rgExtension;
} CERT_INFO, *PCERT_INFO;

typedef struct _CERT_CONTEXT {
    DWORD dwCertEncodingType;
    BYTE* pbCertEncoded;
    DWORD cbCertEncoded;
    PCERT_INFO pCertInfo;
    HCERTSTORE hCertStore;
}CERT_CONTEXT, *PCERT_CONTEXT;

typedef struct _CRL_CONTEXT {
    DWORD dwCertEncodingType;
    BYTE* pbCrlEncoded;
    DWORD cbCrlEncoded;
    PCERT_INFO pCrlInfo;
    HCERTSTORE hCertStore;
} CRL_CONTEXT, *PCRL_CONTEXT;

typedef const CRL_CONTEXT* PCCRL_CONTEXT;

typedef struct _CRL_ENTRY {
    CRYPT_INTEGER_BLOB SerialNumber;
    FILETIME RevocationDate;
    DWORD cExtension;
    PCERT_EXTENSION rgExtension;
} CRL_ENTRY, *PCRL_ENTRY;

typedef struct _CERT_REVOCATION_CRL_INFO {
    DWORD cbSize;
    PCCRL_CONTEXT pBaseCrlContext;
    PCCRL_CONTEXT pDeltaCrlContext;
    PCRL_ENTRY pCrlEntry;
    BOOL fDeltaCrlEntry;
} CERT_REVOCATION_CRL_INFO, *PCERT_REVOCATION_CRL_INFO;

typedef struct _CERT_REVOCATION_INFO {
    DWORD cbSize;
    DWORD dwRevocationResult;
    LPCSTR pszRevocationOid;
    LPVOID pvOidSpecificInfo;
    BOOL fHasFreshnessTime;
    DWORD dwFreshnessTime;
    PCERT_REVOCATION_CRL_INFO pCrlInfo;
} CERT_REVOCATION_INFO, *PCERT_REVOCATION_INFO;

typedef struct _CRYPT_ATTRIBUTE {
    LPSTR pszObjId;
    DWORD cValue;
    PCRYPT_ATTR_BLOB rgValue;
} CRYPT_ATTRIBUTE, *PCRYPT_ATTRIBUTE;

typedef struct _CTL_ENTRY {
    CRYPT_DATA_BLOB  SubjectIdentifier;
    DWORD cAttribute;
    PCRYPT_ATTRIBUTE rgAttribute;
} CTL_ENTRY, *PCTL_ENTRY;

typedef struct _CTL_USAGE {
    DWORD  cUsageIdentifier;
    LPSTR* rgpszUsageIdentifier;
} CTL_USAGE, *PCTL_USAGE, CERT_ENHKEY_USAGE, *PCERT_ENHKEY_USAGE;

typedef struct _CTL_INFO {
    DWORD dwVersion;
    CTL_USAGE SubjectUsage;
    CRYPT_DATA_BLOB ListIdentifier;
    CRYPT_INTEGER_BLOB SequenceNumber;
    FILETIME ThisUpdate;
    FILETIME NextUpdate;
    CRYPT_ALGORITHM_IDENTIFIER SubjectAlgorithm;
    DWORD cCTLEntry;
    PCTL_ENTRY rgCTLEntry;
    DWORD cExtension;
    PCERT_EXTENSION rgExtension;
} CTL_INFO, *PCTL_INFO;

typedef struct _CTL_CONTEXT {
    DWORD dwMsgAndCertEncodingType;
    BYTE* pbCtlEncoded;
    DWORD cbCtlEncoded;
    PCTL_INFO pCtlInfo;
    HCERTSTORE hCertStore;
    HCRYPTMSG hCryptMsg;
    BYTE* pbCtlContent;
    DWORD cbCtlContent;
}CTL_CONTEXT, *PCTL_CONTEXT;

typedef struct _CERT_TRUST_LIST_INFO {
    DWORD cbSize;
    PCTL_ENTRY pCtlEntry;
    PCCTL_CONTEXT pCtlContext;
} CERT_TRUST_LIST_INFO, *PCERT_TRUST_LIST_INFO;

typedef struct _CERT_CHAIN_ELEMENT {
    DWORD cbSize;
    PCCERT_CONTEXT pCertContext;
    CERT_TRUST_STATUS TrustStatus;
    PCERT_REVOCATION_INFO pRevocationInfo;
    PCERT_ENHKEY_USAGE pIssuanceUsage;
    PCERT_ENHKEY_USAGE pApplicationUsage;
    LPCWSTR pwszExtendedErrorInfo;
} CERT_CHAIN_ELEMENT, *PCERT_CHAIN_ELEMENT;

typedef struct _CERT_SIMPLE_CHAIN {
    DWORD cbSize;
    CERT_TRUST_STATUS TrustStatus;
    DWORD cElement;
    PCERT_CHAIN_ELEMENT* rgpElement;
    PCERT_TRUST_LIST_INFO pTrustListInfo;
    BOOL fHasRevocationFreshnessTime;
    DWORD dwRevocationFreshnessTime;
} CERT_SIMPLE_CHAIN, *PCERT_SIMPLE_CHAIN;

typedef struct _SSL_EXTRA_CERT_CHAIN_POLICY_PARA {
    DWORD cbSize;
    DWORD dwAuthType;
    DWORD fdwChecks;
    WCHAR* pwszServerName;
} SSL_EXTRA_CERT_CHAIN_POLICY_PARA, *PSSL_EXTRA_CERT_CHAIN_POLICY_PARA;

typedef struct _CERT_CHAIN_CONTEXT {
    DWORD cbSize;
    CERT_TRUST_STATUS TrustStatus;
    DWORD cChain;
    PCERT_SIMPLE_CHAIN* rgpChain;
    DWORD cLowerQualityChainContext;
    PCCERT_CHAIN_CONTEXT* rgpLowerQualityChainContext;
    BOOL fHasRevocationFreshnessTime;
    DWORD dwRevocationFreshnessTime;
    DWORD dwCreateFlags;
    GUID ChainId;
}CERT_CHAIN_CONTEXT, *PCERT_CHAIN_CONTEXT;

typedef struct _CERT_USAGE_MATCH {
    DWORD dwType;
    CERT_ENHKEY_USAGE Usage;
} CERT_USAGE_MATCH, *PCERT_USAGE_MATCH;

typedef struct _CERT_STRONG_SIGN_SERIALIZED_INFO {
    DWORD dwFlags;
    LPWSTR pwszCNGSignHashAlgids;
    LPWSTR pwszCNGPubKeyMinBitLengths;
} CERT_STRONG_SIGN_SERIALIZED_INFO, *PCERT_STRONG_SIGN_SERIALIZED_INFO;

typedef struct _CERT_STRONG_SIGN_PARA {
    DWORD cbSize;
    DWORD dwInfoChoice;
    union {
        PVOID pvInfo;
        PCERT_STRONG_SIGN_SERIALIZED_INFO pSerializedInfo;
        LPSTR pszOID;
    } DUMMYUNIONNAME;
} CERT_STRONG_SIGN_PARA, *PCERT_STRONG_SIGN_PARA;

typedef const CERT_STRONG_SIGN_PARA* PCCERT_STRONG_SIGN_PARA;

typedef struct _CERT_CHAIN_PARA {
    DWORD cbSize;
    CERT_USAGE_MATCH RequestedUsage;
    CERT_USAGE_MATCH RequestedIssuancePolicy;
    DWORD dwUrlRetrievalTimeout;
    BOOL fCheckRevocationFreshnessTime;
    DWORD dwRevocationFreshnessTime;
    LPFILETIME pftCacheResync;
    PCCERT_STRONG_SIGN_PARA pStrongSignPara;
    DWORD dwStrongSignFlags;
} CERT_CHAIN_PARA, *PCERT_CHAIN_PARA;

typedef struct _CERT_CHAIN_POLICY_STATUS {
    DWORD cbSize;
    DWORD dwError;
    LONG lChainIndex;
    LONG lElementIndex;
    PVOID pvExtraPolicyStatus;
} CERT_CHAIN_POLICY_STATUS, *PCERT_CHAIN_POLICY_STATUS;

#define CERT_CHAIN_POLICY_SSL ((LPCSTR)4)

typedef struct _CERT_CHAIN_POLICY_PARA {
    DWORD cbSize;
    DWORD dwFlags;
    PVOID pvExtraPolicyPara;
} CERT_CHAIN_POLICY_PARA, *PCERT_CHAIN_POLICY_PARA;

typedef struct _SCHANNEL_CRED {
    DWORD dwVersion;
    DWORD cCreds;
    PCCERT_CONTEXT* paCred;
    HCERTSTORE hRootStore;
    DWORD cMappers;
    struct _HMAPPER** aphMappers;
    DWORD cSupportedAlgs;
    PUINT palgSupportedAlgs;
    DWORD grbitEnabledProtocols;
    DWORD dwMinimumCipherStrength;
    DWORD dwMaximumCipherStrength;
    DWORD dwSessionLifespan;
    DWORD dwFlags;
    DWORD dwCredFormat;
} SCHANNEL_CRED, *PSCHANNEL_CRED;

typedef struct _SECURITY_INTEGER {
    ULONG LowPart;
    ULONG HighPart;
} SECURITY_INTEGER, *PSECURITY_INTEGER;

typedef SECURITY_INTEGER TimeStamp;
typedef SECURITY_INTEGER* PTimeStamp;

typedef struct _SecBuffer {
    ULONG cbBuffer;
    ULONG BufferType;
    PVOID pvBuffer;
} SecBuffer, *PSecBuffer;

typedef struct _SecBufferDesc {
    ULONG ulVersion;
    ULONG cBuffers;
    PSecBuffer pBuffers;
} SecBufferDesc, *PSecBufferDesc;

typedef struct _SecPkgContext_StreamSizes {
    ULONG cbHeader;
    ULONG cbTrailer;
    ULONG cbMaximumMessage;
    ULONG cBuffers;
    ULONG cbBlockSize;
} SecPkgContext_StreamSizes, *PSecPkgContext_StreamSizes;

typedef struct _SecHandle {
    ULONG_PTR dwLower;
    ULONG_PTR dwUpper;
} SecHandle, *PSecHandle;

typedef SecHandle CredHandle;
typedef PSecHandle PCredHandle;

typedef SecHandle CtxtHandle;
typedef PSecHandle PCtxtHandle;

typedef LONG SECURITY_STATUS;

typedef VOID(NTAPI* GETKEY)(PVOID, PVOID, ULONG, PVOID*, SECURITY_STATUS*);
typedef SECURITY_STATUS(NTAPI* ACQUIRECREDENTIALSHANDLEW)(PWCHAR, PWCHAR, ULONG, PVOID, PVOID, GETKEY, PVOID, PCredHandle, PTimeStamp);
typedef SECURITY_STATUS(NTAPI* ENCRYPTMESSAGE)(PCtxtHandle, ULONG, PSecBufferDesc, ULONG);
typedef SECURITY_STATUS(NTAPI* DECRYPTMESSAGE)(PCtxtHandle, PSecBufferDesc, ULONG, PULONG);
typedef SECURITY_STATUS(NTAPI* QUERYCONTEXTATTRIBUTESW)(PCtxtHandle, ULONG, PVOID);
typedef SECURITY_STATUS(NTAPI* INITIALIZESECURITYCONTEXTW)(PCredHandle, PCtxtHandle, PWCHAR, ULONG, ULONG, ULONG, PSecBufferDesc, ULONG, PCtxtHandle, PSecBufferDesc, PULONG, PTimeStamp);
typedef SECURITY_STATUS(NTAPI* DELETESECURITYCONTEXT)(PCtxtHandle);
typedef SECURITY_STATUS(NTAPI* FREECONTEXTBUFFER)(PVOID);
typedef SECURITY_STATUS(NTAPI* FREECREDENTIALSHANDLE)(PCredHandle);
typedef SECURITY_STATUS(NTAPI* APPLYCONTROLTOKEN)(PCtxtHandle, PSecBufferDesc);

typedef struct _SECURITY_FUNCTION_TABLE_W {
    ULONG dwVersion;
    PVOID EnumerateSecurityPackagesW;
    PVOID QueryCredentialsAttributesW;
    ACQUIRECREDENTIALSHANDLEW AcquireCredentialsHandleW;
    FREECREDENTIALSHANDLE FreeCredentialsHandle;
    PVOID Reserved2;
    INITIALIZESECURITYCONTEXTW InitializeSecurityContextW;
    PVOID AcceptSecurityContext;
    PVOID CompleteAuthToken;
    DELETESECURITYCONTEXT DeleteSecurityContext;
    APPLYCONTROLTOKEN ApplyControlToken;
    QUERYCONTEXTATTRIBUTESW QueryContextAttributesW;
    PVOID ImpersonateSecurityContext;
    PVOID RevertSecurityContext;
    PVOID MakeSignature;
    PVOID VerifySignature;
    FREECONTEXTBUFFER FreeContextBuffer;
    PVOID QuerySecurityPackageInfoW;
    PVOID Reserved3;
    PVOID Reserved4;
    PVOID ExportSecurityContext;
    PVOID ImportSecurityContextW;
    PVOID AddCredentialsW;
    PVOID Reserved8;
    PVOID QuerySecurityContextToken;
    ENCRYPTMESSAGE EncryptMessage;
    DECRYPTMESSAGE DecryptMessage;
    PVOID SetContextAttributesW;
    PVOID SetCredentialsAttributesW;
    PVOID Reserved9;
} SecurityFunctionTableW, *PSecurityFunctionTableW;

// nt
typedef NTSTATUS(NTAPI* NTDEVICEIOCONTROLFILE)(HANDLE, HANDLE, PVOID, PVOID, PIO_STATUS_BLOCK, ULONG, PVOID, ULONG, PVOID, ULONG);
typedef NTSTATUS(NTAPI* NTCREATEFILE)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, PIO_STATUS_BLOCK, PLARGE_INTEGER, ULONG, ULONG, ULONG, ULONG, PVOID, ULONG);
typedef NTSTATUS(NTAPI* NTCREATEEVENT)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, EVENT_TYPE, BOOLEAN);
typedef NTSTATUS(NTAPI* NTWAITFORSINGLEOBJECT)(HANDLE, BOOLEAN, PLARGE_INTEGER);

// rtl
typedef PVOID(NTAPI* RTLALLOCATEHEAP)(PVOID, ULONG, SIZE_T);
typedef BOOL(NTAPI* RTLFREEHEAP)(PVOID, ULONG, PVOID);
typedef PVOID(NTAPI* RTLREALLOCATEHEAP)(PVOID, ULONG, PVOID, SIZE_T);

// sspi
typedef PSecurityFunctionTableW(NTAPI* INITSECURITYINTERFACEW)(VOID);

// crypt32
typedef VOID(WINAPI* CERTFREECERTIFICATECHAIN)(PCCERT_CHAIN_CONTEXT);
typedef BOOL(WINAPI* CERTFREECERTIFICATECONTEXT)(PCCERT_CONTEXT);
typedef BOOL(WINAPI* CERTVERIFYCERTIFICATECHAINPOLICY)(LPCSTR, PCCERT_CHAIN_CONTEXT, PCERT_CHAIN_POLICY_PARA, PCERT_CHAIN_POLICY_STATUS);
typedef BOOL(WINAPI* CERTGETCERTIFICATECHAIN)(HCERTCHAINENGINE, PCCERT_CONTEXT, LPFILETIME, HCERTSTORE, PCERT_CHAIN_PARA, DWORD, LPVOID, PCCERT_CHAIN_CONTEXT*);

CERTFREECERTIFICATECHAIN CertFreeCertificateChain = NULL;
CERTFREECERTIFICATECONTEXT CertFreeCertificateContext = NULL;
CERTVERIFYCERTIFICATECHAINPOLICY CertVerifyCertificateChainPolicy = NULL;
CERTGETCERTIFICATECHAIN CertGetCertificateChain = NULL;

NTDEVICEIOCONTROLFILE NtDeviceIoControlFile = NULL;
NTCREATEFILE NtCreateFile = NULL;
NTCREATEEVENT NtCreateEvent = NULL;
NTWAITFORSINGLEOBJECT NtWaitForSingleObject = NULL;

RTLALLOCATEHEAP RtlAllocateHeap = NULL;
RTLFREEHEAP RtlFreeHeap = NULL;
RTLREALLOCATEHEAP RtlReAllocateHeap = NULL;

INITSECURITYINTERFACEW InitSecurityInterfaceW = NULL;

PSecurityFunctionTableW SspiVirtualFunctionTable = NULL;

__forceinline UINT HostToNetworkLong(UINT Host)
{
    return ((Host & 0x000000FF) << 24) | ((Host & 0x0000FF00) << 8) | ((Host & 0x00FF0000) >> 8) | ((Host & 0xFF000000) >> 24);
}

__forceinline UINT16 HostToNetworkShort(UINT16 Host)
{
    return (UINT16)((Host << 8) | (Host >> 8));
}

#define InitializeObjectAttributesLocal(p, n, a, r, s) do { \
    (p)->Length = sizeof(OBJECT_ATTRIBUTES);               \
    (p)->RootDirectory = (r);                              \
    (p)->Attributes = (a);                                 \
    (p)->ObjectName = (n);                                 \
    (p)->SecurityDescriptor = (s);                         \
    (p)->SecurityQualityOfService = NULL;                  \
} while(0)

#define IOCTL_AFD_BIND    0x00012003
#define IOCTL_AFD_CONNECT 0x00012007
#define IOCTL_AFD_SEND    0x0001201F
#define IOCTL_AFD_RECV    0x00012017

typedef struct IN_ADDR {
    union {
        struct {
            UCHAR s_b1;
            UCHAR s_b2;
            UCHAR s_b3;
            UCHAR s_b4;
        } S_un_b;
        struct {
            USHORT s_w1;
            USHORT s_w2;
        } S_un_w;
        ULONG S_addr;
    } S_un;
} IN_ADDR, *PIN_ADDR;


typedef struct SOCKADDR_IN {
    SHORT sin_family;   // Address family (AF_INET = 2)
    USHORT sin_port;     // Port number (network byte order)
    IN_ADDR sin_addr;     // IPv4 address
    CHAR sin_zero[8];  // Padding (unused)
} SOCKADDR_IN, * PSOCKADDR_IN;

#ifndef s_addr
#define s_addr S_un.S_addr
#endif

typedef struct AFD_CONNECT_REQUEST_IPV4 {
    ULONGLONG SharedAccessNamespaceActive;
    ULONGLONG RootEndpoint;
    ULONGLONG ConnectEndpoint;
    SOCKADDR_IN Address;
}AFD_CONNECT_REQUEST_IPV4, *PAFD_CONNECT_REQUEST_IPV4;

typedef struct _AFD_OPEN_PACKET_EXTENDED_ATTRIBUTES {
    UINT NextEntryOffset;
    BYTE Flags;
    BYTE ExtendedAttributeNameLength;
    USHORT ExtendedAttributeValueLength;
    CHAR ExtendedAttributeName[16];
    UINT EndpointFlags;
    UINT GroupID;
    UINT AddressFamily;
    UINT SocketType;
    UINT Protocol;
    UINT SizeOfTransportName;
    BYTE Unknown1[9];
}AFD_OPEN_PACKET_EXTENDED_ATTRIBUTES, *PAFD_OPEN_PACKET_EXTENDED_ATTRIBUTES;

typedef struct _AFD_BIND_SOCKET {
    UINT32 Flags;
    SOCKADDR_IN Address;
}AFD_BIND_SOCKET, *PAFD_BIND_SOCKET;

typedef struct _AFD_IO_BUFFER {
    UINT Length;
    PVOID Buffer;
}AFD_IO_BUFFER, *PAFD_IO_BUFFER;

typedef struct AFD_TRANSFER_REQUEST {
    PAFD_IO_BUFFER Buffer;
    UINT BufferCount;
    UINT AfdFlags;
    UINT TdiFlags;
}AFD_TRANSFER_REQUEST, *PAFD_TRANSFER_REQUEST;

typedef struct _NTSOCKET {
    HANDLE Socket;
} NTSOCKET, *PNTSOCKET;

typedef struct _DNS_HEADER {
    UINT16 Id;
    UINT16 Flags;
    UINT16 QdCount;
    UINT16 AnCount;
    UINT16 NsCount;
    UINT16 ArCount;
}DNS_HEADER, *PDNS_HEADER;

typedef struct _TLSCLIENT {
    CredHandle CredentialHandle;
    CtxtHandle ContextHandle;
    BOOL CredentialInitializedFlag;
    BOOL ContextInitializedFlag;
    SecPkgContext_StreamSizes Sizes;
}TLSCLIENT, *PTLSCLIENT;

typedef struct _HTTP_HEADER {
    PCHAR Name;
    PCHAR Value;
} HTTP_HEADER, *PHTTP_HEADER;

typedef struct _HTTP_RESPONSE {
    INT StatusCode;
    PCHAR Reason;
    SIZE_T ReasonLength;
    HTTP_HEADER Headers[64];
    SIZE_T HeaderCount;
    PBYTE Body;
    SIZE_T BodyLength;
} HTTP_RESPONSE, *PHTTP_RESPONSE;

SIZE_T WCharStringToCharString2(PCHAR Destination, PWCHAR Source, SIZE_T MaximumAllowed)
{
    SIZE_T Index = 0;

    for (; Index < MaximumAllowed - 1 && Source[Index]; ++Index)
    {
        WCHAR IndividualCharacter = Source[Index];

        if (IndividualCharacter > 0x7f)
            Destination[Index] = '?';
        else
            Destination[Index] = (CHAR)IndividualCharacter;
    }

    Destination[Index] = '\0';

    return Index;
}

SIZE_T StringLengthA(LPCSTR String)
{
    LPCSTR String2;

    for (String2 = String; *String2; ++String2);

    return (String2 - String);
}

PCHAR StringCopyA(PCHAR String1, PCHAR String2)
{
    PCHAR p = String1;

    while ((*p++ = *String2++) != 0);

    return String1;
}

PCHAR StringConcatA(PCHAR String, PCHAR String2)
{
    StringCopyA(&String[StringLengthA(String)], String2);

    return String;
}

PCHAR HttpUSizeToDecimalA(SIZE_T Value, PCHAR Out, SIZE_T OutSize)
{
    SIZE_T i = 0;

    if (Value == 0)
    {
        Out[0] = '0';
        Out[1] = '\0';
        return Out;
    }

    while (Value != 0)
    {
        if (i + 1 >= OutSize)
            return NULL;

        Out[i++] = (CHAR)('0' + (Value % 10));
        Value /= 10;
    }

    for (SIZE_T FrontIndex = 0, BackIndex = i - 1; FrontIndex < BackIndex; FrontIndex++, BackIndex--)
    {
        CHAR Object = Out[FrontIndex];
        Out[FrontIndex] = Out[BackIndex];
        Out[BackIndex] = Object;
    }

    Out[i] = '\0';
    return Out;
}

SIZE_T StringLengthW(LPCWSTR String)
{
    LPCWSTR String2;

    for (String2 = String; *String2; ++String2);

    return (String2 - String);
}

VOID RtlInitUnicodeString(PUNICODE_STRING DestinationString, PCWSTR SourceString)
{
    SIZE_T DestSize;

    if (SourceString)
    {
        DestSize = StringLengthW(SourceString) * sizeof(WCHAR);
        DestinationString->Length = (USHORT)DestSize;
        DestinationString->MaximumLength = (USHORT)DestSize + sizeof(WCHAR);
    }
    else
    {
        DestinationString->Length = 0;
        DestinationString->MaximumLength = 0;
    }

    DestinationString->Buffer = (PWCHAR)SourceString;
}

VOID RtlInitializeSecurityBuffer(SecBuffer* SecurityBuffer, ULONG BufferType, ULONG cbBuffer, PVOID pvBuffer)
{
    SecurityBuffer->BufferType = BufferType;
    SecurityBuffer->cbBuffer = cbBuffer;
    SecurityBuffer->pvBuffer = pvBuffer;
}

VOID RtlInitializeSecurityDescription(SecBufferDesc* SecurityDescription, ULONG ulVersion, ULONG cBuffers, PSecBuffer pBuffers)
{
    SecurityDescription->ulVersion = ulVersion;
    SecurityDescription->cBuffers = cBuffers;
    SecurityDescription->pBuffers = pBuffers;
}

INT LowercaseCharacter(INT Character)
{
    if (Character >= 'A' && Character <= 'Z')
        return (Character - 'A' + 'a');

    return Character;
}

INT StringCompareByLengthLowerA(PCHAR String1, PCHAR String2, SIZE_T Length)
{
    INT Lower1 = 0;
    INT Lower2 = 0;

    for (SIZE_T i = 0; i < Length; i++)
    {
        Lower1 = LowercaseCharacter((BYTE)String1[i]);
        Lower2 = LowercaseCharacter((BYTE)String2[i]);

        if (Lower1 != Lower2)
            return (Lower1 - Lower2);

        if ((BYTE)String1[i] == 0)
            return 0;
    }

    return 0;
}

PVOID SetMemory(PVOID Destination, INT Value, SIZE_T Size)
{
    PBYTE Pointer = (PBYTE)Destination;

    while (Size--)
        *Pointer++ = (BYTE)Value;

    return Destination;
}

INT CompareMemory(PVOID MemoryA, PVOID MemoryB, SIZE_T Length)
{
    PBYTE Pointer1 = (PBYTE)MemoryA;
    PBYTE Pointer2 = (PBYTE)MemoryB;

    while (Length--)
    {
        if (*Pointer1 != *Pointer2)
            return (INT)(*Pointer1 - *Pointer2);

        Pointer1++;
        Pointer2++;
    }

    return 0;
}

PCHAR FindStringInMemoryRegion(PCHAR Buffer, SIZE_T BufferLength, PCHAR String, SIZE_T StringLength)
{
    for (SIZE_T i = 0; i + StringLength <= BufferLength; i++)
    {
        if (CompareMemory(Buffer + i, String, StringLength) == 0)
            return Buffer + i;
    }

    return NULL;
}

BOOL HttpGetContentLength(PCHAR Header, SIZE_T HeaderLength, PSIZE_T outLen)
{
    PCHAR Pointer = Header;
    PCHAR BufferEnd = Header + HeaderLength;
    PCHAR HttpClrf = NULL;

    CHAR ContentLengthHeader[] = "content-length:";
    SIZE_T ContentLengthLength = sizeof(ContentLengthHeader) - 1;
    PCHAR ContentLengthValue = NULL;
    SIZE_T ContentLengthValueLength = 0;

    BOOL bFlag = FALSE;

    while (Pointer < BufferEnd)
    {
        HttpClrf = FindStringInMemoryRegion(Pointer, (SIZE_T)(BufferEnd - Pointer), (PCHAR)"\r\n", 2);
        if (HttpClrf == NULL)
            HttpClrf = BufferEnd;

        if ((SIZE_T)(HttpClrf - Pointer) >= (sizeof(ContentLengthHeader) - 1) && StringCompareByLengthLowerA(Pointer, ContentLengthHeader, ContentLengthLength) == 0)
        {
            ContentLengthValue = Pointer + (sizeof(ContentLengthHeader) - 1);
            while (ContentLengthValue < HttpClrf && (*ContentLengthValue == ' ' || *ContentLengthValue == '\t'))
                ContentLengthValue++;

            while (ContentLengthValue < HttpClrf && *ContentLengthValue >= '0' && *ContentLengthValue <= '9')
            {
                bFlag = TRUE;
                ContentLengthValueLength = (ContentLengthValueLength * 10) + (SIZE_T)(*ContentLengthValue - '0');
                ContentLengthValue++;
            }

            if (bFlag)
                *outLen = ContentLengthValueLength;

            return bFlag;
        }

        Pointer = (HttpClrf + 2);
    }

    return FALSE;
}

VOID AfdClose(PNTSOCKET Socket) 
{
    if (Socket && Socket->Socket)
        CloseHandle(Socket->Socket);
}

NTSTATUS AfdSendRequest(HANDLE Socket, ULONG IoControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG_PTR InformationOutBuffer)
{
    NTSTATUS Status = STATUS_SUCCESS;
    IO_STATUS_BLOCK Io = { 0 };
    HANDLE AfdEventHandleObject = NULL;

    Status = NtCreateEvent(&AfdEventHandleObject, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE);
    if (!NT_SUCCESS(Status))
        return Status;

    Status = NtDeviceIoControlFile(Socket, AfdEventHandleObject, NULL, NULL, &Io, IoControlCode, InputBuffer, InputBufferLength, OutputBuffer, OutputBufferLength);
    if (Status == STATUS_PENDING) 
    {
        NtWaitForSingleObject(AfdEventHandleObject, FALSE, NULL);
        Status = Io.Status;
    }

    if (InformationOutBuffer) 
        *InformationOutBuffer = Io.Information;

    CloseHandle(AfdEventHandleObject);
    return Status;
}

NTSTATUS AdfCreateSocket(PNTSOCKET Socket, UINT AddressFamily, UINT SocketType, UINT Protocol)
{
    NTSTATUS Status = STATUS_SUCCESS;
    ACCESS_MASK AccessMask = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
    UNICODE_STRING AfdEndpoint = { 0 };
    OBJECT_ATTRIBUTES Attributes = { 0 };
    IO_STATUS_BLOCK Io = { 0 };
    PCHAR AfdExtendedAttributeName = (PCHAR)"AfdOpenPacketXX";

    AFD_OPEN_PACKET_EXTENDED_ATTRIBUTES AfdOpenPacketAttributes = { 0 };

    AfdOpenPacketAttributes.ExtendedAttributeNameLength = 15;
    AfdOpenPacketAttributes.ExtendedAttributeValueLength = 30;

    AfdOpenPacketAttributes.AddressFamily = AddressFamily;
    AfdOpenPacketAttributes.SocketType = SocketType;
    AfdOpenPacketAttributes.Protocol = Protocol;

    CopyMemory(AfdOpenPacketAttributes.ExtendedAttributeName, AfdExtendedAttributeName, 15);
    SetMemory(AfdOpenPacketAttributes.Unknown1, 0xff, sizeof(AfdOpenPacketAttributes.Unknown1));

    RtlInitUnicodeString(&AfdEndpoint, L"\\Device\\Afd\\Endpoint");

    InitializeObjectAttributesLocal(&Attributes, &AfdEndpoint, OBJ_CASE_INSENSITIVE, NULL, NULL);

    return NtCreateFile(
        &Socket->Socket,
        AccessMask,
        &Attributes,
        &Io,
        NULL,
        0,
        FILE_SHARE_READ | FILE_SHARE_WRITE,
        FILE_OPEN_IF,
        FILE_SYNCHRONOUS_IO_NONALERT,
        &AfdOpenPacketAttributes,
        (ULONG)sizeof(AfdOpenPacketAttributes));
}

NTSTATUS AfdCreateTcpSocket(PNTSOCKET Socket) 
{
    return AdfCreateSocket(Socket, AF_INET, SOCK_STREAM, IPPROTO_TCP);
}

NTSTATUS AfdBindSocketToTransportProvider(PNTSOCKET Socket) 
{
    BYTE OutputBlob[16] = { 0 };
    AFD_BIND_SOCKET BindSocketObject = { 0 };
    BindSocketObject.Address.sin_family = AF_INET;

    return AfdSendRequest(Socket->Socket, IOCTL_AFD_BIND, &BindSocketObject, sizeof(BindSocketObject), OutputBlob, sizeof(OutputBlob), NULL);
}

NTSTATUS AfdSendConnectRequestIpv4(PNTSOCKET Socket, UINT IpAddress, UINT16 Port)
{
    AFD_CONNECT_REQUEST_IPV4 ConnectionRequest = { 0 };

    ConnectionRequest.Address.sin_family = AF_INET;
    ConnectionRequest.Address.sin_addr.s_addr = IpAddress;
    ConnectionRequest.Address.sin_port = HostToNetworkShort(Port);

    return AfdSendRequest(Socket->Socket, IOCTL_AFD_CONNECT, &ConnectionRequest, sizeof(ConnectionRequest), NULL, 0, NULL);
}

BOOL AfdSendData(PNTSOCKET Socket, PVOID Buffer, UINT Length)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PBYTE BufferOffsetPointer = (PBYTE)Buffer;
    UINT Remaining = Length;
    UINT Sent = 0;

    while (Remaining)
    {
        AFD_IO_BUFFER IoBuffer = { 0 };
        AFD_TRANSFER_REQUEST Request = { 0 };
        BYTE EmptyOutputBuffer[16] = { 0 };
        ULONG_PTR ReceiveInformation = 0;

        IoBuffer.Buffer = BufferOffsetPointer;
        IoBuffer.Length = Remaining;

        Request.Buffer = &IoBuffer;
        Request.BufferCount = 1;

        Status = AfdSendRequest(Socket->Socket, IOCTL_AFD_SEND, &Request, sizeof(Request), EmptyOutputBuffer, sizeof(EmptyOutputBuffer), &ReceiveInformation);
        if (!NT_SUCCESS(Status))
            return FALSE;

        Sent = (UINT)ReceiveInformation;
        if (Sent == 0)
            return FALSE;

        BufferOffsetPointer += Sent;
        Remaining -= Sent;
    }

    return TRUE;
}

BOOL AfdReceiveData(PNTSOCKET Socket, PVOID Buffer, UINT MaximumLength, PUINT ReturnLength)
{
    NTSTATUS Status = STATUS_SUCCESS;

    AFD_IO_BUFFER IoBuffer = { 0 };
    AFD_TRANSFER_REQUEST Request = { 0 };
    BYTE EmptyOutputBuffer[16] = { 0 };
    ULONG_PTR ReceiveInformation = 0;

    IoBuffer.Buffer = Buffer;
    IoBuffer.Length = MaximumLength;

    Request.Buffer = &IoBuffer;
    Request.BufferCount = 1;
    Request.TdiFlags = 32;

    *ReturnLength = 0;

    Status = AfdSendRequest(Socket->Socket, IOCTL_AFD_RECV, &Request, sizeof(Request), EmptyOutputBuffer, sizeof(EmptyOutputBuffer), &ReceiveInformation);
    if (!NT_SUCCESS(Status)) 
        return FALSE;

    *ReturnLength = (UINT)ReceiveInformation;
    return TRUE;
}

VOID TlsFreeClientCredentialHandle(PTLSCLIENT TlsClient)
{
    if (TlsClient->ContextInitializedFlag)
    {
        SspiVirtualFunctionTable->DeleteSecurityContext(&TlsClient->ContextHandle);
        TlsClient->ContextInitializedFlag = FALSE;
    }

    if (TlsClient->CredentialInitializedFlag)
    {
        SspiVirtualFunctionTable->FreeCredentialsHandle(&TlsClient->CredentialHandle);
        TlsClient->CredentialInitializedFlag = FALSE;
    }
}

BOOL TlsAcquireClientCredentialHandle(TLSCLIENT* TlsClient)
{
    SECURITY_STATUS SecurityStatus = STATUS_SUCCESS;
    TimeStamp Expiration = { 0 };

    SCHANNEL_CRED ChannelCredential = { 0 };
    ChannelCredential.dwVersion = SCHANNEL_CRED_VERSION;

    SecurityStatus = SspiVirtualFunctionTable->AcquireCredentialsHandleW(
        NULL,
        (LPWSTR)L"Microsoft Unified Security Protocol Provider",
        0x00000002,
        NULL,
        &ChannelCredential,
        NULL,
        NULL,
        &TlsClient->CredentialHandle,
        &Expiration);

    if (SecurityStatus != SEC_E_OK)
        return FALSE;

    TlsClient->CredentialInitializedFlag = TRUE;
    return TRUE;
}

BOOL TlsHandshake(TLSCLIENT* TlsClient, PNTSOCKET Socket, PWCHAR Host) 
{
    SECURITY_STATUS SecurityStatus = 0;
    TimeStamp Expiration = { 0 };
    PBYTE Data = NULL;
    DWORD Length = 0;

    BOOL bFlag = FALSE;

    SCHANNEL_CRED ChannelCredential = { 0 };

    DWORD ContextRequirement = 
        ISC_REQ_SEQUENCE_DETECT | 
        ISC_REQ_REPLAY_DETECT | 
        ISC_REQ_CONFIDENTIALITY | 
        ISC_REQ_EXTENDED_ERROR | 
        ISC_REQ_ALLOCATE_MEMORY | 
        ISC_REQ_STREAM;

    SecBufferDesc OutputSecurityDescription = { 0 };
    SecBuffer OutputSecurityBuffer = { 0 };

    SecBufferDesc InputSecurityDescription = { 0 };
    SecBuffer InputSecurityBuffer[2] = { 0 };

    PCCERT_CONTEXT ServerCertificate = NULL;

    CERT_CHAIN_PARA CertChainParameters = { 0 };
    CertChainParameters.cbSize = sizeof(CertChainParameters);

    PCCERT_CHAIN_CONTEXT ChainContext = NULL;

    SSL_EXTRA_CERT_CHAIN_POLICY_PARA Extra = { 0 };
    Extra.cbSize = sizeof(Extra);
    Extra.dwAuthType = AUTHTYPE_SERVER;
    Extra.pwszServerName = Host;

    CERT_CHAIN_POLICY_PARA Policy = { 0 };

    Policy.cbSize = sizeof(Policy);
    Policy.pvExtraPolicyPara = &Extra;

    CERT_CHAIN_POLICY_STATUS PolicyStatus;
    PolicyStatus.cbSize = sizeof(PolicyStatus);

    Data = (PBYTE)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, DEFAULT_BUFFER_ALLOCATION_SIZE);
    if (Data == NULL)
        return FALSE;

    while (TRUE)
    {
        RtlInitializeSecurityBuffer(&InputSecurityBuffer[0], SECBUFFER_TOKEN, Length, ((Length > 0) ? Data : NULL));
        RtlInitializeSecurityBuffer(&InputSecurityBuffer[1], SECBUFFER_EMPTY, 0, NULL);
        RtlInitializeSecurityDescription(&InputSecurityDescription, SECBUFFER_VERSION, 2, InputSecurityBuffer);
        RtlInitializeSecurityBuffer(&OutputSecurityBuffer, SECBUFFER_TOKEN, 0, NULL);
        RtlInitializeSecurityDescription(&OutputSecurityDescription, SECBUFFER_VERSION, 1, &OutputSecurityBuffer);

        DWORD ContextAttributes = ERROR_SUCCESS;
        INT Sent = 0;
        UINT Received = 0;

        ZeroMemory(&Expiration, sizeof(Expiration));
        SecurityStatus = 0;

        if (!TlsClient->ContextInitializedFlag)
        {
            SecurityStatus = SspiVirtualFunctionTable->InitializeSecurityContextW(
                &TlsClient->CredentialHandle,
                NULL,
                Host,
                ContextRequirement,
                0,
                SECURITY_NATIVE_DREP,
                NULL,
                0,
                &TlsClient->ContextHandle,
                &OutputSecurityDescription,
                &ContextAttributes,
                &Expiration);

            TlsClient->ContextInitializedFlag = TRUE;
        }
        else {
            SecurityStatus = SspiVirtualFunctionTable->InitializeSecurityContextW(
                &TlsClient->CredentialHandle,
                &TlsClient->ContextHandle,
                Host,
                ContextRequirement,
                0,
                SECURITY_NATIVE_DREP,
                &InputSecurityDescription,
                0,
                NULL,
                &OutputSecurityDescription,
                &ContextAttributes,
                &Expiration);
        }

        if (OutputSecurityBuffer.pvBuffer && OutputSecurityBuffer.cbBuffer)
        {
            Sent = AfdSendData(Socket, OutputSecurityBuffer.pvBuffer, (INT)OutputSecurityBuffer.cbBuffer);
            if (Sent <= 0)
                goto EXIT_ROUTINE;
            else
                SspiVirtualFunctionTable->FreeContextBuffer(OutputSecurityBuffer.pvBuffer);
        }

        if (SecurityStatus == SEC_E_OK)
            break;

        if (SecurityStatus == SEC_I_CONTINUE_NEEDED || SecurityStatus == SEC_E_INCOMPLETE_MESSAGE)
        {
            if (InputSecurityBuffer[1].BufferType == SECBUFFER_EXTRA)
            {
                MoveMemory(Data, (Data + (Length - InputSecurityBuffer[1].cbBuffer)), InputSecurityBuffer[1].cbBuffer);
                Length = InputSecurityBuffer[1].cbBuffer;
            }
            else
                Length = 0;

            if (Length >= DEFAULT_BUFFER_ALLOCATION_SIZE)
                goto EXIT_ROUTINE;

            if (!AfdReceiveData(Socket, (PCHAR)Data + Length, (INT)(DEFAULT_BUFFER_ALLOCATION_SIZE - Length), &Received))
                goto EXIT_ROUTINE;

            if (Received <= 0)
                goto EXIT_ROUTINE;

            Length += (DWORD)Received;
        }
        else
            goto EXIT_ROUTINE;
    }

    SecurityStatus = 0;
    SecurityStatus = SspiVirtualFunctionTable->QueryContextAttributesW(&TlsClient->ContextHandle, SECPKG_ATTR_STREAM_SIZES, &TlsClient->Sizes);
    if (SecurityStatus != SEC_E_OK)
        goto EXIT_ROUTINE;

    SecurityStatus = 0;
    SecurityStatus = SspiVirtualFunctionTable->QueryContextAttributesW(&TlsClient->ContextHandle, SECPKG_ATTR_REMOTE_CERT_CONTEXT, (PVOID)&ServerCertificate);
    if (SecurityStatus != SEC_E_OK || ServerCertificate == NULL)
        goto EXIT_ROUTINE;

    if (!CertGetCertificateChain(NULL, ServerCertificate, NULL, ServerCertificate->hCertStore, &CertChainParameters, 0, NULL, &ChainContext))
        goto EXIT_ROUTINE;

    if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, ChainContext, &Policy, &PolicyStatus))
        goto EXIT_ROUTINE;

    if (PolicyStatus.dwError != ERROR_SUCCESS)
        goto EXIT_ROUTINE;

    bFlag = TRUE;

EXIT_ROUTINE:

    if (ChainContext)
        CertFreeCertificateChain(ChainContext);

    if (ServerCertificate)
        CertFreeCertificateContext(ServerCertificate);

    if (OutputSecurityBuffer.pvBuffer)
        SspiVirtualFunctionTable->FreeContextBuffer(OutputSecurityBuffer.pvBuffer);

    if (Data)
        RtlFreeHeap(GetProcessHeap(), 0, Data);

    return bFlag;
}

BOOL TlsSendCloseNotification(PTLSCLIENT TlsClient, PNTSOCKET Socket)
{
    SECURITY_STATUS Status = STATUS_SUCCESS;

    SecBuffer InputBuffer = { 0 };
    SecBufferDesc InputDescriptionBuffer = { 0 };
    DWORD ShutdownNotification = SCHANNEL_SHUTDOWN;

    SecBuffer OutputBuffer = { 0 };
    SecBufferDesc OutputDescriptionBuffer = { 0 };

    ULONG Attributes = 0;

    TimeStamp Expiration = { 0 };

    RtlInitializeSecurityBuffer(&InputBuffer, SECBUFFER_TOKEN, sizeof(ShutdownNotification), &ShutdownNotification);
    RtlInitializeSecurityDescription(&InputDescriptionBuffer, SECBUFFER_VERSION, 1, &InputBuffer);

    RtlInitializeSecurityBuffer(&OutputBuffer, SECBUFFER_TOKEN, 0, NULL);
    RtlInitializeSecurityDescription(&OutputDescriptionBuffer, SECBUFFER_VERSION, 1, &OutputBuffer);

    Status = SspiVirtualFunctionTable->ApplyControlToken(&TlsClient->ContextHandle, &InputDescriptionBuffer);
    if (Status != SEC_E_OK)
        return FALSE;

    Status = SspiVirtualFunctionTable->InitializeSecurityContextW(
        &TlsClient->CredentialHandle,
        &TlsClient->ContextHandle,
        NULL,
        ISC_REQ_ALLOCATE_MEMORY,
        0,
        SECURITY_NATIVE_DREP,
        NULL,
        0,
        &TlsClient->ContextHandle,
        &OutputDescriptionBuffer,
        &Attributes,
        &Expiration);

    if (Status != SEC_E_OK && Status != SEC_I_CONTEXT_EXPIRED)
        return FALSE;


    if (!OutputBuffer.pvBuffer || OutputBuffer.cbBuffer == 0)
        return FALSE;

    if (!AfdSendData(Socket, OutputBuffer.pvBuffer, (INT)OutputBuffer.cbBuffer))
        return FALSE;

    return TRUE;
}

BOOL TlsSendData(PTLSCLIENT TlsClient, PNTSOCKET Socket, PBYTE Data, DWORD Length)
{
    SECURITY_STATUS SecurityStatus = 0;
    SIZE_T Offset = 0;
    PBYTE Buffer = NULL;
    BOOL bFlag = FALSE;
    INT BytesReceived = 0;

    for (DWORD Fragment = 0, BufferSize = 0; Offset < Length;)
    {
        SecBuffer SecurityBufferObjectArray[4] = { 0 };
        SecBufferDesc SecurityDescription = { 0 };

        Fragment = (DWORD)InlineGetMinimum((SIZE_T)TlsClient->Sizes.cbMaximumMessage, Length - Offset);
        BufferSize = TlsClient->Sizes.cbHeader + Fragment + TlsClient->Sizes.cbTrailer;

        Buffer = (PBYTE)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, BufferSize);
        if (Buffer == NULL)
            goto EXIT_ROUTINE;

        CopyMemory(Buffer + TlsClient->Sizes.cbHeader, ((PCHAR)Data + Offset), Fragment);

        RtlInitializeSecurityBuffer(
            &SecurityBufferObjectArray[0],
            SECBUFFER_STREAM_HEADER,
            TlsClient->Sizes.cbHeader,
            Buffer);

        RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[1],
            SECBUFFER_DATA,
            Fragment,
            ((PCHAR)Buffer + TlsClient->Sizes.cbHeader));


        RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[2],
            SECBUFFER_STREAM_TRAILER,
            TlsClient->Sizes.cbTrailer,
            ((PCHAR)Buffer + TlsClient->Sizes.cbHeader + Fragment));

        RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[3], SECBUFFER_EMPTY, 0, NULL);
        RtlInitializeSecurityDescription(&SecurityDescription, SECBUFFER_VERSION, 4, SecurityBufferObjectArray);

        SecurityStatus = SspiVirtualFunctionTable->EncryptMessage(&TlsClient->ContextHandle, 0, &SecurityDescription, 0);
        if (SecurityStatus != SEC_E_OK)
            goto EXIT_ROUTINE;

        if (AfdSendData(Socket, Buffer, (INT)(SecurityBufferObjectArray[0].cbBuffer + SecurityBufferObjectArray[1].cbBuffer + SecurityBufferObjectArray[2].cbBuffer)) <= 0)
            goto EXIT_ROUTINE;

        if (Buffer)
            RtlFreeHeap(GetProcessHeap(), 0, Buffer);

        Buffer = NULL;
        Offset += Fragment;
    }

    bFlag = TRUE;

EXIT_ROUTINE:

    if (Buffer)
       RtlFreeHeap(GetProcessHeap(), 0, Buffer);

    return bFlag;
}

BOOL WsaProcessIncomingResponseBuffer(PBYTE* Buffer, PSIZE_T Length, PBYTE Response, SIZE_T ResponseLength)
{
    SIZE_T AllocationBlock = *Length + ResponseLength;
    PBYTE Object = NULL;

    if (*Buffer == NULL)
        Object = (PBYTE)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, AllocationBlock + 1);
    else
        Object = (PBYTE)RtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, *Buffer, *Length + ResponseLength + 1);

    if (Object == NULL)
        return FALSE;

    CopyMemory(Object + *Length, Response, ResponseLength);
    Object[*Length + ResponseLength] = 0;
    *Buffer = Object;
    *Length += ResponseLength;

    return TRUE;
}

PBYTE TlsReceiveData(PTLSCLIENT TlsClient, PNTSOCKET Socket, PSIZE_T Length)
{
    SECURITY_STATUS SecurityStatus = 0;

    SecBuffer SecurityBufferObjectArray[4] = { 0 };
    SecBufferDesc SecurityDescription = { 0 };

    PBYTE NetworkBuffer = NULL;
    SIZE_T NetworkBufferLength = 0;

    PBYTE Response = NULL;
    SIZE_T ResponseLength = 0;

    BYTE ResponseDigestObject[8192] = { 0 };

    PBYTE SecBufferChannelExtraHandler = NULL;
    ULONG SecBufferChannelExtraLength = 0;

    BOOL HttpKeepAliveAreHeadersPresentFlag = FALSE;
    BOOL HttpIsContentLengthPresentFlag = FALSE;
    SIZE_T HttpResponseBodyLength = 0;
    SIZE_T HttpResponseTotal = 0;


    for (INT BytesReceived = 0; TRUE;)
    {
        if (HttpKeepAliveAreHeadersPresentFlag)
        {
            if (HttpIsContentLengthPresentFlag && ResponseLength >= HttpResponseTotal)
            {
                *Length = HttpResponseTotal;
                break;
            }
        }

        if (!AfdReceiveData(Socket, (PCHAR)ResponseDigestObject, sizeof(ResponseDigestObject), (PUINT)&BytesReceived))
            goto EXIT_ROUTINE;

        if (BytesReceived == 0)
            break;

        if (BytesReceived < 0)
            goto EXIT_ROUTINE;

        if (!WsaProcessIncomingResponseBuffer(&NetworkBuffer, &NetworkBufferLength, ResponseDigestObject, (SIZE_T)BytesReceived))
            goto EXIT_ROUTINE;

        while (TRUE)
        {
            if (NetworkBufferLength == ERROR_SUCCESS)
                break;

            RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[0], SECBUFFER_DATA, (ULONG)NetworkBufferLength, NetworkBuffer);
            RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[1], SECBUFFER_EMPTY, 0, NULL);
            RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[2], SECBUFFER_EMPTY, 0, NULL);
            RtlInitializeSecurityBuffer(&SecurityBufferObjectArray[3], SECBUFFER_EMPTY, 0, NULL);
            RtlInitializeSecurityDescription(&SecurityDescription, SECBUFFER_VERSION, 4, SecurityBufferObjectArray);

            SecurityStatus = SspiVirtualFunctionTable->DecryptMessage(&TlsClient->ContextHandle, &SecurityDescription, 0, NULL);

            if (SecurityStatus == SEC_E_INCOMPLETE_MESSAGE)
                break;

            if (SecurityStatus == SEC_I_CONTEXT_EXPIRED)
            {
                RtlFreeHeap(GetProcessHeap(), 0, NetworkBuffer);
                *Length = ResponseLength;
                return Response;
            }

            if (SecurityStatus != SEC_E_OK && SecurityStatus != SEC_I_RENEGOTIATE)
                goto EXIT_ROUTINE;

            for (INT Ordinal = 0; Ordinal < 4; Ordinal++)
            {
                if (SecurityBufferObjectArray[Ordinal].BufferType == SECBUFFER_DATA && SecurityBufferObjectArray[Ordinal].cbBuffer)
                {
                    if (!WsaProcessIncomingResponseBuffer(&Response, &ResponseLength, (PBYTE)SecurityBufferObjectArray[Ordinal].pvBuffer, SecurityBufferObjectArray[Ordinal].cbBuffer))
                        goto EXIT_ROUTINE;
                }
            }

            if (!HttpKeepAliveAreHeadersPresentFlag)
            {
                PCHAR HeaderEndOffset = NULL;
                HeaderEndOffset = FindStringInMemoryRegion((PCHAR)Response, ResponseLength, (PCHAR)"\r\n\r\n", 4);
                if (HeaderEndOffset)
                {
                    HttpKeepAliveAreHeadersPresentFlag = TRUE;

                    HttpIsContentLengthPresentFlag = HttpGetContentLength((PCHAR)Response, ((SIZE_T)(HeaderEndOffset - (PCHAR)Response)), &HttpResponseBodyLength);

                    if (HttpIsContentLengthPresentFlag)
                        HttpResponseTotal = (((SIZE_T)(HeaderEndOffset - (PCHAR)Response)) + 4) + HttpResponseBodyLength;
                }
            }

            if (SecurityStatus == SEC_I_RENEGOTIATE)
                goto EXIT_ROUTINE;

            SecBufferChannelExtraHandler = NULL;
            SecBufferChannelExtraLength = 0;

            for (INT i = 0; i < 4; i++)
            {
                if (SecurityBufferObjectArray[i].BufferType == SECBUFFER_EXTRA && SecurityBufferObjectArray[i].cbBuffer > 0)
                {
                    SecBufferChannelExtraHandler = (PBYTE)SecurityBufferObjectArray[i].pvBuffer;
                    SecBufferChannelExtraLength = SecurityBufferObjectArray[i].cbBuffer;
                    break;
                }
            }

            if (SecBufferChannelExtraLength)
            {
                MoveMemory(NetworkBuffer, SecBufferChannelExtraHandler, SecBufferChannelExtraLength);
                NetworkBufferLength = SecBufferChannelExtraLength;
            }
            else
                NetworkBufferLength = 0;

        }
    }

EXIT_ROUTINE:

    if (NetworkBuffer)
        RtlFreeHeap(GetProcessHeap(), 0, NetworkBuffer);

    *Length = ResponseLength;

    return Response;
}

BOOL DnsEncodeHostNameToQNameWire(PCHAR Host, PBYTE OutputBuffer, SIZE_T OutputBufferMaxLength, PSIZE_T OutputLength) 
{
    SIZE_T WriteIndex = 0;
    
    for (PCHAR Pointer = Host; *Pointer;)
    {
        PCHAR Delimiter = Pointer;
        SIZE_T LabelLength = 0;
       
        while (*Delimiter && *Delimiter != '.') 
            Delimiter++;

        LabelLength = (SIZE_T)(Delimiter - Pointer);

        if (LabelLength == 0 || LabelLength > 63)
            return FALSE;

        if (WriteIndex + 1 + LabelLength >= OutputBufferMaxLength)
            return FALSE;

        OutputBuffer[WriteIndex++] = (BYTE)LabelLength;

        for (SIZE_T i = 0; i < LabelLength; i++)
            OutputBuffer[WriteIndex++] = (BYTE)Pointer[i];

        Pointer = (*Delimiter == '.') ? Delimiter + 1 : Delimiter;
    }

    if (WriteIndex + 1 > OutputBufferMaxLength)
        return FALSE;

    OutputBuffer[WriteIndex++] = 0;

    *OutputLength = WriteIndex;
    return TRUE;
}

SIZE_T DnsSkipOverDnsNameWire(PBYTE Buffer, SIZE_T Length, SIZE_T Offset) 
{
    while (Offset < Length)
    {
        BYTE Ordinal = Buffer[Offset];
        if (Ordinal == 0)
            return Offset + 1;

        if ((Ordinal & 0xC0) == 0xC0)
            return (Offset + 2 <= Length) ? Offset + 2 : Length;

        Offset++;

        if (Offset + Ordinal > Length)
            return Length;

        Offset += Ordinal;
    }

    return Length;
}

BOOL DnsExtractFirstARecordIp(PBYTE Buffer, SIZE_T Length, UINT16 Id, PUINT32 IpAddress)
{
    UINT16 DnsId = 0;
    UINT16 Flags = 0;
    UINT16 QdCount = 0;
    UINT16 AnCount = 0;
    UINT16 Type = 0;
    UINT16 ClassField = 0;
    UINT16 RdataLength = 0;

    SIZE_T Offset = 0;

    DnsId = (Buffer[0] << 8) | Buffer[1];
    if (DnsId != Id)
        return FALSE;

    Flags = (Buffer[2] << 8) | Buffer[3];
    if ((Flags & 0x8000) == 0)
        return FALSE;

    if ((Flags & 0x000F) != 0)
        return FALSE;

    QdCount = (Buffer[4] << 8) | Buffer[5];
    AnCount = (Buffer[6] << 8) | Buffer[7];

    Offset = 12;

    for (UINT16 i = 0; i < QdCount; i++)
    {
        Offset = DnsSkipOverDnsNameWire(Buffer, Length, Offset);
        if (Offset + 4 > Length)
            return FALSE;

        Offset += 4;
    }

    for (UINT16 i = 0; i < AnCount; i++)
    {
        Offset = DnsSkipOverDnsNameWire(Buffer, Length, Offset);
        if (Offset + 10 > Length)
            return FALSE;

        Type = (Buffer[Offset] << 8) | Buffer[Offset + 1]; 
        Offset += 2;

        ClassField = (Buffer[Offset] << 8) | Buffer[Offset + 1];
        Offset += 2;

        Offset += 4;
        RdataLength = (Buffer[Offset] << 8) | Buffer[Offset + 1];
        Offset += 2;

        if (Offset + RdataLength > Length)
            return FALSE;

        if (Type == 1 && ClassField == 1 && RdataLength == 4)
        {
            CopyMemory(IpAddress, Buffer + Offset, sizeof(UINT));
            return TRUE;
        }

        Offset += RdataLength;
    }

    return FALSE;
}

BOOL AfdGetAddressFromHostname(PWCHAR Host, PUINT32 Address)
{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSOCKET Socket = { 0 };

    CHAR DnsHostName[256] = { 0 };
    UINT DnsServer = HostToNetworkLong(0x01010101); //1.1.1.1
    BOOL bFlag = FALSE;

    WCharStringToCharString2(DnsHostName, Host, sizeof(DnsHostName));

    BYTE DnsMessage[512] = { 0 };
    SIZE_T DnsMessageLength = 0;
    
    UINT16 BigEndianLength = 0;
    UINT16 DnsTransactionId = (UINT16)GetTickCount64();
    BYTE DnsResponseBuffer[4096] = { 0 };
    UINT TotalBytesReceived = 0;
    SIZE_T QnameLength = 0;

    UINT16 ResponseBufferBigEndianLength = 0;
    UINT Received = 0;

    UINT16 ResponseLength = 0;

    if (sizeof(DNS_HEADER) + 5 > sizeof(DnsMessage))
        return FALSE;

    PDNS_HEADER Header = (PDNS_HEADER)DnsMessage;
    Header->Id = HostToNetworkShort(DnsTransactionId);
    Header->Flags = HostToNetworkShort(0x0100); //256
    Header->QdCount = HostToNetworkShort(1);
    Header->AnCount = 0;
    Header->NsCount = 0;
    Header->ArCount = 0;
    DnsMessageLength = sizeof(DNS_HEADER);

    if (!DnsEncodeHostNameToQNameWire(DnsHostName, DnsMessage + DnsMessageLength, sizeof(DnsMessage) - DnsMessageLength, &QnameLength))
        return FALSE;

    DnsMessageLength += QnameLength;

    if (DnsMessageLength + 4 > sizeof(DnsMessage))
        return FALSE;

    DnsMessage[DnsMessageLength++] = 0x00;
    DnsMessage[DnsMessageLength++] = 0x01;
    DnsMessage[DnsMessageLength++] = 0x00;
    DnsMessage[DnsMessageLength++] = 0x01;

    Status = AfdCreateTcpSocket(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    Status = AfdBindSocketToTransportProvider(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    Status = AfdSendConnectRequestIpv4(&Socket, DnsServer, 53);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    BigEndianLength = HostToNetworkShort((UINT16)DnsMessageLength);
    if (!AfdSendData(&Socket, &BigEndianLength, sizeof(BigEndianLength)))
        goto EXIT_ROUTINE;

    if (!AfdSendData(&Socket, DnsMessage, (UINT)DnsMessageLength))
        goto EXIT_ROUTINE;

    if (!AfdReceiveData(&Socket, &ResponseBufferBigEndianLength, sizeof(ResponseBufferBigEndianLength), &Received) || Received != sizeof(ResponseBufferBigEndianLength))
        goto EXIT_ROUTINE;

    ResponseLength = HostToNetworkShort(ResponseBufferBigEndianLength);

    if (ResponseLength < sizeof(DNS_HEADER) || ResponseLength > 4096)
        goto EXIT_ROUTINE;

    for (UINT Fragment = 0; TotalBytesReceived < ResponseLength; TotalBytesReceived += Fragment)
    {
        if (!AfdReceiveData(&Socket, DnsResponseBuffer + TotalBytesReceived, ResponseLength - TotalBytesReceived, &Fragment))
            goto EXIT_ROUTINE;

        if (Fragment == 0)
            goto EXIT_ROUTINE;
    }

    if (!DnsExtractFirstARecordIp(DnsResponseBuffer, ResponseLength, DnsTransactionId, Address))
        goto EXIT_ROUTINE;

    bFlag = TRUE;

EXIT_ROUTINE:

    AfdClose(&Socket);

    return bFlag;
}

PCHAR HttpGetCrlf(PCHAR Start, PCHAR End)
{
    for (PCHAR p = Start; p + 1 < End; p++)
    {
        if (p[0] == HTTP_CR && p[1] == HTTP_LF)
            return p;
    }

    return NULL;
}

PBYTE RtlProcessInitialHttpHeaderClrf(PBYTE Data, SIZE_T Length)
{
    PCHAR HttpBufferStart = (PCHAR)Data;
    PCHAR HttpBufferEnd = HttpBufferStart + Length;

    PCHAR InitialCrlfObject = HttpGetCrlf(HttpBufferStart, HttpBufferEnd);
    for (PCHAR CrlfObjectNext = NULL; InitialCrlfObject;)
    {
        CrlfObjectNext = InitialCrlfObject + 2;

        if (CrlfObjectNext + 1 < HttpBufferEnd && CrlfObjectNext[0] == HTTP_CR && CrlfObjectNext[1] == HTTP_LF)
            return (PBYTE)InitialCrlfObject;

        InitialCrlfObject = HttpGetCrlf(InitialCrlfObject + 2, HttpBufferEnd);
    }

    return NULL;
}

PVOID HttpFindByte(PVOID Buffer, INT Target, SIZE_T Length)
{
    PBYTE Pointer = (PBYTE)Buffer;
    PBYTE End = (PBYTE)Buffer + Length;
    BYTE TargetByte = Target;

    for (; Pointer < End; Pointer++)
    {
        if (*Pointer == TargetByte)
            return (PVOID)Pointer;
    }

    return NULL;
}

INT GetHttpStatusCode(PCHAR Buffer, SIZE_T Length)
{
    INT Status = 0;

    for (SIZE_T i = 0; i < Length; i++)
    {
        if (Buffer[i] < '0' || Buffer[i] > '9')
            return -1;

        Status = (Status * 10) + (Buffer[i] - '0');
    }

    if (Status < 100 || Status > 599)
        return -1;

    return Status;
}

PCHAR ParseHttpHeaderValue(PCHAR String, SIZE_T Count)
{
    PCHAR Out = (PCHAR)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Count + 1);
    if (Out == NULL)
        return NULL;

    CopyMemory(Out, String, Count);

    Out[Count] = '\0';

    return Out;
}

BOOL ProcessHttpResponseData(PBYTE Data, SIZE_T DataLength, PHTTP_RESPONSE Response)
{
    PBYTE HeaderDelimitedObject = NULL;
    PCHAR HttpHeaderStart = NULL;
    PCHAR HttpHeaderEnd = NULL;
    PCHAR LineTerminationPointer = NULL;

    PCHAR VersionEnd = NULL;
    PCHAR StatusCodeEnd = NULL;

    HeaderDelimitedObject = RtlProcessInitialHttpHeaderClrf(Data, DataLength);
    if (!HeaderDelimitedObject)
        return FALSE;

    HttpHeaderStart = (PCHAR)Data;
    HttpHeaderEnd = (PCHAR)Data + (SIZE_T)(HeaderDelimitedObject - Data);

    LineTerminationPointer = HttpGetCrlf(HttpHeaderStart, HttpHeaderEnd);
    if (!LineTerminationPointer)
        return FALSE;

    VersionEnd = (PCHAR)HttpFindByte((PVOID)HttpHeaderStart, HTTP_SP, (SIZE_T)(LineTerminationPointer - HttpHeaderStart));
    StatusCodeEnd = (PCHAR)HttpFindByte((PVOID)(VersionEnd + 1), HTTP_SP, (SIZE_T)(LineTerminationPointer - (VersionEnd + 1)));
    if (!VersionEnd || !StatusCodeEnd)
        return FALSE;

    Response->StatusCode = GetHttpStatusCode(VersionEnd + 1, (SIZE_T)(StatusCodeEnd - (VersionEnd + 1)));
    if (Response->StatusCode < 0)
        return FALSE;

    Response->Reason = ParseHttpHeaderValue((PCHAR)(StatusCodeEnd + 1), (SIZE_T)(LineTerminationPointer - ((StatusCodeEnd + 1))));
    if (Response->Reason == NULL)
        return FALSE;

    HttpHeaderStart = ((PCHAR)LineTerminationPointer + 2);

    for (PCHAR HttpLineEndObject = NULL, HttpParseDelimiterPointer = NULL, HttpParseValuePointer = NULL; HttpHeaderStart < HttpHeaderEnd;)
    {
        HttpLineEndObject = NULL;
        HttpParseDelimiterPointer = NULL;
        HttpParseValuePointer = NULL;

        if ((SIZE_T)(HttpHeaderEnd - HttpHeaderStart) < 2)
            break;

        HttpLineEndObject = HttpGetCrlf(HttpHeaderStart, HttpHeaderEnd);
        if (!HttpLineEndObject)
            break;

        HttpParseDelimiterPointer = (PCHAR)HttpFindByte((PVOID)HttpHeaderStart, HTTP_CO, (SIZE_T)(HttpLineEndObject - HttpHeaderStart));
        if (HttpParseDelimiterPointer && Response->HeaderCount < 64)
        {
            HttpParseValuePointer = (HttpParseDelimiterPointer + 1);
            while (HttpParseValuePointer < HttpLineEndObject && (*HttpParseValuePointer == HTTP_SP || *HttpParseValuePointer == HTTP_TB))
                HttpParseValuePointer++;

            Response->Headers[Response->HeaderCount].Name = ParseHttpHeaderValue(HttpHeaderStart, (SIZE_T)(HttpParseDelimiterPointer - HttpHeaderStart));
            Response->Headers[Response->HeaderCount].Value = ParseHttpHeaderValue(HttpParseValuePointer, (SIZE_T)(HttpLineEndObject - HttpParseValuePointer));
            if (!Response->Headers[Response->HeaderCount].Value || !Response->Headers[Response->HeaderCount].Name)
                return FALSE;

            Response->HeaderCount++;
        }

        HttpHeaderStart = HttpLineEndObject + 2;
    }

    Response->Body = (PBYTE)HeaderDelimitedObject + 4;
    Response->BodyLength = DataLength - (SIZE_T)(Response->Body - Data);

    return TRUE;
}

VOID FreeHttpResponse(PHTTP_RESPONSE Response)
{
    if (Response->Reason)
    {
        RtlFreeHeap(GetProcessHeap(), 0, (PVOID)Response->Reason);
        Response->Reason = NULL;
        Response->ReasonLength = 0;
    }

    for (SIZE_T i = 0; i < Response->HeaderCount; i++)
    {
        if (Response->Headers[i].Name)
        {
            RtlFreeHeap(GetProcessHeap(), 0, (PVOID)Response->Headers[i].Name);
            Response->Headers[i].Name = NULL;
        }

        if (Response->Headers[i].Value)
        {
            RtlFreeHeap(GetProcessHeap(), 0, (PVOID)Response->Headers[i].Value);
            Response->Headers[i].Value = NULL;
        }
    }

    Response->HeaderCount = 0;
}

PCHAR HttpCreateSection(PCHAR Header, PCHAR Attribute)
{
    PCHAR Out = NULL;
    SIZE_T BufferSize = HTTP_DELIMITED_LENGTH;

    BufferSize += StringLengthA(Header); BufferSize += StringLengthA(Attribute);

    Out = (PCHAR)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, BufferSize);
    if (Out == NULL)
        return NULL;

    StringCopyA(Out, Header);
    StringConcatA(Out, Attribute);
    StringConcatA(Out, (PCHAR)HTTP_DELIMITER);

    return Out;
}

PCHAR RtlBuildHttpRequest(INT Method, PCHAR Host, PCHAR UserAgent, PCHAR AcceptHeaderAttributes, PCHAR ConnectionHeaderAttributes, PCHAR ContentType, ULONG ContentLength)
{
    PCHAR HttpBufferRequestObject = NULL;
    PCHAR HttpBufferSectionHandler = NULL;
    SIZE_T HttpBufferSize = DEFAULT_BUFFER_ALLOCATION_SIZE;
    BOOL bFlag = FALSE;

    CHAR ContentLengthObject[32] = { 0 };

    HttpBufferRequestObject = (PCHAR)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, HttpBufferSize);
    if (HttpBufferRequestObject == NULL)
        goto EXIT_ROUTINE;

    switch (Method)
    {
        case 0:
        {
            StringCopyA(HttpBufferRequestObject, (PCHAR)"GET /get HTTP/1.1\r\n");
            break;
        }

        case 1:
        {
            StringCopyA(HttpBufferRequestObject, (PCHAR)"POST /post HTTP/1.1\r\n");
            break;
        }
        default:
            goto EXIT_ROUTINE;
    }

    if (Host)
    {
        HttpBufferSectionHandler = HttpCreateSection((PCHAR)"Host: ", Host);
        if (HttpBufferSectionHandler == NULL)
            goto EXIT_ROUTINE;
        else {
            StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

            RtlFreeHeap(GetProcessHeap(), 0, HttpBufferSectionHandler);
        }
    }

    if (UserAgent)
    {
        HttpBufferSectionHandler = HttpCreateSection((PCHAR)"User-Agent: ", UserAgent);
        if (HttpBufferSectionHandler == NULL)
            goto EXIT_ROUTINE;
        else {
            StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

            RtlFreeHeap(GetProcessHeap(), 0, HttpBufferSectionHandler);
        }
    }

    if (AcceptHeaderAttributes)
    {
        HttpBufferSectionHandler = HttpCreateSection((PCHAR)"Accept: ", AcceptHeaderAttributes);
        if (HttpBufferSectionHandler == NULL)
            goto EXIT_ROUTINE;
        else {
            StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

            RtlFreeHeap(GetProcessHeap(), 0, HttpBufferSectionHandler);
        }
    }

    if (ContentType && ContentLength)
    {
        HttpBufferSectionHandler = HttpCreateSection((PCHAR)"Content-Type: ", ContentType);
        if (HttpBufferSectionHandler == NULL)
            goto EXIT_ROUTINE;
        else {
            StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

            RtlFreeHeap(GetProcessHeap(), 0, HttpBufferSectionHandler);
        }

        HttpBufferSectionHandler = HttpCreateSection((PCHAR)"Content-Length: ", HttpUSizeToDecimalA((SIZE_T)ContentLength, ContentLengthObject, sizeof(ContentLengthObject)));
        if (HttpBufferSectionHandler == NULL)
            goto EXIT_ROUTINE;
        else {
            StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

            RtlFreeHeap(GetProcessHeap(), 0, HttpBufferSectionHandler);
        }
    }

    if (ConnectionHeaderAttributes)
    {
        HttpBufferSectionHandler = HttpCreateSection((PCHAR)"Connection: ", ConnectionHeaderAttributes);
        if (HttpBufferSectionHandler == NULL)
            goto EXIT_ROUTINE;
        else {
            StringConcatA(HttpBufferRequestObject, HttpBufferSectionHandler);

            RtlFreeHeap(GetProcessHeap(), 0, HttpBufferSectionHandler);
        }
    }

    StringConcatA(HttpBufferRequestObject, (PCHAR)HTTP_DELIMITER);

    bFlag = TRUE;

EXIT_ROUTINE:

    if (!bFlag)
        RtlFreeHeap(GetProcessHeap(), 0, HttpBufferRequestObject);

    return (bFlag ? HttpBufferRequestObject : NULL);
}

BOOL ResolveNtdllImports(VOID)
{
    HMODULE hModule = NULL;

    hModule = GetModuleHandleA("ntdll.dll");
    if (hModule == NULL)
        return FALSE;

    NtCreateFile = (NTCREATEFILE)GetProcAddress(hModule, "NtCreateFile");
    NtDeviceIoControlFile = (NTDEVICEIOCONTROLFILE)GetProcAddress(hModule, "NtDeviceIoControlFile");
    NtCreateEvent = (NTCREATEEVENT)GetProcAddress(hModule, "NtCreateEvent");
    NtWaitForSingleObject = (NTWAITFORSINGLEOBJECT)GetProcAddress(hModule, "NtWaitForSingleObject");
    RtlAllocateHeap = (RTLALLOCATEHEAP)GetProcAddress(hModule, "RtlAllocateHeap");
    RtlFreeHeap = (RTLFREEHEAP)GetProcAddress(hModule, "RtlFreeHeap");
    RtlReAllocateHeap = (RTLREALLOCATEHEAP)GetProcAddress(hModule, "RtlReAllocateHeap");

    if (!NtCreateFile || !NtDeviceIoControlFile || !NtCreateEvent || !RtlAllocateHeap || !RtlFreeHeap || !RtlReAllocateHeap || !NtWaitForSingleObject)
        return FALSE;

    return TRUE;
}

BOOL InitSspiTable(VOID)
{
    HMODULE hModule = NULL;

    hModule = LoadLibraryA("sspicli.dll");
    if (hModule == NULL)
        return FALSE;

    InitSecurityInterfaceW = (INITSECURITYINTERFACEW)GetProcAddress(hModule, "InitSecurityInterfaceW");
    if (!InitSecurityInterfaceW)
        return FALSE;

    SspiVirtualFunctionTable = InitSecurityInterfaceW();
    if (!SspiVirtualFunctionTable)
        return FALSE;

    return TRUE;
}

BOOL LoadCrypt32CertFunctions(VOID)
{
    HMODULE hModule = NULL;

    hModule = LoadLibraryA("Crypt32.dll");
    if (hModule == NULL)
        return FALSE;

    CertFreeCertificateChain = (CERTFREECERTIFICATECHAIN)GetProcAddress(hModule, "CertFreeCertificateChain");
    CertFreeCertificateContext = (CERTFREECERTIFICATECONTEXT)GetProcAddress(hModule, "CertFreeCertificateContext");
    CertVerifyCertificateChainPolicy = (CERTVERIFYCERTIFICATECHAINPOLICY)GetProcAddress(hModule, "CertVerifyCertificateChainPolicy");
    CertGetCertificateChain = (CERTGETCERTIFICATECHAIN)GetProcAddress(hModule, "CertGetCertificateChain");

    if (!CertFreeCertificateChain || !CertFreeCertificateContext || !CertVerifyCertificateChainPolicy || !CertGetCertificateChain)
        return FALSE;

    return TRUE;
}

BOOL RtlInitializeProcessApis(VOID)
{
    if (!ResolveNtdllImports() || !InitSspiTable() || !LoadCrypt32CertFunctions())
        return FALSE;

    return TRUE;
}

VOID PrintHttpResponse(PHTTP_RESPONSE Response)
{
    printf("HTTP Response\r\n");
    printf("==============\r\n");
    printf("Status : %d\r\n", Response->StatusCode);

    if (Response->Reason && Response->ReasonLength)
        printf("Reason : %.*s\r\n", (int)Response->ReasonLength, Response->Reason);
    else
        printf("Reason : <none>\r\n");

    printf("\nHeaders (%zu):\n", Response->HeaderCount);
    printf("------------------------------\r\n");

    for (SIZE_T i = 0; i < Response->HeaderCount; i++)
    {
        PHTTP_HEADER p = &Response->Headers[i];

        if (!p->Name)
            continue;

        printf("  %s: %s\r\n", p->Name, p->Value ? p->Value : "");
    }

    printf("\nBody:\r\n");
    printf("------------------------------\r\n");
    printf("Length : %zu bytes\r\n", Response->BodyLength);

    if (Response->Body && Response->BodyLength)
    {
        printf("Content:\r\n");

        for (SIZE_T i = 0; i < Response->BodyLength; i++)
        {
            BYTE c = Response->Body[i];
            putchar((c >= 32 && c <= 126) ? c : '.');
        }

        printf("\r\n");
    }
    else
        printf("**NO BODY RECEIVED**\n");

    printf("\r\n");
}

INT EXAMPLE_HttpsSimpleGetRequestClose(VOID)
{
    NTSTATUS Status = STATUS_SUCCESS;

    TLSCLIENT TlsClient = { 0 };
    NTSOCKET Socket = { 0 };

    PBYTE TlsResponse = NULL;
    SIZE_T TlsResponseLength = 0;

    HTTP_RESPONSE HttpResponse = { 0 };
    PCHAR HttpRequestObject = NULL;

    WCHAR Host[] = L"httpbin.org"; 
    CHAR AnsiHost[256] = { 0 };
    UINT32 IpAddress = 0;

    Status = AfdCreateTcpSocket(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    Status = AfdBindSocketToTransportProvider(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    if (!AfdGetAddressFromHostname(Host, &IpAddress))
        goto EXIT_ROUTINE;

    Status = AfdSendConnectRequestIpv4(&Socket, IpAddress, 443);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    if (!TlsAcquireClientCredentialHandle(&TlsClient))
        goto EXIT_ROUTINE;

    if (!TlsHandshake(&TlsClient, &Socket, Host))
        goto EXIT_ROUTINE;

    WCharStringToCharString2(AnsiHost, Host, sizeof(AnsiHost));

    HttpRequestObject = RtlBuildHttpRequest(0, AnsiHost, (PCHAR)"i like kitty cats/1.0", (PCHAR)"*/*", (PCHAR)"close", NULL, 0);
    if (HttpRequestObject == NULL)
        goto EXIT_ROUTINE;

    if (!TlsSendData(&TlsClient, &Socket, (PBYTE)HttpRequestObject, (DWORD)StringLengthA(HttpRequestObject)))
        goto EXIT_ROUTINE;

    TlsResponse = TlsReceiveData(&TlsClient, &Socket, &TlsResponseLength);
    if (TlsResponse == NULL)
        goto EXIT_ROUTINE;

    if (!ProcessHttpResponseData(TlsResponse, TlsResponseLength, &HttpResponse))
        goto EXIT_ROUTINE;

    PrintHttpResponse(&HttpResponse);

EXIT_ROUTINE:

    if (HttpRequestObject)
        RtlFreeHeap(GetProcessHeap(), 0, HttpRequestObject);

    FreeHttpResponse(&HttpResponse);

    if (TlsResponse)
        RtlFreeHeap(GetProcessHeap(), 0, TlsResponse);

    TlsFreeClientCredentialHandle(&TlsClient);

    AfdClose(&Socket);

    return 0;
}

INT EXAMPLE_HttpsSimpleGetRequestKeepAlive(VOID)
{
    NTSTATUS Status = STATUS_SUCCESS;

    TLSCLIENT TlsClient = { 0 };
    NTSOCKET Socket = { 0 };

    PBYTE TlsResponse = NULL;
    SIZE_T TlsResponseLength = 0;

    HTTP_RESPONSE HttpResponse = { 0 };
    PCHAR HttpRequestObject = NULL;

    WCHAR Host[] = L"httpbin.org";
    CHAR AnsiHost[256] = { 0 };
    UINT32 IpAddress = 0;

    Status = AfdCreateTcpSocket(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    Status = AfdBindSocketToTransportProvider(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    if (!AfdGetAddressFromHostname(Host, &IpAddress))
        goto EXIT_ROUTINE;

    Status = AfdSendConnectRequestIpv4(&Socket, IpAddress, 443);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    if (!TlsAcquireClientCredentialHandle(&TlsClient))
        goto EXIT_ROUTINE;

    if (!TlsHandshake(&TlsClient, &Socket, Host))
        goto EXIT_ROUTINE;

    WCharStringToCharString2(AnsiHost, Host, sizeof(AnsiHost));

    HttpRequestObject = RtlBuildHttpRequest(0, AnsiHost, (PCHAR)"i like kitty cats/1.0", (PCHAR)"*/*", (PCHAR)"keep-alive", NULL, 0);
    if (HttpRequestObject == NULL)
        goto EXIT_ROUTINE;

    if (!TlsSendData(&TlsClient, &Socket, (PBYTE)HttpRequestObject, (DWORD)StringLengthA(HttpRequestObject)))
        goto EXIT_ROUTINE;

    TlsResponse = TlsReceiveData(&TlsClient, &Socket, &TlsResponseLength);
    if (TlsResponse == NULL)
        goto EXIT_ROUTINE;

    if (!ProcessHttpResponseData(TlsResponse, TlsResponseLength, &HttpResponse))
        goto EXIT_ROUTINE;

    PrintHttpResponse(&HttpResponse);

EXIT_ROUTINE:

    TlsSendCloseNotification(&TlsClient, &Socket);

    if (HttpRequestObject)
        RtlFreeHeap(GetProcessHeap(), 0, HttpRequestObject);

    FreeHttpResponse(&HttpResponse);

    if (TlsResponse)
        RtlFreeHeap(GetProcessHeap(), 0, TlsResponse);

    TlsFreeClientCredentialHandle(&TlsClient);

    AfdClose(&Socket);

    return 0;
}

INT EXAMPLE_HttpsPostUploadFile(VOID)
{
    NTSTATUS Status = STATUS_SUCCESS;

    TLSCLIENT TlsClient = { 0 };
    NTSOCKET Socket = { 0 };

    PBYTE TlsResponse = NULL;
    SIZE_T TlsResponseLength = 0;

    HTTP_RESPONSE HttpResponse = { 0 };
    PCHAR HttpRequestObject = NULL;

    WCHAR Host[] = L"httpbin.org";
    CHAR AnsiHost[256] = { 0 };
    UINT32 IpAddress = 0;

    //*****************************************************
    // Variables for generic file reading operations
    // required for file upload
    //*****************************************************

    PBYTE FileBuffer = NULL;
    HANDLE hHandle = INVALID_HANDLE_VALUE;
    DWORD BytesRead = 0;
    LARGE_INTEGER FileSize = { 0 };

    //*****************************************************
    //*****************************************************

    Status = AfdCreateTcpSocket(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    Status = AfdBindSocketToTransportProvider(&Socket);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    if (!AfdGetAddressFromHostname(Host, &IpAddress))
        goto EXIT_ROUTINE;

    Status = AfdSendConnectRequestIpv4(&Socket, IpAddress, 443);
    if (!NT_SUCCESS(Status))
        goto EXIT_ROUTINE;

    if (!TlsAcquireClientCredentialHandle(&TlsClient))
        goto EXIT_ROUTINE;

    if (!TlsHandshake(&TlsClient, &Socket, Host))
        goto EXIT_ROUTINE;

    //*****************************************************
    //
    // This code is extremely generic. The next code is
    // simply reading a file into memory, getting it's
    // file size, and then sending it to the remote host
    //
    //*****************************************************

    hHandle = CreateFileW(L"C:\\Users\\maldev\\Desktop\\TestUpload.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hHandle == INVALID_HANDLE_VALUE)
        goto EXIT_ROUTINE;
    
    if (!GetFileSizeEx(hHandle, &FileSize))
        goto EXIT_ROUTINE;

    FileBuffer = (PBYTE)RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, (SIZE_T)FileSize.QuadPart);
    if (FileBuffer == NULL)
        goto EXIT_ROUTINE;

    if (!ReadFile(hHandle, FileBuffer, (DWORD)FileSize.QuadPart, &BytesRead, NULL))
        goto EXIT_ROUTINE;

    //*****************************************************
    //*****************************************************

    WCharStringToCharString2(AnsiHost, Host, sizeof(AnsiHost));

    HttpRequestObject = RtlBuildHttpRequest(1, AnsiHost, (PCHAR)"i like kitty cats/1.0", (PCHAR)"*/*", (PCHAR)"keep-alive", (PCHAR)"application/octet-stream", (ULONG)FileSize.QuadPart);
    if (HttpRequestObject == NULL)
        goto EXIT_ROUTINE;

    if (!TlsSendData(&TlsClient, &Socket, (PBYTE)HttpRequestObject, (DWORD)StringLengthA(HttpRequestObject)))
        goto EXIT_ROUTINE;

    if (!TlsSendData(&TlsClient, &Socket, (PBYTE)FileBuffer, (SIZE_T)FileSize.QuadPart))
        goto EXIT_ROUTINE;

    TlsResponse = TlsReceiveData(&TlsClient, &Socket, &TlsResponseLength);
    if (TlsResponse == NULL)
        goto EXIT_ROUTINE;

    if (!ProcessHttpResponseData(TlsResponse, TlsResponseLength, &HttpResponse))
        goto EXIT_ROUTINE;

    PrintHttpResponse(&HttpResponse);

EXIT_ROUTINE:

    //*****************************************************
    // Variables for generic file reading operations
    // required for file upload
    //*****************************************************

    if (FileBuffer)
        RtlFreeHeap(GetProcessHeap(), 0, FileBuffer);

    if (hHandle)
        CloseHandle(hHandle);

    //*****************************************************
    //*****************************************************

    TlsSendCloseNotification(&TlsClient, &Socket);

    if (HttpRequestObject)
        RtlFreeHeap(GetProcessHeap(), 0, HttpRequestObject);

    FreeHttpResponse(&HttpResponse);

    if (TlsResponse)
        RtlFreeHeap(GetProcessHeap(), 0, TlsResponse);

    TlsFreeClientCredentialHandle(&TlsClient);

    AfdClose(&Socket);

    return 0;
}

INT EXAMPLE_ResolveDnsW(VOID)
{
    UINT32 IpAddress = 0;
    
    AfdGetAddressFromHostname((PWCHAR)L"httpbin.org", &IpAddress);

    if (IpAddress != 0)
    {
        PBYTE p = (PBYTE)&IpAddress;
        printf("%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
    }
        
    return 0;
}

INT main(VOID) 
{
    NTSTATUS Status = STATUS_SUCCESS;
    NTSOCKET Socket = { 0 };
    WCHAR Host[] = L"httpbin.org";
    TLSCLIENT TlsClient = { 0 };
    UINT32 IpAddress = 0;
    BOOL bFlag = FALSE;

    PBYTE TlsResponse = NULL;
    SIZE_T TlsResponseLength = 0;

    HTTP_RESPONSE HttpResponse = { 0 };
    PCHAR HttpRequestObject = NULL;

    if (!RtlInitializeProcessApis())
        return GetLastError();

    //EXAMPLE_HttpsSimpleGetRequestClose();

    //EXAMPLE_HttpsSimpleGetRequestKeepAlive();

    //EXAMPLE_HttpsPostUploadFile();

    //EXAMPLE_ResolveDnsW();

    return ERROR_SUCCESS;
}

Last updated