Reputation: 1043
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
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
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 unsigned
1:
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