简体   繁体   中英

Query a server and get the results as addrinfo struct

I want to query a specific server, and get the result at the same way as we get it via getaddrinfo . I want to get a addrinfo struct, so I can have the ip, port and a pointer to the next result.

I'm using the code below, which query the server I want, and gets the results. But each result is at another struct and they don't point one another (not in a list).

This is the 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;
}

Is it possible to get the results the way I want? something similar to addrinfo struct?

Edit: I can see that I get three answers ns_msg_count(handle, ns_s_an) = 3 and to access each answer I should call ns_parserr(&handle, ns_s_an, answer_index, &rr) But as I said, I want to get those answers as a list just like I get them by calling getaddrinfo .

getaddrinfo returns more than just ip addresses, it will also resolve service names to port number, and it could support different protocols, majorly tcp and udp. So you will need to resolve service name by calling getservbyname_r and manually construct the result addrinfo for each combination of ip, port and protocols. Here is a simple version which parse ip and port, but it only returns addrinfo for TCP. You could extended it to including UDP easily.

Another feature is that getaddrinfo also supports IPV6, in which case you will need to call res_nquery with T_AAAA instead of T_A to resolve IPV6 addresses.

Here is an example, note it returns a linked list of struct addrinfo just like getaddrinfo does, so the result should be free with freeaddrinfo when you are done.

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

example:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM