I'm using Indy 10 + fpc + lazarus to build a cross platform app.
GStack.LocalAddress returns the IP address at Windows, but returns empty at Linux, and GStack.LocalAddresses.Count = 0. The machine has an ip address, checked by ifconfig.
Anybody help me?
Thanks,
Gustavo
(11-05-2018, 12:38 AM)capslock Wrote: [ -> ]I'm using Indy 10 + fpc + lazarus to build a cross platform app.
GStack.LocalAddress returns the IP address at Windows, but returns empty at Linux, and GStack.LocalAddresses.Count = 0. The machine has an ip address, checked by ifconfig.
First, the
GStack.LocalAddress and
GStack.LocalAddresses properties are deprecated, you should be using the
GStack.GetLocalAddressList() method instead.
Second, on FPC for Linux,
GStack points to an instance of either the
TIdStackLibc or
TIdStackUnix class (depending on whether
KYLIXCOMPAT is defined). Both classes
should implement
GetLocalAddressList() using
getifaddrs(), but in actuality they only do so when
HAS_getifaddrs is defined in
IdCompilerDefines.inc, and that is defined only when
LINUX64 is defined by the compiler (in addition to
MACOS,
IOS, and
FREEBSD). Which, I believe is defined by Embarcadero only, not FPC.
When
HAS_getifaddrs() is not defined, both
TIdStackLibc and
TIdStackUnix implement
GetLocalAddressList() using
gethostname() with
gethostbyname() (Libc) or FPC's own
ResolveName/6() (Unix) instead. And I guess that is not finding any IP addresses for the local hostname.
So, try altering
IdCompilerDefines.inc to define
HAS_getifaddrs for
LINUX instead of
LINUX64. If that works, I'll check it in.
The following patch was applied:
Code:
Index: Core/IdCompilerDefines.inc
===================================================================
--- Core/IdCompilerDefines.inc (revision 5478)
+++ Core/IdCompilerDefines.inc (working copy)
@@ -1490,6 +1490,10 @@
{$DEFINE HAS_getifaddrs}
{$ENDIF}
+{$IFDEF LINUX}
+ {$DEFINE HAS_getifaddrs}
+{$ENDIF}
+
{$IFDEF IOS}
{$DEFINE HAS_getifaddrs}
{$DEFINE USE_OPENSSL}
@@ -1866,4 +1870,4 @@
// indexing for now...
{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS}
{$ZEROBASEDSTRINGS OFF}
-{$ENDIF}
\ No newline at end of file
+{$ENDIF}
Index: FCL/IdCompilerDefines.inc
===================================================================
--- FCL/IdCompilerDefines.inc (revision 5478)
+++ FCL/IdCompilerDefines.inc (working copy)
@@ -1490,6 +1490,10 @@
{$DEFINE HAS_getifaddrs}
{$ENDIF}
+{$IFDEF LINUX}
+ {$DEFINE HAS_getifaddrs}
+{$ENDIF}
+
{$IFDEF IOS}
{$DEFINE HAS_getifaddrs}
{$DEFINE USE_OPENSSL}
@@ -1866,4 +1870,4 @@
// indexing for now...
{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS}
{$ZEROBASEDSTRINGS OFF}
-{$ENDIF}
\ No newline at end of file
+{$ENDIF}
Index: Protocols/IdCompilerDefines.inc
===================================================================
--- Protocols/IdCompilerDefines.inc (revision 5478)
+++ Protocols/IdCompilerDefines.inc (working copy)
@@ -1490,6 +1490,10 @@
{$DEFINE HAS_getifaddrs}
{$ENDIF}
+{$IFDEF LINUX}
+ {$DEFINE HAS_getifaddrs}
+{$ENDIF}
+
{$IFDEF IOS}
{$DEFINE HAS_getifaddrs}
{$DEFINE USE_OPENSSL}
@@ -1866,4 +1870,4 @@
// indexing for now...
{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS}
{$ZEROBASEDSTRINGS OFF}
-{$ENDIF}
\ No newline at end of file
+{$ENDIF}
Index: System/IdCompilerDefines.inc
===================================================================
--- System/IdCompilerDefines.inc (revision 5478)
+++ System/IdCompilerDefines.inc (working copy)
@@ -1490,6 +1490,10 @@
{$DEFINE HAS_getifaddrs}
{$ENDIF}
+{$IFDEF LINUX}
+ {$DEFINE HAS_getifaddrs}
+{$ENDIF}
+
{$IFDEF IOS}
{$DEFINE HAS_getifaddrs}
{$DEFINE USE_OPENSSL}
@@ -1866,4 +1870,4 @@
// indexing for now...
{$IFDEF HAS_DIRECTIVE_ZEROBASEDSTRINGS}
{$ZEROBASEDSTRINGS OFF}
-{$ENDIF}
\ No newline at end of file
+{$ENDIF}
GStack.GetLocalAddressList returned empty anyway, before and after patch applied. But the deprecated method GStack.LocalAddresses returned the right IPv4 address after patch. Tested with two network interfaces, GStack.LocalAddresses returned the right two IPv4 addresses.
And about IPv6 addresses, how to get them?
(11-06-2018, 04:58 AM)capslock Wrote: [ -> ]GStack.GetLocalAddressList returned empty anyway, before and after patch applied. But the deprecated method GStack.LocalAddresses returned the right IPv4 address after patch.
The
GStack.LocalAddress(es) properties call
GStack.GetLocalAddressList() internally, so if the local address list is returned empty, the properties must also return empty. Likewise, if the
GStack.LocalAddress(es) properties are not returning empty, then
GStack.GetLocalAddressList() must not be returning an empty list.
Code:
property LocalAddress: string read GetLocalAddress; // {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'use GetLocalAddressList()'{$ENDIF};{$ENDIF}
property LocalAddresses: TStrings read GetLocalAddresses; // {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPRECATED_MSG} 'use GetLocalAddressList()'{$ENDIF};{$ENDIF}
...
function TIdStack.GetLocalAddresses: TStrings;
var
LList: TIdStackLocalAddressList;
I: Integer;
begin
if FLocalAddresses = nil then begin
FLocalAddresses := TStringList.Create;
end;
FLocalAddresses.BeginUpdate;
try
FLocalAddresses.Clear;
LList := TIdStackLocalAddressList.Create;
try
// for backwards compatibility, return only IPv4 addresses
GetLocalAddressList(LList);
for I := 0 to LList.Count-1 do begin
if LList[I].IPVersion = Id_IPv4 then begin
FLocalAddresses.Add(LList[I].IPAddress);
end;
end;
finally
LList.Free;
end;
finally
FLocalAddresses.EndUpdate;
end;
Result := FLocalAddresses;
end;
function TIdStack.GetLocalAddress: string;
var
LList: TIdStackLocalAddressList;
I: Integer;
begin
// RLebeau: using a local list instead of the LocalAddresses
// property so this method can be thread-safe...
//
// old code:
// Result := LocalAddresses[0];
Result := '';
LList := TIdStackLocalAddressList.Create;
try
// for backwards compatibility, return only IPv4 addresses
GetLocalAddressList(LList);
for I := 0 to LList.Count-1 do begin
if LList[I].IPVersion = Id_IPv4 then begin
Result := LList[I].IPAddress;
Exit;
end;
end;
finally
LList.Free;
end;
end;
(11-06-2018, 04:58 AM)capslock Wrote: [ -> ]Tested with two network interfaces, GStack.LocalAddresses returned the right two IPv4 addresses.
Then
GStack.GetLocalAddressList() must be succeeding now.
(11-06-2018, 04:58 AM)capslock Wrote: [ -> ]And about IPv6 addresses, how to get them?
As you can see above, the
GStack.LocalAddress(es) properties are restricted to IPv4 only for backwards compatibility. To get the IPv6 addresses, you must call
GStack.GetLocalAddressList() directly. The returned list will include all local IPv4 and IPv6 addresses that
getifaddrs() reports:
Code:
procedure TIdStackUnix.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
var
{$IFDEF HAS_getifaddrs}
LAddrList, LAddrInfo: pifaddrs;
LSubNetStr: String;
{$ELSE}
...
{$ENDIF}
begin
...
{$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}
...
{$ENDIF}
end;
Code:
procedure TIdStackLibc.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
{$IFNDEF HAS_getifaddrs}
...
{$ENDIF}
var
{$IFDEF HAS_getifaddrs}
LAddrList, LAddrInfo: pifaddrs;
LSubNetStr: string;
{$ELSE}
...
{$ENDIF}
begin
...
{$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}
...
{$ENDIF}
end;
I have now checked in a patch to
IdCompilerDefines.inc to define
HAS_getifaddrs() when
LINUX is defined, not just
LINUX64.
I tested again. You right, I'm wrong... Works like a charm, both IPv4 and IPv6.
Thanks.