这篇文章是古早时期的博客上迁移过来的(什么赛博秽土转生), 如有错误欢迎指正
时间线
背景
为API配置了多条访问线路,以应对部分地区无法访问导致服务不可用的情况。开始是想到在网站里新建一个文件保存节点信息,发现行不通,联不通的地区根本无法获知其他线路;于是想到用DNS解析记录,用一个TXT解析记录来保存相应的节点数据,以备查询
实战
查询资料
一番Bing下来,发现大部分现有的文章都是使用socket来直接发送查询数据包,获取到的也都是A和CNAME类型的记录,方案不可行。最后,把目光锁定到了MSDN上的DnsQuery函数上,同时找到一篇样例:使用DnsQuery解析主机名
函数分析
从官方文档上得知,函数所需头文件为windns.h,需要包含引入库文件Ws2_32.lib和Dnsapi.lib,函数的参数如下:
DNS_STATUS DnsQuery_A(
[in] PCSTR pszName,
[in] WORD wType,
[in] DWORD Options,
[in, out, optional] PVOID pExtra,
[out, optional] PDNS_RECORD *ppQueryResults,
[out, optional] PVOID *pReserved
);
参数解释:
pszName是要查询的主机名;wType是查询类型,如A记录,CNAME,TXT等,具体的MSDN官方给出了文档:DNS ConstantsOptions字面意思上理解是查询方式,我直接使用DNS_QUERY_STANDARD,具体也可以查询文档:DNS ConstantspExtra和pReserved直接给NULL就行ppQueryResults传入查询结果的指针,类型是PDNS_RECORD,这个需要传入变量值的引用。- 返回值,DNS_STATUS类型,如果查询失败会返回Winerror.h中的相应错误码
使用
明白了使用方法,那就简单了。
首先引入库:
#include <windns.h>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Dnsapi.lib")
我的TXT记录域名为test.iyoroy.cn,查询部分代码如下:
PDNS_RECORD pDnsRecord;
DNS_STATUS QueryRet = DnsQuery(L"test.iyoroy.cn", DNS_TYPE_TEXT, DNS_QUERY_STANDARD, NULL, &pDnsRecord, NULL);
if (QueryRet) {
MessageBox(GhWnd, L"DNS查询失败!\r\n将使用默认节点", L"Warning", MB_ICONERROR | MB_OK);
}
std::wstring strQueryRes = *pDnsRecord->Data.TXT.pStringArray;//这个就是查询结果
成功实现功能
后记
TXT记录会自动过滤掉空格、换行,因此我选择记录base64编码,使用时再解码并使用stringstream拆分空格。Base64解码我借鉴了这篇文章:C++进行base64编码和解码,以下是我用宽字节重写的解码函数
//Function:Base64解密
static const std::wstring base64_chars =
L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
L"abcdefghijklmnopqrstuvwxyz"
L"0123456789+/";
static inline bool is_base64(wchar_t c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::wstring base64_decode(std::wstring const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
wchar_t char_array_4[4], char_array_3[3];
std::wstring ret;
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++)
char_array_4[j] = 0;
for (j = 0; j < 4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}
拆分:
wstringstream wDnsRec(base64_decode(*pDnsRecord->Data.TXT.pStringArray));
wstring wNodes[16];
unsigned int nNodes = 0;
while (wDnsRec >> wNodes[nNodes])
nNodes++;
{/collapse-item}
之后再选择可用节点就行
参考文章:
Helped a bunch, thanks!
Am having issues with conversion to std::wstring at the end, but it might just be a skill issue.
P.S. The hours passed today counter has exploded, might wanna take a look.
Glad it helped! About the std::wstring conversion, I hope the following functions can help:
std::string ws2s(const std::wstring& wstr) { // std::wstring to std::string
if (wstr.empty()) return "";
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL);
std::string str(size_needed, 0);
WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size_needed, NULL, NULL);
return str;
}
std::wstring s2ws(const std::string& str) { // std::string to std::wstring
if (str.empty()) return L"";
int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), NULL, 0);
std::wstring wstr(len, 0);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], len);
return wstr;
}
As for the 'exploded' counter, I'm still working on it. Thanks for keeping an eye on it!
The issue was that I did not write
#pragma comment(lib, "Ws2_32.lib") #pragma comment(lib, "Dnsapi.lib")
at the start and the compiler got very confused lol, still many thanks for trying to help.
Best of luck and see you later space cowboy!
Glad it's sorted out! It was my oversight not to make that hint more prominent. I've updated the post with a clearer reminder for those libraries and headers and added a 'Timeline' section to document this update (partly thanks to your feedback!).
Best of luck with your project!