Reputation: 6150
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
.
Upvotes: 3
Views: 985
Reputation: 13786
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
Upvotes: 4