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