因某些运营商会恶意锁死竞争对手的dns,如某动的光猫,直接把电信的114给墙了,导致系统无法通过114这个域名服务器解析域名
这里说明下如何设置libcurl下使用的dns服务器
一 分析
1 下载libcurl包
wget https://curl.haxx.se/download/curl-7.53.0.tar.bz2
tar xjf curl-7.53.0.tar.bz2
cd curl-7.53.0
2 libcurl设置DNS解析服务器
通过如下函数可以设置指定dns域名服务器
curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, “xxx.xxx.xxx.xxx”);
3 libcurl dns部分源码分析
[jenkins@localhost lib]$ grep -r CURLOPT_DNS_SERVERS *
url.c: case CURLOPT_DNS_SERVERS:
进入url.c中,找到CURLOPT_DNS_SERVERS关键字,发现调用Curl_set_dns_servers函数
case CURLOPT_DNS_SERVERS:
result = Curl_set_dns_servers(data, va_arg(param, char *));
break;
在当前目录下索索Curl_set_dns_servers函数
[jenkins@localhost lib]$ grep -r Curl_set_dns_servers *
asyn-ares.c:CURLcode Curl_set_dns_servers(struct Curl_easy *data,
asyn-thread.c:CURLcode Curl_set_dns_servers(struct Curl_easy *data,
hostip.h:CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers);
hostsyn.c:CURLcode Curl_set_dns_servers(struct Curl_easy *data,
url.c: result = Curl_set_dns_servers(data, va_arg(param, char *));
发现有三处函数定义asyn-ares.c,asyn-thread.c,hostsyn.c
asyn-thread.c和hostsyn.c中函数原型如下,可以看到在函数中并没有对两个传入参数做任何引用,直接就返回了。
CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers)
{
(void)data;
(void)servers;
return CURLE_NOT_BUILT_IN;
}
asyn-ares.c 函数原型如下,可以看到当c-ares version >= 0x010704 时候,会将data和servers两个参数传入ares_set_servers_csv函数中,这个函数应该就是ares中设置dns服务器的接口
CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers)
{
CURLcode result = CURLE_NOT_BUILT_IN;
int ares_result;
/* If server is NULL or empty, this would purge all DNS servers
* from ares library, which will cause any and all queries to fail.
* So, just return OK if none are configured and don't actually make
* any changes to c-ares. This lets c-ares use it's defaults, which
* it gets from the OS (for instance from /etc/resolv.conf on Linux).
*/
if(!(servers && servers[0]))
return CURLE_OK;
#if (ARES_VERSION >= 0x010704)
ares_result = ares_set_servers_csv(data->state.resolver, servers);
switch(ares_result) {
case ARES_SUCCESS:
result = CURLE_OK;
break;
case ARES_ENOMEM:
result = CURLE_OUT_OF_MEMORY;
break;
case ARES_ENOTINITIALIZED:
case ARES_ENODATA:
case ARES_EBADSTR:
default:
result = CURLE_BAD_FUNCTION_ARGUMENT;
break;
}
#else /* too old c-ares version! */
(void)data;
(void)(ares_result);
#endif
return result;
}
经过测试发现,curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, “xxx.xxx.xxx.xxx”);函数并没有走asyn-ares.c 中的Curl_set_dns_servers,在libcurl的编译配置文件(arm的buildroot系统),有c-ares的条件编译,发现没有吧c-ares加进去,加进去后测试,发现libcurl使用asyn-ares.c中的函数了
ifeq ($(BR2_PACKAGE_C_ARES),y)
LIBCURL_DEPENDENCIES += c-ares
LIBCURL_CONF_OPTS += --enable-ares
else
LIBCURL_CONF_OPTS += --disable-ares
endifest_libcurl
二 测试
为模拟运营商封掉某些域名服务器,可以通过设置iptable 禁用ip
要封停一个IP,使用下面这条命令:
iptables -I INPUT -s ... -j DROP
要解封一个IP,使用下面这条命令:
iptables -D INPUT -s ... -j DROP
禁用114dns服务器
iptables -I INPUT -s 114.114.114.114 -j DROP
禁用可以ping下验证,应该是ping不通的
测试代码见结尾,使用方法
./test_libcurl dns url
第一个参数为要设置的dns, 如114.114.114.114
第二个参数为要访问的url,如www.baidu.com 这里设置比较简单,建议使用baidu,163等,chnshp.lenovo.com.cn 有特殊的设置,这里只验证使用这种方式更改dns是生效的
1 使用114作为dns服务器解析www.baidu.com
这里iptable是没有被禁用的,可以ping通
ping 114.114.114.114
./test_libcurl 114.114.114.114 www.baidu.com
2 禁用114 IP,再次测试
发现ping不通,然后按照设置10秒后超时
iptables -I INPUT -s 114.114.114.114 -j DROP
ping 114.114.114.114
./test_libcurl 114.114.114.114 www.baidu.com
3 在114被禁用掉的基础上,使用其他dns服务器解析baidu
使用202和路由器dns都是可以的成功的
./test_libcurl 192.168.0.1 www.baidu.com
./test_libcurl 202.106.0.20 www.baidu.com
4 在禁用114的基础上,测试设置使用多个dns服务器
./test_libcurl 202.106.0.20,114.114.114.114 www.baidu.com 这里使用114和202,中间以逗号隔开即可,可以成功解析
5 在此基础上打开114禁用,测试,通过
iptables -D INPUT -s 114.114.114.114 -j DROP
./test_libcurl 114.114.114.114 www.baidu.com
三 结论
综上,在增加c-ares之后,curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, “xx.xx.xx.xx”);这个函数可以设置dns服务器(可以设置多个dns服务器,亲测10个以上都有效),由此可以解决用户因为运营商封IP导致的应用程序升级程序中curl域名解析失败的问题。
四 测试代码test_libcurl.c
#include <stdio.h>
#include <curl/curl.h>
#include <curl/easy.h>
int test(char *URL, char * host) {
CURLcode res;
CURL *curl;
if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) {
printf("curl_global_init() failed\n");
return -1;
curl = curl_easy_init();
if (!curl) {
printf("curl_easy_init() failed\n");
curl_global_cleanup();
return 1;
}
curl_easy_setopt(curl, CURLOPT_URL, URL);
curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, host);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl, CURLOPT_FILETIME, 1L);
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
res = curl_easy_perform(curl);
test_cleanup:
curl_easy_cleanup(curl);
curl_global_cleanup();
return (int)res;
}
int main(int argc, char *argv[]) {
test(argv[2], argv[1]);
return 0;
}