{ $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.