6

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?

Some Noob Student
  • 14,186
  • 13
  • 65
  • 103

2 Answers2

9

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.

Brian Roach
  • 76,169
  • 12
  • 136
  • 161
  • 1
    That's weird, 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. Also, then how do you get the server's ip? – Some Noob Student Oct 30 '11 at 02:54
  • I've read that gethostbyname(gethostname(...)) can give you the machine's ip, but Beej said that these are superseded by getaddrinfo(). Am I supposed to use getaddrinfo? Or gethostbyname? – Some Noob Student Oct 30 '11 at 03:00
  • Well, they're wrong. Read the docs :) As for "getting the server's ip" ... there is no platform independent way of doing so. If you're talking about a *nix system this question should help: http://stackoverflow.com/questions/212528/linux-c-get-the-ip-address-of-local-computer – Brian Roach Oct 30 '11 at 03:04
  • Furthermore ... the way he's using `getaddrinfo` to create a socket that *isn't* a service is IMHO, totally screwy and I don't know why you'd do that. Just my opinion, of course. – Brian Roach Oct 30 '11 at 03:07
  • You don't get the port number until after you `bind()`, and then you can pick it up out of the buffer you provide to `getsockname()`. On a connected socket, you will get more (full local address even if you used INADDR_ANY) and also a useful remote address from `getpeername()`. – Donal Fellows Oct 30 '11 at 03:22
  • Thanks Donal! I'll try that out! – Some Noob Student Oct 30 '11 at 03:24
  • Servers can have multiple IP addresses (e.g., for multiple network cards) so you can only talk about the address of a network interface or the address of a _connected_ socket; the latter is even relatively portable. – Donal Fellows Oct 30 '11 at 03:25
  • Rather than trying to use some form of discovery what you will find is that the IP you wish to bind to is generally passed in on the command line or stored in a config file with the default being to bind to 127.0.0.1 – Brian Roach Oct 30 '11 at 03:30
7

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.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • Thanks Donal for taking the time to make a formal ans! Appreciate it a lot! – Some Noob Student Oct 30 '11 at 04:39
  • So, if I understand your Q1 & 2 correctly, I should call getaddrinfo() AFTER bind(), not before bind like what Beej has done? – Some Noob Student Oct 30 '11 at 04:59
  • 1
    I think I found the correct answer to my problems:http://stackoverflow.com/questions/2146191/obtaining-local-ip-address-using-getaddrinfo-c-function. What do you guys think? – Some Noob Student Oct 30 '11 at 06:03
  • 1
    @Sheldon: Sequence is: 1: getaddrinfo() to create an address. 2: socket() to make a socket with some info from GAI result. 3: bind() to attach address from GAI to socket. 4: getsockaddr() to get real port. 5: connect() or accept() to realize the connection. 6: getpeeraddr() to get remote address/port, getsockaddr() to get real local address. (There should also be a freeaddrinfo() in there.) – Donal Fellows Oct 30 '11 at 11:14
  • And the key is that getaddrinfo() replaces gethostbyname() (and gethostbyaddr() too) which you shouldn't use any more. – Donal Fellows Oct 30 '11 at 11:16
  • 1
    `getifaddrs` can be useful. Another option is to use `getsockname` on the fd returned by accept. That will give you the local ip for that specific connection (once unscrambled by `getnameinfo`). – Per Johansson Oct 30 '11 at 12:16
  • @Per: Cool! Didn't know about getifaddrs. – Donal Fellows Oct 30 '11 at 12:24