Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何选择用于执行主机名查找的接口_C_Linux_Dns_Getaddrinfo - Fatal编程技术网

C 如何选择用于执行主机名查找的接口

C 如何选择用于执行主机名查找的接口,c,linux,dns,getaddrinfo,C,Linux,Dns,Getaddrinfo,我在一个运行Linux和BusyBox的设备中嵌入的应用程序中工作。硬件有两个通信接口:Wi-Fi和3G 在每次连接中,应用程序必须首先尝试使用wi-fi连接,如果失败,应用程序将使用3G再次尝试连接 我强制连接使用所选接口绑定它,如下所示: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h&

我在一个运行Linux和BusyBox的设备中嵌入的应用程序中工作。硬件有两个通信接口:Wi-Fi和3G

在每次连接中,应用程序必须首先尝试使用wi-fi连接,如果失败,应用程序将使用3G再次尝试连接

我强制连接使用所选接口绑定它,如下所示:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <net/if.h>

static void resolveDns(const char *hostname, struct addrinfo **destInfo)
{
    int err;
    struct addrinfo hints;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_DGRAM;

    if ((err = getaddrinfo(hostname, "80", &hints, destInfo)) != 0) {
        fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(err));
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in *addr = (struct sockaddr_in *)((*destInfo)->ai_addr);
    printf("Destination IP: %s\n", inet_ntoa(addr->sin_addr));
}

static void getComInterface(const char *iface, struct ifreq *ifr)
{
    ifr->ifr_addr.sa_family = AF_INET;
    strcpy(ifr->ifr_name, iface);

    int sock = socket(AF_INET, SOCK_DGRAM, 0);
    int err = ioctl(sock, SIOCGIFADDR, ifr);
    close(sock);

    if (err) {
        fprintf(stderr, "ioctl error: %d\n", err);
        exit(EXIT_FAILURE);
    }

    printf("Interface IP: %s\n", inet_ntoa(((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr));
}

int main()
{
    int err;

    struct ifreq ifr;
    getComInterface("wlan0", &ifr);

    struct addrinfo *destInfo;
    resolveDns("www.google.com", &destInfo);

    int s = socket(AF_INET, SOCK_STREAM, 0);
    err = bind(s, &ifr.ifr_addr, sizeof(ifr.ifr_addr));
    if (err) {
        fprintf(stderr, "bind error = %d, %d\n", err, errno);
        exit(EXIT_FAILURE);
    }

    err = connect(s, destInfo->ai_addr, destInfo->ai_addrlen);
    if (err) {
        fprintf(stderr, "connect error = %d, %d \n", err, errno);
        exit(EXIT_FAILURE);
    }

    printf("Ok!\n");

    freeaddrinfo(destInfo);
    close(s);
    return EXIT_SUCCESS;
}

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
静态无效解析DNS(常量字符*主机名,结构addrinfo**destInfo)
{
INTERR;
结构addrinfo提示;
memset(&hints,0,sizeof(hints));
hits.ai_family=AF_unsec;
hits.ai_socktype=SOCK_DGRAM;
if((err=getaddrinfo(主机名,“80”&提示,destInfo))!=0){
fprintf(标准,“getaddrinfo错误:%s\n”,gai_strerror(错误));
退出(退出失败);
}
结构sockaddr_in*addr=(结构sockaddr_in*)((*destInfo)->ai_addr);
printf(“目标IP:%s\n”,inet\u ntoa(addr->sin\u addr));
}
静态void getComInterface(常量字符*iface,结构ifreq*ifr)
{
ifr->ifr\u addr.sa\u family=AF\u INET;
strcpy(ifr->ifr\U名称,iface);
int sock=插座(AF INET,sock DGRAM,0);
int err=ioctl(sock、SIOCGIFADDR、ifr);
关闭(袜子);
如果(错误){
fprintf(标准,“ioctl错误:%d\n”,错误);
退出(退出失败);
}
printf(“接口IP:%s\n”,inet_ntoa(((struct sockaddr_in*)&ifr->ifr_addr)->sin_addr));
}
int main()
{
INTERR;
结构ifreq-ifr;
getComInterface(“wlan0”和ifr);
结构addrinfo*destInfo;
resolveDns(www.google.com,&destInfo);
int s=套接字(AF_INET,SOCK_STREAM,0);
err=bind(s,&ifr.ifr\u addr,sizeof(ifr.ifr\u addr));
如果(错误){
fprintf(stderr,“绑定错误=%d,%d\n”,err,errno);
退出(退出失败);
}
err=connect(s,destInfo->ai_addr,destInfo->ai_addrlen);
如果(错误){
fprintf(标准,“连接错误=%d,%d\n”,错误,错误号);
退出(退出失败);
}
printf(“确定!\n”);
freeaddrinfo(destInfo);
关闭(s);;
返回退出成功;
}
但这并不能解决DNS查找中的问题

有没有办法强制getaddrinfo使用所选接口? 或者,更好的是,有没有一种方法可以强制所有连接使用所选接口而不断开另一个接口

附言:如果你知道如何在更复杂的环境中做到这一点,比如Ubuntu,请分享你的解决方案


谢谢

恐怕仅仅通过标准C库是无法做到的, i、 e.您需要更改默认网关,并为每个连接设置网关。 请考虑下面的伪代码:

  • 系统启动:

    • 通过wifi建立连接
    • 通过3G建立连接
    • 将wifi接口配置为默认网关
  • 在新连接请求时:

    • 进行DNS查找(通过默认路由进行)
      • 如果成功了
        • 将IP地址与文件描述符一起保存
        • 通过作为当前网关的接口配置到此IP的路由
        • 打开IP地址的套接字,流量将通过给定的接口
      • 如果不是
        • 将默认网关更改为其他接口(3G),然后重试
  • 断开连接时:

    • 通过断开连接的文件描述符查找IP地址
    • 从路由表中删除IP
    • 转到“新连接请求时”(这取决于您的应用程序逻辑)
通过这种方式,您将能够更改新连接的默认网关,但保留现有连接的默认网关

路由表的更改可以通过Linux shell命令完成,如
ip route
等。 它们可以通过
系统
从C启动,例如
系统(“ip路由显示”)
您还可以编写具有更复杂逻辑的脚本,并从C代码中启动它们

但是,此解决方案有一个缺陷,通常,如果您当前的接口没有Internet连接,
这意味着使用此接口的所有连接最终都可能会失败。

我将不顾一切地指出,使用Linux标准库中的地址解析函数,您想要的是不可能的。您必须自己编写DNS操作代码,并在代码中将接口显式绑定到套接字。即使这样,一些Linux变体也需要提升权限才能做到这一点。然而,我已经准备好被证明是错误的。这似乎是个好主意,现在我必须研究如何设置路线规则。而且我可能不再需要绑定套接字了。通过分析iproute2的源代码,我发现我可以使用
rtnetlink\u socket=socket(AF\u NETLINK,socket\u类型,NETLINK\u路由)
无需调用
system
execv
即可更改路由表。我还没有决定要实现哪一个。@matheusrosisaciotto,不幸的是绑定一个“客户端”套接字不会有帮助。当然,您可以自己实现路由,但我建议至少在开始时使用busybox中现有的工具。