{
  $Project$
  $Workfile$
  $Revision$
  $DateUTC$
  $Id$

  This file is part of the Indy (Internet Direct) project, and is offered
  under the dual-licensing agreement described on the Indy website.
  (http://www.indyproject.org/)

  Copyright:
   (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
{
  $Log$
}
{
  Rev 1.0    2004.02.03 3:14:52 PM  czhower
  Move and updates

  Rev 1.2    10/15/2003 9:43:20 PM  DSiders
  Added localization comments.

  Rev 1.1    1-10-2003 19:44:28  BGooijen
  fixed leak in CloseLibrary()

  Rev 1.0    11/13/2002 09:03:24 AM  JPMugaas
}

unit IdWship6;

interface

{$I IdCompilerDefines.inc}

{$IFDEF FPC}
  {$IFDEF WIN32}
    {$ALIGN OFF}
  {$ELSE}
    //It turns out that Win64 and WinCE require record alignment
    {$PACKRECORDS C}
  {$ENDIF}
{$ELSE}
  {$IFDEF WIN64}
    {$ALIGN ON}
    {$MINENUMSIZE 4}
  {$ELSE}
    {$MINENUMSIZE 4}
    {$IFDEF REQUIRES_PROPER_ALIGNMENT}
      {$ALIGN ON}
    {$ELSE}
      {$ALIGN OFF}
      {$WRITEABLECONST OFF}
    {$ENDIF}
  {$ENDIF}
{$ENDIF}

uses
  {$IFDEF HAS_TInterlocked}
  syncobjs, //here to facilitate inlining with Delphi
  {$ENDIF}
  {$IFNDEF HAS_SIZE_T}
  IdGlobal,
  {$ENDIF}
  Windows,
  IdWinsock2;

const
  Wship6_dll =   'Wship6.dll';    {do not localize}
  iphlpapi_dll = 'iphlpapi.dll';  {do not localize}
  fwpuclnt_dll = 'Fwpuclnt.dll'; {Do not localize}

  // Error codes from getaddrinfo().

  //JPM
  //Note that I am adding a GIA_ prefix on my own because
  //some names here share some names defined in IdWinsock2 causing
  //an unpredictible problem. The values are not defined the same in IdWinsock2
  {$EXTERNALSYM GIA_EAI_ADDRFAMILY}
  GIA_EAI_ADDRFAMILY = 1  ; // Address family for nodename not supported.
  {$EXTERNALSYM GIA_EAI_AGAIN}
  GIA_EAI_AGAIN      = 2  ; // Temporary failure in name resolution.
  {$EXTERNALSYM GIA_EAI_BADFLAGS}
  GIA_EAI_BADFLAGS   = 3  ; // Invalid value for ai_flags.
  {$EXTERNALSYM GIA_EAI_FAIL}
  GIA_EAI_FAIL       = 4  ; // Non-recoverable failure in name resolution.
  {$EXTERNALSYM GIA_EAI_FAMILY}
  GIA_EAI_FAMILY     = 5  ; // Address family ai_family not supported.
  {$EXTERNALSYM GIA_EAI_MEMORY}
  GIA_EAI_MEMORY     = 6  ; // Memory allocation failure.
  {$EXTERNALSYM GIA_EAI_NODATA}
  GIA_EAI_NODATA     = 7  ; // No address associated with nodename.
  {$EXTERNALSYM GIA_EAI_NONAME}
  GIA_EAI_NONAME     = 8  ; // Nodename nor servname provided, or not known.
  {$EXTERNALSYM GIA_EAI_SERVICE}
  GIA_EAI_SERVICE    = 9  ; // Servname not supported for ai_socktype.
  {$EXTERNALSYM GIA_EAI_SOCKTYPE}
  GIA_EAI_SOCKTYPE   = 10 ; // Socket type ai_socktype not supported.
  {$EXTERNALSYM GIA_EAI_SYSTEM}
  GIA_EAI_SYSTEM     = 11 ; // System error returned in errno.

  {$EXTERNALSYM NI_MAXHOST}
  NI_MAXHOST  = 1025;      // Max size of a fully-qualified domain name.
  {$EXTERNALSYM NI_MAXSERV}
  NI_MAXSERV  =   32;      // Max size of a service name.

  // Flags for getnameinfo().

  {$EXTERNALSYM NI_NOFQDN}
  NI_NOFQDN       =   $1  ;  // Only return nodename portion for local hosts.
  {$EXTERNALSYM NI_NUMERICHOST}
  NI_NUMERICHOST  =   $2  ;  // Return numeric form of the host's address.
  {$EXTERNALSYM NI_NAMEREQD}
  NI_NAMEREQD     =   $4  ;  // Error if the host's name not in DNS.
  {$EXTERNALSYM NI_NUMERICSERV}
  NI_NUMERICSERV  =   $8  ;  // Return numeric form of the service (port #).
  {$EXTERNALSYM NI_DGRAM}
  NI_DGRAM        =   $10 ;  // Service is a datagram service.

  //JPM - These may not be supported in WinCE 4.2
  {$EXTERNALSYM PROTECTION_LEVEL_RESTRICTED}
  PROTECTION_LEVEL_RESTRICTED   = 30;  //* for Intranet apps      /*
  {$EXTERNALSYM PROTECTION_LEVEL_DEFAULT}
  PROTECTION_LEVEL_DEFAULT      = 20;  //* default level          /*
  {$EXTERNALSYM PROTECTION_LEVEL_UNRESTRICTED}
  PROTECTION_LEVEL_UNRESTRICTED = 10;  //* for peer-to-peer apps  /*

  {$EXTERNALSYM SOCKET_SETTINGS_GUARANTEE_ENCRYPTION}
  SOCKET_SETTINGS_GUARANTEE_ENCRYPTION = $00000001;
  {$EXTERNALSYM SOCKET_SETTINGS_ALLOW_INSECURE}
  SOCKET_SETTINGS_ALLOW_INSECURE = $00000002;
  
  {$EXTERNALSYM SOCKET_INFO_CONNECTION_SECURED}
  SOCKET_INFO_CONNECTION_SECURED =  $00000001;
  {$EXTERNALSYM SOCKET_INFO_CONNECTION_ENCRYPTED}
  SOCKET_INFO_CONNECTION_ENCRYPTED = $00000002;

type
  // RLebeau: find a better place for this
  {$IFNDEF HAS_UInt64}
  {$EXTERNALSYM UINT64}
  UINT64 = {$IFDEF HAS_QWord}QWord{$ELSE}Int64{$ENDIF};
  {$ENDIF}

  {$NODEFINE PPaddrinfo}
  PPaddrinfo = ^PAddrInfo;
  {$NODEFINE PPaddrinfoW}
  PPaddrinfoW = ^PAddrInfoW;

  {$IFNDEF WINCE}
  {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL}
  {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL_DEFAULT}
  {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL_IPSEC}
  {$EXTERNALSYM SOCKET_SECURITY_PROTOCOL_INVALID}
  SOCKET_SECURITY_PROTOCOL = (
    SOCKET_SECURITY_PROTOCOL_DEFAULT, SOCKET_SECURITY_PROTOCOL_IPSEC, SOCKET_SECURITY_PROTOCOL_INVALID
    );

  {$EXTERNALSYM SOCKET_SECURITY_SETTINGS_IPSEC}
  SOCKET_SECURITY_SETTINGS_IPSEC = record
    SecurityProtocol : SOCKET_SECURITY_PROTOCOL;
    SecurityFlags : ULONG;
    IpsecFlags : ULONG;
    AuthipMMPolicyKey : TGUID;
    AuthipQMPolicyKey : TGUID;
    Reserved : TGUID;
    Reserved2 : UINT64;
    UserNameStringLen : ULONG;
    DomainNameStringLen : ULONG;
    PasswordStringLen : ULONG;
  //  wchar_t AllStrings[0];
  end;
  {$EXTERNALSYM PSOCKET_SECURITY_SETTINGS_IPSEC}
  PSOCKET_SECURITY_SETTINGS_IPSEC = ^SOCKET_SECURITY_SETTINGS_IPSEC;
  {$EXTERNALSYM SOCKET_SECURITY_SETTINGS}
  SOCKET_SECURITY_SETTINGS = record
    SecurityProtocol : SOCKET_SECURITY_PROTOCOL;
    SecurityFlags : ULONG;
  end;
  {$EXTERNALSYM PSOCKET_SECURITY_SETTINGS}
  PSOCKET_SECURITY_SETTINGS = ^SOCKET_SECURITY_SETTINGS;
  {$EXTERNALSYM SOCKET_PEER_TARGET_NAME}
  SOCKET_PEER_TARGET_NAME = record
    SecurityProtocol : SOCKET_SECURITY_PROTOCOL;
    PeerAddress : SOCKADDR_STORAGE;
    PeerTargetNameStringLen : ULONG;
    //wchar_t AllStrings[0];
  end;
  {$EXTERNALSYM PSOCKET_PEER_TARGET_NAME}
  PSOCKET_PEER_TARGET_NAME = ^SOCKET_PEER_TARGET_NAME;

  {$EXTERNALSYM SOCKET_SECURITY_QUERY_INFO}
  SOCKET_SECURITY_QUERY_INFO = record
     SecurityProtocol : SOCKET_SECURITY_PROTOCOL;
     Flags : ULONG;
     PeerApplicationAccessTokenHandle : UINT64;
     PeerMachineAccessTokenHandle : UINT64;
  end;
  {$EXTERNALSYM PSOCKET_SECURITY_QUERY_INFO}
  PSOCKET_SECURITY_QUERY_INFO = ^SOCKET_SECURITY_QUERY_INFO;
  {$EXTERNALSYM SOCKET_SECURITY_QUERY_TEMPLATE}
  SOCKET_SECURITY_QUERY_TEMPLATE = record
    SecurityProtocol : SOCKET_SECURITY_PROTOCOL;
    PeerAddress : SOCKADDR_STORAGE;
    PeerTokenAccessMask : ULONG;
  end;
  {$EXTERNALSYM PSOCKET_SECURITY_QUERY_TEMPLATE}
  PSOCKET_SECURITY_QUERY_TEMPLATE = ^SOCKET_SECURITY_QUERY_TEMPLATE;

//callback defs
type
  {$EXTERNALSYM LPLOOKUPSERVICE_COMPLETION_ROUTINE}
  LPLOOKUPSERVICE_COMPLETION_ROUTINE = procedure (const dwError, dwBytes : DWORD; lpOverlapped : LPWSAOVERLAPPED); stdcall;
{$ENDIF}

type
  {$EXTERNALSYM LPFN_GETADDRINFO}
  LPFN_GETADDRINFO = function(NodeName: PAnsiChar; ServiceName: PAnsiChar; Hints: Paddrinfo; ppResult: PPaddrinfo): Integer; stdcall;
  {$EXTERNALSYM LPFN_GETADDRINFOW}
  LPFN_GETADDRINFOW = function(NodeName: PWideChar; ServiceName: PWideChar; Hints: PaddrinfoW; ppResult: PPaddrinfoW): Integer; stdcall;
  {$EXTERNALSYM LPFN_GETNAMEINFO}
  //The IPv6 preview for Win2K defines hostlen and servelen as size_t but do not use them
  //for these definitions as the newer SDK's define those as DWORD.
  LPFN_GETNAMEINFO = function(sa: psockaddr; salen: u_int; host: PAnsiChar; hostlen: u_int; serv: PAnsiChar; servlen: u_int; flags: Integer): Integer; stdcall;
  {$EXTERNALSYM LPFN_GETNAMEINFOW}
  LPFN_GETNAMEINFOW = function(sa: psockaddr; salen: u_int; host: PWideChar; hostlen: u_int; serv: PWideChar; servlen: u_int; flags: Integer): Integer; stdcall;
  {$EXTERNALSYM LPFN_FREEADDRINFO}
  LPFN_FREEADDRINFO = procedure(ai: Paddrinfo); stdcall;
  {$EXTERNALSYM LPFN_FREEADDRINFOW}
  LPFN_FREEADDRINFOW = procedure(ai: PaddrinfoW); stdcall;

//function GetAdaptersAddresses( Family:ULONG; Flags:ULONG; Reserved:Pointer; pAdapterAddresses: PIP_ADAPTER_ADDRESSES; pOutBufLen:PULONG):ULONG;stdcall;  external iphlpapi_dll;

{ the following are not used, nor tested}
{function getipnodebyaddr(const src:pointer;  len:integer; af:integer;var error_num:integer) :phostent;stdcall; external Wship6_dll;
procedure freehostent(ptr:phostent);stdcall; external Wship6_dll;
function inet_pton(af:integer; const src:pchar; dst:pointer):integer;stdcall; external Wship6_dll;
function inet_ntop(af:integer; const src:pointer; dst:pchar;size:integer):pchar;stdcall; external Wship6_dll;
}
 {$IFNDEF WINCE}
  {$EXTERNALSYM LPFN_INET_PTON}
  LPFN_INET_PTON = function (af: Integer; const src: PAnsiChar; dst: Pointer): Integer; stdcall;
  {$EXTERNALSYM LPFN_INET_PTONW}
  LPFN_INET_PTONW = function (af: Integer; const src: PWideChar; dst: Pointer): Integer; stdcall;
  {$EXTERNALSYM LPFN_INET_NTOP}
  LPFN_INET_NTOP = function (af: Integer; const src: Pointer; dst: PAnsiChar; size: size_t): PAnsiChar; stdcall;
  {$EXTERNALSYM LPFN_INET_NTOPW}
  LPFN_INET_NTOPW = function (af: Integer; const src: Pointer; dst: PWideChar; size: size_t): PAnsiChar; stdcall;

{ end the following are not used, nor tested}
//These are provided in case we need them later
//Windows Vista
  {$EXTERNALSYM LPFN_GETADDRINFOEXA}
  LPFN_GETADDRINFOEXA = function(pName : PAnsiChar; pServiceName : PAnsiChar;
    const dwNameSpace: DWord; lpNspId : LPGUID; hints : PADDRINFOEXA;
    var ppResult : PADDRINFOEXA; timeout : Ptimeval; lpOverlapped : LPWSAOVERLAPPED;
    lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE;
    lpNameHandle : PHandle) : Integer; stdcall;
  {$EXTERNALSYM LPFN_GETADDRINFOEXW}
  LPFN_GETADDRINFOEXW = function(pName : PWideChar; pServiceName : PWideChar;
    const dwNameSpace: DWord; lpNspId : LPGUID;hints : PADDRINFOEXW;
    var ppResult : PADDRINFOEXW; timeout : Ptimeval; lpOverlapped : LPWSAOVERLAPPED;
    lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE;
    lpNameHandle : PHandle) : Integer; stdcall;
  {$EXTERNALSYM LPFN_SETADDRINFOEXA}
  LPFN_SETADDRINFOEXA= function(pName : PAnsiChar; pServiceName : PAnsiChar;
    pAddresses : PSOCKET_ADDRESS; const dwAddressCount : DWord; lpBlob : LPBLOB;
    const dwFlags : DWord; const dwNameSpace : DWord; lpNspId : LPGUID;
    timeout : Ptimeval;
    lpOverlapped : LPWSAOVERLAPPED;
    lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE; lpNameHandle : PHandle) : Integer; stdcall;
  {$EXTERNALSYM LPFN_SETADDRINFOEXW}
  LPFN_SETADDRINFOEXW= function(pName : PWideChar; pServiceName : PWideChar;
    pAddresses : PSOCKET_ADDRESS; const dwAddressCount : DWord; lpBlob : LPBLOB;
    const dwFlags : DWord; const dwNameSpace : DWord; lpNspId : LPGUID;
    timeout : Ptimeval;
    lpOverlapped : LPWSAOVERLAPPED;
    lpCompletionRoutine : LPLOOKUPSERVICE_COMPLETION_ROUTINE; lpNameHandle : PHandle) : Integer; stdcall;

  {$EXTERNALSYM LPFN_FREEADDRINFOEX}
  LPFN_FREEADDRINFOEX = procedure(pAddrInfoEx : PADDRINFOEXA) ; stdcall;
  {$EXTERNALSYM LPFN_FREEADDRINFOEXW}
  LPFN_FREEADDRINFOEXW = procedure(pAddrInfoEx : PADDRINFOEXW) ; stdcall;

  {$EXTERNALSYM LPFN_GETADDRINFOEX}
  {$EXTERNALSYM LPFN_SETADDRINFOEX}
  {$IFDEF UNICODE}
  LPFN_GETADDRINFOEX = LPFN_GETADDRINFOEXW;
  LPFN_SETADDRINFOEX = LPFN_SETADDRINFOEXW;
  {$ELSE}
  LPFN_GETADDRINFOEX = LPFN_GETADDRINFOEXA;
  LPFN_SETADDRINFOEX = LPFN_SETADDRINFOEXA;
  {$ENDIF}

  //  Fwpuclnt.dll - API
  {$EXTERNALSYM LPFN_WSASetSocketSecurity}
  LPFN_WSASetSocketSecurity = function (socket : TSocket;
    SecuritySettings : PSOCKET_SECURITY_SETTINGS; const SecuritySettingsLen : ULONG;
    OVERLAPPED : LPWSAOVERLAPPED; CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall;
  {$EXTERNALSYM LPFN_WSADELETESOCKETPEERTARGETNAME}
  LPFN_WSADELETESOCKETPEERTARGETNAME = function (Socket : TSocket;
    PeerAddr : Psockaddr; PeerAddrLen : ULONG;
    Overlapped : LPWSAOVERLAPPED;  CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE): Integer; stdcall;
  {$EXTERNALSYM LPFN_WSASETSOCKETPEERTARGETNAME}
  LPFN_WSASETSOCKETPEERTARGETNAME = function (Socket : TSocket;
    PeerTargetName : PSOCKET_PEER_TARGET_NAME; PeerTargetNameLen : ULONG;
    Overlapped : LPWSAOVERLAPPED; CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall;
  {$EXTERNALSYM LPFN_WSAIMPERSONATESOCKETPEER}
  LPFN_WSAIMPERSONATESOCKETPEER = function (Socket : TSocket;
    PeerAddress : Psockaddr;  peerAddressLen :  ULONG) : Integer; stdcall;
  {$EXTERNALSYM LPFN_WSAQUERYSOCKETSECURITY}
  LPFN_WSAQUERYSOCKETSECURITY = function (Socket : TSocket;
    SecurityQueryTemplate : PSOCKET_SECURITY_QUERY_TEMPLATE; const SecurityQueryTemplateLen : ULONG;
    SecurityQueryInfo : PSOCKET_SECURITY_QUERY_INFO; var SecurityQueryInfoLen : ULONG;
     Overlapped : LPWSAOVERLAPPED;  CompletionRoutine : LPWSAOVERLAPPED_COMPLETION_ROUTINE) : Integer; stdcall;
  {$EXTERNALSYM LPFN_WSAREVERTIMPERSONATION}
  LPFN_WSAREVERTIMPERSONATION = function : Integer; stdcall;
{$ENDIF}

const
  {$NODEFINE fn_GetAddrInfo}
  {$NODEFINE fn_getnameinfo}
  {$NODEFINE fn_freeaddrinfo}
  {$IFNDEF WINCE}
  {$NODEFINE fn_GetAddrInfoEx}
  {$NODEFINE fn_SetAddrInfoEx}
  {$NODEFINE fn_FreeAddrInfoEx}
  {$NODEFINE fn_inet_pton}
  {$NODEFINE fn_inet_ntop}
  {$ENDIF}
  {$IFDEF UNICODE}
  // WinCE does not support GetAddrInfoW(), GetNameInfoW(), or FreeAddrInfoW().
  // To support IPv6 on WinCE when UNICODE is defined, we will use our own
  // wrappers that internally call WinCE's functions...
  fn_GetAddrInfo = {$IFDEF WINCE}'getaddrinfo'{$ELSE}'GetAddrInfoW'{$ENDIF};
  fn_getnameinfo = {$IFDEF WINCE}'getnameinfo'{$ELSE}'GetNameInfoW'{$ENDIF};
  fn_freeaddrinfo = {$IFDEF WINCE}'freeaddrinfo'{$ELSE}'FreeAddrInfoW'{$ENDIF};
    {$IFNDEF WINCE}
  fn_GetAddrInfoEx = 'GetAddrInfoExW';
  fn_SetAddrInfoEx = 'SetAddrInfoExW';
  fn_FreeAddrInfoEx = 'FreeAddrInfoExW';
  fn_inet_pton = 'InetPtonW';
  fn_inet_ntop = 'InetNtopW';
    {$ENDIF}
  {$ELSE}
  fn_GetAddrInfo = 'getaddrinfo';
  fn_getnameinfo = 'getnameinfo';
  fn_freeaddrinfo = 'freeaddrinfo';
    {$IFNDEF WINCE}
  fn_GetAddrInfoEx = 'GetAddrInfoExA';
  fn_SetAddrInfoEx = 'SetAddrInfoExA';
  fn_FreeAddrInfoEx = 'FreeAddrInfoEx';
  fn_inet_pton = 'inet_pton';
  fn_inet_ntop = 'inet_ntop';
    {$ENDIF}
  {$ENDIF}

{$UNDEF WINCE_UNICODE}
{$IFDEF WINCE}
  {$IFDEF UNICODE}
    {$DEFINE WINCE_UNICODE}
  {$ENDIF}
{$ENDIF}

var
  {$EXTERNALSYM getaddrinfo}
  {$EXTERNALSYM getnameinfo}
  {$EXTERNALSYM freeaddrinfo}
  {$IFNDEF WINCE}
  {$EXTERNALSYM inet_pton}
  {$EXTERNALSYM inet_ntop}
  {$ENDIF}
  {$IFDEF UNICODE}
    {$IFDEF WINCE}
  getaddrinfoCE: LPFN_GETADDRINFO = nil;
  getnameinfoCE: LPFN_GETNAMEINFO = nil;
  freeaddrinfoCE: LPFN_FREEADDRINFO = nil;
    {$ENDIF}
  getaddrinfo: LPFN_GETADDRINFOW = nil;
  getnameinfo: LPFN_GETNAMEINFOW = nil;
  freeaddrinfo: LPFN_FREEADDRINFOW = nil;
    {$IFNDEF WINCE}
  //These are here for completeness
  inet_pton : LPFN_inet_ptonW = nil;
  inet_ntop : LPFN_inet_ntopW = nil;
    {$ENDIF}
  {$ELSE}
  getaddrinfo: LPFN_GETADDRINFO = nil;
  getnameinfo: LPFN_GETNAMEINFO = nil;
  freeaddrinfo: LPFN_FREEADDRINFO = nil;
      {$IFNDEF WINCE}
  //These are here for completeness
  inet_pton : LPFN_inet_pton = nil;
  inet_ntop : LPFN_inet_ntop = nil;
    {$ENDIF}
  {$ENDIF}
  {$IFNDEF WINCE}
  {
  IMPORTANT!!!

  These are Windows Vista functions and there's no guarantee that you will have
  them so ALWAYS check the function pointer before calling them.
  }
  {$EXTERNALSYM GetAddrInfoEx}
  GetAddrInfoEx :  LPFN_GETADDRINFOEX = nil;
  {$EXTERNALSYM SetAddrInfoEx}
  SetAddrInfoEx : LPFN_SETADDRINFOEX = nil;
  {$EXTERNALSYM FreeAddrInfoEx}
  //You can't alias the LPFN for this because the ASCII version of this
  //does not end with an "a"
  {$IFDEF UNICODE}
  FreeAddrInfoEx : LPFN_FREEADDRINFOEXW = nil;
  {$ELSE}
  FreeAddrInfoEx : LPFN_FREEADDRINFOEX = nil;
  {$ENDIF}

  //Fwpuclnt.dll available for Windows Vista and later
  {$EXTERNALSYM WSASetSocketSecurity}
  WSASetSocketSecurity : LPFN_WSASetSocketSecurity = nil;
  {$EXTERNALSYM WSASETSOCKETPEERTARGETNAME}
  WSASetSocketPeerTargetName : LPFN_WSASETSOCKETPEERTARGETNAME = nil;
  {$EXTERNALSYM WSADELETESOCKETPEERTARGETNAME}
  WSADeleteSocketPeerTargetName : LPFN_WSADELETESOCKETPEERTARGETNAME = nil;
  {$EXTERNALSYM WSAImpersonateSocketPeer}
  WSAImpersonateSocketPeer : LPFN_WSAIMPERSONATESOCKETPEER = nil;
  {$EXTERNALSYM WSAQUERYSOCKETSECURITY}
  WSAQUERYSOCKETSECURITY : LPFN_WSAQUERYSOCKETSECURITY = nil;
  {$EXTERNALSYM WSAREVERTIMPERSONATION}
  WSARevertImpersonation : LPFN_WSAREVERTIMPERSONATION = nil;
  {$ENDIF}

var
  GIdIPv6FuncsAvailable: Boolean = False {$IFDEF HAS_DEPRECATED}deprecated{$ENDIF};

function gaiErrorToWsaError(const gaiError: Integer): Integer;

//We want to load this library only after loading Winsock and unload immediately
//before unloading Winsock.
procedure InitLibrary;
procedure CloseLibrary;

implementation

uses
  SysUtils
  {$IFDEF HAS_SIZE_T}
  , IdGlobal
  {$ENDIF};

var
  hWship6Dll : THandle = 0; // Wship6.dll handle
  //Use this instead of hWship6Dll because this will point to the correct lib.
  hProcHandle : THandle = 0;
  {$IFNDEF WINCE}
  hfwpuclntDll : THandle = 0;
  {$ENDIF}

function gaiErrorToWsaError(const gaiError: Integer): Integer;
begin
  case gaiError of
    GIA_EAI_ADDRFAMILY: Result := 0; // TODO: find a decent error for here
    GIA_EAI_AGAIN:      Result := WSATRY_AGAIN;
    GIA_EAI_BADFLAGS:   Result := WSAEINVAL;
    GIA_EAI_FAIL:       Result := WSANO_RECOVERY;
    GIA_EAI_FAMILY:     Result := WSAEAFNOSUPPORT;
    GIA_EAI_MEMORY:     Result := WSA_NOT_ENOUGH_MEMORY;
    GIA_EAI_NODATA:     Result := WSANO_DATA;
    GIA_EAI_NONAME:     Result := WSAHOST_NOT_FOUND;
    GIA_EAI_SERVICE:    Result := WSATYPE_NOT_FOUND;
    GIA_EAI_SOCKTYPE:   Result := WSAESOCKTNOSUPPORT;
    GIA_EAI_SYSTEM:
      begin
        Result := 0; // avoid warning
        IndyRaiseLastError;
      end;
    else
      Result := gaiError;
  end;
end;

procedure CloseLibrary;
var
  h : THandle;
begin
  h := InterlockedExchangeTHandle(hWship6Dll, 0);
  if h <> 0 then begin
    FreeLibrary(h);
  end;
  {$IFNDEF WINCE}
  h := InterlockedExchangeTHandle(hfwpuclntDll, 0);
  if h <> 0 then begin
    FreeLibrary(h);
  end;
  {$ENDIF}

  {$I IdSymbolDeprecatedOff.inc}
  GIdIPv6FuncsAvailable := False;
  {$I IdSymbolDeprecatedOn.inc}

  {$IFDEF WINCE_UNICODE}
  getaddrinfoCE := nil;
  getnameinfoCE := nil;
  freeaddrinfoCE := nil;
  {$ENDIF}
  getaddrinfo := nil;
  getnameinfo := nil;
  freeaddrinfo := nil;
  {$IFNDEF WINCE}
  inet_pton := nil;
  inet_ntop := nil;
  GetAddrInfoEx := nil;
  SetAddrInfoEx := nil;
  FreeAddrInfoEx := nil;
  WSASetSocketPeerTargetName := nil;
  WSADeleteSocketPeerTargetName := nil;
  WSAImpersonateSocketPeer := nil;
  WSAQuerySocketSecurity := nil;
  WSARevertImpersonation := nil;
  {$ENDIF}
end;

{$IFDEF FPC} //{$IFDEF STRING_IS_ANSI}
  {$IFDEF UNICODE}

// FreePascal does not have PWideChar overloads of these functions

function StrComp(const Str1, Str2: PWideChar): Integer; overload;
var
  P1, P2: PWideChar;
begin
  P1 := Str1;
  P2 := Str2;
  while True do
  begin
    if (P1^ <> P2^) or (P1^ = #0) then
    begin
      Result := Ord(P1^) - Ord(P2^);
      Exit;
    end;
    Inc(P1);
    Inc(P2);
  end;
  Result := 0;
end;

function StrScan(const Str: PWideChar; Chr: WideChar): PWideChar; overload;
begin
  Result := Str;
  while Result^ <> #0 do
  begin
    if Result^ = Chr then begin
      Exit;
    end;
    Inc(Result);
  end;
  if Chr <> #0 then begin
    Result := nil;
  end;
end;

  {$ENDIF}
{$ENDIF}

// The IPv6 functions were added to the Ws2_32.dll on Windows XP and later.
// To execute an application that uses these functions on earlier versions of
// Windows, the functions are defined as inline functions in the Wspiapi.h file.
// At runtime, the functions are implemented in such a way that if the Ws2_32.dll
// or the Wship6.dll (the file containing the functions in the IPv6 Technology
// Preview for Windows 2000) does not include them, then versions are implemented
// inline based on code in the Wspiapi.h header file. This inline code will be
// used on older Windows platforms that do not natively support the functions.

// RLebeau: Wspiapi.h only defines Ansi versions of the legacy functions, but we
// need to handle Unicode as well...

function WspiapiMalloc(tSize: size_t): Pointer;
begin
  try
    GetMem(Result, tSize);
    ZeroMemory(Result, tSize);
  except
    Result := nil;
  end;
end;

procedure WspiapiFree(p: Pointer);
begin
  FreeMem(p);
end;

procedure WspiapiSwap(var a, b, c: PIdPlatformChar);
  {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
  c := a;
  a := b;
  b := c;
end;

function WspiapiStrdup(const pszString: PIdPlatformChar): PIdPlatformChar; stdcall;
var
  pszMemory: PIdPlatformChar;
  cchMemory: size_t;
begin
  if pszString = nil then begin
    Result := nil;
    Exit;
  end;

  cchMemory := StrLen(pszString) + 1;
  pszMemory := PIdPlatformChar(WspiapiMalloc(cchMemory * SizeOf(TIdPlatformChar)));
  if pszMemory = nil then begin
    Result := nil;
    Exit;
  end;

  StrLCopy(pszMemory, pszString, cchMemory);
  Result := pszMemory;
end;

function WspiapiParseV4Address(const pszAddress: PIdPlatformChar; var pdwAddress: DWORD): BOOL; stdcall;
var
  dwAddress: DWORD;
  pcNext: PIdPlatformChar;
  iCount: Integer;
begin
  iCount := 0;

  // ensure there are 3 '.' (periods)
  pcNext := pszAddress;
  while pcNext^ <> TIdPlatformChar(0) do begin
    if pcNext^ = '.' then begin
      Inc(iCount);
    end;
    Inc(pcNext);
  end;
  if iCount <> 3 then begin
    Result := FALSE;
    Exit;
  end;

  // return an error if dwAddress is INADDR_NONE (255.255.255.255)
  // since this is never a valid argument to getaddrinfo.
  dwAddress := inet_addr(
    {$IFNDEF UNICODE}
    pszAddress
    {$ELSE}
    PAnsiChar(AnsiString(pszAddress))
    {$ENDIF}
  );
  if dwAddress = INADDR_NONE then begin
    Result := FALSE;
    Exit;
  end;

  pdwAddress := dwAddress;
  Result := TRUE;
end;

function WspiapiNewAddrInfo(iSocketType, iProtocol: Integer; wPort: WORD; dwAddress: DWORD): {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF}; stdcall;
var
  ptNew: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF};
  ptAddress: PSockAddrIn;
begin
  // allocate a new addrinfo structure.
  {$IFDEF UNICODE}
  ptNew := PaddrinfoW(WspiapiMalloc(SizeOf(addrinfoW)));
  {$ELSE}
  ptNew := Paddrinfo(WspiapiMalloc(SizeOf(addrinfo)));
  {$ENDIF}
  if ptNew = nil then begin
    Result := nil;
    Exit;
  end;

  ptAddress := PSockAddrIn(WspiapiMalloc(SizeOf(sockaddr_in)));
  if ptAddress = nil then begin
    WspiapiFree(ptNew);
    Result := nil;
    Exit;
  end;
  ptAddress^.sin_family       := AF_INET;
  ptAddress^.sin_port         := wPort;
  ptAddress^.sin_addr.s_addr  := dwAddress;

  // fill in the fields...
  ptNew^.ai_family            := PF_INET;
  ptNew^.ai_socktype          := iSocketType;
  ptNew^.ai_protocol          := iProtocol;
  ptNew^.ai_addrlen           := SizeOf(sockaddr_in);
  ptNew^.ai_addr              := Psockaddr(ptAddress);

  Result := ptNew;
end;

function WspiapiQueryDNS(const pszNodeName: PIdPlatformChar; iSocketType, iProtocol: Integer;
  wPort: WORD; pszAlias: PIdPlatformChar; var pptResult: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF}): Integer; stdcall;
var
  pptNext: {$IFDEF UNICODE}PPaddrinfoW{$ELSE}PPaddrinfo{$ENDIF};
  ptHost: Phostent;
  ppAddresses: ^PInAddr;
begin
  pptNext := @pptResult;

  pptNext^ := nil;
  pszAlias^ := TIdPlatformChar(0);

  ptHost := gethostbyname(
    {$IFNDEF UNICODE}
    pszNodeName
    {$ELSE}
    PAnsiChar(AnsiString(pszNodeName))
    {$ENDIF}
  );
  if ptHost <> nil then begin
    if (ptHost^.h_addrtype = AF_INET) and (ptHost^.h_length = SizeOf(in_addr)) then begin
      ppAddresses := Pointer(ptHost^.h_address_list);
      while ppAddresses^ <> nil do begin
        // create an addrinfo structure...
        pptNext^ := WspiapiNewAddrInfo(iSocketType, iProtocol, wPort, ppAddresses^^.s_addr);
        if pptNext^ = nil then begin
          Result := EAI_MEMORY;
          Exit;
        end;

        pptNext := @((pptNext^)^.ai_next);
        Inc(ppAddresses);
      end;
    end;

    // pick up the canonical name.
    StrLCopy(pszAlias,
      {$IFNDEF UNICODE}
      ptHost^.h_name
      {$ELSE}
      PIdPlatformChar(TIdPlatformString(ptHost^.h_name))
      {$ENDIF}
      , NI_MAXHOST);

    Result := 0;
    Exit;
  end;

  case WSAGetLastError() of
    WSAHOST_NOT_FOUND: Result := EAI_NONAME;
    WSATRY_AGAIN:      Result := EAI_AGAIN;
    WSANO_RECOVERY:    Result := EAI_FAIL;
    WSANO_DATA:        Result := EAI_NODATA;
  else
    Result := EAI_NONAME;
  end;
end;

function WspiapiLookupNode(const pszNodeName: PIdPlatformChar; iSocketType: Integer;
  iProtocol: Integer; wPort: WORD; bAI_CANONNAME: BOOL; var pptResult: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF}): Integer; stdcall;
var
  iError: Integer;
  iAliasCount: Integer;
  szFQDN1: array[0..NI_MAXHOST-1] of TIdPlatformChar;
  szFQDN2: array[0..NI_MAXHOST-1] of TIdPlatformChar;
  pszName: PIdPlatformChar;
  pszAlias: PIdPlatformChar;
  pszScratch: PIdPlatformChar;
begin
  iAliasCount := 0;

  ZeroMemory(@szFQDN1, SizeOf(szFQDN1));
  ZeroMemory(@szFQDN2, SizeOf(szFQDN2));
  pszName := @szFQDN1[0];
  pszAlias := @szFQDN2[0];
  pszScratch := nil;
  StrLCopy(pszName, pszNodeName, NI_MAXHOST);

  repeat
    iError := WspiapiQueryDNS(pszNodeName, iSocketType, iProtocol, wPort, pszAlias, pptResult);
    if iError <> 0 then begin
      Break;
    end;

    // if we found addresses, then we are done.
    if pptResult <> nil then begin
      Break;
    end;

    // stop infinite loops due to DNS misconfiguration.  there appears
    // to be no particular recommended limit in RFCs 1034 and 1035.
    if (StrLen(pszAlias) = 0) or (StrComp(pszName, pszAlias) = 0) then begin
      iError := EAI_FAIL;
      Break;
    end;
    Inc(iAliasCount);
    if iAliasCount = 16 then begin
      iError := EAI_FAIL;
      Break;
    end;

    // there was a new CNAME, look again.
    WspiapiSwap(pszName, pszAlias, pszScratch);
  until False;

  if (iError = 0) and bAI_CANONNAME then begin
    pptResult^.ai_canonname := WspiapiStrdup(pszAlias);
    if pptResult^.ai_canonname = nil then begin
      iError := EAI_MEMORY;
    end;
  end;

  Result := iError;
end;

function WspiapiClone(wPort: WORD; ptResult: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF}): Integer; stdcall;
var
  ptNext, ptNew: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF};
begin
  ptNext := ptResult;
  while ptNext <> nil do begin
    // create an addrinfo structure...
    ptNew := WspiapiNewAddrInfo(SOCK_DGRAM, ptNext^.ai_protocol, wPort, PSockAddrIn(ptNext^.ai_addr)^.sin_addr.s_addr);
    if ptNew = nil then begin
      Break;
    end;

    // link the cloned addrinfo
    ptNew^.ai_next  := ptNext^.ai_next;
    ptNext^.ai_next := ptNew;
    ptNext          := ptNew^.ai_next;
  end;

  if ptNext <> nil then begin
    Result := EAI_MEMORY;
    Exit;
  end;

  Result := 0;
end;

procedure WspiapiLegacyFreeAddrInfo(ptHead: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF}); stdcall;
var
 ptNext: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF};
begin
  ptNext := ptHead;
  while ptNext <> nil do
  begin
    if ptNext^.ai_canonname <> nil then begin
      WspiapiFree(ptNext^.ai_canonname);
    end;
    if ptNext^.ai_addr <> nil then begin
      WspiapiFree(ptNext^.ai_addr);
    end;
    ptHead := ptNext^.ai_next;
    WspiapiFree(ptNext);
    ptNext := ptHead;
  end;
end;

{$IFNDEF HAS_TryStrToInt}
// TODO: use the implementation already in IdGlobalProtocols...
function TryStrToInt(const S: string; out Value: Integer): Boolean;
{$IFDEF USE_INLINE}inline;{$ENDIF}
var
  E: Integer;
begin
  Val(S, Value, E);
  Result := E = 0;
end;
{$ENDIF}

function WspiapiLegacyGetAddrInfo(const pszNodeName: PIdPlatformChar; const pszServiceName: PIdPlatformChar;
  const ptHints: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF};
  var pptResult: {$IFDEF UNICODE}PaddrinfoW{$ELSE}Paddrinfo{$ENDIF}): Integer; stdcall;
var
  iError: Integer;
  iFlags: Integer;
  iSocketType: Integer;
  iProtocol: Integer;
  wPort: WORD;
  iTmp: Integer;
  dwAddress: DWORD;
  ptService: Pservent;
  bClone: BOOL;
  wTcpPort: WORD;
  wUdpPort: WORD;
begin
  iError := 0;
  iFlags := 0;
  iSocketType := 0;
  iProtocol := 0;
  wPort := 0;
  dwAddress := 0;
  bClone := FALSE;
  wTcpPort := 0;
  wUdpPort := 0;

  // initialize pptResult with default return value.
  pptResult := nil;

  ////////////////////////////////////////
  // validate arguments...
  //

  // both the node name and the service name can't be NULL.
  if (pszNodeName = nil) and (pszServiceName = nil) then begin
    Result := EAI_NONAME;
    Exit;
  end;

  // validate hints.
  if ptHints <> nil then
  begin
    // all members other than ai_flags, ai_family, ai_socktype
    // and ai_protocol must be zero or a null pointer.
    if (ptHints^.ai_addrlen <> 0) or
       (ptHints^.ai_canonname <> nil) or
       (ptHints^.ai_addr <> nil) or
       (ptHints^.ai_next <> nil) then
    begin
      Result := EAI_FAIL;
      Exit;
    end;

    // the spec has the "bad flags" error code, so presumably we
    // should check something here.  insisting that there aren't
    // any unspecified flags set would break forward compatibility,
    // however.  so we just check for non-sensical combinations.
    //
    // we cannot come up with a canonical name given a null node name.
    iFlags := ptHints^.ai_flags;
    if ((iFlags and AI_CANONNAME) <> 0) and (pszNodeName = nil) then begin
      Result := EAI_BADFLAGS;
      Exit;
    end;

    // we only support a limited number of protocol families.
    if (ptHints^.ai_family <> PF_UNSPEC) and (ptHints^.ai_family <> PF_INET) then begin
      Result := EAI_FAMILY;
      Exit;
    end;

    // we only support only these socket types.
    iSocketType := ptHints^.ai_socktype;
    if (iSocketType <> 0) and
       (iSocketType <> SOCK_STREAM) and
       (iSocketType <> SOCK_DGRAM) and
       (iSocketType <> SOCK_RAW) then
    begin
      Result := EAI_SOCKTYPE;
      Exit;
    end;

    // REVIEW: What if ai_socktype and ai_protocol are at odds?
    iProtocol := ptHints^.ai_protocol;
  end;

  ////////////////////////////////////////
  // do service lookup...

  if pszServiceName <> nil then begin
    if TryStrToInt(pszServiceName, iTmp) and (iTmp >= 0) then begin
      wPort := htons(WORD(iTmp));
      //wTcpPort := wPort; // never used
      wUdpPort := wPort;
      if iSocketType = 0 then begin
        bClone := TRUE;
        iSocketType := SOCK_STREAM;
      end;
    end else
    begin
      if (iSocketType = 0) or (iSocketType = SOCK_DGRAM) then begin
        ptService := getservbyname(
          {$IFNDEF UNICODE}
          pszServiceName
          {$ELSE}
          PAnsiChar(AnsiString(pszServiceName))
          {$ENDIF}
          , 'udp'); {do not localize}
        if ptService <> nil then begin
          wPort := ptService^.s_port;
          wUdpPort := wPort;
        end;
      end;

      if (iSocketType = 0) or (iSocketType = SOCK_STREAM) then begin
        ptService := getservbyname(
          {$IFNDEF UNICODE}
          pszServiceName
          {$ELSE}
          PAnsiChar(AnsiString(pszServiceName))
          {$ENDIF}
          , 'tcp'); {do not localize}
        if ptService <> nil then begin
          wPort := ptService^.s_port;
          wTcpPort := wPort;
        end;
      end;

      // assumes 0 is an invalid service port...
      if wPort = 0 then begin
        Result := iif(iSocketType <> 0, EAI_SERVICE, EAI_NONAME);
        Exit;
      end;

      if iSocketType = 0 then begin
        // if both tcp and udp, process tcp now & clone udp later.
        iSocketType := iif(wTcpPort <> 0, SOCK_STREAM, SOCK_DGRAM);
        bClone := (wTcpPort <> 0) and (wUdpPort <> 0);
      end;
    end;
  end;

  ////////////////////////////////////////
  // do node name lookup...

  // if we weren't given a node name,
  // return the wildcard or loopback address (depending on AI_PASSIVE).
  //
  // if we have a numeric host address string,
  // return the binary address.
  //
  if ((pszNodeName = nil) or WspiapiParseV4Address(pszNodeName, dwAddress)) then begin
    if pszNodeName = nil then begin
      dwAddress := htonl(iif((iFlags and AI_PASSIVE) <> 0, INADDR_ANY, INADDR_LOOPBACK));
    end;

    // create an addrinfo structure...
    pptResult := WspiapiNewAddrInfo(iSocketType, iProtocol, wPort, dwAddress);
    if pptResult = nil then begin
      iError := EAI_MEMORY;
    end;

    if (iError = 0) and (pszNodeName <> nil) then begin
      // implementation specific behavior: set AI_NUMERICHOST
      // to indicate that we got a numeric host address string.
      pptResult^.ai_flags := pptResult^.ai_flags or AI_NUMERICHOST;
      // return the numeric address string as the canonical name
      if (iFlags and AI_CANONNAME) <> 0 then begin
        pptResult^.ai_canonname := WspiapiStrdup(
          {$IFNDEF UNICODE}
          inet_ntoa(PInAddr(@dwAddress)^)
          {$ELSE}
          PWideChar(TIdUnicodeString(inet_ntoa(PInAddr(@dwAddress)^)))
          {$ENDIF}
          );
        if pptResult^.ai_canonname = nil then begin
          iError := EAI_MEMORY;
        end;
      end;
    end;
  end

  // if we do not have a numeric host address string and
  // AI_NUMERICHOST flag is set, return an error!
  else if ((iFlags and AI_NUMERICHOST) <> 0) then begin
    iError := EAI_NONAME;
  end

  // since we have a non-numeric node name,
  // we have to do a regular node name lookup.
  else begin
    iError := WspiapiLookupNode(pszNodeName, iSocketType, iProtocol, wPort, (iFlags and AI_CANONNAME) <> 0, pptResult);
  end;

  if (iError = 0) and bClone then begin
    iError := WspiapiClone(wUdpPort, pptResult);
  end;

  if iError <> 0 then begin
    WspiapiLegacyFreeAddrInfo(pptResult);
    pptResult := nil;
  end;

  Result := iError;
end;

function iif(ATest: Boolean; const ATrue, AFalse: PAnsiChar): PAnsiChar;
  {$IFDEF USE_INLINE}inline;{$ENDIF}
begin
  if ATest then begin
    Result := ATrue;
  end else begin
    Result := AFalse;
  end;
end;

function WspiapiLegacyGetNameInfo(ptSocketAddress: Psockaddr;
  tSocketLength: u_int; pszNodeName: PIdPlatformChar; tNodeLength: size_t;
  pszServiceName: PIdPlatformChar; tServiceLength: size_t; iFlags: Integer): Integer; stdcall;
var
  ptService: Pservent;
  wPort: WORD;
  szBuffer: array[0..5] of TIdPlatformChar;
  pszService: PIdPlatformChar;
  ptHost: Phostent;
  tAddress: in_addr;
  pszNode: PIdPlatformChar;
  pc: PIdPlatformChar;
  {$IFDEF UNICODE}
  tmpService: TIdUnicodeString;
  tmpNode: TIdUnicodeString;
  {$ENDIF}
begin
  StrCopy(szBuffer, '65535');
  pszService := szBuffer;

  // sanity check ptSocketAddress and tSocketLength.
  if (ptSocketAddress = nil) or (tSocketLength < SizeOf(sockaddr)) then begin
    Result := EAI_FAIL;
    Exit;
  end;

  if ptSocketAddress^.sa_family <> AF_INET then begin
    Result := EAI_FAMILY;
    Exit;
  end;

  if tSocketLength < SizeOf(sockaddr_in) then begin
    Result := EAI_FAIL;
    Exit;
  end;

  if (not ((pszNodeName <> nil) and (tNodeLength > 0))) and (not ((pszServiceName <> nil) and (tServiceLength > 0))) then begin
    Result := EAI_NONAME;
    Exit;
  end;

  // the draft has the "bad flags" error code, so presumably we
  // should check something here.  insisting that there aren't
  // any unspecified flags set would break forward compatibility,
  // however.  so we just check for non-sensical combinations.
  if ((iFlags and NI_NUMERICHOST) <> 0) and ((iFlags and NI_NAMEREQD) <> 0) then begin
    Result := EAI_BADFLAGS;
    Exit;
  end;

  // translate the port to a service name (if requested).
  if (pszServiceName <> nil) and (tServiceLength > 0) then begin
    wPort := PSockAddrIn(ptSocketAddress)^.sin_port;

    if (iFlags and NI_NUMERICSERV) <> 0 then begin
      // return numeric form of the address.
      StrPLCopy(szBuffer, IntToStr(ntohs(wPort)), Length(szBuffer));
    end else
    begin
      // return service name corresponding to port.
      ptService := getservbyport(wPort, iif((iFlags and NI_DGRAM) <> 0, 'udp', nil));
      if (ptService <> nil) and (ptService^.s_name <> nil) then begin
        // lookup successful.
        {$IFNDEF UNICODE}
        pszService := ptService^.s_name;
        {$ELSE}
        tmpService := TIdUnicodeString(ptService^.s_name);
        pszService := PWideChar(tmpService);
        {$ENDIF}
      end else begin
        // DRAFT: return numeric form of the port!
        StrPLCopy(szBuffer, IntToStr(ntohs(wPort)), Length(szBuffer));
      end;
    end;

    if tServiceLength > size_t(StrLen(pszService)) then begin
      StrLCopy(pszServiceName, pszService, tServiceLength);
    end else begin
      Result := EAI_FAIL;
      Exit;
    end;
  end;

  // translate the address to a node name (if requested).
  if (pszNodeName <> nil) and (tNodeLength > 0) then begin
    // this is the IPv4-only version, so we have an IPv4 address.
    tAddress := PSockAddrIn(ptSocketAddress)^.sin_addr;

    if (iFlags and NI_NUMERICHOST) <> 0 then begin
      // return numeric form of the address.
      {$IFNDEF UNICODE}
      pszNode := inet_ntoa(tAddress);
      {$ELSE}
      tmpNode := TIdUnicodeString(inet_ntoa(tAddress));
      pszNode := PWideChar(tmpNode);
      {$ENDIF}
    end else
    begin
      // return node name corresponding to address.
      ptHost := gethostbyaddr(PAnsiChar(@tAddress), SizeOf(in_addr), AF_INET);
      if (ptHost <> nil) and (ptHost^.h_name <> nil) then begin
        // DNS lookup successful.
        // stop copying at a "." if NI_NOFQDN is specified.
        {$IFNDEF UNICODE}
        pszNode := ptHost^.h_name;
        {$ELSE}
        tmpNode := TIdUnicodeString(ptHost^.h_name);
        pszNode := PWideChar(tmpNode);
        {$ENDIF}
        if (iFlags and NI_NOFQDN) <> 0 then begin
          pc := StrScan(pszNode, '.');
          if pc <> nil then begin
            pc^ := TIdPlatformChar(0);
          end;
        end;
      end else
      begin
        // DNS lookup failed.  return numeric form of the address.
        if (iFlags and NI_NAMEREQD) <> 0 then begin
          case WSAGetLastError() of
            WSAHOST_NOT_FOUND: Result := EAI_NONAME;
            WSATRY_AGAIN:      Result := EAI_AGAIN;
            WSANO_RECOVERY:    Result := EAI_FAIL;
          else
            Result := EAI_NONAME;
          end;
          Exit;
        end else begin
          {$IFNDEF UNICODE}
          pszNode := inet_ntoa(tAddress);
          {$ELSE}
          tmpNode := TIdUnicodeString(inet_ntoa(tAddress));
          pszNode := PWideChar(tmpNode);
          {$ENDIF}
        end;
      end;
    end;

    if tNodeLength > size_t(StrLen(pszNode)) then begin
      StrLCopy(pszNodeName, pszNode, tNodeLength);
    end else begin
      Result := EAI_FAIL;
      Exit;
    end;
  end;

  Result := 0;
end;

{$IFDEF WINCE_UNICODE}

function IndyStrdupAToW(const pszString: PAnsiChar): PWideChar;
var
  szStr: TIdUnicodeString;
  pszMemory: PWideChar;
  cchMemory: size_t;
begin
  if pszString = nil then begin
    Result := nil;
    Exit;
  end;

  szStr := TIdUnicodeString(pszString);
  cchMemory := Length(szStr) + 1;

  pszMemory := PWideChar(WspiapiMalloc(cchMemory * SizeOf(WideChar)));
  if pszMemory = nil then begin
    Result := nil;
    Exit;
  end;

  StrLCopy(pszMemory, PWideChar(szStr), cchMemory);
  Result := pszMemory;
end;

procedure IndyFreeAddrInfoW(ptHead: PaddrinfoW); stdcall;
var
 ptNext: PaddrinfoW;
begin
  ptNext := ptHead;
  while ptNext <> nil do
  begin
    if ptNext^.ai_canonname <> nil then begin
      WspiapiFree(ptNext^.ai_canonname);
    end;
    if ptNext^.ai_addr <> nil then begin
      WspiapiFree(ptNext^.ai_addr);
    end;
    ptHead := ptNext^.ai_next;
    WspiapiFree(ptNext);
    ptNext := ptHead;
  end;
end;

function IndyAddrInfoConvert(AddrInfo: Paddrinfo): PaddrinfoW;
var
  ptNew: PaddrinfoW;
  ptAddress: Pointer;
begin
  Result := nil;

  if AddrInfo = nil then begin
    Exit;
  end;

  // allocate a new addrinfo structure.
  ptNew := PaddrinfoW(WspiapiMalloc(SizeOf(addrinfoW)));
  if ptNew = nil then begin
    WspiapiFree(ptNew);
    Exit;
  end;

  ptAddress := WspiapiMalloc(AddrInfo^.ai_addrlen);
  if ptAddress = nil then begin
    WspiapiFree(ptNew);
    Exit;
  end;
  Move(AddrInfo^.ai_addr^, ptAddress^, AddrInfo^.ai_addrlen);

  // fill in the fields...
  ptNew^.ai_flags             := AddrInfo^.ai_flags;
  ptNew^.ai_family            := AddrInfo^.ai_family;
  ptNew^.ai_socktype          := AddrInfo^.ai_socktype;
  ptNew^.ai_protocol          := AddrInfo^.ai_protocol;
  ptNew^.ai_addrlen           := AddrInfo^.ai_addrlen;
  ptNew^.ai_canonname         := nil;
  ptNew^.ai_addr              := Psockaddr(ptAddress);
  ptNew^.ai_next              := nil;

  if AddrInfo^.ai_canonname <> nil then begin
    ptNew^.ai_canonname := IndyStrdupAToW(AddrInfo^.ai_canonname);
    if ptNew^.ai_canonname = nil then begin
      IndyFreeAddrInfoW(ptNew);
      Exit;
    end;
  end;

  if AddrInfo^.ai_next <> nil then begin
    ptNew^.ai_next := IndyAddrInfoConvert(AddrInfo^.ai_next);
    if ptNew^.ai_next = nil then begin
      IndyFreeAddrInfoW(ptNew);
      Exit;
    end;
  end;

  Result := ptNew;
end;

function IndyGetAddrInfoW(const pszNodeName: PWideChar; const pszServiceName: PWideChar;
  const ptHints: PaddrinfoW; var pptResult: PaddrinfoW): Integer; stdcall;
var
  LNodeName: AnsiString;
  LPNodeName: PAnsiChar;
  LServiceName: AnsiString;
  LPServiceName: PAnsiChar;
  LHints: addrinfo;
  LPHints: Paddrinfo;
  LResult: Paddrinfo;
begin
  // initialize pptResult with default return value.
  pptResult := nil;

  if pszNodeName <> nil then begin
    LNodeName := AnsiString(pszNodeName);
    LPNodeName := PAnsiChar(LNodeName);
  end else begin
    LPNodeName := nil;
  end;

  if pszServiceName <> nil then begin
    LServiceName := AnsiString(pszServiceName);
    LPServiceName := PAnsiChar(LServiceName);
  end else begin
    LPServiceName := nil;
  end;

  if ptHints <> nil then begin
    ZeroMemory(@LHints, SizeOf(LHints));
    LHints.ai_flags     := ptHints^.ai_flags;
    LHints.ai_family    := ptHints^.ai_family;
    LHints.ai_socktype  := ptHints^.ai_socktype;
    LHints.ai_protocol  := ptHints^.ai_protocol;
    LPHints := @LHints;
  end else begin
    LPHints := nil;
  end;

  Result := getaddrinfoCE(LPNodeName, LPServiceName, LPHints, @LResult);
  if Result = 0 then begin
    try
      pptResult := IndyAddrInfoConvert(LResult);
    finally
      freeaddrinfoCE(LResult);
    end;
    if pptResult = nil then begin
      Result := EAI_MEMORY;
    end;
  end;
end;

function IndyGetNameInfoW(ptSocketAddress: Psockaddr; tSocketLength: u_int;
  pszNodeName: PWideChar; tNodeLength: size_t; pszServiceName: PWideChar;
  tServiceLength: size_t; iFlags: Integer): Integer; stdcall;
var
  LHost: array[0..NI_MAXHOST-1] of AnsiChar;
  LPHost: PAnsiChar;
  LHostLen: u_int;
  LServ: array[0..NI_MAXSERV-1] of AnsiChar;
  LPServ: PAnsiChar;
  LServLen: u_int;
begin
  if pszNodeName <> nil then
  begin
    LPHost := @LHost[0];
    LHostLen := Length(LHost);
  end else begin
    LPHost := nil;
    LHostLen := 0;
  end;

  if pszServiceName <> nil then
  begin
    LPServ := @LServ[0];
    LServLen := Length(LServ);
  end else begin
    LPServ := nil;
    LServLen := 0;
  end;

  Result := getnameinfoCE(ptSocketAddress, tSocketLength, LPHost, LHostLen, LPServ, LServLen, iFlags);
  if Result = 0 then begin
    if pszNodeName <> nil then begin
      StrPLCopy(pszNodeName, TIdUnicodeString(LPHost), tNodeLength);
    end;
    if pszServiceName <> nil then begin
      StrPLCopy(pszServiceName, TIdUnicodeString(LPServ), tServiceLength);
    end;
  end;
end;

{$ENDIF}

procedure InitLibrary;
var
  {$IFDEF WINCE_UNICODE}
  gai: LPFN_GETADDRINFO;
  gni: LPFN_GETNAMEINFO;
  fai: LPFN_FREEADDRINFO;
  {$ELSE}
  gai: {$IFDEF UNICODE}LPFN_GETADDRINFOW{$ELSE}LPFN_GETADDRINFO{$ENDIF};
  gni: {$IFDEF UNICODE}LPFN_GETNAMEINFOW{$ELSE}LPFN_GETNAMEINFO{$ENDIF};
  fai: {$IFDEF UNICODE}LPFN_FREEADDRINFOW{$ELSE}LPFN_FREEADDRINFO{$ENDIF};
  {$ENDIF}
begin
{
IMPORTANT!!!

I am doing things this way because the functions we want are probably in
the Winsock2 dll.  If they are not there, only then do you actually want
to try the Wship6.dll.   I know it's a mess but I found that the functions
may not load if they aren't in Wship6.dll (and they aren't there in some
versions of Windows).

hProcHandle provides a transparant way of managing the two possible library
locations.  hWship6Dll is kept so we can unload the Wship6.dll if necessary.
}
  //Winsock2 has to be loaded by IdWinsock first.
  if not IdWinsock2.Winsock2Loaded then
  begin
    IdWinsock2.InitializeWinSock;
  end;
  hProcHandle := IdWinsock2.WinsockHandle;

  gai := GetProcAddress(hProcHandle, fn_getaddrinfo);
  if not Assigned(gai) then
  begin
    hWship6Dll := SafeLoadLibrary(Wship6_dll);
    hProcHandle := hWship6Dll;
    gai := GetProcAddress(hProcHandle, fn_getaddrinfo);  {do not localize}
  end;

  if Assigned(gai) then
  begin
    gni := GetProcAddress(hProcHandle, fn_getnameinfo);  {do not localize}
    if Assigned(gni) then
    begin
      fai := GetProcAddress(hProcHandle, fn_freeaddrinfo);  {do not localize}
      if Assigned(fai) then
      begin
        {$IFDEF WINCE_UNICODE}
        getaddrinfoCE := gai;
        getnameinfoCE := gni;
        freeaddrinfoCE := fai;
        getaddrinfo := @IndyGetAddrInfoW;
        getnameinfo := @IndyGetNameInfoW;
        freeaddrinfo := @IndyFreeAddrInfoW;
        {$ELSE}
        getaddrinfo := gai;
        getnameinfo := gni;
        freeaddrinfo := fai;
        {$ENDIF}

        //Additional functions should be initialized here.
        {$IFNDEF WINCE}
        inet_pton := GetProcAddress(hProcHandle, fn_inet_pton);  {do not localize}
        inet_ntop := GetProcAddress(hProcHandle, fn_inet_ntop);  {do not localize}
        GetAddrInfoEx := GetProcAddress(hProcHandle, fn_GetAddrInfoEx); {Do not localize}
        SetAddrInfoEx := GetProcAddress(hProcHandle, fn_SetAddrInfoEx); {Do not localize}
        FreeAddrInfoEx := GetProcAddress(hProcHandle, fn_FreeAddrInfoEx); {Do not localize}
        hfwpuclntDll := SafeLoadLibrary(fwpuclnt_dll);
        if hfwpuclntDll <> 0 then
        begin
          WSASetSocketSecurity := GetProcAddress(hfwpuclntDll,
             'WSASetSocketSecurity');
          WSAQuerySocketSecurity := GetProcAddress(hfwpuclntDll, 'WSAQuerySocketSecurity'); {Do not localize}
          WSASetSocketPeerTargetName := GetProcAddress(hfwpuclntDll, 'WSASetSocketPeerTargetName'); {Do not localize}
          WSADeleteSocketPeerTargetName := GetProcAddress(hfwpuclntDll, 'WSADeleteSocketPeerTargetName');  {Do not localize}
          WSAImpersonateSocketPeer := GetProcAddress(hfwpuclntDll, 'WSAImpersonateSocketPeer'); {Do not localize}

          WSARevertImpersonation := GetProcAddress(hfwpuclntDll, 'WSARevertImpersonation'); {Do not localize}
        end;
        {$ENDIF}

        Exit;
      end;
    end;
  end;

  CloseLibrary;

  getaddrinfo := Addr(WspiapiLegacyGetAddrInfo);
  getnameinfo := Addr(WspiapiLegacyGetNameInfo);
  freeaddrinfo := Addr(WspiapiLegacyFreeAddrInfo);

  {$I IdSymbolDeprecatedOff.inc}
  GIdIPv6FuncsAvailable := True;
  {$I IdSymbolDeprecatedOn.inc}
end;

initialization
finalization
  CloseLibrary;

end.
