0

I'm learning socket programing and pretty new to this... My question is, I want to write a UDP server that is able to receive packets from both remote server and local host via a OS-assigned socket.

I know I should somehow pass INADDR_ANY to bind() to let it accept packet from both. and I guess I need to pass char *service=NULL to getaddrinfo so that it will assign a port. However, I don't know if so what to do with the node parameter of getaddrinfo.

According to getaddrino man page, node and service cannot both be NULL, yet the INADDR_ANY will only be set in the returned socket address when (1) hints.ai_flags=AI_PASSIVE and (2) node is set to NULL. I'm confused with this confliction.. And since "have an OS-assigned port" is the assignment requirement, I can't change the service I guess.

I read that INADDR_ANY is basically 0.0.0.0, does that mean I can just pass this string as node?

Any comment is welcomed!

Crystina
  • 990
  • 1
  • 5
  • 16
  • What happens if you just call `listen` without first calling `bind`? – Filipp Sep 24 '20 at 01:09
  • `getaddrinfo` does not assign anything. It only looks up well-known ports. There is no law that makes it illegal to call `bind()` to bind to specific port without calling `getaddrinfo` first. You are free to call `bind()` to bind to a specific port on `INADDR_ANY`. The End. – Sam Varshavchik Sep 24 '20 at 01:17

1 Answers1

2

It is not getaddrinfo() that assigns a random available port. It is bind() that does so when port 0 is requested.

The getaddrinfo() man page is correct. The node and service parameters cannot both be NULL at the same time. You can set node to NULL to get INADDR_ANY - or - you can set service to NULL to get port 0. But, to get both, you will have to either:

  • set node to NULL, service to "0", and hints.ai_flags to AI_PASSIVE | AI_NUMERICSERV.

  • set node to "0.0.0.0" and service to NULL (AI_PASSIVE is ignored)

  • set node to "0.0.0.0", service to "0", and hints.ai_flags to AI_NUMERICSERV (AI_PASSIVE is ignored)

Any of those combinations will cause getaddrinfo() to return a pointer to a sockaddr_in that has its sin_addr set to INADDR_ANY and its sin_port set to 0.

You can then bind() using that sockaddr_in, and bind() will choose a random available port, which you can retrieve using getsockname() afterwards.

int server_fd = -1;

struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP;

struct addrinfo *result;
if( getaddrinfo(NULL, "0", &hints, &result) != 0 )
{
    // error handling...
}
else if( (server_fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol)) < 0 )
{
    // error handling...
    freeaddrinfo(result);
}
else if( bind(server_fd, result->ai_addr, result->ai_addrlen) < 0 )
{
    // error handling...
    close(server_fd);
    freeaddrinfo(result);
}
else
{
    freeaddrinfo(result);

    // use server_fd as needed...

    struct sockaddr_in bound_addr;
    socklen_t addrlen = sizeof(bound_addr); 
  
    if( getsockname(server_fd, (struct sockaddr*)&bound_addr, &addrlen) < 0 )
    {
        // error handling...
    } 
    else
    {
        // use ntohs(bound_addr.sin_port) as needed...
    }

    ...

    close(server_fd);
}

Or, since you know exactly what you want to bind() to, you can simply ignore getaddrinfo() and populate a sockaddr_in manually:

int server_fd;

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = 0;

if( (server_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 )
{
    // error handling...
}
else if( bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0 )
{
    // error handling...
    close(server_fd);
}
else
{
    // use server_fd as needed...

    struct sockaddr_in bound_addr;
    socklen_t addrlen = sizeof(bound_addr); 
  
    if( getsockname(server_fd, (struct sockaddr*)&bound_addr, &addrlen) < 0 )
    {
        // error handling...
    } 
    else
    {
        // use ntohs(bound_addr.sin_port) as needed...
    }

    ...

    close(server_fd);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • great answer! thanks so much! Just one more question: so does that mean when writing the server end, we don't really need `getaddrino` since we know both the host IP (INADDR_ANY) and the port (any one available), and `getaddrinfo` is only needed on client side to determine the IP according to the host name (e.g. www.google.com) – Crystina Sep 24 '20 at 04:17
  • 1
    @Crystina commonly, yes. Though getaddrinfo does have uses on the server end, such as when combining AI_PASSIVE with a specific IP or port, and/or with AF_UNSPEC to create binding sockets for both IPv4 and IPv6. – Remy Lebeau Sep 24 '20 at 05:35