【问题标题】:Getting wrong IP addresses获取错误的 IP 地址
【发布时间】:2014-02-23 10:14:54
【问题描述】:

我正在尝试自动检测和报告用于 Delphi 套接字编程的 IPv6 可用性。

对于“localhost”,以下代码返回 IPv6 的“1700::”和 IPv4 的“2.0.0.0”。此地址用于简单测试,应该为我的机器返回 ::1 或 127.0.0.1。我在调用代码中没有收到任何套接字 API 错误,因此我试图找出在尝试将“localhost”转换为各种 IP 地址时我做错了什么。我正在使用 Indy 标头。

类型声明是:

TLSocketAddress = record
  case Integer of
    AF_INET:
      (IPv4: TSockAddrIn);
    AF_INET6:
      (IPv6: TSockAddrIn6);
end;

TLIpInfo = record
private
  function GetFamily: TProtocolFamily;
public
  Address : TLSocketAddress;
  HostName: string;
  Machine: string;
  IP6 : string;
  IP4 : string;
  Port : integer;
  Error : integer;
  property Family : TProtocolFamily read GetFamily;
end;

检索程序是:

function GetIpInfo(
  const AHostName : string;
  const APort     : integer
  ): TLIpInfo;
var
  nameRet   : PAnsiChar;
  ptr       : Pointer;
  Hints     : TAddrInfoW;
  AddrInfo  : PAddrInfoW;
  NextInfo  : PAddrInfoW;
  RetVal    : Integer;
  ip,
  machine   : string;
  namelen   : integer;
begin
  ZeroMemory(@Result, SizeOf(Result));
  FillChar(Hints, SizeOf(Hints), 0);
  Hints.ai_family := AF_UNSPEC;

  AddrInfo := nil;
  RetVal := GetAddrInfo(PWideChar(AHostName), nil, @Hints, @AddrInfo);
  if RetVal = 0 then
  try
    Result.HostName := AHostName;
    Result.Port := APort;
    NextInfo := AddrInfo;
    while NextInfo <> nil do
    begin
      if (NextInfo.ai_family = AF_INET) or (NextInfo.ai_family = AF_INET6) then
      begin
        if (Result.Machine = '') then
        begin
          SetLength(machine, NI_MAXHOST);
          RetVal := GetNameInfo(NextInfo.ai_addr, NextInfo.ai_addrlen,
            PChar(machine), NI_MAXHOST, nil, 0, 0);
          if (RetVal <> 0) then
          begin
            NextInfo := NextInfo.ai_next;
            continue;
          end;
          Result.Machine := PChar(machine);
        end;

        if NextInfo.ai_family = AF_INET then
        begin
          ptr := PSockAddrIn(NextInfo.ai_addr);
          namelen := INET_ADDRSTRLEN;
          SetLength(ip, namelen);
        end
        else
        begin
          ptr := PSockAddrIn6(NextInfo.ai_addr);
          namelen := INET6_ADDRSTRLEN;
          SetLength(ip, namelen);
        end;

        nameRet := inet_ntop(NextInfo.ai_family, ptr, PChar(ip), namelen);
        if (nameRet = nil) then
        begin
          NextInfo := NextInfo.ai_next;
          continue;
        end;

        if (NextInfo.ai_family = AF_INET6) then
          Result.IP6 := PChar(ip)
        else
          Result.IP4 := PChar(ip);
      end;
      NextInfo := NextInfo.ai_next;
    end;
  finally
    FreeAddrInfo(AddrInfo);
  end;
end;

【问题讨论】:

  • 你为什么要查找“localhost”?这可以在/etc/hosts、DNS等中设置。
  • 确实,如果有人愿意,可以将 LocalHost 设置为指向8.8.8.8
  • 另外,如果您在域中,您可能会收到localhost.example.com,在这种情况下,您可以尝试localhost.。
  • 这是通用的本地测试代码。本地主机仍然是默认的,所以这些查找值是不正确的。 localhost 都无法访问这些值,默认情况下 IIUC 应该是 ::1 或 127.0.0.1,这就是它们在我的机器上的值。
  • 尝试打开命令提示符并查看ipconfig /all 实际报告的内容,您可能会看到1700:: 和2.0.0.0 列出。如果存在多个 IP,您的代码将存储每种类型的最后找到的 IP,并且如果找到另一个相同类型的 IP,您不会检查是否已经存储了 IP。由于您正在对 "localhost" 进行 DNS 查找,因此除非您安装了环回适配器,否则您实际上不可能在 getaddrinfo() 的输出中看到 ::1 和 127.0.0.1。

标签: delphi sockets ipv6 ipv4


【解决方案1】:

我知道作者可能为时已晚,但我今天遇到了同样的问题,并注意到您将指向整个 ADDRINFO 结构的指针传递给 inet_ntop ,但是您应该只将指针传递给二进制地址。
所以调用 intet_ntop 获取 IPv4 地址应该是:

inet_ntop(NextInfo.ai_family, @PSockAddrIn(NextInfo.ai_addr).sin_addr, PChar(ip), namelen)

对于 IPv6 应该是:

inet_ntop(NextInfo.ai_family, @PSockAddrIn6(NextInfo.ai_addr).sin6_addr, PChar(ip), namelen)

【讨论】:

  • 感谢您的跟进!自从我问这个问题已经很长时间了,它已经解决了,但是该代码是根据合同完成的,所以我无法再访问它了。你做了很棒的侦探工作——我想我最终注意到了同样的事情并纠正了它。
猜你喜欢
  • 2011-03-14
  • 2015-02-23
  • 2010-11-04
  • 1970-01-01
  • 2017-10-12
  • 2016-06-21
  • 1970-01-01
  • 2011-10-11
  • 1970-01-01
相关资源
最近更新 更多