1428 lines
44 KiB
Plaintext
1428 lines
44 KiB
Plaintext
unit IdStackVCLPosix;
|
|
|
|
interface
|
|
|
|
{$I IdCompilerDefines.inc}
|
|
|
|
{IMPORTANT!!!
|
|
|
|
Platform warnings in this unit should be disabled because Indy we have no
|
|
intention of porting this unit to Windows or any non-Unix-like operating system.
|
|
|
|
Any differences between Unix-like operating systems have to dealt with in other
|
|
ways.
|
|
}
|
|
|
|
{$I IdSymbolPlatformOff.inc}
|
|
{$I IdUnitPlatformOff.inc}
|
|
|
|
uses
|
|
Classes,
|
|
IdCTypes,
|
|
Posix.SysSelect,
|
|
Posix.SysSocket,
|
|
Posix.SysTime,
|
|
IdStack,
|
|
IdStackConsts,
|
|
IdGlobal,
|
|
IdStackBSDBase;
|
|
|
|
type
|
|
{$IFDEF USE_VCL_POSIX}
|
|
{$IFDEF ANDROID}
|
|
EIdAccessWifiStatePermissionNeeded = class(EIdAndroidPermissionNeeded);
|
|
EIdAccessNetworkStatePermissionNeeded = class(EIdAndroidPermissionNeeded);
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
TIdSocketListVCLPosix = class (TIdSocketList)
|
|
protected
|
|
FCount: Integer;
|
|
FFDSet: fd_set;
|
|
//
|
|
class function FDSelect(AReadSet, AWriteSet,
|
|
AExceptSet: Pfd_set; const ATimeout: Integer): Integer;
|
|
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: fd_set);
|
|
procedure SetFDSet(var VSet: fd_set);
|
|
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;
|
|
|
|
TIdStackVCLPosix = class(TIdStackBSDBase)
|
|
protected
|
|
procedure WriteChecksumIPv6(s: TIdStackSocketHandle; var VBuffer: TIdBytes;
|
|
const AOffset: Integer; const AIP: String; const APort: TIdPort);
|
|
function GetLastError: Integer;
|
|
procedure SetLastError(const AError: Integer);
|
|
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
|
|
constructor Create; override;
|
|
destructor Destroy; override;
|
|
procedure SetBlocking(ASocket: TIdStackSocketHandle; const ABlocking: Boolean); override;
|
|
function WouldBlock(const AResult: Integer): Boolean; override;
|
|
function Accept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: TIdPort;
|
|
var VIPVersion: TIdIPVersion): TIdStackSocketHandle; 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;
|
|
function HostByAddress(const AAddress: string;
|
|
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
|
|
function WSGetLastError: Integer; override;
|
|
procedure WSSetLastError(const AErr : Integer); override;
|
|
function WSGetServByName(const AServiceName: string): TIdPort; override;
|
|
procedure AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings); 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;
|
|
procedure Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer); override;
|
|
function HostToNetwork(AValue: UInt16): UInt16; override;
|
|
function NetworkToHost(AValue: UInt16): UInt16; override;
|
|
function HostToNetwork(AValue: UInt32): UInt32; override;
|
|
function NetworkToHost(AValue: UInt32): UInt32; override;
|
|
function HostToNetwork(AValue: TIdUInt64): TIdUInt64; override;
|
|
function NetworkToHost(AValue: TIdUInt64): TIdUInt64; 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;
|
|
procedure Disconnect(ASocket: TIdStackSocketHandle); 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 SupportsIPv6: Boolean; overload; override;
|
|
function CheckIPVersionSupport(const AIPVersion: TIdIPVersion): boolean; override;
|
|
//In Windows, this writes a checksum into a buffer. In Linux, it would probably
|
|
//simply have the kernal write the checksum with something like this (RFC 2292):
|
|
//
|
|
// int offset = 2;
|
|
// setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset));
|
|
//
|
|
// Note that this should be called
|
|
//IMMEDIATELY before you do a SendTo because the Local IPv6 address might change
|
|
|
|
procedure WriteChecksum(s : TIdStackSocketHandle; var VBuffer : TIdBytes;
|
|
const AOffset : Integer; const AIP : String; const APort : TIdPort;
|
|
const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
|
|
function IOControl(const s: TIdStackSocketHandle; const cmd: UInt32;
|
|
var arg: UInt32): Integer; override;
|
|
|
|
procedure GetLocalAddressList(AAddresses: TIdStackLocalAddressList); override;
|
|
end;
|
|
|
|
implementation
|
|
|
|
{$O-}
|
|
|
|
uses
|
|
IdResourceStrings,
|
|
IdResourceStringsUnix,
|
|
IdResourceStringsVCLPosix,
|
|
IdException,
|
|
IdVCLPosixSupplemental,
|
|
Posix.Base,
|
|
Posix.ArpaInet,
|
|
Posix.Errno,
|
|
Posix.NetDB,
|
|
{$IFDEF HAS_getifaddrs}
|
|
Posix.NetIf,
|
|
{$ENDIF}
|
|
Posix.NetinetIn,
|
|
Posix.StrOpts,
|
|
Posix.SysTypes,
|
|
Posix.SysUio,
|
|
Posix.Unistd,
|
|
SysUtils;
|
|
|
|
{$UNDEF HAS_MSG_NOSIGNAL}
|
|
{$IFDEF LINUX} //this LINUX ifdef is deliberate
|
|
{$DEFINE HAS_MSG_NOSIGNAL}
|
|
{$ENDIF}
|
|
|
|
|
|
const
|
|
{$IFDEF HAS_MSG_NOSIGNAL}
|
|
//fancy little trick since OS X does not have MSG_NOSIGNAL
|
|
Id_MSG_NOSIGNAL = MSG_NOSIGNAL;
|
|
{$ELSE}
|
|
Id_MSG_NOSIGNAL = 0;
|
|
{$ENDIF}
|
|
Id_WSAEPIPE = EPIPE;
|
|
|
|
|
|
|
|
//helper functions for some structs
|
|
|
|
{Note: These hide an API difference in structures.
|
|
|
|
BSD 4.4 introduced a minor API change. sa_family was changed from a 16bit
|
|
word to an 8 bit byteee and an 8 bit byte feild named sa_len was added.
|
|
|
|
}
|
|
procedure InitSockAddr_In(var VSock : SockAddr_In);
|
|
{$IFDEF USE_INLINE} inline; {$ENDIF}
|
|
begin
|
|
FillChar(VSock, SizeOf(SockAddr_In), 0);
|
|
VSock.sin_family := PF_INET;
|
|
{$IFDEF SOCK_HAS_SINLEN}
|
|
VSock.sin_len := SizeOf(SockAddr_In);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure InitSockAddr_in6(var VSock : SockAddr_in6);
|
|
{$IFDEF USE_INLINE} inline; {$ENDIF}
|
|
begin
|
|
FillChar(VSock, SizeOf(SockAddr_in6), 0);
|
|
{$IFDEF SOCK_HAS_SINLEN}
|
|
VSock.sin6_len := SizeOf(SockAddr_in6);
|
|
{$ENDIF}
|
|
VSock.sin6_family := PF_INET6;
|
|
end;
|
|
//
|
|
|
|
{ TIdSocketListVCLPosix }
|
|
|
|
procedure TIdSocketListVCLPosix.Add(AHandle: TIdStackSocketHandle);
|
|
begin
|
|
Lock;
|
|
try
|
|
if not __FD_ISSET(AHandle, FFDSet) then begin
|
|
if Count >= FD_SETSIZE then begin
|
|
raise EIdStackSetSizeExceeded.Create(RSSetSizeExceeded);
|
|
end;
|
|
__FD_SET(AHandle, FFDSet);
|
|
Inc(FCount);
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdSocketListVCLPosix.Clear;
|
|
begin
|
|
Lock;
|
|
try
|
|
__FD_ZERO(FFDSet);
|
|
FCount := 0;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TIdSocketListVCLPosix.Clone: TIdSocketList;
|
|
begin
|
|
Result := TIdSocketListVCLPosix.Create;
|
|
try
|
|
Lock;
|
|
try
|
|
TIdSocketListVCLPosix(Result).SetFDSet(FFDSet);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
except
|
|
FreeAndNil(Result);
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
function TIdSocketListVCLPosix.ContainsSocket(
|
|
AHandle: TIdStackSocketHandle): Boolean;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := __FD_ISSET(AHandle, FFDSet);
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TIdSocketListVCLPosix.Count: Integer;
|
|
begin
|
|
Lock;
|
|
try
|
|
Result := FCount;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
class function TIdSocketListVCLPosix.FDSelect(AReadSet, AWriteSet,
|
|
AExceptSet: Pfd_set; const ATimeout: Integer): Integer;
|
|
var
|
|
LTime: TimeVal;
|
|
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;
|
|
// TODO: calculate the actual nfds value based on the Sets provided...
|
|
Result := Posix.SysSelect.select(FD_SETSIZE, AReadSet, AWriteSet, AExceptSet, LTimePtr);
|
|
end;
|
|
|
|
procedure TIdSocketListVCLPosix.GetFDSet(var VSet: fd_set);
|
|
begin
|
|
Lock;
|
|
try
|
|
VSet := FFDSet;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
function TIdSocketListVCLPosix.GetItem(AIndex: Integer): TIdStackSocketHandle;
|
|
var
|
|
LIndex, i: Integer;
|
|
begin
|
|
Result := 0;
|
|
Lock;
|
|
try
|
|
LIndex := 0;
|
|
//? use FMaxHandle div x
|
|
for i:= 0 to FD_SETSIZE - 1 do begin
|
|
if __FD_ISSET(i, FFDSet) then begin
|
|
if LIndex = AIndex then begin
|
|
Result := i;
|
|
Break;
|
|
end;
|
|
Inc(LIndex);
|
|
end;
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdSocketListVCLPosix.Remove(AHandle: TIdStackSocketHandle);
|
|
begin
|
|
Lock;
|
|
try
|
|
if __FD_ISSET(AHandle, FFDSet) then begin
|
|
Dec(FCount);
|
|
__FD_CLR(AHandle, FFDSet);
|
|
end;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
class function TIdSocketListVCLPosix.Select(AReadList, AWriteList,
|
|
AExceptList: TIdSocketList; const ATimeout: Integer): Boolean;
|
|
|
|
var
|
|
LReadSet: fd_set;
|
|
LWriteSet: fd_set;
|
|
LExceptSet: fd_set;
|
|
LPReadSet: Pfd_set;
|
|
LPWriteSet: Pfd_set;
|
|
LPExceptSet: Pfd_set;
|
|
|
|
procedure ReadSet(AList: TIdSocketList; var ASet: fd_set; var APSet: Pfd_set);
|
|
begin
|
|
if AList <> nil then begin
|
|
TIdSocketListVCLPosix(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) >0;
|
|
//
|
|
if AReadList <> nil then begin
|
|
TIdSocketListVCLPosix(AReadList).SetFDSet(LReadSet);
|
|
end;
|
|
if AWriteList <> nil then begin
|
|
TIdSocketListVCLPosix(AWriteList).SetFDSet(LWriteSet);
|
|
end;
|
|
if AExceptList <> nil then begin
|
|
TIdSocketListVCLPosix(AExceptList).SetFDSet(LExceptSet);
|
|
end;
|
|
end;
|
|
|
|
function TIdSocketListVCLPosix.SelectRead(const ATimeout: Integer): Boolean;
|
|
var
|
|
LSet: fd_set;
|
|
begin
|
|
Lock;
|
|
try
|
|
LSet := FFDSet;
|
|
// select() updates this structure on return,
|
|
// so we need to copy it each time we need it
|
|
finally
|
|
Unlock;
|
|
end;
|
|
Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
|
|
end;
|
|
|
|
function TIdSocketListVCLPosix.SelectReadList(var VSocketList: TIdSocketList;
|
|
const ATimeout: Integer): Boolean;
|
|
var
|
|
LSet: fd_set;
|
|
begin
|
|
Lock;
|
|
try
|
|
LSet := FFDSet;
|
|
// select() updates this structure on return,
|
|
// so we need to copy it each time we need it
|
|
finally
|
|
Unlock;
|
|
end;
|
|
Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
|
|
if Result then begin
|
|
if VSocketList = nil then begin
|
|
VSocketList := TIdSocketList.CreateSocketList;
|
|
end;
|
|
TIdSocketListVCLPosix(VSocketList).SetFDSet(LSet);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdSocketListVCLPosix.SetFDSet(var VSet: fd_set);
|
|
begin
|
|
Lock;
|
|
try
|
|
FFDSet := VSet;
|
|
finally
|
|
Unlock;
|
|
end;
|
|
end;
|
|
|
|
{ TIdStackVCLPosix }
|
|
|
|
{
|
|
IMPORTANT!!!
|
|
|
|
Throughout much of this code, you will see stuff such as:
|
|
|
|
var
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
|
|
This is just a fancy way to do typecasting with various types of address type.
|
|
Many functions take a sockaddr parameter but that parameter is typecast for various
|
|
address types. The structures mentioned above are designed just for such
|
|
typecasting. The reason we use sockaddr_storage instead of sockaddr is that
|
|
we need something that is guaranteed to be able to contain various address types
|
|
and sockaddr would be too short for some of them and we can't know what
|
|
someone else will add to Indy as time goes by.
|
|
}
|
|
|
|
function TIdStackVCLPosix.Accept(ASocket: TIdStackSocketHandle; var VIP: string;
|
|
var VPort: TIdPort; var VIPVersion: TIdIPVersion): TIdStackSocketHandle;
|
|
var
|
|
LN: socklen_t;
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
|
|
begin
|
|
LN := SizeOf(LAddrStore);
|
|
Result := Posix.SysSocket.accept(ASocket, LAddr, LN);
|
|
if Result <> -1 then begin
|
|
case LAddrStore.ss_family of
|
|
Id_PF_INET4: begin
|
|
VIP := TranslateTInAddrToString( LAddrIPv4.sin_addr, Id_IPv4);
|
|
VPort := ntohs(LAddrIPv4.sin_port);
|
|
VIPVersion := Id_IPV4;
|
|
end;
|
|
Id_PF_INET6: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv6.sin6_addr, Id_IPv6);
|
|
VPort := ntohs(LAddrIPv6.sin6_port);
|
|
VIPVersion := Id_IPV6;
|
|
end
|
|
else begin
|
|
__close(Result);
|
|
Result := Id_INVALID_SOCKET;
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
end else begin
|
|
if GetLastError = EBADF then begin
|
|
SetLastError(EINTR);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
{$IFDEF HAS_getifaddrs}
|
|
function getifaddrs(ifap: pifaddrs): Integer; cdecl; external libc name _PU + 'getifaddrs'; {do not localize}
|
|
procedure freeifaddrs(ifap: pifaddrs); cdecl; external libc name _PU + 'freeifaddrs'; {do not localize}
|
|
{$ELSE}
|
|
{$IFDEF ANDROID}
|
|
// TODO: implement getifaddrs() manually using code from https://github.com/kmackay/android-ifaddrs
|
|
{.$DEFINE HAS_getifaddrs}
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
|
|
procedure TIdStackVCLPosix.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
|
|
var
|
|
{$IFDEF HAS_getifaddrs}
|
|
LAddrList, LAddrInfo: pifaddrs;
|
|
LSubNetStr: String;
|
|
{$ELSE}
|
|
LRetVal: Integer;
|
|
LHostName: string;
|
|
Hints: AddrInfo;
|
|
LAddrList, LAddrInfo: pAddrInfo;
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
M: TMarshaller;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
begin
|
|
// TODO: Using gethostname() and getaddrinfo() like this 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 getifaddrs() on platforms that support it...
|
|
|
|
{$IFDEF HAS_getifaddrs}
|
|
|
|
if getifaddrs(@LAddrList) = 0 then // TODO: raise an exception if it fails
|
|
try
|
|
AAddresses.BeginUpdate;
|
|
try
|
|
LAddrInfo := LAddrList;
|
|
repeat
|
|
if (LAddrInfo^.ifa_addr <> nil) and ((LAddrInfo^.ifa_flags and IFF_LOOPBACK) = 0) then
|
|
begin
|
|
case LAddrInfo^.ifa_addr^.sa_family of
|
|
Id_PF_INET4: begin
|
|
if LAddrInfo^.ifa_netmask <> nil then begin
|
|
LSubNetStr := TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ifa_netmask)^.sin_addr, Id_IPv4);
|
|
end else begin
|
|
LSubNetStr := '';
|
|
end;
|
|
TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ifa_addr)^.sin_addr, Id_IPv4), LSubNetStr);
|
|
end;
|
|
Id_PF_INET6: begin
|
|
TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In6(LAddrInfo^.ifa_addr)^.sin6_addr, Id_IPv6));
|
|
end;
|
|
end;
|
|
end;
|
|
LAddrInfo := LAddrInfo^.ifa_next;
|
|
until LAddrInfo = nil;
|
|
finally
|
|
AAddresses.EndUpdate;
|
|
end;
|
|
finally
|
|
freeifaddrs(LAddrList);
|
|
end;
|
|
|
|
{$ELSE}
|
|
|
|
// TODO: on Android, either implement getifaddrs() (https://github.com/kmackay/android-ifaddrs)
|
|
// or use the Java API to enumerate the local network interfaces and their IP addresses, eg:
|
|
{
|
|
var
|
|
en, enumIpAddr: Enumeration;
|
|
intf: NetworkInterface;
|
|
inetAddress: InetAddress;
|
|
begin
|
|
try
|
|
en := NetworkInterface.getNetworkInterfaces;
|
|
if en.hasMoreElements then begin
|
|
AAddresses.BeginUpdate;
|
|
try
|
|
repeat
|
|
intf := en.nextElement;
|
|
enumIpAddr := intf.getInetAddresses();
|
|
while enumIpAddr.hasMoreElements do begin
|
|
inetAddress := enumIpAddr.nextElement;
|
|
if not inetAddress.isLoopbackAddress then begin
|
|
if (inetAddress instanceof Inet4Address) then begin
|
|
TIdStackLocalAddressIPv4.Create(AAddresses, inetAddress.getHostAddress.toString, '');
|
|
end
|
|
else if (inetAddress instanceof Inet6Address) then begin
|
|
TIdStackLocalAddressIPv6.Create(AAddresses, inetAddress.getHostAddress.toString);
|
|
end;
|
|
end;
|
|
end;
|
|
until not en.hasMoreElements;
|
|
finally
|
|
AAddresses.EndUpdate;
|
|
end;
|
|
end;
|
|
except
|
|
if not HasAndroidPermission('android.permission.INTERNET') then begin
|
|
IndyRaiseOuterException(EIdInternetPermissionNeeded.CreateError(0, ''));
|
|
end;
|
|
if not HasAndroidPermission('android.permission.ACCESS_NETWORK_STATE') then begin
|
|
IndyRaiseOuterException(EIdAccessNetworkStatePermissionNeeded.CreateError(0, ''));
|
|
end;
|
|
raise;
|
|
end;
|
|
end;
|
|
|
|
Note that this require the application to have INTERNET and ACCESS_NETWORK_STATE permissions.
|
|
|
|
Or:
|
|
|
|
uses
|
|
if XE7+
|
|
Androidapi.Helpers
|
|
else
|
|
FMX.Helpers.Android
|
|
;
|
|
|
|
var
|
|
wifiManager: WifiManager;
|
|
ipAddress: Integer;
|
|
begin
|
|
try
|
|
wifiManager := (WifiManager) SharedActivityContext.getSystemService(WIFI_SERVICE);
|
|
ipAddress := wifiManager.getConnectionInfo.getIpAddress;
|
|
except
|
|
if not HasAndroidPermission('android.permission.ACCESS_WIFI_STATE') then begin
|
|
IndyRaiseOuterException(EIdAccessWifiStatePermissionNeeded.CreateError(0, ''));
|
|
end;
|
|
raise;
|
|
end;
|
|
TIdStackLocalAddressIPv4.Create(AAddresses, Format('%d.%d.%d.%d', [ipAddress and $ff, (ipAddress shr 8) and $ff, (ipAddress shr 16) and $ff, (ipAddress shr 24) and $ff]), '');
|
|
end;
|
|
|
|
This requires only ACCESS_WIFI_STATE permission.
|
|
}
|
|
|
|
//IMPORTANT!!!
|
|
//
|
|
//The Hints structure must be zeroed out or you might get an AV.
|
|
//I've seen this in Mac OS X
|
|
FillChar(Hints, SizeOf(Hints), 0);
|
|
Hints.ai_family := PF_UNSPEC; // returns both IPv4 and IPv6 addresses
|
|
Hints.ai_socktype := SOCK_STREAM;
|
|
|
|
LHostName := HostName;
|
|
|
|
LRetVal := getaddrinfo(
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
M.AsAnsi(LHostName).ToPointer
|
|
{$ELSE}
|
|
PAnsiChar(
|
|
{$IFDEF STRING_IS_ANSI}
|
|
LHostName
|
|
{$ELSE}
|
|
AnsiString(LHostName) // explicit convert to Ansi
|
|
{$ENDIF}
|
|
)
|
|
{$ENDIF},
|
|
nil, Hints, LAddrList);
|
|
if LRetVal <> 0 then begin
|
|
if LRetVal = EAI_SYSTEM then begin
|
|
RaiseLastOSError;
|
|
end else begin
|
|
raise EIdReverseResolveError.CreateFmt(RSReverseResolveError, [LHostName, gai_strerror(LRetVal), LRetVal]);
|
|
end;
|
|
end;
|
|
try
|
|
AAddresses.BeginUpdate;
|
|
try
|
|
LAddrInfo := LAddrList;
|
|
repeat
|
|
case LAddrInfo^.ai_addr^.sa_family of
|
|
Id_PF_INET4 :
|
|
begin
|
|
TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ai_addr)^.sin_addr, Id_IPv4), ''); // TODO: SubNet
|
|
end;
|
|
Id_PF_INET6 :
|
|
begin
|
|
TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In6(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;
|
|
|
|
procedure TIdStackVCLPosix.Bind(ASocket: TIdStackSocketHandle;
|
|
const AIP: string; const APort: TIdPort; const AIPVersion: TIdIPVersion);
|
|
var
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
begin
|
|
case AIPVersion of
|
|
Id_IPv4: begin
|
|
InitSockAddr_In(LAddrIPv4);
|
|
if AIP <> '' then begin
|
|
TranslateStringToTInAddr(AIP, LAddrIPv4.sin_addr, Id_IPv4);
|
|
end;
|
|
LAddrIPv4.sin_port := htons(APort);
|
|
CheckForSocketError(Posix.SysSocket.bind(ASocket, LAddr, SizeOf(LAddrIPv4)));
|
|
end;
|
|
Id_IPv6: begin
|
|
InitSockAddr_in6(LAddrIPv6);
|
|
if AIP <> '' then begin
|
|
TranslateStringToTInAddr(AIP, LAddrIPv6.sin6_addr, Id_IPv6);
|
|
end;
|
|
LAddrIPv6.sin6_port := htons(APort);
|
|
CheckForSocketError(Posix.SysSocket.bind(ASocket,LAddr, SizeOf(LAddrIPv6)));
|
|
end;
|
|
else begin
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
function TIdStackVCLPosix.CheckIPVersionSupport(
|
|
const AIPVersion: TIdIPVersion): boolean;
|
|
var
|
|
LTmpSocket: TIdStackSocketHandle;
|
|
begin
|
|
// TODO: on nix systems (or maybe just Linux?), an alternative would be to
|
|
// check for the existance of the '/proc/net/if_inet6' kernel pseudo-file
|
|
LTmpSocket := WSSocket(IdIPFamily[AIPVersion], Id_SOCK_STREAM, Id_IPPROTO_IP );
|
|
Result := LTmpSocket <> Id_INVALID_SOCKET;
|
|
if Result then begin
|
|
WSCloseSocket(LTmpSocket);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.Connect(const ASocket: TIdStackSocketHandle;
|
|
const AIP: string; const APort: TIdPort; const AIPVersion: TIdIPVersion);
|
|
var
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
begin
|
|
case AIPVersion of
|
|
Id_IPv4: begin
|
|
InitSockAddr_In(LAddrIPv4);
|
|
TranslateStringToTInAddr(AIP, LAddrIPv4.sin_addr, Id_IPv4);
|
|
LAddrIPv4.sin_port := htons(APort);
|
|
CheckForSocketError(Posix.SysSocket.connect(ASocket, LAddr, SizeOf(LAddrIPv4)));
|
|
end;
|
|
Id_IPv6: begin
|
|
InitSockAddr_in6(LAddrIPv6);
|
|
TranslateStringToTInAddr(AIP, LAddrIPv6.sin6_addr, Id_IPv6);
|
|
LAddrIPv6.sin6_port := htons(APort);
|
|
CheckForSocketError(Posix.SysSocket.connect(ASocket, LAddr, SizeOf(LAddrIPv6)));
|
|
end;
|
|
else begin
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
constructor TIdStackVCLPosix.Create;
|
|
begin
|
|
inherited Create;
|
|
end;
|
|
|
|
destructor TIdStackVCLPosix.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.Disconnect(ASocket: TIdStackSocketHandle);
|
|
begin
|
|
// Windows uses Id_SD_Send, Linux should use Id_SD_Both
|
|
WSShutdown(ASocket, Id_SD_Both);
|
|
// SO_LINGER is false - socket may take a little while to actually close after this
|
|
WSCloseSocket(ASocket);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.GetLastError: Integer;
|
|
begin
|
|
Result := errno;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.GetPeerName(ASocket: TIdStackSocketHandle;
|
|
var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
|
|
var
|
|
i: socklen_t;
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
begin
|
|
i := SizeOf(LAddrStore);
|
|
CheckForSocketError(Posix.SysSocket.getpeername(ASocket, LAddr, i));
|
|
case LAddrStore.ss_family of
|
|
Id_PF_INET4: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv4.sin_addr, Id_IPv4);
|
|
VPort := ntohs(LAddrIPv4.sin_port);
|
|
VIPVersion := Id_IPV4;
|
|
end;
|
|
Id_PF_INET6: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv6.sin6_addr, Id_IPv6);
|
|
VPort := ntohs(LAddrIPv6.sin6_port);
|
|
VIPVersion := Id_IPV6;
|
|
end;
|
|
else begin
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.GetSocketName(ASocket: TIdStackSocketHandle;
|
|
var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
|
|
var
|
|
LiSize: socklen_t;
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
begin
|
|
LiSize := SizeOf(LAddrStore);
|
|
CheckForSocketError(getsockname(ASocket, LAddr, LiSize));
|
|
case LAddrStore.ss_family of
|
|
Id_PF_INET4: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv4.sin_addr, Id_IPv4);
|
|
VPort := ntohs(LAddrIPv4.sin_port);
|
|
VIPVersion := Id_IPV4;
|
|
end;
|
|
Id_PF_INET6: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv6.sin6_addr, Id_IPv6);
|
|
VPort := ntohs(LAddrIPv6.sin6_port);
|
|
VIPVersion := Id_IPV6;
|
|
end;
|
|
else begin
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.HostByAddress(const AAddress: string;
|
|
const AIPVersion: TIdIPVersion): string;
|
|
var
|
|
LiSize: socklen_t;
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
LHostName : array[0..NI_MAXHOST] of TIdAnsiChar;
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LHostNamePtr: TPtrWrapper;
|
|
{$ENDIF}
|
|
LRet : Integer;
|
|
LHints : addrinfo;
|
|
LAddrInfo: pAddrInfo;
|
|
begin
|
|
LiSize := 0;
|
|
case AIPVersion of
|
|
Id_IPv4 :
|
|
begin
|
|
InitSockAddr_In(LAddrIPv4);
|
|
TranslateStringToTInAddr(AAddress,LAddrIPv4.sin_addr,Id_IPv4);
|
|
LiSize := SizeOf(SockAddr_In);
|
|
end;
|
|
Id_IPv6 :
|
|
begin
|
|
InitSockAddr_In6(LAddrIPv6);
|
|
TranslateStringToTInAddr(AAddress,LAddrIPv6.sin6_addr,Id_IPv6);
|
|
LiSize := SizeOf(SockAddr_In6);
|
|
end
|
|
else
|
|
IPVersionUnsupported;
|
|
end;
|
|
FillChar(LHostName[0],Length(LHostName),0);
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LHostNamePtr := TPtrWrapper.Create(@LHostName[0]);
|
|
{$ENDIF}
|
|
LRet := getnameinfo(LAddr,LiSize,
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LHostNamePtr.ToPointer
|
|
{$ELSE}
|
|
LHostName
|
|
{$ENDIF},
|
|
NI_MAXHOST,nil,0,NI_NAMEREQD );
|
|
if LRet <> 0 then begin
|
|
if LRet = EAI_SYSTEM then begin
|
|
RaiseLastOSError;
|
|
end else begin
|
|
raise EIdReverseResolveError.CreateFmt(RSReverseResolveError, [AAddress, gai_strerror(LRet), LRet]);
|
|
end;
|
|
end;
|
|
{
|
|
IMPORTANT!!!
|
|
|
|
getnameinfo can return either results from a numeric to text conversion or
|
|
results from a DNS reverse lookup. Someone could make a malicous PTR record
|
|
such as
|
|
|
|
1.0.0.127.in-addr.arpa. IN PTR 10.1.1.1
|
|
|
|
and trick a caller into beleiving the socket address is 10.1.1.1 instead of
|
|
127.0.0.1. If there is a numeric host in LAddr, than this is the case and
|
|
we disregard the result and raise an exception.
|
|
}
|
|
FillChar(LHints, SizeOf(LHints), 0);
|
|
LHints.ai_socktype := SOCK_DGRAM; //*dummy*/
|
|
LHints.ai_flags := AI_NUMERICHOST;
|
|
if getaddrinfo(
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LHostNamePtr.ToPointer
|
|
{$ELSE}
|
|
LHostName
|
|
{$ENDIF},
|
|
'0', LHints, LAddrInfo) = 0 then
|
|
begin
|
|
freeaddrinfo(LAddrInfo^);
|
|
Result := '';
|
|
raise EIdMaliciousPtrRecord.Create(RSMaliciousPtrRecord);
|
|
end;
|
|
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
Result := TMarshal.ReadStringAsAnsi(LHostNamePtr);
|
|
{$ELSE}
|
|
Result := String(LHostName);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TIdStackVCLPosix.HostByName(const AHostName: string;
|
|
const AIPVersion: TIdIPVersion): string;
|
|
var
|
|
LAddrInfo: pAddrInfo;
|
|
LHints: AddrInfo;
|
|
LRetVal: Integer;
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
M: TMarshaller;
|
|
{$ENDIF}
|
|
begin
|
|
if not (AIPVersion in [Id_IPv4, Id_IPv6]) then begin
|
|
IPVersionUnsupported;
|
|
end;
|
|
//IMPORTANT!!!
|
|
//
|
|
//The Hints structure must be zeroed out or you might get an AV.
|
|
//I've seen this in Mac OS X
|
|
FillChar(LHints, SizeOf(LHints), 0);
|
|
LHints.ai_family := IdIPFamily[AIPVersion];
|
|
LHints.ai_socktype := SOCK_STREAM;
|
|
LAddrInfo := nil;
|
|
|
|
LRetVal := getaddrinfo(
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
M.AsAnsi(AHostName).ToPointer
|
|
{$ELSE}
|
|
PAnsiChar(
|
|
{$IFDEF STRING_IS_ANSI}
|
|
AHostName
|
|
{$ELSE}
|
|
AnsiString(AHostName) // explicit convert to Ansi
|
|
{$ENDIF}
|
|
)
|
|
{$ENDIF},
|
|
nil, LHints, LAddrInfo);
|
|
if LRetVal <> 0 then begin
|
|
if LRetVal = EAI_SYSTEM then begin
|
|
RaiseLastOSError;
|
|
end else begin
|
|
raise EIdResolveError.CreateFmt(RSReverseResolveError, [AHostName, gai_strerror(LRetVal), LRetVal]);
|
|
end;
|
|
end;
|
|
try
|
|
if AIPVersion = Id_IPv4 then begin
|
|
Result := TranslateTInAddrToString( PSockAddr_In( LAddrInfo^.ai_addr)^.sin_addr, AIPVersion);
|
|
end else begin
|
|
Result := TranslateTInAddrToString( PSockAddr_In6( LAddrInfo^.ai_addr)^.sin6_addr, AIPVersion);
|
|
end;
|
|
finally
|
|
freeaddrinfo(LAddrInfo^);
|
|
end;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.HostToNetwork(AValue: UInt32): UInt32;
|
|
begin
|
|
Result := htonl(AValue);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.HostToNetwork(AValue: UInt16): UInt16;
|
|
begin
|
|
Result := htons(AValue);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.HostToNetwork(AValue: TIdUInt64): TIdUInt64;
|
|
var
|
|
LParts: TIdUInt64Parts;
|
|
L: UInt32;
|
|
begin
|
|
LParts.QuadPart := AValue;
|
|
L := htonl(LParts.HighPart);
|
|
if (L <> LParts.HighPart) then begin
|
|
LParts.HighPart := htonl(LParts.LowPart);
|
|
LParts.LowPart := L;
|
|
end;
|
|
Result := LParts.QuadPart;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.IOControl(const s: TIdStackSocketHandle;
|
|
const cmd: UInt32; var arg: UInt32): Integer;
|
|
begin
|
|
Result := ioctl(s, cmd, @arg);
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.Listen(ASocket: TIdStackSocketHandle;
|
|
ABackLog: Integer);
|
|
begin
|
|
CheckForSocketError(Posix.SysSocket.listen(ASocket, ABacklog));
|
|
end;
|
|
|
|
function TIdStackVCLPosix.NetworkToHost(AValue: UInt32): UInt32;
|
|
begin
|
|
Result := ntohl(AValue);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.NetworkToHost(AValue: TIdUInt64): TIdUInt64;
|
|
var
|
|
LParts: TIdUInt64Parts;
|
|
L: UInt32;
|
|
begin
|
|
LParts.QuadPart := AValue;
|
|
L := ntohl(LParts.HighPart);
|
|
if (L <> LParts.HighPart) then begin
|
|
LParts.HighPart := ntohl(LParts.LowPart);
|
|
LParts.LowPart := L;
|
|
end;
|
|
Result := LParts.QuadPart;
|
|
|
|
end;
|
|
|
|
function TIdStackVCLPosix.NetworkToHost(AValue: UInt16): UInt16;
|
|
begin
|
|
Result := ntohs(AValue);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.ReadHostName: string;
|
|
const
|
|
sMaxHostSize = 250;
|
|
var
|
|
LStr: array[0..sMaxHostSize] of TIdAnsiChar;
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LStrPtr: TPtrWrapper;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LStrPtr := TPtrWrapper.Create(@LStr[0]);
|
|
{$ENDIF}
|
|
gethostname(
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
LStrPtr.ToPointer
|
|
{$ELSE}
|
|
LStr
|
|
{$ENDIF}, sMaxHostSize);
|
|
LStr[sMaxHostSize] := TIdAnsiChar(0);
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
Result := TMarshal.ReadStringAsAnsi(LStrPtr);
|
|
{$ELSE}
|
|
Result := String(LStr);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TIdStackVCLPosix.ReceiveMsg(ASocket: TIdStackSocketHandle;
|
|
var VBuffer: TIdBytes; APkt: TIdPacketInfo): UInt32;
|
|
var
|
|
LSize: socklen_t;
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
LMsg : msghdr;
|
|
LIOV : iovec;
|
|
LControl : TIdBytes;
|
|
LCurCmsg : Pcmsghdr; //for iterating through the control buffer
|
|
LByte : PByte;
|
|
|
|
begin
|
|
//we call the macro twice because we specified two possible structures.
|
|
//Id_IPV6_HOPLIMIT and Id_IPV6_PKTINFO
|
|
LSize := CMSG_LEN(CMSG_LEN(Length(VBuffer)));
|
|
SetLength( LControl,LSize);
|
|
|
|
LIOV.iov_len := Length(VBuffer); // Length(VMsgData);
|
|
LIOV.iov_base := @VBuffer[0]; // @VMsgData[0];
|
|
|
|
FillChar(LMsg,SizeOf(LMsg),0);
|
|
|
|
LMsg.msg_iov := @LIOV;//lpBuffers := @LMsgBuf;
|
|
LMsg.msg_iovlen := 1;
|
|
|
|
LMsg.msg_controllen := LSize;
|
|
LMsg.msg_control := @LControl[0];
|
|
|
|
LMsg.msg_name := @LAddr;
|
|
LMsg.msg_namelen := SizeOf(LAddrStore);
|
|
|
|
Result := 0;
|
|
CheckForSocketError(RecvMsg(ASocket, LMsg, Result));
|
|
APkt.Reset;
|
|
|
|
case LAddrStore.ss_family of
|
|
Id_PF_INET4: begin
|
|
APkt.SourceIP := TranslateTInAddrToString(LAddrIPv4.sin_addr, Id_IPv4);
|
|
APkt.SourcePort := ntohs(LAddrIPv4.sin_port);
|
|
APkt.SourceIPVersion := Id_IPv4;
|
|
end;
|
|
Id_PF_INET6: begin
|
|
APkt.SourceIP := TranslateTInAddrToString(LAddrIPv6.sin6_addr, Id_IPv6);
|
|
APkt.SourcePort := ntohs(LAddrIPv6.sin6_port);
|
|
APkt.SourceIPVersion := Id_IPv6;
|
|
end;
|
|
else begin
|
|
Result := 0; // avoid warning
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
|
|
LCurCmsg := nil;
|
|
repeat
|
|
LCurCmsg := CMSG_NXTHDR(@LMsg, LCurCmsg);
|
|
if LCurCmsg = nil then begin
|
|
break;
|
|
end;
|
|
case LCurCmsg^.cmsg_type of
|
|
IPV6_PKTINFO : //done this way because IPV6_PKTINF and IP_PKTINFO are both 19
|
|
begin
|
|
case LAddrStore.ss_family of
|
|
Id_PF_INET4: begin
|
|
{$IFDEF IOS}
|
|
ToDo('PKTINFO not implemented for IPv4 under iOS yet');
|
|
{$ELSE}
|
|
{$IFNDEF DARWIN}
|
|
//This is not supported in OS X.
|
|
with Pin_pktinfo(CMSG_DATA(LCurCmsg))^ do begin
|
|
APkt.DestIP := TranslateTInAddrToString(ipi_addr, Id_IPv4);
|
|
APkt.DestIF := ipi_ifindex;
|
|
end;
|
|
APkt.DestIPVersion := Id_IPv4;
|
|
{$ENDIF}
|
|
{$ENDIF}
|
|
end;
|
|
Id_PF_INET6: begin
|
|
with pin6_pktinfo(CMSG_DATA(LCurCmsg))^ do begin
|
|
APkt.DestIP := TranslateTInAddrToString(ipi6_addr, Id_IPv6);
|
|
APkt.DestIF := ipi6_ifindex;
|
|
end;
|
|
APkt.DestIPVersion := Id_IPv6;
|
|
end;
|
|
end;
|
|
end;
|
|
Id_IPV6_HOPLIMIT :
|
|
begin
|
|
LByte := PByte(CMSG_DATA(LCurCmsg));
|
|
APkt.TTL := LByte^;
|
|
end;
|
|
end;
|
|
until False;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.RecvFrom(const ASocket: TIdStackSocketHandle;
|
|
var VBuffer; const ALength, AFlags: Integer; var VIP: string;
|
|
var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
|
|
var
|
|
LiSize: socklen_t;
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
|
|
begin
|
|
LiSize := SizeOf(LAddrStore);
|
|
Result := Posix.SysSocket.recvfrom(ASocket,VBuffer, ALength, AFlags or Id_MSG_NOSIGNAL, LAddr, LiSize);
|
|
if Result >= 0 then
|
|
begin
|
|
case LAddrStore.ss_family of
|
|
Id_PF_INET4: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv4.sin_addr, Id_IPv4);
|
|
VPort := ntohs(LAddrIPv4.sin_port);
|
|
VIPVersion := Id_IPV4;
|
|
end;
|
|
Id_PF_INET6: begin
|
|
VIP := TranslateTInAddrToString(LAddrIPv6.sin6_addr, Id_IPv6);
|
|
VPort := ntohs(LAddrIPv6.sin6_port);
|
|
VIPVersion := Id_IPV6;
|
|
end;
|
|
else begin
|
|
Result := 0;
|
|
IPVersionUnsupported;
|
|
end;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.SetBlocking(ASocket: TIdStackSocketHandle;
|
|
const ABlocking: Boolean);
|
|
begin
|
|
if not ABlocking then begin
|
|
raise EIdNonBlockingNotSupported.Create(RSStackNonBlockingNotSupported);
|
|
end;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.SetLastError(const AError: Integer);
|
|
begin
|
|
__error^ := AError;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.{$IFDEF VCL_XE3_OR_ABOVE}GetSocketOption{$ELSE}WSGetSocketOption{$ENDIF}
|
|
(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption;
|
|
var AOptVal; var AOptLen: Integer);
|
|
var
|
|
LLen : socklen_t;
|
|
begin
|
|
LLen := AOptLen;
|
|
CheckForSocketError(Posix.SysSocket.getsockopt(ASocket, ALevel, AOptName, AOptVal, LLen));
|
|
AOptLen := LLen;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.{$IFDEF VCL_XE3_OR_ABOVE}SetSocketOption{$ELSE}WSSetSocketOption{$ENDIF}
|
|
(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel; AOptName: TIdSocketOption;
|
|
const AOptVal; const AOptLen: Integer);
|
|
begin
|
|
CheckForSocketError(Posix.SysSocket.setsockopt(ASocket, ALevel, AOptName, AOptVal, AOptLen));
|
|
end;
|
|
|
|
function TIdStackVCLPosix.SupportsIPv6: Boolean;
|
|
begin
|
|
//In Windows, this does something else. It checks the LSP's installed.
|
|
Result := CheckIPVersionSupport(Id_IPv6);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WouldBlock(const AResult: Integer): Boolean;
|
|
begin
|
|
//non-blocking does not exist in Linux, always indicate things will block
|
|
Result := True;
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.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 TIdStackVCLPosix.WriteChecksumIPv6(s: TIdStackSocketHandle;
|
|
var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
|
|
const APort: TIdPort);
|
|
begin
|
|
//we simply request that the kernal write the checksum when the data
|
|
//is sent. All of the parameters required are because Windows is bonked
|
|
//because it doesn't have the IPV6CHECKSUM socket option meaning we have
|
|
//to querry the network interface in TIdStackWindows -- yuck!!
|
|
SetSocketOption(s, Id_IPPROTO_IPV6, IPV6_CHECKSUM, AOffset);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSCloseSocket(ASocket: TIdStackSocketHandle): Integer;
|
|
begin
|
|
Result := __close(ASocket);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSGetLastError: Integer;
|
|
begin
|
|
//IdStackWindows just uses result := WSAGetLastError;
|
|
Result := GetLastError; //System.GetLastOSError; - FPC doesn't define it in System
|
|
if Result = Id_WSAEPIPE then begin
|
|
Result := Id_WSAECONNRESET;
|
|
end;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSGetServByName(const AServiceName: string): TIdPort;
|
|
var
|
|
Lps: PServEnt;
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
M: TMarshaller;
|
|
{$ENDIF}
|
|
begin
|
|
Lps := Posix.NetDB.getservbyname(
|
|
{$IFDEF USE_MARSHALLED_PTRS}
|
|
M.AsAnsi(AServiceName).ToPointer
|
|
{$ELSE}
|
|
PAnsiChar(
|
|
{$IFDEF STRING_IS_ANSI}
|
|
AServiceName
|
|
{$ELSE}
|
|
AnsiString(AServiceName) // explicit convert to Ansi
|
|
{$ENDIF}
|
|
)
|
|
{$ENDIF},
|
|
nil);
|
|
if Lps <> nil then begin
|
|
Result := ntohs(Lps^.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 TIdStackVCLPosix.AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings);
|
|
//function TIdStackVCLPosix.WSGetServByPort(const APortNumber: TIdPort): TStrings;
|
|
type
|
|
PPAnsiCharArray = ^TPAnsiCharArray;
|
|
TPAnsiCharArray = packed array[0..(MaxInt div SizeOf(PIdAnsiChar))-1] of PIdAnsiChar;
|
|
var
|
|
Lps: PServEnt;
|
|
Li: Integer;
|
|
Lp: PPAnsiCharArray;
|
|
begin
|
|
Lps := Posix.NetDB.getservbyport(htons(APortNumber), nil);
|
|
if Lps <> nil then begin
|
|
AAddresses.BeginUpdate;
|
|
try
|
|
AAddresses.Add(String(Lps^.s_name));
|
|
Li := 0;
|
|
Lp := Pointer(Lps^.s_aliases);
|
|
while Lp[Li] <> nil do begin
|
|
AAddresses.Add(String(Lp[Li]));
|
|
Inc(Li);
|
|
end;
|
|
finally
|
|
AAddresses.EndUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
|
|
const ABufferLength, AFlags: Integer): Integer;
|
|
begin
|
|
//IdStackWindows is just: Result := Recv(ASocket, ABuffer, ABufferLength, AFlags);
|
|
Result := Posix.SysSocket.Recv(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSSend(ASocket: TIdStackSocketHandle; const ABuffer;
|
|
const ABufferLength, AFlags: Integer): Integer;
|
|
begin
|
|
//CC: Should Id_MSG_NOSIGNAL be included?
|
|
// Result := Send(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
|
|
Result := CheckForSocketError(Posix.SysSocket.send(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL));
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.WSSendTo(ASocket: TIdStackSocketHandle;
|
|
const ABuffer; const ABufferLength, AFlags: Integer; const AIP: string;
|
|
const APort: TIdPort; AIPVersion: TIdIPVersion);
|
|
var
|
|
LAddrStore: sockaddr_storage;
|
|
LAddrIPv4 : SockAddr_In absolute LAddrStore;
|
|
LAddrIPv6 : sockaddr_in6 absolute LAddrStore;
|
|
LAddr : sockaddr absolute LAddrStore;
|
|
LiSize: socklen_t;
|
|
LBytesSent: Integer;
|
|
begin
|
|
case AIPVersion of
|
|
Id_IPv4: begin
|
|
InitSockAddr_In(LAddrIPv4);
|
|
TranslateStringToTInAddr(AIP, LAddrIPv4.sin_addr, Id_IPv4);
|
|
LAddrIPv4.sin_port := htons(APort);
|
|
LiSize := SizeOf(LAddrIPv4);
|
|
end;
|
|
Id_IPv6: begin
|
|
InitSockAddr_in6(LAddrIPv6);
|
|
TranslateStringToTInAddr(AIP, LAddrIPv6.sin6_addr, Id_IPv6);
|
|
LAddrIPv6.sin6_port := htons(APort);
|
|
LiSize := SizeOf(LAddrIPv6);
|
|
end;
|
|
else
|
|
LiSize := 0; // avoid warning
|
|
IPVersionUnsupported;
|
|
end;
|
|
LBytesSent := Posix.SysSocket.sendto(
|
|
ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL, LAddr, LiSize);
|
|
if LBytesSent = 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 LBytesSent <> ABufferLength then begin
|
|
raise EIdNotAllBytesSent.Create(RSNotAllBytesSent);
|
|
end;
|
|
|
|
end;
|
|
|
|
procedure TIdStackVCLPosix.WSSetLastError(const AErr: Integer);
|
|
begin
|
|
__error^ := AErr;
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSShutdown(ASocket: TIdStackSocketHandle;
|
|
AHow: Integer): Integer;
|
|
begin
|
|
Result := Posix.SysSocket.shutdown(ASocket, AHow);
|
|
end;
|
|
|
|
function TIdStackVCLPosix.WSSocket(AFamily : Integer; AStruct : TIdSocketType; AProtocol: Integer;
|
|
const AOverlapped: Boolean = False): TIdStackSocketHandle;
|
|
begin
|
|
Result := Posix.SysSocket.socket(AFamily, AStruct, AProtocol);
|
|
{$IFDEF HAS_SOCKET_NOSIGPIPE}
|
|
if Result <> INVALID_SOCKET then begin
|
|
SetSocketOption(Result, SOL_SOCKET, SO_NOSIGPIPE, 1);
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
{$I IdUnitPlatformOn.inc}
|
|
{$I IdSymbolPlatformOn.inc}
|
|
initialization
|
|
GSocketListClass := TIdSocketListVCLPosix;
|
|
end.
|