2014-08-29 26 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