PoVa
PoVa

Reputation: 1043

gethostbyname() returns a struct with negative address

I'm writing a program that would check if a port on given url or ip is open or not. In order to obtain the ip adress of given url, I'm using gethostbyname(). When trying to look up the address of localhost it returns the correct value, however, trying to look up the address of a remote host it usually fails and returns an ip address with negative numbers. For example:

/test.out google.com
ip: -40.58.-42.78

/test.out reddit.com
ip: -105.101.-127.-116

./test.out facebook.com
ip: 31.13.84.36

Weirdly, the last one works. Here's my code:

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>

int main(int argc, char **argv)
{
    struct hostent *he;
    struct in_addr **addr_list;

    if ((he = gethostbyname(argv[1])) == NULL) {
        herror("gethostbyname");
        return 1;
    }

    printf("ip: ");
    for (int i = 0; i < he->h_length; i++) {
        printf("%d", he->h_addr_list[0][i]);
        if (i != he->h_length - 1) printf(".");
    }
    printf("\n");
}

Also, why is the type of h_addr_list char **? Shouldn't it be an integer, or even better an unsigned one?

Upvotes: 1

Views: 435

Answers (2)

user2404501
user2404501

Reputation:

This answer is specifically bout the type of h_addr_list.

In the minds of the ancient BSD programmers who invented gethostbyname, it was going to be used to look up all kinds of network addresses, not just IP addresses. That's why it also has an h_addrtype. The interpretation of h_addr_list[n] would be dependent on h_addrtype. For h_addrtype==AF_INET, an address is the 4-byte IP address format you're familiar with. For other address types, it could be other things.

The char ** type of h_addr_list should be understood as "dynamically allocated array of opaque buffers". It might have been void ** but void hadn't been invented yet.

As it turned out, IPv4 became the only network protocol anyone cared about, until IPv6 came along, and then it was decided to replace the host lookup interface entirely (see getaddrinfo). So there have been few opportunities to see a non-AF_INET h_addrtype in the wild.

Upvotes: 2

axiac
axiac

Reputation: 72425

The components of an IP address are unsigned bytes but, in a struct hostent they are stored as char (i.e. signed). This means the values 128..255 are interpreted as negative numbers.

You print them using the %d format that prints the values as signed because this is how it receives them. Convert the values to unsigned char (or unsigned int if you prefer) when you pass them to printf():

printf("%d", (unsigned char)he->h_addr_list[0][i]);

You can also use %u instead of %d (it treats as unsigned the values it receives), but you still need to convert the values to pass to it to unsigned1:

printf("%u", (unsigned char)he->h_addr_list[0][i]);

Another option is to force printf() use only the least significant byte of the value it gets and print it as unsigned, using "%hhu". However, this looks more like a hack than the correct solution.


1 Without the conversion, because printf() is a variadic function, the values passed to it as arguments (he->h_addr_list[0][i]) are promoted from (signed) char to (signed) int. Using "%u" to print them produces very large numbers instead of negative numbers for the components that are larger than 127.

Upvotes: 4

Related Questions