# 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;
}

```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://malwaresourcecode.com/home/my-projects/proof-of-concepts/https-tls-with-afd.sys-winsocks-not-necessary.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
