You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Delphi 7与Indy10:判断域名对应服务器是否仅支持IPv6及DNS服务器获取方案咨询

嘿,我来帮你理清这个问题——你已经搞定了IPv6转IPv4代理的核心逻辑,现在卡在域名解析的判断环节,对吧?咱们一步步拆解来看:

一、判断目标服务器是否仅支持IPv6的核心逻辑

不管用哪种工具,本质都是解析域名的两种DNS记录:

  • A记录:对应IPv4地址
  • AAAA记录:对应IPv6地址

判断规则很清晰:

  • 如果A记录为空,但AAAA记录存在 → 目标仅支持IPv6,需要走代理;
  • 如果A记录存在 → 直接用IPv4发起请求,跳过代理。

二、是否必须用TIdDNSResolver

完全没必要。Indy的组件只是封装了DNS解析逻辑,你也可以直接调用Windows原生API来实现,甚至更轻量(不用依赖Indy的额外代码)。不过如果你已经在用Indy开发,继续用它的组件也很方便,看你偏好。

方案1:用TIdDNSResolver实现(适配你的现有技术栈)

你之前找到的两段代码思路是对的,但其实不用手动指定DNS服务器——Indy10的TIdDNSResolver有个UseLocalDNS属性,设为True后会自动读取系统默认的DNS服务器,省了手动配置的麻烦。

给你补个完整的示例:

// 获取域名的所有IPv4地址
function GetIPv4List(const AHostName: string): TStringList;
var
  LDNS: TIdDNSResolver;
  I: Integer;
begin
  Result := TStringList.Create;
  LDNS := TIdDNSResolver.Create(nil);
  try
    LDNS.UseLocalDNS := True; // 关键:使用系统默认DNS
    LDNS.Timeout := 5000; // 设置超时,避免阻塞
    LDNS.Resolve(AHostName);
    for I := 0 to LDNS.QueryResult.Count - 1 do
    begin
      if LDNS.QueryResult[I].RecType = rtA then
        Result.Add(TIdARecord(LDNS.QueryResult[I]).IPAddress);
    end;
  finally
    LDNS.Free;
  end;
end;

// 获取域名的所有IPv6地址
function GetIPv6List(const AHostName: string): TStringList;
var
  LDNS: TIdDNSResolver;
  I: Integer;
begin
  Result := TStringList.Create;
  LDNS := TIdDNSResolver.Create(nil);
  try
    LDNS.UseLocalDNS := True;
    LDNS.Timeout := 5000;
    LDNS.Resolve(AHostName);
    for I := 0 to LDNS.QueryResult.Count - 1 do
    begin
      if LDNS.QueryResult[I].RecType = rtAAAA then
        Result.Add(TIdAAAARecord(LDNS.QueryResult[I]).IPAddress);
    end;
  finally
    LDNS.Free;
  end;
end;

// 判断是否需要走代理
function NeedIPv6Proxy(const AHostName: string): Boolean;
var
  LIPv4List, LIPv6List: TStringList;
begin
  LIPv4List := GetIPv4List(AHostName);
  LIPv6List := GetIPv6List(AHostName);
  try
    // 仅当无IPv4但有IPv6时,需要代理
    Result := (LIPv4List.Count = 0) and (LIPv6List.Count > 0);
  finally
    LIPv4List.Free;
    LIPv6List.Free;
  end;
end;

方案2:用Windows原生API实现(不依赖Indy)

如果你不想依赖Indy,用getaddrinfo这个标准Win32网络API就够了,它直接支持解析指定地址族的记录,而且自动使用系统DNS。

示例代码如下(需要引用WinSock2单元):

// 检查域名是否有IPv4地址
function HasIPv4(const AHostName: string): Boolean;
var
  LHints: addrinfo;
  LRes: Paddrinfo;
  LStatus: Integer;
begin
  Result := False;
  ZeroMemory(@LHints, SizeOf(LHints));
  LHints.ai_family := AF_INET; // 只查询IPv4
  LHints.ai_socktype := SOCK_STREAM;
  LHints.ai_protocol := IPPROTO_TCP;

  LStatus := getaddrinfo(PChar(AHostName), nil, @LHints, LRes);
  if LStatus = 0 then
  begin
    Result := True;
    freeaddrinfo(LRes);
  end;
end;

// 检查域名是否有IPv6地址
function HasIPv6(const AHostName: string): Boolean;
var
  LHints: addrinfo;
  LRes: Paddrinfo;
  LStatus: Integer;
begin
  Result := False;
  ZeroMemory(@LHints, SizeOf(LHints));
  LHints.ai_family := AF_INET6; // 只查询IPv6
  LHints.ai_socktype := SOCK_STREAM;
  LHints.ai_protocol := IPPROTO_TCP;

  LStatus := getaddrinfo(PChar(AHostName), nil, @LHints, LRes);
  if LStatus = 0 then
  begin
    Result := True;
    freeaddrinfo(LRes);
  end;
end;

// 判断是否需要走代理
function NeedIPv6Proxy(const AHostName: string): Boolean;
begin
  Result := not HasIPv4(AHostName) and HasIPv6(AHostName);
end;

三、关于获取系统DNS服务器的补充

如果你确实需要手动获取系统DNS列表(比如自定义解析逻辑),可以用Windows API的GetAdaptersAddresses函数,它能获取当前系统所有网络适配器的DNS配置。示例代码大概是这样:

function GetSystemDNS: TStringList;
var
  LBufLen: ULONG;
  LAdapterInfo: PIP_ADAPTER_ADDRESSES;
  LStatus: DWORD;
  LAdapter: PIP_ADAPTER_ADDRESSES;
  LDns: PIP_ADAPTER_DNS_SERVER_ADDRESS;
  LIPBuf: array[0..63] of Char;
  LBufSize: DWORD;
begin
  Result := TStringList.Create;
  LBufLen := 0;
  // 先获取所需缓冲区大小
  LStatus := GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nil, nil, @LBufLen);
  if LStatus = ERROR_BUFFER_OVERFLOW then
  begin
    GetMem(LAdapterInfo, LBufLen);
    try
      LStatus := GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nil, LAdapterInfo, @LBufLen);
      if LStatus = ERROR_SUCCESS then
      begin
        LAdapter := LAdapterInfo;
        while LAdapter <> nil do
        begin
          LDns := LAdapter.FirstDnsServerAddress;
          while LDns <> nil do
          begin
            // 把IP地址转为字符串
            LBufSize := SizeOf(LIPBuf);
            if WSAAddressToString(LDns.Address.lpSockaddr, LDns.Address.iSockaddrLength, nil, LIPBuf, @LBufSize) = 0 then
              Result.Add(LIPBuf);
            LDns := LDns.Next;
          end;
          LAdapter := LAdapter.Next;
        end;
      end;
    finally
      FreeMem(LAdapterInfo);
    end;
  end;
end;

这段代码需要引用IPHlpApiWinSock2单元,Delphi7里可能需要手动添加这些单元的引用。

最后总结

  • 核心判断逻辑是检查A/AAAA记录的存在性,和用什么工具无关;
  • 不用强制依赖TIdDNSResolver,Windows原生API更轻量;
  • 用Indy的话,开启UseLocalDNS就能自动用系统DNS,不用手动配置;
  • 手动获取系统DNS可以用GetAdaptersAddresses实现。

内容的提问来源于stack exchange,提问作者Ross

火山引擎 最新活动