我正在通过编写一个向指定服务器发送 DNS 查询的小型应用程序来学习 C。这是网络代码的示例:
int send_query()
{
int sockfd;
struct sockaddr_in server;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("cannot create socket\n");
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(53);
inet_pton(AF_INET, "8.8.8.8", &(server.sin_addr));
sendto(sockfd, const void *buffer, size_t length, 0, (struct sockaddr *) &server, sizeof(server));
}
这工作正常,因为查询已成功发送,并收到回复。但是,通过使用 Wireshark 嗅探流量,我可以看到消息:Destination unreachable (Port unreachable)
。
我发现我可以通过在 sendto()
之前调用 bind()
来避免这种情况:
int send_query()
{
int sockfd;
struct sockaddr_in server;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
perror("cannot create socket\n");
}
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(53);
inet_pton(AF_INET, "8.8.8.8", &(server.sin_addr));
if(bind(sockfd, (struct sockaddr *) &server, sizeof(server)) < 0) {
perror("bind failed\n");
}
sendto(sockfd, const void *buffer, size_t length, 0, (struct sockaddr *) &server, sizeof(server));
}
现在 Destination unreachable (Port unreachable)
消息消失了,但应用程序必须以 root 权限运行,因为它将绑定(bind)到端口 53。
是否可以修改代码以便改用随机的非特权源端口?
问题已解决
问题的发生是因为一个愚蠢的错误。我只是简单地注释掉了 recvfrom()
。当我在测试应用程序时嗅探流量时,我可以看到响应和错误到达我的计算机,并在应用程序接收时错误地将其混淆。因为我不知道自己到底在做什么,所以我开始乱搞 bind()
等,然后就开始了这种雪崩式的失败。
为简洁起见,我没有发布所有代码,但如果这样做的话,问题可能会立即得到解决。
最佳答案
您可以绑定(bind)到端口 0,让操作系统随机选择一个可用的端口(就像 INADDR_ANY 为 0)。参见 https://stackoverflow.com/a/1077305/3543692
此外,绑定(bind)到端口 53 毫无意义。 53端口是DNS服务器的端口,不是DNS客户端的端口。想一想如果你电脑上的所有DNS客户端都使用53作为DNS客户端端口,那么只能同时对一台服务器进行一个DNS请求。通常,所有客户端(包括 TCP/UDP)都使用操作系统分配的随机未使用端口。
关于c - UDP套接字的源端口和目标端口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29711247/