Getting server ip 0.0.0.0:0 from getaddrinfo() - c

I'm following Beej's guide on NP.
I did a few modifications and am trying to obtain the IP of my server program through getaddrinfo().
(original can be found here http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#simpleserver)
Below is the parts I've changed/added.
if ((rv = getaddrinfo(NULL, "0", &hints, &servinfo)) != 0) { //0 for random port?
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
//... some code emitted ...
//freeaddrinfo(servinfo); //I still need it!
printf("ip: %s\nport: %d\n",
inet_ntop(AF_INET, &((struct sockaddr_in *)p->ai_addr)->sin_addr, ip4, INET_ADDRSTRLEN),
ntohs(((struct sockaddr_in *)p->ai_addr)->sin_port)
);
The problem is that I get results
ip: 0.0.0.0
port: 0
Q1:I've read from a couple of websites saying that setting "0" for the port tells the OS that you want the next available port, not actually 0. Is this true?
Q2:I've also read that gethostbyname(gethostname(...)) can give you the machine's ip, but Beej said that these are superseded by getaddrinfo(). So, am I supposed to use getaddrinfo? Or gethostbyname?
Q3:Is there anything else I'm doing wrong?

It's returning exactly what you would expect it to.
From man getaddrinfo:
If the AI_PASSIVE flag is specified in hints.ai_flags, and node is NULL, then the returned socket addresses will be suitable for bind(2)ing a socket that will accept(2) connections. The returned socket address will contain the "wildcard address" (INADDR_ANY for IPv4 addresses, IN6ADDR_ANY_INIT for IPv6 address). The wild‐card address is used by applications (typically servers) that intend to accept connections on any of the hosts's network addresses. If node is not NULL, then the AI_PASSIVE flag is ignored.
The code you link to sets hints.ai_flags to AI_PASSIVE and you're passing NULL for node. The wildcard address is 0.0.0.0. Working as specified. Binding to that address means you're binding to every IP address on your machine.
As for the port ... you're specifying "0" which ... is exactly what you're getting back. You need to set it an actual port you wish to listen on as the example code you link to does.

Q1:I've read from a couple of websites saying that setting "0" for the port tells the OS that you want the next available port, not actually 0. Is this true?
Yes, but only after you have used bind() to attach the address to a real socket. At that point, use getsockname() to get the bound address from the socket; the port will be part of that.
Q2:I've also read that gethostbyname(gethostname(...)) can give you the machine's ip, but Beej said that these are superseded by getaddrinfo(). So, am I supposed to use getaddrinfo? Or gethostbyname?
Use getaddrinfo(); it does all that gethostbyname() did and more, and the interface sucks a lot less. (For example, it's typically thread-safe.)
Q3:Is there anything else I'm doing wrong?
There's no good defined concept of the server's IP address. Servers can have many due to things like multiple network cards (much more common for servers than desktop systems), and the one that the outside world knows it by might not be one of them (thanks to NAT firewalls). Occasionally, you can know exactly where the messages are coming from before the client connects — the most common option is to know that the client will be on localhost — which is part of the information you set during bind() but that's rare. You can find the address of the client once the connection has happened by using getpeername() but NAT might still make that functionally useless. Such addresses are typically set in the app's config file during deployment.
If you only need the info for logging purposes, go right ahead. But beware of using it for anything else, as it doesn't actually tell you that much.

Related

Does bind() modify sin_port in C?

Does the bind() function in linux can modify its argument ? I ask this because a sin_port of 0 means that the OS will choose the port, and I would like to get this port after this call.
No, it does not modify the argument. Instead you use getsockname after binding to find out what port you got.
You can also use getsockname after connect to get both the local port assignment and the local address (if you have more than one address) correspomnding to the route to the remote host. This works even with UDP where connect doesn't actually send any packets and only logically binds addresses.

what is significance of bind to zero address

I was seeing EADDRNOTAVAIL errors in connect() calls. I dig deeper found that naming of sockets were being done of over zero ip addresses . See following where both calls were sucessful:-
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&y, sizeof(y)); /* y is int with value 1 */
bind(s, (struct sockaddr *)lockaddr, sizeof(rtinetaddr_tp));
where
lockaddr={.sin_family=2, .sin_port=0, .sin_addr={.s_addr=0}, .sin_zero=""}
This, I found in RH site and also I have the same kernel.
My question is what if I remove doing any bind() at client side of the application? Will that be a quick cure OR will lead to any disaster?
Other way I have running sample programs without bind at client. But the app I am talking about that establishes hundreds of connections. So what may happen in worst case?
Binding to a zero address is the same as binding to INADDR_ANY (which is defined as zero). This means you can make a connection on any local IP address (server side) or use the egress interface IP address (client side). This is quite normal.
If you are not interessed in using any particular address:port on the client side, the calls to bind() aren't necessary.
The Kernel will chose the suitable interface and a random port to establish the outgoing connection initiated by the client's call to connect().

How to listen on all IPV6 addresses using C sockets API

I maintain GPSD, a widely-deployed open-source service daemon that monitors GPSes and other geodetic sensors. It listens for client-application connections on port 2947 on both IPv4 and IPv6. For security and privacy it normally listens only on the loopback address, but there is a -G option to the daemon that is intended to cause it to listen on any address.
The problem: the -G option works in IPv4, but I can't figure out how to make it work with IPv6. The method that should work based on various tutorial examples does not, producing instead an error suggesting the address is already in use. I'm seeking help to fix this from people experienced with IPv6 network programming.
Relevant code is at http://git.berlios.de/cgi-bin/gitweb.cgi?p=gpsd;a=blob;f=gpsd.c;h=ee2156caf03ca23405f57f3e04e9ef306a75686f;hb=HEAD
This code operates correctly in both the -G and non -G cases under IPv4, as is easily verified with netstat -l.
Now look around line 398 after "case AF_INET6:". The listen_global option is set by -G; when false, the code succeeds. There is presently a following comment, inherited from an unknown contributor, that reads like this:
/* else */
/* BAD: sat.sa_in6.sin6_addr = in6addr_any;
* the simple assignment will not work (except as an initializer)
* because sin6_addr is an array not a simple type
* we could do something like this:
* memcpy(sat.sa_in6.sin6_addr, in6addr_any, sizeof(sin6_addr));
* BUT, all zeros is IPv6 wildcard, and we just zeroed the array
* so really nothing to do here
*/
According to various tutorial examples I have looked up, the assignment "sat.sa_in6.sin6_addr = in6addr_any;" is (despite the comment) correct, and it does compile. However, startup with -G fails claiming the listen address is already in use.
Is the assignment "sat.sa_in6.sin6_addr = in6addr_any;" nominally correct here? What else, if anything, am I missing?
The reason the address is already in use is because on many IPv6 networking stacks, by default an IPv6 socket will listen to both IPv4 and IPv6 at the same time. IPv4 connections will be handled transparently and mapped to a subset of the IPv6 space. However, this means you cannot bind to an IPv6 socket on the same port as an IPv4 socket without changing the settings on the IPv6 socket. Make sense?
Just do this before your call to bind (this is taken from one of my projects):
int on = 1;
if (addr->sa_family == AF_INET6) {
r = setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
if (r)
/* error */
}
Unfortunately, there is no default value across platforms for IPV6_V6ONLY -- which basically means you always need to turn it either on or off explicitly if you care, unless you don't care about other platforms. Linux leaves it off by default, Windows leaves it on by default...
From a look in a random Linux system's include files, in6addr_any is declared like so:
extern const struct in6_addr in6addr_any; /* :: */
extern const struct in6_addr in6addr_loopback; /* ::1 */
#define IN6ADDR_ANY_INIT { { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } }
So, perhaps the nearness to the INIT array confused whoever left that comment in GPSD's sources. The actual type is clearly struct in6_addr, which is assignable.
I did look around, and found some hints that suggested that if IPv4 is already listening to the "any" address, IPv6 can't also. Perhaps that's what's biting you.

How to ensure that a server listens on IPv6 if possible and IPv4 otherwise?

I am trying to write a server application that listens to both IPv6 and IPv4 connections. The proper way to accomplish this seems to be listening on IPv6 address, which will also accept IPv4 connections.
The relevant piece of code is:
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(NULL, MYPORT, &hints, &res);
(pretty much copypasted from Beej's Guide)
The problem is that at least on my system, getaddrinfo returns an entry with AF_INET first and AF_INET6 second -- whereas client's getaddrinfo returns AF_INET6 first, as per spec. With my naive approach, server picks IPv4, client picks IPv6 and the connection fails.
I tried to fix this by setting hints.ai_family = AF_INET6, but that fails on systems where IPv6 is not available.
I see two obvious solutions:
a) try asking for IPv6 first and fall back to IPv4 if that fails, or
b) walk through results of getaddrinfo, look for IPv6, and if is not present, pick the first entry
but i don't like either one too much ;) I feel that there should be a way to convince getaddrinfo to do the right thing, or perhaps a different way to accomplish my goal.
The order of the address that getaddrinfo() returns is unspecified, so you have to be prepared to handle either case. That probably means traversing the list, keeping track of the "best address seen so far".
Alternatively, you could try to bind() and listen() on all the addresses returned by getaddrinfo(). This is probably the best option, since some OSes do not accept IPv4 connections to IPv6 sockets listening on 0::0.
Your code should work the way you described. Unfortunately, there's a bug in glibc as described in launchpad bug #673708, which causes it to choose IPv4 first.
Computer configuration solution
There is a work-around which can be done on each Linux computer on which you're running your server program: edit /etc/gai.conf, enable all the default rules (uncomment them):
label ::1/128 0
label ::/0 1
label 2002::/16 2
label ::/96 3
label ::ffff:0:0/96 4
label fec0::/10 5
label fc00::/7 6
label 2001:0::/32 7
then add:
label ::ffff:7f00:1/128 8
Then your code should open IPv6 if supported, and will also accept IPv4 connections.
Code solution
If the above is not practical (it's only practical if you're willing to change the configuration on every computer you run on), then modify your code to prefer IPv6. E.g. I've done this:
Do three passes through the getaddrinfo() results.
The first pass, prefer IPv6. Attempt to clear the IPV6_V6ONLY socket option to support both IPv6 and IPv4.
The second pass, prefer IPv4.
The third pass, take whatever is available.

How to get my non-loopback network ip address in C?

For a communication between two hosts, I need to send the IP address of my host to the other site. The problem is that if I request my IP address, it might be that I get back my local loopback IP addres (127.x.x.x) , not the network (ethernet) IP address.
I use the following code:
char myhostname[32];
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
if( (my_ip % 256) == 127) {
/* Wrong IP adress as it's 127.x.x.x */
printf("Error, local IP address!");
return;
}
The only way to solve it is to make sure my hostname in /etc/hosts is behind the real network address, not the local loopback (the default for e.g. Ubuntu).
Is there a way to solve this without relying on the content of /etc/hosts?
Edit: I changed the above code so it makes use of getaddrinfo, but I still get back the loopback device's number (127.0,0,1) instead of the real IP address:
struct addrinfo hint = {0};
struct addrinfo *aip = NULL;
unsigned ip = 0;
struct sockaddr_in *sinp = NULL;
hint.ai_family = AF_INET; /* IPv4 */
hint.ai_socktype = SOCK_STREAM;
if(getaddrinfo(hostname, NULL, &hint, &aip) != 0) {
return 0;
}
sinp = (struct sockaddr_in *) aip->ai_addr;
ip = *(unsigned *) &sinp->sin_addr;
(I used to get back a list of 3 addrinfo's with the three SOCK_STREAM,SOCK_DGRAM and SOCK_RAW, but the hint prevents that)
So my question still stands...
There is POSIX function getaddrinfo() that returns linked list of addresses for given hostname, so you just need to go through that list and find non-loopback address.
See man getaddrinfo.
Not an answer, but a relevant comment: be aware that as soon as you start sending addressing information in the content of packets, you run the risk of making your application unable to work across NAT:ing routers and/or through firewalls.
These technologies rely on the information in IP packet headers to keep track of the traffic, and if applications exchange addressing information inside packets, where they remain invisible to this inspection, they might break.
Of course, this might be totally irrelevant to your application, but I thought it worth pointing out in this context.
The originating address will be included in the packet sent... there's no need to duplicate this information. It's obtained when accepting the communication from the remote host (see beej's guide to networking, specifically the part on accept())
I just ran into a situation where when only /etc/hosts has information in it and when I used getaddrinfo to get the IP address list, it returned 127.0.0.1 each time. As it turned out, the hostname was aliased to localhost...something often easy to overlook. Here's what happened:
The /etc/hosts file:
127.0.0.1 localhost.localdomain localhost foo
::1 localhost6.localdomain6 localhost6
172.16.1.248 foo
172.16.1.249 bie
172.16.1.250 bletch
So, now, when you call getaddrinfo with host="foo", it returns 127.0.0.1 3 times. The error here, is that foo appears both on the line with "127.0.0.1" and "172.16.1.248". Once I removed foo from the line with "127.0.0.1" things worked fine.
Hope this helps someone.
Look at this:
Discovering public IP programmatically
Note that in some cases a computer can have more than one non-loopback IP address, and in that case the answers to that question tell you how to get the one that is exposed to the internet.
Even if the computer has only one physical network interface (an assumption that may or may not hold, even netbooks have two - ethernet and WLAN), VPNs can add even more IP adresses. Anyway, the host on the other side should be able to determine the IP your host used to contact it.
Use getaddrinfo()
You're almost there. I'm not sure how you're getting my_ip from hp.
gethostbyname() returns a pointer to a hostent structure which has an h_addr_list field.
The h_addr_list field is a null-terminated list of all the ip addresses bound to that host.
I think you're getting the loopback address because it's the first entry in h_addr_list.
EDIT: It should work something like this:
gethostname(myhostname, 32);
hp = gethostbyname(myhostname);
unsigned my_ip = *(unsigned*)(hp->h_addr);
for (int i = 0; hp->h_addr_list[i] != 0; ++i) {
if (hp->h_addr_list[i] != INADDR_LOOPBACK) {
// hp->addr_list[i] is a non-loopback address
}
}
// no address found
If /etc/hosts is still there and still the same, looking for all entries of h_addr_list won't help.
Your new code hardwires the use of IPv4 (in the hint.ai_family field) which is a terrible idea.
Apart from that, you're close, you just should loop through the results of getaddrinfo. Your code just gets the first IP address but there is an aip->ai_next field to follow...
struct addrinfo {
...
struct addrinfo *ai_next; /* next structure in linked list */
};

Resources