Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[SOLVED] GStack.LocalAddress returns empty
#4
(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.

Reply


Messages In This Thread
RE: GStack.LocalAddress returns empty - by rlebeau - 11-06-2018, 05:36 PM

Forum Jump:


Users browsing this thread: 1 Guest(s)