Skip to content

GStack.GetLocalAddressList: retrieving IP6-adr for a given address prefix #675

@JB-DX

Description

@JB-DX

I need to get the IP6-Address that has PrefixOrigin = IpPrefixOriginDhcp.
https://learn.microsoft.com/en-us/windows-hardware/drivers/network/nl-prefix-origin

AFAIK there's no way to achieve that with GStack.GetLocalAddressList, as it does not return the prefixes.
As a workaround I modified GetLocalAddressesByAdaptersAddresses in IdStackWindows.pas ( XXXX marked)
by using the global variable gIndyIP6FilterPrefixOrigin
But it would be wiser to pass that as an additional GetLocalAddressList-parameter.
Could this be implemented?

In the meantime is there a way to add that filter without modifying the original Indy source?
I tried a class helper but that does not give access to the local variables (in the 'implement' section)
.
IdStackWindows.pas modifications: ( XXXX marked)

global variable defined in IdStackWindows.pas:  (and set by my application)
  gIndyIP6FilterPrefixOrigin: integer = -1; // -1 : no filter  XXXX

  function GetLocalAddressesByAdaptersAddresses: Boolean;
  var
    Ret: DWORD;
    BufLen: ULONG;
    Adapter, Adapters: PIP_ADAPTER_ADDRESSES;
    UnicastAddr: PIP_ADAPTER_UNICAST_ADDRESS;
    IPAddr: string;
    SubNetStr: String;
    SubNetMasks: TStringList;
  begin
    // assume True unless ERROR_NOT_SUPPORTED is reported...
    Result := True;

    // MSDN says:
    // The recommended method of calling the GetAdaptersAddresses function is
    // to pre-allocate a 15KB working buffer pointed to by the AdapterAddresses
    // parameter. On typical computers, this dramatically reduces the chances
    // that the GetAdaptersAddresses function returns ERROR_BUFFER_OVERFLOW,
    // which would require calling GetAdaptersAddresses function multiple times.

    BufLen := 1024*15;
    GetMem(Adapters, BufLen);
    try
      repeat
        // TODO: include GAA_FLAG_INCLUDE_PREFIX on XPSP1+?
        // TODO: include GAA_FLAG_INCLUDE_ALL_INTERFACES on Vista+?
        Ret := GetAdaptersAddresses(PF_UNSPEC, GAA_FLAG_SKIP_ANYCAST or GAA_FLAG_SKIP_MULTICAST or GAA_FLAG_SKIP_DNS_SERVER or GAA_FLAG_SKIP_FRIENDLY_NAME, nil, Adapters, BufLen);
        case Ret of
          ERROR_SUCCESS:
          begin
            // Windows CE versions earlier than 4.1 may return ERROR_SUCCESS and
            // BufLen=0 if no adapter info is available, instead of returning
            // ERROR_NO_DATA as documented...
            if BufLen = 0 then begin
              Exit;
            end;
            Break;
          end;
          ERROR_NOT_SUPPORTED:
          begin
            Result := False;
            Exit;
          end;
          ERROR_NO_DATA,
          ERROR_ADDRESS_NOT_ASSOCIATED:
            Exit;
          ERROR_BUFFER_OVERFLOW:
            ReallocMem(Adapters, BufLen);
        else
          SetLastError(Ret);
          IndyRaiseLastError;
        end;
      until False;

      if Ret = ERROR_SUCCESS then
      begin
        SubNetMasks := nil;
        try
          AAddresses.BeginUpdate;
          try
            Adapter := Adapters;
            repeat
              if (Adapter.IfType <> IF_TYPE_SOFTWARE_LOOPBACK) and
                ((Adapter.Flags and IP_ADAPTER_RECEIVE_ONLY) = 0) then
              begin
                UnicastAddr := Adapter^.FirstUnicastAddress;
                while UnicastAddr <> nil do
                begin
                  if UnicastAddr^.DadState = IpDadStatePreferred then
                  begin
                    case UnicastAddr^.Address.lpSockaddr.sin_family of
                      AF_INET:
                       if (gIndyIP6FilterPrefixOrigin < 0) then   // XXXX  ignore IP4 entries when IP6-filter is actice
                        begin
                        IPAddr := TranslateTInAddrToString(PSockAddrIn(UnicastAddr^.Address.lpSockaddr)^.sin_addr, Id_IPv4);
                        // The OnLinkPrefixLength member is only available on Windows Vista and later
                        if IndyCheckWindowsVersion(6) then begin
                          SubNetStr := IPv4MaskLengthToString(UnicastAddr^.OnLinkPrefixLength);
                        end else
                        begin
                          // TODO: on XP SP1+, can the subnet mask be determined
                          // by analyzing the Adapter's Prefix list without resorting
                          // to reading the Registry?
                          if SubNetMasks = nil then
                          begin
                            SubNetMasks := TStringList.Create;
                            GetIPv4SubNetMasks(SubNetMasks);
                          end;
                          SubNetStr := SubNetMasks.Values[IPAddr];
                        end;
                        TIdStackLocalAddressIPv4.Create(AAddresses, IPAddr, SubNetStr);
                      end;
                      AF_INET6: begin
                        if (gIndyIP6FilterPrefixOrigin < 0) or (gIndyIP6FilterPrefixOrigin= ord(UnicastAddr^.PrefixOrigin) ) then // XXXX
                          TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString(PSockAddrIn6(UnicastAddr^.Address.lpSockaddr)^.sin6_addr, Id_IPv6));
                      end;
                    end;
                  end;
                  UnicastAddr := UnicastAddr^.Next;
                end;
              end;
              Adapter := Adapter^.Next;
            until Adapter = nil;
          finally
            AAddresses.EndUpdate;
          end;
        finally
          SubNetMasks.Free;
        end;
      end;
    finally
      FreeMem(Adapters);
    end;
  end;

.
using that in my application:

function GetAllAdaptors(onlyIP6dhpc: boolean=false) : string;
const
 // IdStackWindows IP6 IpPrefixOrigin
 IP6_FLT_NoFilter               = -1;
 IP6_FLT_IpPrefixOriginOther    = 0;
 IP6_FLT_IpPrefixOriginManual   = 1;
 IP6_FLT_IpPrefixOriginWellKnown= 2;
 IP6_FLT_IpPrefixOriginDhcp     = 3;
 IP6_FLT_IpPrefixOriginRouterAdvertisement= 4;

var
 i: integer;
 LList: TIdStackLocalAddressList;
 PcAdapters: TStringlist;
begin
  result:= '';
  if onlyIP6dhpc then
    IdStackWindows.gIndyIP6FilterPrefixOrigin:= IP6_FLT_IpPrefixOriginDhcp;
  PcAdapters:= TStringlist.Create;
  LList:= TIdStackLocalAddressList.Create;
  try
    TIdStack.IncUsage;
    try
      GStack.GetLocalAddressList(LList);
    finally
      TIdStack.DecUsage;
    end;
    IdStackWindows.gIndyIP6FilterPrefixOrigin:= IP6_FLT_NoFilter;

    if LList.Count > 0 then
     begin
      // use reported IP(s) as needed...
      for i:= 0 to LList.Count-1 do
       if LList.addresses[i].IPVersion = Id_IPv4 then
         PcAdapters.Add(LList.addresses[i].IPAddress);

      for i:= 0 to LList.Count-1 do
       if LList.addresses[i].IPVersion = Id_IPv6 then
         PcAdapters.Add(LList.addresses[i].IPAddress);
     end
    else
     begin
      // use 127.0.0.1/::1 as needed...
     end;
  finally
    LList.Free;
  end;
  result:= PcAdapters.text;
  PcAdapters.Free;
end;

Metadata

Metadata

Assignees

No one assigned

    Labels

    Element: Socket StacksIssues related to OS socket APIs, TIdStack and TIdSocketList descedants, etcStatus: DeferredIssue to be re-reviewed in a future releaseType: EnhancementIssue is proposing a new feature/enhancement
    No fields configured for Feature.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions