2017-04-04 4 views
1

Je veux interroger un serveur spécifique, et obtenir le résultat de la même manière que nous l'obtenons via getaddrinfo. Je veux obtenir une struct addrinfo, donc je peux avoir l'ip, le port et un pointeur vers le résultat suivant. J'utilise le code ci-dessous, qui interroge le serveur que je veux, et obtient les résultats. Mais chaque résultat est à une autre structure et ils ne se pointent pas les uns les autres (pas dans une liste).Interroger un serveur et obtenir les résultats en tant que structure addrinfo

Voici le code:

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; 
} 

Est-il possible d'obtenir les résultats de la façon dont je veux? quelque chose de similaire à addrinfo struct?

Edit: je peux voir que je reçois trois réponses ns_msg_count(handle, ns_s_an) = 3 et pour accéder à chaque réponse que je devrais appeler ns_parserr(&handle, ns_s_an, answer_index, &rr) Mais comme je le disais, je veux obtenir ces réponses comme une liste comme je les reçois en appelant getaddrinfo.

+0

Avez-vous fait quoi que ce soit essayé? D'après votre description, il semble que vous sachiez ce que vous faites, alors où est exactement votre problème? Recherchez quel paramètre est l'index dans le jeu de résultats (je suppose que c'est le '0'), parcourez les résultats et créez une liste de' addrinfo's au fur et à mesure. – Phillip

+0

Je sais que je peux obtenir in_addr, mais comment puis-je obtenir des addrinfos à partir du résultat? – Roee84

+0

Ou vous voulez juste appeler getaddrinfo pour chaque ip .. – Roee84

Répondre

2

getaddrinfo renvoie plus que des adresses IP, il va également résoudre les noms de service au numéro de port, et il pourrait prendre en charge différents protocoles, tcp majoritaire et udp. Vous devrez donc résoudre le nom du service en appelant le getservbyname_r et en construisant manuellement le résultat addrinfo pour chaque combinaison d'adresse IP, de port et de protocole. Voici une version simple qui analyse ip et port, mais elle ne renvoie que l'addrinfo pour TCP. Vous pourriez l'étendre à inclure UDP facilement.

Une autre caractéristique est que getaddrinfo prend également en charge IPV6, auquel cas vous devrez appeler res_nquery avec T_AAAA au lieu de T_A pour résoudre les adresses IPV6.

Voici un exemple, notez qu'il renvoie une liste chaînée de struct addrinfo comme le fait getaddrinfo, donc le résultat devrait être libre avec freeaddrinfo lorsque vous avez terminé.

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; 
} 

exemple:

$ ./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

Ça a l'air génial, mais en utilisant memcpy (state-> nsaddr_list, ai-> ai_addr, sizeof state-> nsaddr_list [0]); après libre (ai) 2: Im obtenir une erreur pour getservbyname_r (déclaration implicite de la fonction getservbyname_r est invalide en c99), toute autre façon que je peux obtenir le port? – Roee84

+0

@ Roee84 vous pouvez utiliser 'getservbyname', si _r n'est pas disponible, mais il sera non-réentrant, j'ai utilisé la version réentrante. – fluter

+0

@ Roee84 Avez-vous essayé d'inclure '#include '? c'est là que 'getservbyname_r' a été défini, il s'agit en fait d'une fonction standard posix, donc incluez l'en-tête droit, il ne s'agit pas d'une fonction standard C, cela n'a rien à voir avec la version de C que vous utilisez. – fluter