基于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;
这段代码需要引用IPHlpApi和WinSock2单元,Delphi7里可能需要手动添加这些单元的引用。
最后总结
- 核心判断逻辑是检查A/AAAA记录的存在性,和用什么工具无关;
- 不用强制依赖
TIdDNSResolver,Windows原生API更轻量; - 用Indy的话,开启
UseLocalDNS就能自动用系统DNS,不用手动配置; - 手动获取系统DNS可以用
GetAdaptersAddresses实现。
内容的提问来源于stack exchange,提问作者Ross




