2017-04-04 79 views
1

我想查詢特定的服務器,並以與我們通過getaddrinfo獲得結果相同的方式獲得結果。我想得到一個addrinfo結構,所以我可以有IP,端口和指向下一個結果的指針。查詢服務器並以addrinfo的形式得到結果struct

我正在使用下面的代碼,查詢我想要的服務器,並獲得結果。但是每個結果都在另一個結構中,並且它們不會彼此指向(不在列表中)。

這是代碼:

static int my_getaddrinfo(const char *dns_server_s, const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) { 
    int retValue = 1; 
    struct __res_state result; 
    char ip[16]; 
    memset(ip, '\0', sizeof(ip)); 

    res_ninit(&result); 
    struct in_addr addr; 
    inet_aton(dns_server_s, &addr); 

    result.nsaddr_list[0].sin_addr = addr; 
    result.nsaddr_list[0].sin_family = AF_INET; 
    result.nsaddr_list[0].sin_port = htons(NS_DEFAULTPORT); 
    result.nscount = 1; 

    u_char answer[NS_PACKETSZ]; 
    int len = res_nquery(&result, node, ns_c_in, ns_t_a, answer, sizeof(answer)); 
    ns_msg handle; 
    ns_initparse(answer, len, &handle); 

    if(ns_msg_count(handle, ns_s_an) > 0) { 
     ns_rr rr; 
     if(ns_parserr(&handle, ns_s_an, 0, &rr) == 0) { 
      strcpy(ip, inet_ntoa(*(struct in_addr *)ns_rr_rdata(rr))); 
      getaddrinfo(ip, service, hints, res); 
      retValue = 0; 
     } 
    } 

    return retValue; 
} 

是否有可能得到的結果我想要的方式?類似於 addrinfo結構?

編輯: 我可以看到,我得到三個答案ns_msg_count(handle, ns_s_an) = 3 和訪問每個答案我應該叫ns_parserr(&handle, ns_s_an, answer_index, &rr) 但正如我所說,我希望得到這些答案的列表,就像我通過調用讓他們getaddrinfo

+0

你有沒有真的試圖什麼嗎?根據你的描述,你看起來好像你知道你在做什麼,那麼你的問題究竟在哪裏?查找哪些參數是結果集中的索引(我假設它是'0'),然後遍歷結果,並隨時創建一個「addrinfo」列表。 – Phillip

+0

我知道我可以獲取in_addr,但是如何從結果中獲得addrinfos? – Roee84

+0

或者你的意思是隻爲每個ip調用getaddrinfo .. – Roee84

回答

2

getaddrinfo返回的不僅僅是IP地址,它還會解析服務名稱到端口號,它可以支持不同的協議,主要是tcp和udp。因此,您需要通過調用getservbyname_r來解析服務名稱,併爲ip,端口和協議的每個組合手動構建結果addrinfo。這是一個解析IP和端口的簡單版本,但它只返回TCP的addrinfo。你可以很容易地將它擴展到包含UDP。

另一個特點是,getaddrinfo也支持IPv6,在這種情況下,你需要調用res_nqueryT_AAAA,而不是T_A解決IPv6地址。

下面是一個例子,注意它返回一個鏈接列表struct addrinfo就像getaddrinfo一樣,所以結果應該是免費的,當你完成後,請使用freeaddrinfo

static int my_getaddrinfo(const char *dns_server, 
     const char *node, const char *service, 
     const struct addrinfo *hints, struct addrinfo **res) { 

    int ret; 

    // get dns server sockaddr 
    struct addrinfo hint = {0}; 
    struct addrinfo *ai = NULL; 

    hint.ai_family = AF_INET; 
    hint.ai_socktype = SOCK_DGRAM; 
    hint.ai_protocol = IPPROTO_UDP; 
    ret = getaddrinfo(dns_server, "domain", &hint, &ai); 
    if (ret != 0) { 
     puts("getaddrinfo dns error"); 
     return 1; 
    } 

    if (!ai) { 
     printf("getaddrinfo returned no result\n"); 
     return 1; 
    } 

    freeaddrinfo(ai); 

    int port = 0; 
    // get service port 
    if (service) { 
     struct servent srv, *sres; 
     char buf[1024]; 
     ret = getservbyname_r(service, NULL, &srv, buf, sizeof buf, &sres); 
     if (ret != 0) { 
      printf("getservbyname error\n"); 
      return 1; 
     } 
     port = sres->s_port; 
    } 

    struct __res_state p = {0}; 
    res_state state = &p; 
    unsigned char ans[NS_MAXMSG]; 
    ns_msg msg; 
    ns_rr rr; 
    char line[1024]; 
    int len ; 

    ret = res_ninit(state); 
    if (ret != 0) { 
     printf("res_ninit error\n"); 
     return 1; 
    } 

    state->nscount = 1; 
    memset(state->nsaddr_list, 0, sizeof state->nsaddr_list); 
    memcpy(state->nsaddr_list, ai->ai_addr, sizeof state->nsaddr_list[0]); 

    ret = res_nquery(state, node, C_IN, T_A, ans, sizeof ans); 
    if (ret < 0) { 
     printf("res_nquery error\n"); 
     return 1; 
    } 

    len = ret; 
    ret = ns_initparse(ans, len, &msg); 
    if (ret != 0) { 
     printf("ns_initparse error\n"); 
     return 1; 
    } 

    len = ns_msg_count(msg, ns_s_an); 

    if (len == 0) { 
     printf("no address found\n"); 
     return 0; 
    } 

    struct addrinfo *head = NULL; 
    struct addrinfo *cur = NULL; 
    struct addrinfo *prev = NULL; 
    struct sockaddr_in *sin; 

    for (int i = 0; i < len; i++) { 
     ret = ns_parserr(&msg, ns_s_an, i, &rr); 
     if (ret != 0) { 
      printf("ns_parserr error\n"); 
     } 

     if (ns_rr_rdlen(rr) != NS_INADDRSZ) { 
      continue; 
     } 

     cur = malloc(sizeof *cur + sizeof(struct sockaddr_in)); 
     memset(cur, 0, sizeof *cur); 
     cur->ai_family = AF_INET; 
     cur->ai_socktype = SOCK_STREAM; 
     cur->ai_protocol = IPPROTO_TCP; 
     cur->ai_addrlen = sizeof(struct sockaddr_in); 
     cur->ai_addr = (void*)(cur + 1); 
     cur->ai_canonname = NULL; 
     cur->ai_next = NULL; 
     sin = (struct sockaddr_in*)(cur->ai_addr); 
     sin->sin_family = cur->ai_family; 
     sin->sin_port = port; 
     memcpy(&sin->sin_addr, ns_rr_rdata(rr), sizeof sin->sin_addr); 
     if (prev) 
      prev->ai_next = cur; 
     if (head == NULL) 
      head = cur; 
     prev = cur; 
    } 
    *res = head; 

    return 0; 
} 

int main(int argc, char *argv[]) 
{ 
    const char *node = "bing.com"; 
    struct addrinfo *res = NULL; 
    if (argc == 2) 
     node = argv[1]; 
    int ret = my_getaddrinfo("8.8.8.8", node, "http", NULL, &res); 
    if (ret != 0) { 
     puts("getaddrinfo error"); 
     return 1; 
    } 

    // do stuff with res 
    struct addrinfo *rp; 
    struct sockaddr_in *sin; 
    char p[1024]; 
    for (rp = res; rp != NULL; rp = rp->ai_next) { 
     sin = (struct sockaddr_in*)rp->ai_addr; 
     const char *s = inet_ntop(rp->ai_family, 
       &sin->sin_addr, p, sizeof p); 
     printf("Got %s: %d\n", s, ntohs(sin->sin_port)); 
    } 
    freeaddrinfo(res); 
    return 0; 
} 

例如:

$ ./a.out bing.com 
Got 204.79.197.200: 80 
Got 13.107.21.200: 80 
$ ./a.out google.com 
Got 172.217.24.14: 80 
+0

看起來不錯,但是1:使用memcpy(state-> nsaddr_list,ai-> ai_addr,sizeof state-> nsaddr_list [0]);免費後(AI) 2:我得到一個錯誤getservbyname_r(隱式聲明的函數getservbyname_r在c99無效),任何其他方式我可以得到端口? – Roee84

+0

@ Roee84你可以使用'getservbyname',如果_r不可用,但它將是非reentrent,我使用了reentrent版本。 – fluter

+0

@ Roee84你試過包括'#include '嗎?這是'getservbyname_r'被定義的地方,它實際上是一個posix標準函數,所以包含正確的頭文件應該這樣做,它不是C標準函數,因此它與您正在使用的C版本無關。 – fluter

相關問題