【问题标题】:Wrong port on startup (recvfrom)启动时端口错误(recvfrom)
【发布时间】:2014-10-23 21:22:21
【问题描述】:

我在编写小型 DNS 服务器时偶然发现了一种奇怪的行为,并将其条带化到最低限度。该程序应在 127.0.0.1:1337 上侦听 DNS 查询并以拒绝进行回复。我通过发出 dig @localhost -p 1337 foo.bar. 来测试它的行为如果第 48 行被注释掉 //char bout[bufferSize]; // <-- WTF 它就像魅力一样工作。

程序:

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

static int SOCKET;


void bindSocket(){
    int s = socket (AF_INET, SOCK_DGRAM, 0);
    if (s < 0) {
        fprintf (stderr, "Could not create socket\n");
        exit (EXIT_FAILURE);
     }

    struct sockaddr_in address;
    memset((char *)&address, 0, sizeof(address));

    inet_aton("127.0.0.1", &address.sin_addr);
    address.sin_family  = AF_INET;
    address.sin_port    = htons(1337);

    int rc = bind ( s, (struct sockaddr *) &address, sizeof (address) );
    if (rc < 0) {
         fprintf (stderr, "Could not bind Socket\n %s \n", strerror(errno));
         exit (EXIT_FAILURE);
    }
    SOCKET = s;
}

void decline(uint16_t err, char *bin, struct sockaddr *to){
    char bout[12];
    memset((bout + 4), 0, 8);
    memcpy(bout, bin, 4);
    bout[2] = (bout[2] | 0x80) & 0xFE;
    bout[3] = (bout[3] | err ) & 0x7F;
    sendto( SOCKET, bout, 12, 0, to, sizeof(struct sockaddr) );
}

void hereBeDragons(){
    size_t bufferSize = 512;
    char bin[bufferSize];
    char bout[bufferSize]; // <-- WTF
    struct sockaddr sender;
    socklen_t len;
    while(1){
        memset(bin,  0, bufferSize);
        int n = recvfrom( SOCKET, bin, bufferSize, 0, &sender, &len );
        if (n < 0) continue;
        puts("receved a query");

        /* Strictly decline all invalid queries */
        decline( 2, bin, &sender);
    }
}

int main(){
    bindSocket();
    hereBeDragons();
    return EXIT_FAILURE;
}

程序输出:

received a query

挖掘输出:

; <<>> DiG 9.9.5-3-Ubuntu <<>> @localhost -p 1337 foo.bar.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 37520
;; flags: qr ad; QUERY: 0, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 0

;; Query time: 1 msec
;; SERVER: 127.0.0.1#1337(127.0.0.1)
;; WHEN: Fri Aug 29 21:37:46 CEST 2014
;; MSG SIZE  rcvd: 12

这只是一个缩减示例,在实际代码中,bout 用于构造对传入查询的有效响应。但是当我把它留在里面时,会出现以下问题:

程序将答案发送到错误的端口,5 秒后 dig 重试,程序发送到正确的端口。

我做错了什么?

【问题讨论】:

  • 如果你没有在函数中使用 bout 那么为什么还要初始化它呢???还是代码不同??
  • 构成问题一部分的代码必须包含在问题中,而不是发布在其他地方。

标签: c linux dns udp port


【解决方案1】:

这是一个非常微妙的错误。

套接字地址长度参数,recvfrom() 的第六个参数,必须初始化。它必须设置为指示第五个参数中传递的地址缓冲区的长度。当 recvfrom() 返回时,更新长度以反映写入它的网络地址结构的实际大小。

在调用 recvfrom() 之前,添加:

len=sizeof(sender);

这在recvfrom(2) man page中有解释:

参数 addrlen 是一个值结果参数,调用者 应该在调用之前初始化相关的缓冲区大小 带有 src_addr,并在返回时修改以指示实际大小 源地址

【讨论】:

  • srcaddraddrlen不是recvfrom的第5个和第6个参数,不是第3个和第4个吗?
  • 是的,我弄错了——经过编辑以进行更正。
  • 哇,我的手册页已经过时了。谢谢你的回答,很遗憾我不能投票给喷气机。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-07
  • 1970-01-01
  • 2018-08-02
  • 2015-09-23
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
相关资源
最近更新 更多