I encounter this problem while doing an network course project. Easy as it sounds, it's actually not a trivial task.
Old-fashioned gethostbyname
I did some network programing in old days, so I was tempted to use the
straightforward way using gethostbyname
.
#include <unistd.h>
#include <netdb.h>
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) < 0) {
perror("gethostname");
return -1;
}
struct hostent* host = gethostbyname(hostname);
if (host == NULL) {
perror("gethostbyname");
return -1;
}
struct in_addr ip = *(struct in_addr*)host->h_addr_list[0];
printf("My IP is %s\n", inet_ntoa(ip));
Yet when I run the program, this code snippet will always print out 127.0.0.1
,
which is not useful since I want to get the real (or external) IP address.
Apparently, this is because some nasty settings in the /etc/hosts
file, there
is an entry looks like this
127.0.0.1 timberlake.cse.buffalo.edu timberlake localhost.localdomain localhost
Since gethostbyname
is actually a DNS looking up process, that DNS request,
unfortunately, is served by the /etc/hosts
file, instead of a real decent DNS
server.
More Advanced getifaddrs
I searched the web and found this stackoverflow threads talking
about using getifaddrs
to get NIC's IP address. I tried and it seems to work.
Since the machine I worked on uses "eth0" as external NIC, so when looping the
result, I just match the results that has the name "eth0".
Although it works well, the solution is a little bit ad-hoc. Since the network interface's name is not necessarily "eth0", for example, in some laptop or netbook, the primary interface may be "wlan0" instead of "eth0".
Most Elegant Way
Finally, I adopted the solution that mentioned later on that thread. Basically,
I connected to a well-known server (e.g., Google's DNS server) and then get my
local socket's information (more specifically, IP) using getsockname
.
Here is the final code snippet.
/* get my hostname */
char hostname[256];
if (gethostname(hostname, sizeof(hostname)) < 0) {
perror("gethostname");
return -1;
}
// Google's DNS server IP
char* target_name = "8.8.8.8";
// DNS port
char* target_port = "53";
/* get peer server */
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
struct addrinfo* info;
int ret = 0;
if ((ret = getaddrinfo(target_name, target_port, &hints, &info)) != 0) {
printf("[ERROR]: getaddrinfo error: %s\n", gai_strerror(ret));
return -1;
}
if (info->ai_family == AF_INET6) {
printf("[ERROR]: do not support IPv6 yet.\n");
return -1;
}
/* create socket */
int sock = socket(info->ai_family, info->ai_socktype, info->ai_protocol);
if (sock <= 0) {
perror("socket");
return -1;
}
/* connect to server */
if (connect(sock, info->ai_addr, info->ai_addrlen) < 0) {
perror("connect");
close(sock);
return -1;
}
/* get local socket info */
struct sockaddr_in local_addr;
socklen_t addr_len = sizeof(local_addr);
if (getsockname(sock, (struct sockaddr*)&local_addr, &addr_len) < 0) {
perror("getsockname");
close(sock);
return -1;
}
/* get peer ip addr */
char myip[INET_ADDRSTRLEN];
if (inet_ntop(local_addr.sin_family, &(local_addr.sin_addr), myip, sizeof(myip)) == NULL) {
perror("inet_ntop");
return -1;
}