restemplate/indy/System/IdStackWindows.pas

2407 lines
72 KiB
Plaintext

{
$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.8 10/26/2004 8:20:04 PM JPMugaas
Fixed some oversights with conversion. OOPS!!!
Rev 1.7 07/06/2004 21:31:24 CCostelloe
Kylix 3 changes
Rev 1.6 4/18/04 10:43:24 PM RLebeau
Fixed syntax error
Rev 1.5 4/18/04 10:29:58 PM RLebeau
Renamed Int64Parts structure to TIdInt64Parts
Rev 1.4 4/18/04 2:47:46 PM RLebeau
Conversion support for Int64 values
Rev 1.3 2004.03.07 11:45:28 AM czhower
Flushbuffer fix + other minor ones found
Rev 1.2 3/6/2004 5:16:34 PM JPMugaas
Bug 67 fixes. Do not write to const values.
Rev 1.1 3/6/2004 4:23:52 PM JPMugaas
Error #62 fix. This seems to work in my tests.
Rev 1.0 2004.02.03 3:14:48 PM czhower
Move and updates
Rev 1.33 2/1/2004 6:10:56 PM JPMugaas
GetSockOpt.
Rev 1.32 2/1/2004 3:28:36 AM JPMugaas
Changed WSGetLocalAddress to GetLocalAddress and moved into IdStack since
that will work the same in the DotNET as elsewhere. This is required to
reenable IPWatch.
Rev 1.31 1/31/2004 1:12:48 PM JPMugaas
Minor stack changes required as DotNET does support getting all IP addresses
just like the other stacks.
Rev 1.30 12/4/2003 3:14:52 PM BGooijen
Added HostByAddress
Rev 1.29 1/3/2004 12:38:56 AM BGooijen
Added function SupportsIPv6
Rev 1.28 12/31/2003 9:52:02 PM BGooijen
Added IPv6 support
Rev 1.27 10/26/2003 05:33:14 PM JPMugaas
LocalAddresses should work.
Rev 1.26 10/26/2003 5:04:28 PM BGooijen
UDP Server and Client
Rev 1.25 10/26/2003 09:10:26 AM JPMugaas
Calls necessary for IPMulticasting.
Rev 1.24 10/22/2003 04:40:52 PM JPMugaas
Should compile with some restored functionality. Still not finished.
Rev 1.23 10/21/2003 11:04:20 PM BGooijen
Fixed name collision
Rev 1.22 10/21/2003 01:20:02 PM JPMugaas
Restore GWindowsStack because it was needed by SuperCore.
Rev 1.21 10/21/2003 06:24:28 AM JPMugaas
BSD Stack now have a global variable for refercing by platform specific
things. Removed corresponding var from Windows stack.
Rev 1.20 10/19/2003 5:21:32 PM BGooijen
SetSocketOption
Rev 1.19 2003.10.11 5:51:16 PM czhower
-VCL fixes for servers
-Chain suport for servers (Super core)
-Scheduler upgrades
-Full yarn support
Rev 1.18 2003.10.02 8:01:08 PM czhower
.Net
Rev 1.17 2003.10.02 12:44:44 PM czhower
Fix for Bind, Connect
Rev 1.16 2003.10.02 10:16:32 AM czhower
.Net
Rev 1.15 2003.10.01 9:11:26 PM czhower
.Net
Rev 1.14 2003.10.01 12:30:08 PM czhower
.Net
Rev 1.12 10/1/2003 12:14:12 AM BGooijen
DotNet: removing CheckForSocketError
Rev 1.11 2003.10.01 1:12:40 AM czhower
.Net
Rev 1.10 2003.09.30 1:23:04 PM czhower
Stack split for DotNet
Rev 1.9 9/8/2003 02:13:10 PM JPMugaas
SupportsIP6 function added for determining if IPv6 is installed on a system.
Rev 1.8 2003.07.14 1:57:24 PM czhower
-First set of IOCP fixes.
-Fixed a threadsafe problem with the stack class.
Rev 1.7 7/1/2003 05:20:44 PM JPMugaas
Minor optimizations. Illiminated some unnecessary string operations.
Rev 1.5 7/1/2003 03:39:58 PM JPMugaas
Started numeric IP function API calls for more efficiency.
Rev 1.4 7/1/2003 12:46:06 AM JPMugaas
Preliminary stack functions taking an IP address numerical structure instead
of a string.
Rev 1.3 5/19/2003 6:00:28 PM BGooijen
TIdStackWindows.WSGetHostByAddr raised an ERangeError when the last number in
the ip>127
Rev 1.2 5/10/2003 4:01:28 PM BGooijen
Rev 1.1 2003.05.09 10:59:28 PM czhower
Rev 1.0 11/13/2002 08:59:38 AM JPMugaas
}
unit IdStackWindows;
interface
{$I IdCompilerDefines.inc}
uses
Classes,
IdGlobal, IdException, IdStackBSDBase, IdStackConsts, IdWinsock2, IdStack,
SysUtils,
Windows;
type
EIdIPv6Unavailable = class(EIdException);
TIdSocketListWindows = class(TIdSocketList)
protected
FFDSet: TFDSet;
//
class function FDSelect(AReadSet: PFDSet; AWriteSet: PFDSet; AExceptSet: PFDSet;
const ATimeout: Integer = IdTimeoutInfinite): Boolean;
function GetItem(AIndex: Integer): TIdStackSocketHandle; override;
public
procedure Add(AHandle: TIdStackSocketHandle); override;
procedure Remove(AHandle: TIdStackSocketHandle); override;
function Count: Integer; override;
procedure Clear; override;
function Clone: TIdSocketList; override;
function ContainsSocket(AHandle: TIdStackSocketHandle): boolean; override;
procedure GetFDSet(var VSet: TFDSet);
procedure SetFDSet(var VSet: TFDSet);
class function Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
function SelectRead(const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
function SelectReadList(var VSocketList: TIdSocketList;
const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
end;
TIdStackWindows = class(TIdStackBSDBase)
protected
procedure WSQuerryIPv6Route(ASocket: TIdStackSocketHandle;
const AIP: String; const APort : UInt16; var VSource; var VDest);
procedure WriteChecksumIPv6(s : TIdStackSocketHandle; var VBuffer : TIdBytes;
const AOffset : Integer; const AIP : String; const APort : TIdPort);
function HostByName(const AHostName: string;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
function ReadHostName: string; override;
function WSCloseSocket(ASocket: TIdStackSocketHandle): Integer; override;
function WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
const ABufferLength, AFlags: Integer): Integer; override;
function WSSend(ASocket: TIdStackSocketHandle; const ABuffer;
const ABufferLength, AFlags: Integer): Integer; override;
function WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer; override;
{$IFNDEF VCL_XE3_OR_ABOVE}
procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
procedure WSSetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
AOptName: TIdSocketOption; const AOptVal; const AOptLen: Integer); override;
{$ENDIF}
public
function Accept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: TIdPort;
var VIPVersion: TIdIPVersion): TIdStackSocketHandle; override;
function HostToNetwork(AValue: UInt16): UInt16; override;
function HostToNetwork(AValue: UInt32): UInt32; override;
function HostToNetwork(AValue: TIdUInt64): TIdUInt64; override;
procedure Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer); override;
function NetworkToHost(AValue: UInt16): UInt16; override;
function NetworkToHost(AValue: UInt32): UInt32; override;
function NetworkToHost(AValue: TIdUInt64): TIdUInt64; override;
procedure SetBlocking(ASocket: TIdStackSocketHandle; const ABlocking: Boolean); override;
function WouldBlock(const AResult: Integer): Boolean; override;
//
function HostByAddress(const AAddress: string;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
function WSGetServByName(const AServiceName: string): TIdPort; override;
procedure AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings); override;
function RecvFrom(const ASocket: TIdStackSocketHandle; var VBuffer;
const ALength, AFlags: Integer; var VIP: string; var VPort: TIdPort;
var VIPVersion: TIdIPVersion): Integer; override;
function ReceiveMsg(ASocket: TIdStackSocketHandle; var VBuffer: TIdBytes;
APkt : TIdPacketInfo): UInt32; override;
procedure WSSendTo(ASocket: TIdStackSocketHandle; const ABuffer;
const ABufferLength, AFlags: Integer; const AIP: string; const APort: TIdPort; AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
function WSSocket(AFamily : Integer; AStruct : TIdSocketType; AProtocol: Integer;
const AOverlapped: Boolean = False): TIdStackSocketHandle; override;
function WSTranslateSocketErrorMsg(const AErr: integer): string; override;
function WSGetLastError: Integer; override;
procedure WSSetLastError(const AErr : Integer); override;
//
procedure Bind(ASocket: TIdStackSocketHandle; const AIP: string;
const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
procedure Connect(const ASocket: TIdStackSocketHandle; const AIP: string;
const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
constructor Create; override;
destructor Destroy; override;
procedure Disconnect(ASocket: TIdStackSocketHandle); override;
procedure GetPeerName(ASocket: TIdStackSocketHandle; var VIP: string;
var VPort: TIdPort; var VIPVersion: TIdIPVersion); override;
procedure GetSocketName(ASocket: TIdStackSocketHandle; var VIP: string;
var VPort: TIdPort; var VIPVersion: TIdIPVersion); override;
{$IFDEF VCL_XE3_OR_ABOVE}
procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
procedure SetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
AOptName: TIdSocketOption; const AOptVal; const AOptLen: Integer); override;
{$ENDIF}
function IOControl(const s: TIdStackSocketHandle; const cmd: UInt32; var arg: UInt32): Integer; override;
function SupportsIPv6: Boolean; override;
function CheckIPVersionSupport(const AIPVersion: TIdIPVersion): boolean; override;
procedure WriteChecksum(s : TIdStackSocketHandle;
var VBuffer : TIdBytes;
const AOffset : Integer;
const AIP : String;
const APort : TIdPort;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
procedure GetLocalAddressList(AAddresses: TIdStackLocalAddressList); override;
procedure SetKeepAliveValues(ASocket: TIdStackSocketHandle;
const AEnabled: Boolean; const ATimeMS, AInterval: Integer); override;
end;
var
//This is for the Win32-only package (SuperCore)
GWindowsStack : TIdStackWindows = nil;
implementation
{$DEFINE USE_IPHLPAPI}
{$IFDEF USE_IPHLPAPI}
{$IFDEF VCL_XE2_OR_ABOVE}
{$DEFINE HAS_UNIT_IpTypes}
{$DEFINE HAS_UNIT_IpHlpApi}
{$ENDIF}
{$ENDIF}
uses
IdIDN, IdResourceStrings, IdWship6
{$IFDEF USE_IPHLPAPI}
{$IFDEF HAS_UNIT_IpTypes}
, IpTypes
{$ENDIF}
{$IFDEF HAS_UNIT_IpHlpApi}
, IpHlpApi
{$ENDIF}
{$ENDIF}
;
{$IFNDEF WINCE}
type
TGetFileSizeEx = function(hFile : THandle; var lpFileSize : LARGE_INTEGER) : BOOL; stdcall;
{$ENDIF}
const
SIZE_HOSTNAME = 250;
var
GStarted: Boolean = False;
{$IFNDEF WINCE}
GetFileSizeEx : TGetFileSizeEx = nil;
{$ENDIF}
{ IPHLPAPI support }
{$IFDEF USE_IPHLPAPI}
const
IPHLPAPI_DLL = 'iphlpapi.dll';
{$IFNDEF HAS_UNIT_IpTypes}
MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
MAX_ADAPTER_NAME_LENGTH = 256;
MAX_ADAPTER_ADDRESS_LENGTH = 8;
MAX_DHCPV6_DUID_LENGTH = 130;
MAX_DNS_SUFFIX_STRING_LENGTH = 256;
GAA_FLAG_SKIP_UNICAST = $0001;
GAA_FLAG_SKIP_ANYCAST = $0002;
GAA_FLAG_SKIP_MULTICAST = $0004;
GAA_FLAG_SKIP_DNS_SERVER = $0008;
GAA_FLAG_INCLUDE_PREFIX = $0010;
GAA_FLAG_SKIP_FRIENDLY_NAME = $0020;
IP_ADAPTER_RECEIVE_ONLY = $08;
{$ENDIF}
IF_TYPE_SOFTWARE_LOOPBACK = 24;
type
PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS = ^IP_UNIDIRECTIONAL_ADAPTER_ADDRESS;
IP_UNIDIRECTIONAL_ADAPTER_ADDRESS = record
NumAdapters: ULONG;
Address: array[0..0] of TInAddr;
end;
{$IFNDEF HAS_UNIT_IpTypes}
{$MINENUMSIZE 4}
time_t = TIdNativeInt;
IFTYPE = ULONG;
IF_INDEX = ULONG;
NET_IF_COMPARTMENT_ID = UINT32;
NET_IF_NETWORK_GUID = TGUID;
IP_PREFIX_ORIGIN = (
IpPrefixOriginOther,
IpPrefixOriginManual,
IpPrefixOriginWellKnown,
IpPrefixOriginDhcp,
IpPrefixOriginRouterAdvertisement,
{$IFNDEF HAS_ENUM_ELEMENT_VALUES}
ippoUnused5,
ippoUnused6,
ippoUnused7,
ippoUnused8,
ippoUnused9,
ippoUnused10,
ippoUnused11,
ippoUnused12,
ippoUnused13,
ippoUnused14,
ippoUnused15,
{$ENDIF}
IpPrefixOriginUnchanged);
IP_SUFFIX_ORIGIN = (
IpSuffixOriginOther,
IpSuffixOriginManual,
IpSuffixOriginWellKnown,
IpSuffixOriginDhcp,
IpSuffixOriginLinkLayerAddress,
IpSuffixOriginRandom,
{$IFNDEF HAS_ENUM_ELEMENT_VALUES}
ipsoUnued6,
ipsoUnued7,
ipsoUnued8,
ipsoUnued9,
ipsoUnued10,
ipsoUnued11,
ipsoUnued12,
ipsoUnued13,
ipsoUnued14,
ipsoUnued15,
{$ENDIF}
IpSuffixOriginUnchanged);
IP_DAD_STATE = (
IpDadStateInvalid,
IpDadStateTentative,
IpDadStateDuplicate,
IpDadStateDeprecated,
IpDadStatePreferred);
IF_OPER_STATUS = (
{$IFNDEF HAS_ENUM_ELEMENT_VALUES}
ifosUnused,
IfOperStatusUp,
{$ELSE}
IfOperStatusUp = 1,
{$ENDIF}
IfOperStatusDown,
IfOperStatusTesting,
IfOperStatusUnknown,
IfOperStatusDormant,
IfOperStatusNotPresent,
IfOperStatusLowerLayerDown);
NET_IF_CONNECTION_TYPE = (
{$IFNDEF HAS_ENUM_ELEMENT_VALUES}
nictUnused,
NetIfConnectionDedicated,
{$ELSE}
NetIfConnectionDedicated = 1,
{$ENDIF}
NetIfConnectionPassive,
NetIfConnectionDemand,
NetIfConnectionMaximum);
TUNNEL_TYPE = (
TunnelTypeNone,
TunnelTypeOther,
TunnelTypeDirect,
TunnelType6To4,
TunnelTypeIsatap,
TunnelTypeTeredo,
TunnelTypeIPHTTPS);
IP_ADDRESS_STRING = record
S: array [0..15] of AnsiChar;
end;
IP_MASK_STRING = IP_ADDRESS_STRING;
PIP_ADDR_STRING = ^IP_ADDR_STRING;
IP_ADDR_STRING = record
Next: PIP_ADDR_STRING;
IpAddress: IP_ADDRESS_STRING;
IpMask: IP_MASK_STRING;
Context: DWORD;
end;
PIP_ADAPTER_INFO = ^IP_ADAPTER_INFO;
IP_ADAPTER_INFO = record
Next: PIP_ADAPTER_INFO;
ComboIndex: DWORD;
AdapterName: array [0..MAX_ADAPTER_NAME_LENGTH + 3] of AnsiChar;
Description: array [0..MAX_ADAPTER_DESCRIPTION_LENGTH + 3] of AnsiChar;
AddressLength: UINT;
Address: array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE;
Index: DWORD;
Type_: UINT;
DhcpEnabled: UINT;
CurrentIpAddress: PIP_ADDR_STRING;
IpAddressList: IP_ADDR_STRING;
GatewayList: IP_ADDR_STRING;
DhcpServer: IP_ADDR_STRING;
HaveWins: BOOL;
PrimaryWinsServer: IP_ADDR_STRING;
SecondaryWinsServer: IP_ADDR_STRING;
LeaseObtained: time_t;
LeaseExpires: time_t;
end;
SOCKET_ADDRESS = record
lpSockaddr: IdWinsock2.LPSOCKADDR;
iSockaddrLength: Integer;
end;
PIP_ADAPTER_UNICAST_ADDRESS = ^IP_ADAPTER_UNICAST_ADDRESS;
IP_ADAPTER_UNICAST_ADDRESS = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Flags: DWORD);
end;
Next: PIP_ADAPTER_UNICAST_ADDRESS;
Address: SOCKET_ADDRESS;
PrefixOrigin: IP_PREFIX_ORIGIN;
SuffixOrigin: IP_SUFFIX_ORIGIN;
DadState: IP_DAD_STATE;
ValidLifetime: ULONG;
PreferredLifetime: ULONG;
LeaseLifetime: ULONG;
// This structure member is only available on Windows Vista and later
OnLinkPrefixLength: UCHAR;
end;
PIP_ADAPTER_ANYCAST_ADDRESS = ^IP_ADAPTER_ANYCAST_ADDRESS;
IP_ADAPTER_ANYCAST_ADDRESS = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Flags: DWORD);
end;
Next: PIP_ADAPTER_ANYCAST_ADDRESS;
Address: SOCKET_ADDRESS;
end;
PIP_ADAPTER_MULTICAST_ADDRESS = ^IP_ADAPTER_MULTICAST_ADDRESS;
IP_ADAPTER_MULTICAST_ADDRESS = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Flags: DWORD);
end;
Next: PIP_ADAPTER_MULTICAST_ADDRESS;
Address: SOCKET_ADDRESS;
end;
PIP_ADAPTER_DNS_SERVER_ADDRESS = ^IP_ADAPTER_DNS_SERVER_ADDRESS;
IP_ADAPTER_DNS_SERVER_ADDRESS = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Reserved: DWORD);
end;
Next: PIP_ADAPTER_DNS_SERVER_ADDRESS;
Address: SOCKET_ADDRESS;
end;
PIP_ADAPTER_PREFIX = ^IP_ADAPTER_PREFIX;
IP_ADAPTER_PREFIX = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Flags: DWORD);
end;
Next: PIP_ADAPTER_PREFIX;
Address: SOCKET_ADDRESS;
PrefixLength: ULONG;
end;
PIP_ADAPTER_WINS_SERVER_ADDRESS_LH = ^IP_ADAPTER_WINS_SERVER_ADDRESS_LH;
IP_ADAPTER_WINS_SERVER_ADDRESS_LH = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Reserved: DWORD);
end;
Next: PIP_ADAPTER_WINS_SERVER_ADDRESS_LH;
Address: SOCKET_ADDRESS;
end;
PIP_ADAPTER_GATEWAY_ADDRESS_LH = ^IP_ADAPTER_GATEWAY_ADDRESS_LH;
IP_ADAPTER_GATEWAY_ADDRESS_LH = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
Reserved: DWORD);
end;
Next: PIP_ADAPTER_GATEWAY_ADDRESS_LH;
Address: SOCKET_ADDRESS;
end;
IF_LUID = record
case Integer of
0: (
Value: ULONG64);
1: (
Info: ULONG64);
end;
PIP_ADAPTER_DNS_SUFFIX = ^IP_ADAPTER_DNS_SUFFIX;
IP_ADAPTER_DNS_SUFFIX = record
Next: PIP_ADAPTER_DNS_SUFFIX;
AString: array[0..MAX_DNS_SUFFIX_STRING_LENGTH - 1] of WCHAR;
end;
PIP_ADAPTER_ADDRESSES = ^IP_ADAPTER_ADDRESSES;
IP_ADAPTER_ADDRESSES = record
Union: record
case Integer of
0: (
Alignment: ULONGLONG);
1: (
Length: ULONG;
IfIndex: DWORD);
end;
Next: PIP_ADAPTER_ADDRESSES;
AdapterName: PAnsiChar;
FirstUnicastAddress: PIP_ADAPTER_UNICAST_ADDRESS;
FirstAnycastAddress: PIP_ADAPTER_ANYCAST_ADDRESS;
FirstMulticastAddress: PIP_ADAPTER_MULTICAST_ADDRESS;
FirstDnsServerAddress: PIP_ADAPTER_DNS_SERVER_ADDRESS;
DnsSuffix: PWCHAR;
Description: PWCHAR;
FriendlyName: PWCHAR;
PhysicalAddress: array [0..MAX_ADAPTER_ADDRESS_LENGTH - 1] of BYTE;
PhysicalAddressLength: DWORD;
Flags: DWORD;
Mtu: DWORD;
IfType: IFTYPE;
OperStatus: IF_OPER_STATUS;
Ipv6IfIndex: IF_INDEX;
ZoneIndices: array [0..15] of DWORD;
FirstPrefix: PIP_ADAPTER_PREFIX;
TransmitLinkSpeed: ULONG64;
ReceiveLinkSpeed: ULONG64;
FirstWinsServerAddress: PIP_ADAPTER_WINS_SERVER_ADDRESS_LH;
FirstGatewayAddress: PIP_ADAPTER_GATEWAY_ADDRESS_LH;
Ipv4Metric: ULONG;
Ipv6Metric: ULONG;
Luid: IF_LUID;
Dhcpv4Server: SOCKET_ADDRESS;
CompartmentId: NET_IF_COMPARTMENT_ID;
NetworkGuid: NET_IF_NETWORK_GUID;
ConnectionType: NET_IF_CONNECTION_TYPE;
TunnelType: TUNNEL_TYPE;
//
// DHCP v6 Info.
//
Dhcpv6Server: SOCKET_ADDRESS;
Dhcpv6ClientDuid: array [0..MAX_DHCPV6_DUID_LENGTH - 1] of Byte;
Dhcpv6ClientDuidLength: ULONG;
Dhcpv6Iaid: ULONG;
FirstDnsSuffix: PIP_ADAPTER_DNS_SUFFIX;
end;
{$ENDIF}
PMIB_IPADDRROW = ^MIB_IPADDRROW;
MIB_IPADDRROW = record
dwAddr: DWORD;
dwIndex: DWORD;
dwMask: DWORD;
dwBCastAddr: DWORD;
dwReasmSize: DWORD;
unused1: Word;
wType: Word;
end;
PMIB_IPADDRTABLE = ^MIB_IPADDRTABLE;
MIB_IPADDRTABLE = record
dwNumEntries: DWORD;
table: array[0..0] of MIB_IPADDRROW;
end;
NETIO_STATUS = DWORD;
TGetIpAddrTable = function(pIpAddrTable: PMIB_IPADDRTABLE; var pdwSize: ULONG; bOrder: BOOL): DWORD; stdcall;
TGetUniDirectionalAdapterInfo = function(pIPIfInfo: PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS; var dwOutBufLen: ULONG): DWORD; stdcall;
TGetAdaptersInfo = function(pAdapterInfo: PIP_ADAPTER_INFO; var pOutBufLen: ULONG): DWORD; stdcall;
TGetAdaptersAddresses = function(Family: ULONG; Flags: DWORD; Reserved: PVOID; pAdapterAddresses: PIP_ADAPTER_ADDRESSES; var OutBufLen: ULONG): DWORD; stdcall;
TConvertLengthToIpv4Mask = function(MaskLength: ULONG; var Mask: ULONG): NETIO_STATUS; stdcall;
var
hIpHlpApi: THandle = 0;
GetIpAddrTable: TGetIpAddrTable = nil;
GetUniDirectionalAdapterInfo: TGetUniDirectionalAdapterInfo = nil;
GetAdaptersInfo: TGetAdaptersInfo = nil;
GetAdaptersAddresses: TGetAdaptersAddresses = nil;
ConvertLengthToIpv4Mask: TConvertLengthToIpv4Mask = nil;
function FixupIPHelperStub(const AName:{$IFDEF WINCE}TIdUnicodeString{$ELSE}string{$ENDIF}; DefImpl: Pointer): Pointer;
{$IFDEF USE_INLINE}inline;{$ENDIF}
begin
Result := nil;
if hIpHlpApi <> 0 then begin
Result := Windows.GetProcAddress(hIpHlpApi, {$IFDEF WINCE}PWideChar{$ELSE}PChar{$ENDIF}(AName));
end;
if Result = nil then begin
Result := DefImpl;
end;
end;
function Impl_GetIpAddrTable(pIpAddrTable: PMIB_IPADDRTABLE; var pdwSize: ULONG; bOrder: BOOL): DWORD; stdcall;
begin
pdwSize := 0;
Result := ERROR_NOT_SUPPORTED;
end;
function Stub_GetIpAddrTable(pIpAddrTable: PMIB_IPADDRTABLE; var pdwSize: ULONG; bOrder: BOOL): DWORD; stdcall;
begin
@GetIpAddrTable := FixupIPHelperStub('GetIpAddrTable', @Impl_GetIpAddrTable); {Do not localize}
Result := GetIpAddrTable(pIpAddrTable, pdwSize, bOrder);
end;
function Impl_GetUniDirectionalAdapterInfo(pIPIfInfo: PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS; var dwOutBufLen: ULONG): DWORD; stdcall;
begin
dwOutBufLen := 0;
Result := ERROR_NOT_SUPPORTED;
end;
function Stub_GetUniDirectionalAdapterInfo(pIPIfInfo: PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS; var dwOutBufLen: ULONG): DWORD; stdcall;
begin
@GetUniDirectionalAdapterInfo := FixupIPHelperStub('GetUniDirectionalAdapterInfo', @Impl_GetUniDirectionalAdapterInfo); {Do not localize}
Result := GetUniDirectionalAdapterInfo(pIPIfInfo, dwOutBufLen);
end;
function Impl_GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO; var pOutBufLen: ULONG): DWORD; stdcall;
begin
pOutBufLen := 0;
Result := ERROR_NOT_SUPPORTED;
end;
function Stub_GetAdaptersInfo(pAdapterInfo: PIP_ADAPTER_INFO; var pOutBufLen: ULONG): DWORD; stdcall;
begin
@GetAdaptersInfo := FixupIPHelperStub('GetAdaptersInfo', @Impl_GetAdaptersInfo); {Do not localize}
Result := GetAdaptersInfo(pAdapterInfo, pOutBufLen);
end;
function Impl_GetAdaptersAddresses(Family: ULONG; Flags: DWORD; Reserved: PVOID; pAdapterAddresses: PIP_ADAPTER_ADDRESSES; var OutBufLen: ULONG): DWORD; stdcall;
begin
OutBufLen := 0;
Result := ERROR_NOT_SUPPORTED;
end;
function Stub_GetAdaptersAddresses(Family: ULONG; Flags: DWORD; Reserved: PVOID; pAdapterAddresses: PIP_ADAPTER_ADDRESSES; var OutBufLen: ULONG): DWORD; stdcall;
begin
@GetAdaptersAddresses := FixupIPHelperStub('GetAdaptersAddresses', @Impl_GetAdaptersAddresses); {Do not localize}
Result := GetAdaptersAddresses(Family, Flags, Reserved, pAdapterAddresses, OutBufLen);
end;
function Impl_ConvertLengthToIpv4Mask(MaskLength: ULONG; var Mask: ULONG): NETIO_STATUS; stdcall;
begin
// TODO: implement manually
Mask := INADDR_NONE;
if MaskLength > 32 then begin
Result := ERROR_INVALID_PARAMETER;
end else begin
Result := ERROR_NOT_SUPPORTED;
end;
end;
function Stub_ConvertLengthToIpv4Mask(MaskLength: ULONG; var Mask: ULONG): NETIO_STATUS; stdcall;
begin
@ConvertLengthToIpv4Mask := FixupIPHelperStub('ConvertLengthToIpv4Mask', @Impl_ConvertLengthToIpv4Mask); {Do not localize}
Result := ConvertLengthToIpv4Mask(MaskLength, Mask);
end;
procedure InitializeIPHelperStubs;
begin
GetIpAddrTable := Stub_GetIpAddrTable;
GetUniDirectionalAdapterInfo := Stub_GetUniDirectionalAdapterInfo;
GetAdaptersInfo := Stub_GetAdaptersInfo;
GetAdaptersAddresses := Stub_GetAdaptersAddresses;
ConvertLengthToIpv4Mask := Stub_ConvertLengthToIpv4Mask;
end;
procedure InitializeIPHelperAPI;
begin
if hIpHlpApi = 0 then begin
hIpHlpApi := SafeLoadLibrary(IPHLPAPI_DLL);
end;
end;
procedure UninitializeIPHelperAPI;
begin
if hIpHlpApi <> 0 then
begin
FreeLibrary(hIpHlpApi);
hIpHlpApi := 0;
end;
InitializeIPHelperStubs;
end;
{$ENDIF}
{ TIdStackWindows }
constructor TIdStackWindows.Create;
begin
inherited Create;
if not GStarted then begin
try
InitializeWinSock;
IdWship6.InitLibrary;
IdIDN.InitIDNLibrary;
{$IFDEF USE_IPHLPAPI}
InitializeIPHelperAPI;
{$ENDIF}
except
on E: Exception do begin
IndyRaiseOuterException(EIdStackInitializationFailed.Create(E.Message));
end;
end;
GStarted := True;
end;
GWindowsStack := Self;
end;
destructor TIdStackWindows.Destroy;
begin
//DLL Unloading and Cleanup is done at finalization
inherited Destroy;
end;
function TIdStackWindows.Accept(ASocket: TIdStackSocketHandle;
var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion): TIdStackSocketHandle;
var
LSize: Integer;
LAddr: SOCKADDR_STORAGE;
begin
LSize := SizeOf(LAddr);
Result := IdWinsock2.accept(ASocket, IdWinsock2.PSOCKADDR(@LAddr), @LSize);
if Result <> INVALID_SOCKET then begin
case LAddr.ss_family of
Id_PF_INET4: begin
VIP := TranslateTInAddrToString(PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
VPort := ntohs(PSockAddrIn(@LAddr)^.sin_port);
VIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
VIP := TranslateTInAddrToString(PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
VPort := ntohs(PSockAddrIn6(@LAddr)^.sin6_port);
VIPVersion := Id_IPv6;
end;
else begin
CloseSocket(Result);
Result := INVALID_SOCKET;
IPVersionUnsupported;
end;
end;
end;
end;
procedure TIdStackWindows.Bind(ASocket: TIdStackSocketHandle;
const AIP: string; const APort: TIdPort;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
var
LAddr: SOCKADDR_STORAGE;
LSize: Integer;
begin
FillChar(LAddr, SizeOf(LAddr), 0);
case AIPVersion of
Id_IPv4: begin
PSockAddrIn(@LAddr)^.sin_family := Id_PF_INET4;
if AIP <> '' then begin
TranslateStringToTInAddr(AIP, PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
end;
PSockAddrIn(@LAddr)^.sin_port := htons(APort);
LSize := SIZE_TSOCKADDRIN;
end;
Id_IPv6: begin
PSockAddrIn6(@LAddr)^.sin6_family := Id_PF_INET6;
if AIP <> '' then begin
TranslateStringToTInAddr(AIP, PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
end;
PSockAddrIn6(@LAddr)^.sin6_port := htons(APort);
LSize := SIZE_TSOCKADDRIN6;
end;
else begin
LSize := 0; // avoid warning
IPVersionUnsupported;
end;
end;
CheckForSocketError(IdWinsock2.bind(ASocket, IdWinsock2.PSOCKADDR(@LAddr), LSize));
end;
function TIdStackWindows.WSCloseSocket(ASocket: TIdStackSocketHandle): Integer;
begin
Result := CloseSocket(ASocket);
end;
function TIdStackWindows.HostByAddress(const AAddress: string;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
var
{$IFDEF UNICODE}
Hints: TAddrInfoW;
LAddrInfo: pAddrInfoW;
{$ELSE}
Hints: TAddrInfo;
LAddrInfo: pAddrInfo;
{$ENDIF}
RetVal: Integer;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp: TIdPlatformString;
{$ENDIF}
begin
if not (AIPVersion in [Id_IPv4, Id_IPv6]) then begin
IPVersionUnsupported;
end;
// TODO: should this be calling getnameinfo() first and then getaddrinfo()
// to check for a malicious PTR record, like the other TIdStack classes do?
// TODO: use TranslateStringToTInAddr() instead of getaddrinfo() to convert
// the IP address to a sockaddr struct for getnameinfo(), like other TIdStack
// classes do.
FillChar(Hints, SizeOf(Hints), 0);
Hints.ai_family := IdIPFamily[AIPVersion];
Hints.ai_socktype := Integer(SOCK_STREAM);
Hints.ai_flags := AI_NUMERICHOST;
LAddrInfo := nil;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp := TIdPlatformString(AAddress); // explicit convert to Ansi/Unicode
{$ENDIF}
RetVal := getaddrinfo(
{$IFDEF STRING_UNICODE_MISMATCH}PIdPlatformChar(LTemp){$ELSE}PChar(AAddress){$ENDIF},
nil, @Hints, @LAddrInfo);
if RetVal <> 0 then begin
RaiseSocketError(gaiErrorToWsaError(RetVal));
end;
try
SetLength(
{$IFDEF STRING_UNICODE_MISMATCH}LTemp{$ELSE}Result{$ENDIF},
NI_MAXHOST);
RetVal := getnameinfo(
LAddrInfo.ai_addr, LAddrInfo.ai_addrlen,
{$IFDEF STRING_UNICODE_MISMATCH}PIdPlatformChar(LTemp){$ELSE}PChar(Result){$ENDIF},
NI_MAXHOST, nil, 0, NI_NAMEREQD);
if RetVal <> 0 then begin
RaiseSocketError(gaiErrorToWsaError(RetVal));
end;
Result := {$IFDEF STRING_UNICODE_MISMATCH}PIdPlatformChar(LTemp){$ELSE}PChar(Result){$ENDIF};
finally
freeaddrinfo(LAddrInfo);
end;
end;
function TIdStackWindows.ReadHostName: string;
var
// Note that there is no Unicode version of gethostname.
// Maybe use getnameinfo() instead?
LStr: AnsiString;
begin
SetLength(LStr, SIZE_HOSTNAME);
gethostname(PAnsiChar(LStr), SIZE_HOSTNAME);
//we have to specifically type cast a PAnsiChar to a string for D2009+.
//otherwise, we will get a warning about implicit typecast from AnsiString
//to string
Result := String(PAnsiChar(LStr));
end;
procedure TIdStackWindows.Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer);
begin
CheckForSocketError(IdWinsock2.listen(ASocket, ABacklog));
end;
// RLebeau 12/16/09: MS Hotfix #971383 supposedly fixes a bug in Windows
// Server 2003 when client and server are running on the same machine.
// The bug can cause recv() to return 0 bytes prematurely even though data
// is actually pending. Uncomment the below define if you do not want to
// rely on the Hotfix always being installed. The workaround described by
// MS is to simply call recv() again to make sure data is really not pending.
//
{.$DEFINE IGNORE_KB971383_FIX}
function TIdStackWindows.WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
const ABufferLength, AFlags: Integer) : Integer;
begin
Result := recv(ASocket, ABuffer, ABufferLength, AFlags);
{$IFDEF IGNORE_KB971383_FIX}
if Result = 0 then begin
Result := recv(ASocket, ABuffer, ABufferLength, AFlags);
end;
{$ENDIF}
end;
function TIdStackWindows.RecvFrom(const ASocket: TIdStackSocketHandle;
var VBuffer; const ALength, AFlags: Integer; var VIP: string;
var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
var
LSize: Integer;
LAddr: SOCKADDR_STORAGE;
begin
LSize := SizeOf(LAddr);
Result := IdWinsock2.recvfrom(ASocket, VBuffer, ALength, AFlags, IdWinsock2.PSOCKADDR(@LAddr), @LSize);
if Result >= 0 then
begin
case LAddr.ss_family of
Id_PF_INET4: begin
VIP := TranslateTInAddrToString(PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
VPort := ntohs(PSockAddrIn(@LAddr)^.sin_port);
VIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
VIP := TranslateTInAddrToString(PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
VPort := ntohs(PSockAddrIn6(@LAddr)^.sin6_port);
VIPVersion := Id_IPv6;
end;
else begin
IPVersionUnsupported;
end;
end;
end;
end;
function TIdStackWindows.WSSend(ASocket: TIdStackSocketHandle;
const ABuffer; const ABufferLength, AFlags: Integer): Integer;
begin
Result := CheckForSocketError(IdWinsock2.send(ASocket, ABuffer, ABufferLength, AFlags));
end;
procedure TIdStackWindows.WSSendTo(ASocket: TIdStackSocketHandle;
const ABuffer; const ABufferLength, AFlags: Integer; const AIP: string;
const APort: TIdPort; AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
var
LAddr: SOCKADDR_STORAGE;
LSize: Integer;
begin
FillChar(LAddr, SizeOf(LAddr), 0);
case AIPVersion of
Id_IPv4: begin
PSockAddrIn(@LAddr)^.sin_family := Id_PF_INET4;
TranslateStringToTInAddr(AIP, PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
PSockAddrIn(@LAddr)^.sin_port := htons(APort);
LSize := SIZE_TSOCKADDRIN;
end;
Id_IPv6: begin
PSockAddrIn6(@LAddr)^.sin6_family := Id_PF_INET6;
TranslateStringToTInAddr(AIP, PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
PSockAddrIn6(@LAddr)^.sin6_port := htons(APort);
LSize := SIZE_TSOCKADDRIN6;
end;
else begin
LSize := 0; // avoid warning
IPVersionUnsupported;
end;
end;
LSize := IdWinsock2.sendto(ASocket, ABuffer, ABufferLength, AFlags, IdWinsock2.PSOCKADDR(@LAddr), LSize);
// TODO: call CheckForSocketError() here
if LSize = Id_SOCKET_ERROR then begin
// TODO: move this into RaiseLastSocketError() directly
if WSGetLastError() = Id_WSAEMSGSIZE then begin
raise EIdPackageSizeTooBig.Create(RSPackageSizeTooBig);
end else begin
RaiseLastSocketError;
end;
end
else if LSize <> ABufferLength then begin
raise EIdNotAllBytesSent.Create(RSNotAllBytesSent);
end;
end;
function TIdStackWindows.WSGetLastError: Integer;
begin
Result := WSAGetLastError;
if Result = -1073741251{STATUS_HOST_UNREACHABLE} then begin
Result := WSAEHOSTUNREACH;
end
end;
procedure TIdStackWindows.WSSetLastError(const AErr : Integer);
begin
WSASetLastError(AErr);
end;
function TIdStackWindows.WSSocket(AFamily : Integer; AStruct : TIdSocketType; AProtocol: Integer;
const AOverlapped: Boolean = False): TIdStackSocketHandle;
begin
if AOverlapped then begin
Result := WSASocket(AFamily, AStruct, AProtocol, nil, 0, WSA_FLAG_OVERLAPPED);
end else begin
Result := IdWinsock2.socket(AFamily, AStruct, AProtocol);
end;
end;
function TIdStackWindows.WSGetServByName(const AServiceName: string): TIdPort;
var
ps: PServEnt;
{$IFDEF STRING_IS_UNICODE}
LTemp: AnsiString;
{$ENDIF}
begin
{$IFDEF STRING_IS_UNICODE}
LTemp := AnsiString(AServiceName); // explicit convert to Ansi
{$ENDIF}
ps := getservbyname(
PAnsiChar({$IFDEF STRING_IS_UNICODE}LTemp{$ELSE}AServiceName{$ENDIF}),
nil);
if ps <> nil then begin
Result := ntohs(ps^.s_port);
end else begin
try
Result := IndyStrToInt(AServiceName);
except
on EConvertError do begin
Result := 0;
IndyRaiseOuterException(EIdInvalidServiceName.CreateFmt(RSInvalidServiceName, [AServiceName]));
end;
end;
end;
end;
procedure TIdStackWindows.AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings);
type
// Note that there is no Unicode version of getservbyport.
PPAnsiCharArray = ^TPAnsiCharArray;
TPAnsiCharArray = packed array[0..(MaxInt div SizeOf(PAnsiChar))-1] of PAnsiChar;
var
ps: PServEnt;
i: integer;
p: PPAnsiCharArray;
begin
ps := getservbyport(htons(APortNumber), nil);
if ps = nil then begin
RaiseLastSocketError;
end;
AAddresses.BeginUpdate;
try
//we have to specifically type cast a PAnsiChar to a string for D2009+.
//otherwise, we will get a warning about implicit typecast from AnsiString
//to string
AAddresses.Add(String(ps^.s_name));
i := 0;
p := Pointer(ps^.s_aliases);
while p[i] <> nil do
begin
AAddresses.Add(String(p[i]));
Inc(i);
end;
finally
AAddresses.EndUpdate;
end;
end;
function TIdStackWindows.HostToNetwork(AValue: UInt16): UInt16;
begin
Result := htons(AValue);
end;
function TIdStackWindows.NetworkToHost(AValue: UInt16): UInt16;
begin
Result := ntohs(AValue);
end;
function TIdStackWindows.HostToNetwork(AValue: UInt32): UInt32;
begin
Result := htonl(AValue);
end;
function TIdStackWindows.NetworkToHost(AValue: UInt32): UInt32;
begin
Result := ntohl(AValue);
end;
function TIdStackWindows.HostToNetwork(AValue: TIdUInt64): TIdUInt64;
var
LParts: TIdUInt64Parts;
L: UInt32;
begin
LParts.QuadPart := AValue{$IFDEF TIdUInt64_IS_NOT_NATIVE}.QuadPart{$ENDIF};
L := htonl(LParts.HighPart);
LParts.HighPart := htonl(LParts.LowPart);
LParts.LowPart := L;
Result{$IFDEF TIdUInt64_IS_NOT_NATIVE}.QuadPart{$ENDIF} := LParts.QuadPart;
end;
function TIdStackWindows.NetworkToHost(AValue: TIdUInt64): TIdUInt64;
var
LParts: TIdUInt64Parts;
L: UInt32;
begin
LParts.QuadPart := AValue{$IFDEF TIdUInt64_IS_NOT_NATIVE}.QuadPart{$ENDIF};
L := ntohl(LParts.HighPart);
LParts.HighPart := ntohl(LParts.LowPart);
LParts.LowPart := L;
Result{$IFDEF TIdUInt64_IS_NOT_NATIVE}.QuadPart{$ENDIF} := LParts.QuadPart;
end;
procedure TIdStackWindows.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
{$IFNDEF USE_IPHLPAPI}
{$IFNDEF WINCE}
type
TaPInAddr = array[0..250] of PInAddr;
PaPInAddr = ^TaPInAddr;
TaPIn6Addr = array[0..250] of PIn6Addr;
PaPIn6Addr = ^TaPIn6Addr;
{$ENDIF}
{$ENDIF}
{$IFDEF USE_IPHLPAPI}
function IPv4MaskLengthToString(MaskLength: ULONG): String;
var
Mask: ULONG;
begin
if ConvertLengthToIpv4Mask(MaskLength, Mask) = ERROR_SUCCESS then begin
Result := TranslateTInAddrToString(Mask, Id_IPv4);
end else begin
Result := '';
end;
end;
procedure GetIPv4SubNetMasks(ASubNetMasks: TStrings);
var
Ret: DWORD;
BufLen: ULONG;
Table: PMIB_IPADDRTABLE;
pRow: PMIB_IPADDRROW;
I: ULONG;
begin
BufLen := 0;
Table := nil;
try
repeat
Ret := GetIpAddrTable(Table, BufLen, FALSE);
case Ret of
ERROR_SUCCESS:
begin
if BufLen = 0 then begin
Exit;
end;
Break;
end;
ERROR_NOT_SUPPORTED:
Exit;
ERROR_INSUFFICIENT_BUFFER:
ReallocMem(Table, BufLen);
else
SetLastError(Ret);
IndyRaiseLastError;
end;
until False;
if Ret = ERROR_SUCCESS then
begin
if Table^.dwNumEntries > 0 then
begin
pRow := @(Table^.table[0]);
for I := 0 to Table^.dwNumEntries-1 do begin
ASubNetMasks.Add(TranslateTInAddrToString(pRow^.dwAddr, Id_IPv4) + '=' + TranslateTInAddrToString(pRow^.dwMask, Id_IPv4));
Inc(pRow);
end;
end;
end;
finally
FreeMem(Table);
end;
end;
function GetLocalAddressesByAdaptersAddresses: Boolean;
var
Ret: DWORD;
BufLen: ULONG;
Adapter, Adapters: PIP_ADAPTER_ADDRESSES;
UnicastAddr: PIP_ADAPTER_UNICAST_ADDRESS;
IPAddr: string;
SubNetStr: String;
SubNetMasks: TStringList;
begin
// assume True unless ERROR_NOT_SUPPORTED is reported...
Result := True;
// MSDN says:
// The recommended method of calling the GetAdaptersAddresses function is
// to pre-allocate a 15KB working buffer pointed to by the AdapterAddresses
// parameter. On typical computers, this dramatically reduces the chances
// that the GetAdaptersAddresses function returns ERROR_BUFFER_OVERFLOW,
// which would require calling GetAdaptersAddresses function multiple times.
BufLen := 1024*15;
GetMem(Adapters, BufLen);
try
repeat
// TODO: include GAA_FLAG_INCLUDE_PREFIX on XPSP1+?
// TODO: include GAA_FLAG_INCLUDE_ALL_INTERFACES on Vista+?
Ret := GetAdaptersAddresses(PF_UNSPEC, GAA_FLAG_SKIP_ANYCAST or GAA_FLAG_SKIP_MULTICAST or GAA_FLAG_SKIP_DNS_SERVER or GAA_FLAG_SKIP_FRIENDLY_NAME, nil, Adapters, BufLen);
case Ret of
ERROR_SUCCESS:
begin
// Windows CE versions earlier than 4.1 may return ERROR_SUCCESS and
// BufLen=0 if no adapter info is available, instead of returning
// ERROR_NO_DATA as documented...
if BufLen = 0 then begin
Exit;
end;
Break;
end;
ERROR_NOT_SUPPORTED:
begin
Result := False;
Exit;
end;
ERROR_NO_DATA,
ERROR_ADDRESS_NOT_ASSOCIATED:
Exit;
ERROR_BUFFER_OVERFLOW:
ReallocMem(Adapters, BufLen);
else
SetLastError(Ret);
IndyRaiseLastError;
end;
until False;
if Ret = ERROR_SUCCESS then
begin
SubNetMasks := nil;
try
AAddresses.BeginUpdate;
try
Adapter := Adapters;
repeat
if (Adapter.IfType <> IF_TYPE_SOFTWARE_LOOPBACK) and
((Adapter.Flags and IP_ADAPTER_RECEIVE_ONLY) = 0) then
begin
UnicastAddr := Adapter^.FirstUnicastAddress;
while UnicastAddr <> nil do
begin
if UnicastAddr^.DadState = IpDadStatePreferred then
begin
case UnicastAddr^.Address.lpSockaddr.sin_family of
AF_INET: begin
IPAddr := TranslateTInAddrToString(PSockAddrIn(UnicastAddr^.Address.lpSockaddr)^.sin_addr, Id_IPv4);
// The OnLinkPrefixLength member is only available on Windows Vista and later
if IndyCheckWindowsVersion(6) then begin
SubNetStr := IPv4MaskLengthToString(UnicastAddr^.OnLinkPrefixLength);
end else
begin
// TODO: on XP SP1+, can the subnet mask be determined
// by analyzing the Adapter's Prefix list without resorting
// to reading the Registry?
if SubNetMasks = nil then
begin
SubNetMasks := TStringList.Create;
GetIPv4SubNetMasks(SubNetMasks);
end;
SubNetStr := SubNetMasks.Values[IPAddr];
end;
TIdStackLocalAddressIPv4.Create(AAddresses, IPAddr, SubNetStr);
end;
AF_INET6: begin
TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString(PSockAddrIn6(UnicastAddr^.Address.lpSockaddr)^.sin6_addr, Id_IPv6));
end;
end;
end;
UnicastAddr := UnicastAddr^.Next;
end;
end;
Adapter := Adapter^.Next;
until Adapter = nil;
finally
AAddresses.EndUpdate;
end;
finally
SubNetMasks.Free;
end;
end;
finally
FreeMem(Adapters);
end;
end;
procedure GetUniDirAddresseses(AUniDirAddresses: TStrings);
var
Ret: DWORD;
BufLen: ULONG;
Adapters: PIP_UNIDIRECTIONAL_ADAPTER_ADDRESS;
pUniDirAddr: PInAddr;
I: ULONG;
begin
BufLen := 1024*15;
GetMem(Adapters, BufLen);
try
repeat
Ret := GetUniDirectionalAdapterInfo(Adapters, BufLen);
case Ret of
ERROR_SUCCESS:
begin
if BufLen = 0 then begin
Exit;
end;
Break;
end;
ERROR_NOT_SUPPORTED,
ERROR_NO_DATA:
Exit;
ERROR_MORE_DATA:
ReallocMem(Adapters, BufLen);
else
SetLastError(Ret);
IndyRaiseLastError;
end;
until False;
if Ret = ERROR_SUCCESS then
begin
if Adapters^.NumAdapters > 0 then
begin
pUniDirAddr := @(Adapters^.Address[0]);
for I := 0 to Adapters^.NumAdapters-1 do begin
AUniDirAddresses.Add(TranslateTInAddrToString(pUniDirAddr^, Id_IPv4));
Inc(pUniDirAddr);
end;
end;
end;
finally
FreeMem(Adapters);
end;
end;
procedure GetLocalAddressesByAdaptersInfo;
var
Ret: DWORD;
BufLen: ULONG;
UniDirAddresses: TStringList;
Adapter, Adapters: PIP_ADAPTER_INFO;
IPAddr: PIP_ADDR_STRING;
IPStr: String;
begin
BufLen := 1024*15;
GetMem(Adapters, BufLen);
try
repeat
Ret := GetAdaptersInfo(Adapters, BufLen);
case Ret of
ERROR_SUCCESS:
begin
// Windows CE versions earlier than 4.1 may return ERROR_SUCCESS and
// BufLen=0 if no adapter info is available, instead of returning
// ERROR_NO_DATA as documented...
if BufLen = 0 then begin
Exit;
end;
Break;
end;
ERROR_NOT_SUPPORTED,
ERROR_NO_DATA:
Exit;
ERROR_BUFFER_OVERFLOW:
ReallocMem(Adapters, BufLen);
else
SetLastError(Ret);
IndyRaiseLastError;
end;
until False;
if Ret = ERROR_SUCCESS then
begin
// on XP and later, GetAdaptersInfo() includes uni-directional adapters.
// Need to use GetUniDirectionalAdapterInfo() to filter them out of the
// list ...
if IndyCheckWindowsVersion(5, 1) then begin
UniDirAddresses := TStringList.Create;
end else begin
UniDirAddresses := nil;
end;
try
if UniDirAddresses <> nil then begin
GetUniDirAddresseses(UniDirAddresses);
end;
AAddresses.BeginUpdate;
try
Adapter := Adapters;
repeat
IPAddr := @(Adapter^.IpAddressList);
repeat
IPStr := String(IPAddr^.IpAddress.S);
if (IPStr <> '') and (IPStr <> '0.0.0.0') then
begin
if UniDirAddresses <> nil then begin
if UniDirAddresses.IndexOf(IPStr) <> -1 then begin
IPAddr := IPAddr^.Next;
Continue;
end;
end;
TIdStackLocalAddressIPv4.Create(AAddresses, IPStr, String(IPAddr^.IpMask.S));
end;
IPAddr := IPAddr^.Next;
until IPAddr = nil;
Adapter := Adapter^.Next;
until Adapter = nil;
finally
AAddresses.EndUpdate;
end;
finally
UniDirAddresses.Free;
end;
end;
finally
FreeMem(Adapters);
end;
end;
{$ELSE}
var
{$IFDEF UNICODE}
Hints: TAddrInfoW;
LAddrList, LAddrInfo: pAddrInfoW;
{$ELSE}
Hints: TAddrInfo;
LAddrList, LAddrInfo: pAddrInfo;
{$ENDIF}
RetVal: Integer;
LHostName: String;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp: TIdPlatformString;
{$ENDIF}
{$ENDIF}
begin
// Using gethostname() and gethostbyname/getaddrinfo() may not always return
// just the machine's IP addresses. Technically speaking, they will return
// the local hostname, and then return the address(es) to which that hostname
// resolves. It is possible for a machine to (a) be configured such that its
// name does not resolve to an IP, or (b) be configured such that its name
// resolves to multiple IPs, only one of which belongs to the local machine.
// For better results, we should use the Win32 API GetAdaptersInfo() and/or
// GetAdaptersAddresses() functions instead. GetAdaptersInfo() only supports
// IPv4, but GetAdaptersAddresses() supports both IPv4 and IPv6...
{$IFDEF USE_IPHLPAPI}
// try GetAdaptersAddresses() first, then fall back to GetAdaptersInfo()...
if not GetLocalAddressesByAdaptersAddresses then begin
GetLocalAddressesByAdaptersInfo;
end;
{$ELSE}
LHostName := HostName;
ZeroMemory(@Hints, SIZE_TADDRINFO);
Hints.ai_family := PF_UNSPEC; // returns both IPv4 and IPv6 addresses
Hints.ai_socktype := SOCK_STREAM;
LAddrList := nil;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp := TIdPlatformString(LHostName); // explicit convert to Ansi/Unicode
{$ENDIF}
RetVal := getaddrinfo(
{$IFDEF STRING_UNICODE_MISMATCH}PIdPlatformChar(LTemp){$ELSE}PChar(LHostName){$ENDIF},
nil, @Hints, @LAddrList);
if RetVal <> 0 then begin
RaiseSocketError(gaiErrorToWsaError(RetVal));
end;
try
AAddresses.BeginUpdate;
try
LAddrInfo := LAddrList;
repeat
case LAddrInfo^.ai_addr^.sa_family of
Id_AF_INET: begin
TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString(PSockAddrIn(LAddrInfo^.ai_addr)^.sin_addr, Id_IPv4), ''); // TODO: SubNet
end;
Id_AF_INET6: begin
TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString(PSockAddrIn6(LAddrInfo^.ai_addr)^.sin6_addr, Id_IPv6));
end;
end;
LAddrInfo := LAddrInfo^.ai_next;
until LAddrInfo = nil;
finally
AAddresses.EndUpdate;
end;
finally
freeaddrinfo(LAddrList);
end;
{$ENDIF}
end;
{ TIdStackVersionWinsock }
function TIdStackWindows.WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer;
begin
Result := Shutdown(ASocket, AHow);
end;
procedure TIdStackWindows.GetSocketName(ASocket: TIdStackSocketHandle;
var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
var
LSize: Integer;
LAddr: SOCKADDR_STORAGE;
begin
LSize := SizeOf(LAddr);
CheckForSocketError(getsockname(ASocket, IdWinsock2.PSOCKADDR(@LAddr), LSize));
case LAddr.ss_family of
Id_PF_INET4: begin
VIP := TranslateTInAddrToString(PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
VPort := ntohs(PSockAddrIn(@LAddr)^.sin_port);
VIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
VIP := TranslateTInAddrToString(PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
VPort := Ntohs(PSockAddrIn6(@LAddr)^.sin6_port);
VIPVersion := Id_IPv6;
end;
else begin
IPVersionUnsupported;
end;
end;
end;
{ TIdSocketListWindows }
procedure TIdSocketListWindows.Add(AHandle: TIdStackSocketHandle);
begin
Lock;
try
if FFDSet.fd_count >= FD_SETSIZE then begin
raise EIdStackSetSizeExceeded.Create(RSSetSizeExceeded);
end;
FFDSet.fd_array[FFDSet.fd_count] := AHandle;
Inc(FFDSet.fd_count);
finally
Unlock;
end;
end;
procedure TIdSocketListWindows.Clear;
begin
Lock;
try
fd_zero(FFDSet);
finally
Unlock;
end;
end;
function TIdSocketListWindows.ContainsSocket(AHandle: TIdStackSocketHandle): Boolean;
begin
Lock;
try
Result := fd_isset(AHandle, FFDSet);
finally
Unlock;
end;
end;
function TIdSocketListWindows.Count: Integer;
begin
Lock;
try
Result := FFDSet.fd_count;
finally
Unlock;
end;
end;
function TIdSocketListWindows.GetItem(AIndex: Integer): TIdStackSocketHandle;
begin
Result := 0;
Lock;
try
//We can't redefine AIndex to be a UInt32 because the libc Interface
//and DotNET define it as a LongInt. OS/2 defines it as a UInt16.
if (AIndex >= 0) and (u_int(AIndex) < FFDSet.fd_count) then begin
Result := FFDSet.fd_array[AIndex];
end else begin
raise EIdStackSetSizeExceeded.Create(RSSetSizeExceeded);
end;
finally
Unlock;
end;
end;
procedure TIdSocketListWindows.Remove(AHandle: TIdStackSocketHandle);
var
i: Integer;
begin
Lock;
try
{
IMPORTANT!!!
Sometimes, there may not be a member of the FDSET. If you attempt to "remove"
an item, the loop would execute once.
}
if FFDSet.fd_count > 0 then
begin
for i:= 0 to FFDSet.fd_count - 1 do
begin
if FFDSet.fd_array[i] = AHandle then
begin
Dec(FFDSet.fd_count);
FFDSet.fd_array[i] := FFDSet.fd_array[FFDSet.fd_count];
FFDSet.fd_array[FFDSet.fd_count] := 0; //extra purity
Break;
end;//if found
end;
end;
finally
Unlock;
end;
end;
function TIdStackWindows.WSTranslateSocketErrorMsg(const AErr: Integer): string;
begin
if AErr = WSAHOST_NOT_FOUND then begin
Result := IndyFormat(RSStackError, [AErr, RSStackHOST_NOT_FOUND]);
end else begin
Result := inherited WSTranslateSocketErrorMsg(AErr);
end;
end;
function TIdSocketListWindows.SelectRead(const ATimeout: Integer): Boolean;
var
LSet: TFDSet;
begin
// Windows updates this structure on return, so we need to copy it each time we need it
GetFDSet(LSet);
FDSelect(@LSet, nil, nil, ATimeout);
Result := LSet.fd_count > 0;
end;
class function TIdSocketListWindows.FDSelect(AReadSet, AWriteSet,
AExceptSet: PFDSet; const ATimeout: Integer): Boolean;
var
LResult: Integer;
LTime: TTimeVal;
LTimePtr: PTimeVal;
begin
if ATimeout = IdTimeoutInfinite then begin
LTimePtr := nil;
end else begin
LTime.tv_sec := ATimeout div 1000;
LTime.tv_usec := (ATimeout mod 1000) * 1000;
LTimePtr := @LTime;
end;
LResult := IdWinsock2.select(0, AReadSet, AWriteSet, AExceptSet, LTimePtr);
//TODO: Remove this cast
Result := GStack.CheckForSocketError(LResult) > 0;
end;
function TIdSocketListWindows.SelectReadList(var VSocketList: TIdSocketList;
const ATimeout: Integer): Boolean;
var
LSet: TFDSet;
begin
// Windows updates this structure on return, so we need to copy it each time we need it
GetFDSet(LSet);
FDSelect(@LSet, nil, nil, ATimeout);
Result := LSet.fd_count > 0;
if Result then
begin
if VSocketList = nil then begin
VSocketList := TIdSocketList.CreateSocketList;
end;
TIdSocketListWindows(VSocketList).SetFDSet(LSet);
end;
end;
class function TIdSocketListWindows.Select(AReadList, AWriteList,
AExceptList: TIdSocketList; const ATimeout: Integer): Boolean;
var
LReadSet: TFDSet;
LWriteSet: TFDSet;
LExceptSet: TFDSet;
LPReadSet: PFDSet;
LPWriteSet: PFDSet;
LPExceptSet: PFDSet;
procedure ReadSet(AList: TIdSocketList; var ASet: TFDSet; var APSet: PFDSet);
begin
if AList <> nil then begin
TIdSocketListWindows(AList).GetFDSet(ASet);
APSet := @ASet;
end else begin
APSet := nil;
end;
end;
begin
ReadSet(AReadList, LReadSet, LPReadSet);
ReadSet(AWriteList, LWriteSet, LPWriteSet);
ReadSet(AExceptList, LExceptSet, LPExceptSet);
Result := FDSelect(LPReadSet, LPWriteSet, LPExceptSet, ATimeout);
if AReadList <> nil then begin
TIdSocketListWindows(AReadList).SetFDSet(LReadSet);
end;
if AWriteList <> nil then begin
TIdSocketListWindows(AWriteList).SetFDSet(LWriteSet);
end;
if AExceptList <> nil then begin
TIdSocketListWindows(AExceptList).SetFDSet(LExceptSet);
end;
end;
procedure TIdSocketListWindows.SetFDSet(var VSet: TFDSet);
begin
Lock;
try
FFDSet := VSet;
finally
Unlock;
end;
end;
procedure TIdSocketListWindows.GetFDSet(var VSet: TFDSet);
begin
Lock;
try
VSet := FFDSet;
finally
Unlock;
end;
end;
procedure TIdStackWindows.SetBlocking(ASocket: TIdStackSocketHandle;
const ABlocking: Boolean);
var
LValue: UInt32;
begin
LValue := UInt32(not ABlocking);
CheckForSocketError(ioctlsocket(ASocket, FIONBIO, LValue));
end;
function TIdSocketListWindows.Clone: TIdSocketList;
begin
Result := TIdSocketListWindows.Create;
try
Lock;
try
TIdSocketListWindows(Result).SetFDSet(FFDSet);
finally
Unlock;
end;
except
FreeAndNil(Result);
raise;
end;
end;
function TIdStackWindows.WouldBlock(const AResult: Integer): Boolean;
begin
Result := CheckForSocketError(AResult, [WSAEWOULDBLOCK]) <> 0;
end;
function TIdStackWindows.HostByName(const AHostName: string;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
{$IFNDEF WINCE}
type
TaPInAddr = array[0..250] of PInAddr;
PaPInAddr = ^TaPInAddr;
{$ENDIF}
var
{$IFDEF UNICODE}
LAddrInfo: pAddrInfoW;
Hints: TAddrInfoW;
{$ELSE}
LAddrInfo: pAddrInfo;
Hints: TAddrInfo;
{$ENDIF}
RetVal: Integer;
LHostName: String;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp: TIdPlatformString;
{$ENDIF}
begin
if not (AIPVersion in [Id_IPv4, Id_IPv6]) then begin
IPVersionUnsupported;
end;
ZeroMemory(@Hints, SIZE_TADDRINFO);
Hints.ai_family := IdIPFamily[AIPVersion];
Hints.ai_socktype := SOCK_STREAM;
LAddrInfo := nil;
if UseIDNAPI then begin
LHostName := IDNToPunnyCode(
{$IFDEF STRING_IS_UNICODE}
AHostName
{$ELSE}
TIdUnicodeString(AHostName) // explicit convert to Unicode
{$ENDIF}
);
end else begin
LHostName := AHostName;
end;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp := TIdPlatformString(LHostName); // explicit convert to Ansi/Unicode
{$ENDIF}
RetVal := getaddrinfo(
{$IFDEF STRING_UNICODE_MISMATCH}PIdPlatformChar(LTemp){$ELSE}PChar(LHostName){$ENDIF},
nil, @Hints, @LAddrInfo);
if RetVal <> 0 then begin
RaiseSocketError(gaiErrorToWsaError(RetVal));
end;
try
if AIPVersion = Id_IPv4 then begin
Result := TranslateTInAddrToString(PSockAddrIn(LAddrInfo^.ai_addr)^.sin_addr, Id_IPv4)
end else begin
Result := TranslateTInAddrToString(PSockAddrIn6(LAddrInfo^.ai_addr)^.sin6_addr, Id_IPv6);
end;
finally
freeaddrinfo(LAddrInfo);
end;
end;
procedure TIdStackWindows.Connect(const ASocket: TIdStackSocketHandle;
const AIP: string; const APort: TIdPort;
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
var
LAddr: SOCKADDR_STORAGE;
LSize: Integer;
begin
FillChar(LAddr, SizeOf(LAddr), 0);
case AIPVersion of
Id_IPv4: begin
PSockAddrIn(@LAddr)^.sin_family := Id_PF_INET4;
TranslateStringToTInAddr(AIP, PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
PSockAddrIn(@LAddr)^.sin_port := htons(APort);
LSize := SIZE_TSOCKADDRIN;
end;
Id_IPv6: begin
PSockAddrIn6(@LAddr)^.sin6_family := Id_PF_INET6;
TranslateStringToTInAddr(AIP, PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
PSockAddrIn6(@LAddr)^.sin6_port := htons(APort);
LSize := SIZE_TSOCKADDRIN6;
end;
else begin
LSize := 0; // avoid warning
IPVersionUnsupported;
end;
end;
CheckForSocketError(IdWinsock2.connect(ASocket, IdWinsock2.PSOCKADDR(@LAddr), LSize));
end;
procedure TIdStackWindows.GetPeerName(ASocket: TIdStackSocketHandle;
var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
var
LSize: Integer;
LAddr: SOCKADDR_STORAGE;
begin
LSize := SizeOf(LAddr);
CheckForSocketError(IdWinsock2.getpeername(ASocket, IdWinsock2.PSOCKADDR(@LAddr), LSize));
case LAddr.ss_family of
Id_PF_INET4: begin
VIP := TranslateTInAddrToString(PSockAddrIn(@LAddr)^.sin_addr, Id_IPv4);
VPort := ntohs(PSockAddrIn(@LAddr)^.sin_port);
VIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
VIP := TranslateTInAddrToString(PSockAddrIn6(@LAddr)^.sin6_addr, Id_IPv6);
VPort := ntohs(PSockAddrIn6(@LAddr)^.sin6_port);
VIPVersion := Id_IPv6;
end;
else begin
IPVersionUnsupported;
end;
end;
end;
procedure TIdStackWindows.Disconnect(ASocket: TIdStackSocketHandle);
begin
// Windows uses Id_SD_Send, Linux should use Id_SD_Both
WSShutdown(ASocket, Id_SD_Send);
// SO_LINGER is false - socket may take a little while to actually close after this
WSCloseSocket(ASocket);
end;
procedure TIdStackWindows.{$IFDEF VCL_XE3_OR_ABOVE}GetSocketOption{$ELSE}WSGetSocketOption{$ENDIF}
(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption;
var AOptVal; var AOptLen: Integer);
begin
CheckForSocketError(
getsockopt(ASocket, ALevel, AOptName,
{$IFNDEF HAS_PAnsiChar}
// TODO: use TPtrWrapper here?
{PAnsiChar}@AOptVal
{$ELSE}
PAnsiChar(@AOptVal)
{$ENDIF},
AOptLen
)
);
end;
procedure TIdStackWindows.{$IFDEF VCL_XE3_OR_ABOVE}SetSocketOption{$ELSE}WSSetSocketOption{$ENDIF}
(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption;
const AOptVal; const AOptLen: Integer);
begin
CheckForSocketError(
setsockopt(ASocket, ALevel, Aoptname,
{$IFNDEF HAS_PAnsiChar}
// TODO: use TPtrWrapper here?
{PAnsiChar}@AOptVal
{$ELSE}
PAnsiChar(@AOptVal)
{$ENDIF},
AOptLen
)
);
end;
{
based on
http://groups.google.com/groups?q=Winsock2+Delphi+protocol&hl=en&lr=&ie=UTF-8&oe=utf-8&selm=3cebe697_2%40dnews&rnum=9
}
function TIdStackWindows.SupportsIPv6: Boolean;
var
LLen : DWORD;
LPInfo, LPCurPtr: LPWSAPROTOCOL_INFO;
LCount : Integer;
i : Integer;
begin
Result := False;
LPInfo := nil;
try
LLen := 0;
// Note: WSAEnumProtocols returns -1 when it is just called to get the needed Buffer Size!
repeat
LCount := IdWinsock2.WSAEnumProtocols(nil, LPInfo, LLen);
if LCount = SOCKET_ERROR then
begin
if WSAGetLastError() <> WSAENOBUFS then begin
Exit;
end;
ReallocMem(LPInfo, LLen);
end else begin
Break;
end;
until False;
if LCount > 0 then
begin
LPCurPtr := LPInfo;
for i := 0 to LCount-1 do
begin
if LPCurPtr^.iAddressFamily = AF_INET6 then
begin
Result := True;
Exit;
end;
Inc(LPCurPtr);
end;
end;
finally
FreeMem(LPInfo);
end;
end;
function TIdStackWindows.IOControl(const s: TIdStackSocketHandle;
const cmd: UInt32; var arg: UInt32): Integer;
begin
Result := IdWinsock2.ioctlsocket(s, cmd, arg);
end;
procedure TIdStackWindows.WSQuerryIPv6Route(ASocket: TIdStackSocketHandle;
const AIP: String; const APort: TIdPort; var VSource; var VDest);
var
Llocalif : TSockAddrIn6;
LAddr : TSockAddrIn6;
Bytes : DWORD;
begin
//make our LAddrInfo structure
FillChar(LAddr, SizeOf(LAddr), 0);
LAddr.sin6_family := AF_INET6;
TranslateStringToTInAddr(AIP, LAddr.sin6_addr, Id_IPv6);
Move(LAddr.sin6_addr, VDest, SizeOf(in6_addr));
LAddr.sin6_port := htons(APort);
// Find out which local interface for the destination
// RLebeau: in XE4+, PDWORD is NOT defined as ^DWORD, so we have to use a type-cast!
CheckForSocketError(WSAIoctl(ASocket, SIO_ROUTING_INTERFACE_QUERY,
@LAddr, SizeOf(LAddr), @Llocalif, SizeOf(Llocalif), PDWORD(@Bytes), nil, nil));
Move(Llocalif.sin6_addr, VSource, SizeOf(in6_addr));
end;
procedure TIdStackWindows.WriteChecksum(s: TIdStackSocketHandle;
var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
const APort: TIdPort; const AIPVersion: TIdIPVersion);
begin
case AIPVersion of
Id_IPv4 : CopyTIdUInt16(HostToLittleEndian(CalcCheckSum(VBuffer)), VBuffer, AOffset);
Id_IPv6 : WriteChecksumIPv6(s, VBuffer, AOffset, AIP, APort);
else
IPVersionUnsupported;
end;
end;
procedure TIdStackWindows.WriteChecksumIPv6(s: TIdStackSocketHandle;
var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
const APort: TIdPort);
var
LSource : TIdIn6Addr;
LDest : TIdIn6Addr;
LTmp : TIdBytes;
LIdx : Integer;
LC : UInt32;
{
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Source Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
+ +
| |
+ Destination Address +
| |
+ +
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Upper-Layer Packet Length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| zero | Next Header |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
}
begin
WSQuerryIPv6Route(s, AIP, APort, LSource, LDest);
SetLength(LTmp, 40+Length(VBuffer));
//16
Move(LSource, LTmp[0], SIZE_TSOCKADDRIN6);
LIdx := SIZE_TSOCKADDRIN6;
//32
Move(LDest, LTmp[LIdx], SIZE_TSOCKADDRIN6);
Inc(LIdx, SIZE_TSOCKADDRIN6);
//use a word so you don't wind up using the wrong network byte order function
LC := UInt32(Length(VBuffer));
CopyTIdUInt32(HostToNetwork(LC), LTmp, LIdx);
Inc(LIdx, 4);
//36
//zero the next three bytes
FillChar(LTmp[LIdx], 3, 0);
Inc(LIdx, 3);
//next header (protocol type determines it
LTmp[LIdx] := Id_IPPROTO_ICMPV6; // Id_IPPROTO_ICMP6;
Inc(LIdx);
//combine the two
CopyTIdBytes(VBuffer, 0, LTmp, LIdx, Length(VBuffer));
//zero out the checksum field
CopyTIdUInt16(0, LTmp, LIdx+AOffset);
CopyTIdUInt16(HostToLittleEndian(CalcCheckSum(LTmp)), VBuffer, AOffset);
end;
function TIdStackWindows.ReceiveMsg(ASocket: TIdStackSocketHandle; var VBuffer : TIdBytes;
APkt: TIdPacketInfo): UInt32;
var
LIP : String;
LPort : TIdPort;
LIPVersion : TIdIPVersion;
{Windows CE does not have WSARecvMsg}
{$IFNDEF WINCE}
LSize: PtrUInt;
LAddr: TIdBytes;
PAddr: PSOCKADDR_STORAGE;
LMsg : TWSAMSG;
LMsgBuf : TWSABUF;
LControl : TIdBytes;
LCurCmsg : LPWSACMSGHDR; //for iterating through the control buffer
PPktInfo: PInPktInfo;
PPktInfo6: PIn6PktInfo;
{$ENDIF}
begin
{$IFNDEF WINCE}
//This runs only on WIndows XP or later
// XP 5.1 at least, Vista 6.0
if IndyCheckWindowsVersion(5, 1) then
begin
//we call the macro twice because we specified two possible structures.
//Id_IPV6_HOPLIMIT and Id_IPV6_PKTINFO
LSize := WSA_CMSG_LEN(WSA_CMSG_LEN(Length(VBuffer)));
SetLength(LControl, LSize);
LMsgBuf.len := Length(VBuffer); // Length(VMsgData);
LMsgBuf.buf := PAnsiChar(Pointer(VBuffer)); // @VMsgData[0];
FillChar(LMsg, SIZE_TWSAMSG, 0);
LMsg.lpBuffers := @LMsgBuf;
LMsg.dwBufferCount := 1;
LMsg.Control.Len := LSize;
LMsg.Control.buf := PAnsiChar(Pointer(LControl));
// RLebeau: despite that we are not performing an overlapped I/O operation,
// WSARecvMsg() does not like the SOCKADDR variable being allocated on the
// stack, at least on my tests with Windows 7. So we will allocate it on
// the heap instead to keep WinSock happy...
SetLength(LAddr, SizeOf(SOCKADDR_STORAGE));
PAddr := PSOCKADDR_STORAGE(@LAddr[0]);
LMsg.name := IdWinsock2.PSOCKADDR(PAddr);
LMsg.namelen := Length(LAddr);
CheckForSocketError(WSARecvMsg(ASocket, @LMsg, Result, nil, nil));
APkt.Reset;
case PAddr^.ss_family of
Id_PF_INET4: begin
APkt.SourceIP := TranslateTInAddrToString(PSockAddrIn(PAddr)^.sin_addr, Id_IPv4);
APkt.SourcePort := ntohs(PSockAddrIn(PAddr)^.sin_port);
APkt.SourceIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
APkt.SourceIP := TranslateTInAddrToString(PSockAddrIn6(PAddr)^.sin6_addr, Id_IPv6);
APkt.SourcePort := ntohs(PSockAddrIn6(PAddr)^.sin6_port);
APkt.SourceIPVersion := Id_IPv6;
end;
else begin
Result := 0; // avoid warning
IPVersionUnsupported;
end;
end;
LCurCmsg := nil;
repeat
LCurCmsg := WSA_CMSG_NXTHDR(@LMsg, LCurCmsg);
if LCurCmsg = nil then begin
Break;
end;
case LCurCmsg^.cmsg_type of
IP_PKTINFO : //done this way because IPV6_PKTINF and IP_PKTINFO are both 19
begin
case PAddr^.ss_family of
Id_PF_INET4: begin
PPktInfo := PInPktInfo(WSA_CMSG_DATA(LCurCmsg));
APkt.DestIP := TranslateTInAddrToString(PPktInfo^.ipi_addr, Id_IPv4);
APkt.DestIF := PPktInfo^.ipi_ifindex;
APkt.DestIPVersion := Id_IPv4;
end;
Id_PF_INET6: begin
PPktInfo6 := PIn6PktInfo(WSA_CMSG_DATA(LCurCmsg));
APkt.DestIP := TranslateTInAddrToString(PPktInfo6^.ipi6_addr, Id_IPv6);
APkt.DestIF := PPktInfo6^.ipi6_ifindex;
APkt.DestIPVersion := Id_IPv6;
end;
end;
end;
Id_IPV6_HOPLIMIT :
begin
APkt.TTL := WSA_CMSG_DATA(LCurCmsg)^;
end;
end;
until False;
end else
begin
{$ENDIF}
Result := RecvFrom(ASocket, VBuffer, Length(VBuffer), 0, LIP, LPort, LIPVersion);
APkt.Reset;
APkt.SourceIP := LIP;
APkt.SourcePort := LPort;
APkt.SourceIPVersion := LIPVersion;
APkt.DestIPVersion := LIPVersion;
{$IFNDEF WINCE}
end;
{$ENDIF}
end;
function TIdStackWindows.CheckIPVersionSupport(const AIPVersion: TIdIPVersion): Boolean;
var
LTmpSocket: TIdStackSocketHandle;
begin
LTmpSocket := WSSocket(IdIPFamily[AIPVersion], Id_SOCK_STREAM, Id_IPPROTO_IP);
Result := LTmpSocket <> Id_INVALID_SOCKET;
if Result then begin
WSCloseSocket(LTmpSocket);
end;
end;
{$IFNDEF WINCE}
{
This is somewhat messy but I wanted to do things this way to support Int64
file sizes.
}
function ServeFile(ASocket: TIdStackSocketHandle; const AFileName: string): Int64;
var
LFileHandle: THandle;
LSize: LARGE_INTEGER;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp: TIdPlatformString;
{$ENDIF}
begin
Result := 0;
{$IFDEF STRING_UNICODE_MISMATCH}
LTemp := TIdPlatformString(AFileName); // explicit convert to Ansi/Unicode
{$ENDIF}
LFileHandle := CreateFile(
{$IFDEF STRING_UNICODE_MISMATCH}PIdPlatformChar(LTemp){$ELSE}PChar(AFileName){$ENDIF},
GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL or FILE_FLAG_SEQUENTIAL_SCAN, 0);
if LFileHandle <> INVALID_HANDLE_VALUE then
begin
try
if TransmitFile(ASocket, LFileHandle, 0, 0, nil, nil, 0) then
begin
if Assigned(GetFileSizeEx) then
begin
if not GetFileSizeEx(LFileHandle, LSize) then begin
Exit;
end;
end else
begin
LSize.LowPart := GetFileSize(LFileHandle, @LSize.HighPart);
if (LSize.LowPart = $FFFFFFFF) and (GetLastError() <> 0) then begin
Exit;
end;
end;
Result := LSize.QuadPart;
end;
finally
CloseHandle(LFileHandle);
end;
end;
end;
{$ENDIF}
procedure TIdStackWindows.SetKeepAliveValues(ASocket: TIdStackSocketHandle;
const AEnabled: Boolean; const ATimeMS, AInterval: Integer);
var
ka: _tcp_keepalive;
Bytes: DWORD;
begin
// SIO_KEEPALIVE_VALS is supported on Win2K+ and WinCE 4.x only
if AEnabled and IndyCheckWindowsVersion({$IFDEF WINCE}4{$ELSE}5{$ENDIF}) then
begin
ka.onoff := 1;
ka.keepalivetime := ATimeMS;
ka.keepaliveinterval := AInterval;
// RLebeau: in XE4+, PDWORD is NOT defined as ^DWORD, so we have to use a type-cast!
WSAIoctl(ASocket, SIO_KEEPALIVE_VALS, @ka, SizeOf(ka), nil, 0, PDWORD(@Bytes), nil, nil);
end else begin
SetSocketOption(ASocket, Id_SOL_SOCKET, Id_SO_KEEPALIVE, iif(AEnabled, 1, 0));
end;
end;
initialization
GStarted := False;
GSocketListClass := TIdSocketListWindows;
// Check if we are running under windows NT
{$IFNDEF WINCE}
if IndyWindowsPlatform = VER_PLATFORM_WIN32_NT then begin
GetFileSizeEx := Windows.GetProcAddress(GetModuleHandle('Kernel32.dll'), 'GetFileSizeEx');
GServeFileProc := ServeFile;
end;
{$ENDIF}
{$IFDEF USE_IPHLPAPI}
InitializeIPHelperStubs;
{$ENDIF}
finalization
IdWship6.CloseLibrary;
UninitializeWinSock;
{$IFDEF USE_IPHLPAPI}
UninitializeIPHelperAPI;
{$ENDIF}
GStarted := False;
end.