2014-08-29 108 views
0

我在编写小型DNS服务器时发现了一个奇怪的行为,并将其划分到最低限度。该程序应该在127.0.0.1:1337上监听DNS查询并回复拒绝。我通过发行dig @localhost -p 1337 foo.bar.来测试其行为。如果第48行被注释掉//char bout[bufferSize]; // <-- WTF,它的作用就像魅力一样。启动时错误的端口(recvfrom)

程序:

#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 

这只是一个削减例如,在实际的代码回合被用来构建对传入查询的有效响应。但是,当我离开它时,出现以下proplem:

Wireshark packet capture

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

我在做什么错?

+0

如果你没有在函数中使用回合,那么为什么要麻烦初始化呢?或者它是不同的代码? – Arpit 2014-08-29 20:20:08

+0

构成问题一部分的代码必须包含在问题中,而不是发布在别处。 – EJP 2014-08-29 23:10:02

回答

1

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

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

电话给recvfrom()之前,添加:

len=sizeof(sender); 

这在recvfrom(2) man page解释:

的参数addrlen中是一个值结果参数,其中主叫 应初始化之前调用缓冲区的大小关联 与src_addr,并修改返回时指示的实际大小 源地址

+1

不是_srcaddr_和_addrlen_“recvfrom”的第5和第6个参数,而不是第3个和第4个参数? – 2014-08-30 12:59:14

+0

是的,我错了 - 编辑进行更正。 – 2014-08-30 14:05:16

+0

哇,我的手册页已过时。谢谢你的答案,不幸的是我不能upvote jet。 – charly 2014-08-30 20:09:52