Getting loopback address instead of actual address? - c

/* program to print the IP address of the Host*/
------------------------------------------------
I am trying to print the Host IP address. when I execute the following program
I am getting loop back address that is 127.0.0.1. What should I change to get the
actual IP address.
# include <stdio.h>
# include <stdlib.h>
# include <arpa/inet.h>
# include <string.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <netdb.h>
# include <netinet/in.h>
int main () {
void *addr;
char ipstr[INET6_ADDRSTRLEN];
int rv;
struct addrinfo hints, *res, *p;
memset ( &hints, 0, sizeof hints );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
//hints.ai_flags = AI_ADDRCONFIG;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL ,"3490" , &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = res;p != NULL; p = p->ai_next) {
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s\n", ipstr);
}
}

As long as your development machine is not connected to a network you'll get the loopback address (which is perfectly valid for local network, of course). As soon as you connect the machine to the network you can determine the "right" address.
ipconfig, btw, behaves the same.
If you call getaddrinfo with NULL for the node name (first parameter) it is the same as when you call it with "localhost". You'll get the loopback IP. However, if you use the "real" hostname, you'll get the "real" ip.

Related

getaddrinfo() returns only ipv6 when using AF_UNSPEC

When i want to connect to a server (running locally) and don't know if the application uses ipv4, ipv6 or both, should I call getaddrinfo() twice, once with AF_INET and once with AF_INET6, and try all returned addresses?
Some context:
getaddrinfo() provides a way for ipv4/ipv6-agnostic hostname resolution. Online i found guidelines stating a common algorithm to connect to a server is to use getaddrinfo() with an AF_UNSPEC hint, and try addresses returned in the list.
However, in my setup, getaddrinfo() only returns an ipv6 entry when I use AF_UNSPEC, the host being "localhost".
On the other hand, if I ask for IPv4 explicitly, getaddrinfo() does return an IPv4 address.
This example calls getaddrinfo() once with AF_UNSPEC and once with AF_INET, and iterates over the returned list and prints the address families of the entries:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <string.h>
const char* family_to_string(int family) {
switch (family) {
case AF_INET:
return "AF_INET";
case AF_INET6:
return "AF_INET6";
case AF_UNSPEC:
return "AF_UNSPEC";
default:
return "UNKNOWN";
}
}
int main(void) {
struct addrinfo hints;
struct addrinfo *res, *it;
static const char* host = "localhost";
static const char* port = "42420";
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_UNSPEC;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
int ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_family = AF_INET;
printf("getaddrinfo(.. %s ..):\n", family_to_string(hints.ai_family));
ret = getaddrinfo(host, port, &hints, &res);
if (ret != 0) {
return 1;
}
for (it = res; it != NULL; it = it->ai_next) {
printf("entry for %s\n", family_to_string(it->ai_family));
}
printf("\n");
freeaddrinfo(res);
return 0;
}
It surprised me quite a bit when I received this output:
getaddrinfo(.. AF_UNSPEC ..):
entry for AF_INET6
getaddrinfo(.. AF_INET ..):
entry for AF_INET
After digging a little into the behavior of getaddrinfo(), it appears that it first checks for entries in /etc/hosts, and if it finds matching entries, it only returns those, and does not try to resolve the hostname differently.
Since my /etc/hosts file only contains an ipv6 entry for localhost, only that is returned for AF_UNSPEC. For AF_INET, the entry is not considered eligible and localhost is correctly resolved to 127.0.0.1.
This is indeed an interesting question:
This is a case, which is specially handled by the nss-files module of glibc; if a request for localhost with address family AF_INET is made and the v6 localhost entry of the /etc/hosts is parsed, it is implicitly converted to the v4 localhost entry with address 127.0.0.1.
See nss/nss_files/files-hosts.c (around line 70), where this conversion is performed:
else if (IN6_IS_ADDR_LOOPBACK (entdata->host_addr))
{
in_addr_t localhost = htonl (INADDR_LOOPBACK);
memcpy (entdata->host_addr, &localhost, sizeof (localhost));
}
This branch is not taken, when asking for AF_UNSPEC, so you will only resolv a v4 localhost address when having an explicit entry in /etc/hosts for it.

Service port is missed when using getaddrinfo to convert IPv4 addr to IPv6 addr

I am testing IPv6 in Mac OS X 10.11.2 and I find a strange problem.
I use getaddrinfo to resolve hostname to IPv6 addr :
#include <stdio.h>
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
int main(int argc, const char * argv[]) {
struct addrinfo * res, * addr;
struct addrinfo hints;
char buffer[128];
struct sockaddr_in6 * sockaddr_v6;
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_DEFAULT;
if (getaddrinfo("www.google.com", "80", &hints, &res)) {
//if (getaddrinfo("216.58.199.4", "80", &hints, &res)) {
printf("getaddrinfo failed with errno(-%d)\n", errno);
return 0;
}
for (addr = res;addr;addr = addr->ai_next)
{
if (addr->ai_family == AF_INET6)
{
sockaddr_v6 = (struct sockaddr_in6 *)addr->ai_addr;
printf("ipv6 addr is %s %d)\n", inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, buffer, sizeof(buffer)), ntohs(sockaddr_v6->sin6_port));
}
}
freeaddrinfo(res);
return 0;
}
output is
"ipv6 addr is 64:ff9b::d83a:c704 80". everything is ok !
"www.google.com" is resolved to "64:ff9b::d83a:c704", sin6_port is 80.
but when I use "216.58.199.4" instead of "www.google.com", "216.58.199.4" is IPv4 addr of "www.google.com".
//if (getaddrinfo("www.google.com", "80", &hints, &res)) {
if (getaddrinfo("216.58.199.4", "80", &hints, &res)) {
output is "ipv6 addr is 64:ff9b::d83a:c704 0". it is ok that "216.58.199.4" is converted to "64:ff9b::d83a:c704", but it is strange that the service port of 80 become 0.
Is anyone can explain it ?
This is a bug impacting iOS 9 and Mac OS X 10.11. It has been fixed in iOS 10 and macOS 10.12, but here are workarounds that can be used to support devices running iOS 9 and Mac OS X 10.11:
Workaround 1: Use a service name
If you're using a well known or registered port number, you can pass in the service name instead of the port number as a string. In this example, simply replace "80" with "http":
if (getaddrinfo("216.58.199.4", "http", &hints, &res)) {
Since the bug is limited to the number parsing, using service names still works. The full list of known services can be found in /etc/services.
Workaround 2: Manually write the port into the sockaddr
If you don't use a port number from /etc/services, you can manually write the correct port number into your struct sockaddr. If you do this, it's important to:
Only write if the port is zero to make sure this code deactivates once the bug is fixed.
Remember that the sockaddr uses network byte order, so you need to use htons() to convert your port number.
Here is the workaround applied to your example:
for (addr = res;addr;addr = addr->ai_next)
{
if (addr->ai_family == AF_INET6)
{
sockaddr_v6 = (struct sockaddr_in6 *)addr->ai_addr;
// START WORKAROUND
if (sockaddr_v6->sin6_port == 0)
{
sockaddr_v6->sin6_port = htons(80);
}
// END WORKAROUND
printf("ipv6 addr is %s %d)\n", inet_ntop(AF_INET6, &sockaddr_v6->sin6_addr, buffer, sizeof(buffer)), ntohs(sockaddr_v6->sin6_port));
}
}

sockaddr value changes unexpectedly after calling getaddrinfo()

I am programming an UDP client. I want to bind the socket to a given port on the client machine, so that the same port is always used for all sends. I get the sockaddr for the server using getaddrinfo, and I do the same to get the sockaddr which I pass to the call to getaddrinfo. However, after the second call to getaddrinfo the address of the server machine changes, and I end up sending the packet from the client machine to the client machine itself.
The following code is a standalone example that reproduces the error:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_HOST "www.google.com"
#define UDP_PORT "4000"
static struct sockaddr_in *destination_addr = NULL;
static int client_port;
int main(){
uint8_t bytes[5] = { 0xaa, 0xab, 0xac, 0xad, 0xaf}; //some data to send
uint16_t length = 5;
int status;
//initialize socket and bind
if (destination_addr == NULL) {
struct addrinfo hints;
struct addrinfo *servinfo, *p;
srand(time(NULL));
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(SERVER_HOST, UDP_PORT, &hints, &servinfo)) != 0) {
printf("Unable to send UDP. Reason: %s", gai_strerror(status));
return 0;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
if (p->ai_addr != NULL)
destination_addr = (struct sockaddr_in *) p->ai_addr;
}
client_port = 1027 + rand()%50000;
freeaddrinfo(servinfo);
printf("Created destination_addr with IP %s\n", inet_ntoa(destination_addr->sin_addr));
}
int send_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_socket_fd == -1) {
printf("Unable to create UDP socket. Reason: %s", strerror(errno));
return 0;
}
printf("IP after socket creation is %s\n", inet_ntoa(destination_addr->sin_addr));
int yes = 1;
if (setsockopt(send_socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1) {
perror("setsockopt");
return 0;
}
printf("IP after sockopt is %s\n", inet_ntoa(destination_addr->sin_addr));
// bind to local address
char str_client_port[6];
snprintf(str_client_port, 5, "%d", client_port);
struct addrinfo *source_addr_info;
struct addrinfo hints;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
// ***** destination_addr changes after this call *****
getaddrinfo (NULL, str_client_port, &hints, &source_addr_info);
printf("IP after getaddrinfo is %s\n", inet_ntoa(destination_addr->sin_addr));
bind(send_socket_fd, source_addr_info->ai_addr, source_addr_info->ai_addrlen);
printf("IP after binding is %s\n", inet_ntoa(destination_addr->sin_addr));
// send
int bytes_sent = sendto(send_socket_fd, bytes, length, 0, (struct sockaddr *)destination_addr, sizeof *destination_addr);
printf("Sent to IP %s\n", inet_ntoa(destination_addr->sin_addr));
if (bytes_sent != length){
if (bytes_sent == -1){
printf("UDP send failed. Reason: %s", strerror(errno));
}
else {
printf("UDP: not all bytes could be sent.");
}
}
close(send_socket_fd);
return 1;
}
The output generated by the execution of this program in my machine is:
Created destination_addr with IP 64.233.167.105
IP after socket creation is 64.233.167.105
IP after sockopt is 64.233.167.105
IP after getaddrinfo is 0.0.0.0
IP after binding is 0.0.0.0
Sent to IP 0.0.0.0
I am rather new to socket programming in C, and pretty sure I am doing some silly mistake, but after googling a lot and trying many things, I am still stuck with this. Any idea?
Solved. As #molbdnilo pointed out, the error was caused by the call to freeaddrinfo. To fix it I now copy the value pointed by p->ai_addr, so that it is not lost when freeing. I substituted:
if (p->ai_addr != NULL)
destination_addr = (struct sockaddr_in *) p->ai_addr;
with
if (p->ai_addr != NULL){
destination_addr = malloc(sizeof *destination_addr);
memcpy(destination_addr, (struct sockaddr_in *)p->ai_addr, sizeof *p->ai_addr);
}
and it worked.

Why set the whole `hints` variable to 0?

I am reading Beej's guide to network programming and in chapter 5.1, in the showip.c program I see the following line of code:
memset(&hints, 0, sizeof hints);
After a discussion on the ##c channel on freenode I deducted that the reasoning of that memset call could be to set the value of hints.ai_flags to 0(note that the program works fine I remove that line and I explicitly initialize hints.ai_flags to 0). If this is true, why does he need to set the whole struct to 0?
This is the full source:
/*
** showip.c -- show IP addresses for a host given on the command line
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
struct addrinfo hints, *res, *p;
int status;
char ipstr[INET6_ADDRSTRLEN];
if (argc != 2) {
fprintf(stderr,"usage: showip hostname\n");
return 1;
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // AF_INET or AF_INET6 to force version
hints.ai_socktype = SOCK_STREAM;
if ((status = getaddrinfo(argv[1], NULL, &hints, &res)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
return 2;
}
printf("IP addresses for %s:\n\n", argv[1]);
for(p = res;p != NULL; p = p->ai_next) {
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
// convert the IP to a string and print it:
inet_ntop(p->ai_family, addr, ipstr, sizeof ipstr);
printf(" %s: %s\n", ipver, ipstr);
}
freeaddrinfo(res); // free the linked list
return 0;
}
It's required by getaddrinfo() function documentation (where you pass your hints variable as parameter). From man getaddrinfo:
All the other fields in the structure pointed to by hints must contain either 0 or a NULL pointer, as appropriate.
It's because you are only going to fill/use/initialize some of the fields of the struct, giving 0 to the other fields prevents reading an uninitialzied variable, and sometimes 0 is the default value for those variables.
note that the program works fine I remove that line and I explicitly initialize hints.ai_flags to 0
Not necessarily, if you are on linux, I suggest using valgrind to detect reads to uninitialized variables, since that causes undefined behavior, the behavior could be that nothing wierd happens.

getaddrinfo() function returns the wrong IP address

I am trying to resolve a URL's IP address using getaddrinfo(), but it always returns the wrong IP address, I have tried with several URL's and the result is same. any help wold be greatly appreciated.
The program returnes the ip address : 209.85.175.99 insted of returning the real IP which is 74.125.39.147
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
int
main()
{
char *hostname = "www.google.lk";
struct addrinfo hints, *res;
struct in_addr addr;
int err;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) {
printf("error %d\n", err);
return 1;
}
addr.s_addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
printf("ip address : %s\n", inet_ntoa(addr));
freeaddrinfo(res);
return 0;
}
google.com can resolve to different IP addresses depending on your own location. It's kind of load balancing trick.

Resources