ICU的IDNA/Punycode API默认不转换纯ASCII域名大小写?如何解决?
让libicu对纯ASCII域名执行小写转换的解决方案
问题重现
测试程序使用libicu的uidna_IDNToASCII函数对Unicode域名进行Punycode编码,测试结果如下:
$ g++ -std=c++11 -W -Wall test.cpp -licucore $ ./a.out EXAMΠLE.com xn--examle-s0e.com $ ./a.out EXAMPLE.com EXAMPLE.com
带希腊字母的域名会被转成小写后编码为Punycode,但纯ASCII的EXAMPLE.com输出保持原大写,未执行小写转换。
原因分析
libicu的uidna_IDNToASCII函数遵循IDNA规范:当输入包含非ASCII字符时,会先将域名转换为小写再进行Punycode编码;但对于纯ASCII域名,函数默认认为其符合域名格式要求,不会额外处理大小写转换。
解决方案
在调用uidna_IDNToASCII之前,使用libicu的u_strToLower函数将输入字符串统一转换为小写,无论输入是否包含非ASCII字符。
修改后的完整代码如下:
#include <cstdio> #include <string> #include <unicode/uidna.h> #include <unicode/ustring.h> #include <vector> #include <cassert> std::string convert_utf8_to_idna(const std::string& input) { UErrorCode err = U_ZERO_ERROR; std::int32_t needed = 0; // 将UTF-8转换为UChar auto src = std::vector<UChar>(1000); (void)u_strFromUTF8WithSub( src.data(), src.size(), &needed, input.data(), input.size(), 0xFFFD, nullptr, &err ); src.resize(needed); assert(err == U_ZERO_ERROR); // 统一转换为小写 std::vector<UChar> lower_src(src.size()); u_strToLower( lower_src.data(), lower_src.size(), src.data(), src.size(), nullptr, &err ); assert(err == U_ZERO_ERROR); // 执行IDNA编码 auto dest = std::vector<UChar>(1000); needed = uidna_IDNToASCII( lower_src.data(), lower_src.size(), dest.data(), dest.size(), UIDNA_ALLOW_UNASSIGNED, nullptr, &err ); assert(err == U_ZERO_ERROR); dest.resize(needed); return std::string(dest.begin(), dest.end()); } int main(int argc, char **argv) { std::string input = (argc >= 2) ? argv[1] : "example.com"; std::string output = convert_utf8_to_idna(input); printf("%s\n", output.c_str()); }
测试验证
修改后重新编译运行:
$ g++ -std=c++11 -W -Wall test.cpp -licucore $ ./a.out EXAMΠLE.com xn--examle-s0e.com # 结果不变,依然是小写编码 $ ./a.out EXAMPLE.com example.com # 纯ASCII域名现在转为小写输出
内容的提问来源于stack exchange,提问作者Quuxplusone
相关产品推荐
相关产品推荐

