Reputation:
I am still learning sockets and am unclear why this doesn't print out 127.0.0.1. Even if I replace the word localhost with 127.0.0.1 I receive some other ip's which I guess are my router or something. I always thought this should return 127.0.0.1. Here's the output I receive:
hostname: 28.30.0.0
hostname: 28.30.0.0
hostname: 28.30.0.0
hostname: 28.30.0.0
hostname: 16.2.0.0
hostname: 16.2.0.0
Here is the basic code:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
int main()
{
struct addrinfo* feed_server = NULL;
getaddrinfo("localhost", NULL, NULL, &feed_server);
struct addrinfo *res;
for(res = feed_server; res != NULL; res = res->ai_next)
{
printf("hostname: %s\n", inet_ntoa(*((struct in_addr*)(res->ai_addr))));
}
return 0;
}
Upvotes: 5
Views: 21431
Reputation: 1901
There are two problems with the original code:
One thing I typically see at least on Linux systems is that getaddrinfo for localhost usually returns the IPv6 ::1 address first.
From the addresses being printed I can tell you are running on an OS that includes the sockaddrs length in the struct. For example the definition of struct sockaddr on OS X is:
struct sockaddr {
__uint8_t sa_len; /* total length */
sa_family_t sa_family; /* [XSI] address family */
char sa_data[14]; /* [XSI] addr value (actually larger) */
};
For both struct sockaddr_in and sockaddr_in6 the very next member after sa_family is the port which is always two bytes. So when you cast either of these structs to a struct in_addr you will get an address that is sa_len.sa_family.0.0 (assuming you don't provide a port to getaddrinfo - if you provide a port the 0.0 will be replaced with the ports byte values).
So gettaddr info is returning you two IPv6 addresses: 28.30.0.0 - sizeof struct sockaddr_in6 = 28 and af_family = 30
and two IPv4 addresses: 16.2.0.0 - sizeof struct sockaddr_in = 16 and af_family = 2
To do this properly you could do what the other answer said and use getnameinfo. However using inet_ntop (not inet_ntoa) can be equally as good.
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h> /* for memset */
int main()
{
char addr_buf[64];
struct addrinfo* feed_server = NULL;
memset(addr_buf, 0, sizeof(addr_buf));
getaddrinfo("localhost", NULL, NULL, &feed_server);
struct addrinfo *res;
for(res = feed_server; res != NULL; res = res->ai_next)
{
if ( res->ai_family == AF_INET )
{
inet_ntop(AF_INET, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addr_buf, sizeof(addr_buf));
}
else
{
inet_ntop(AF_INET6, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addr_buf, sizeof(addr_buf));
}
printf("hostname: %s\n", addr_buf);
}
return 0;
}
```
Upvotes: 6
Reputation: 182
You should use hints for call of getaddrinfo
. Because to resolve "localhost" or any /etc/hosts
record hints.af_family
must be set to AF_INET
.
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
int main()
{
struct addrinfo hints;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
getaddrinfo("localhost", NULL, &hints, &feed_server);
struct addrinfo *res;
for(res = feed_server; res != NULL; res = res->ai_next){
struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
printf("hostname: %s\n", inet_ntoa(saddr->sin_addr))
}
return 0;
}
Upvotes: 3
Reputation: 14183
res->ai_addr
is of type struct sockaddr*
, not struct in_addr*
.
You need to do something like this:
for(res = feed_server; res != NULL; res = res->ai_next)
{
/* ideally look at the sa_family here to make sure it is AF_INET before casting */
struct sockaddr_in* saddr = (struct sockaddr_in*)res->ai_addr;
printf("hostname: %s\n", inet_ntoa(saddr->sin_addr));
}
Upvotes: 4