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.
