Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What additional purpose can ai_protocol serve in hints while calling getaddrinfo() when ai_socktype is already specified?

Tags:

c

linux

sockets

The getaddrinfo accepts struct addrinfo *hints as the third argument which can be used to specify the criteria for selecting socket addresses to be returned by this function.

The documentation says that we could set ai_socktype as well as ai_protocol to specify our selection criteria. However, I am unable to understand why ai_protocol is required if we already specify ai_socktype. If one of these two is specified, then the other seems redundant.

Here is some code that I wrote to experiment with this.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>

void getaddrinfo_demo(const char *node, const char *service,
                      int socktype, int protocol)
{
    struct addrinfo hints, *res, *p; 
    int error;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = socktype;
    hints.ai_protocol = protocol;

    error = getaddrinfo(node, service, &hints, &res);
    if (error) {
        printf("Error %d: %s\n\n", error, gai_strerror(error));
        return;
    }

    for (p = res; p != NULL; p = p->ai_next) {
        struct sockaddr_in *addr = ((struct sockaddr_in *) p->ai_addr);
        char ip[INET_ADDRSTRLEN];
        int port = ntohs(addr->sin_port);

        inet_ntop(AF_INET, &addr->sin_addr, ip, INET_ADDRSTRLEN);
        printf("ip: %s; port: %d; protocol: %d\n", ip, port, p->ai_protocol);
    }
    printf("\n");

    freeaddrinfo(res);
}

int main()
{
    /* Consistent family and socktype works fine. */
    getaddrinfo_demo("localhost", "http", SOCK_STREAM, IPPROTO_TCP);
    getaddrinfo_demo("localhost", "http", SOCK_DGRAM, IPPROTO_UDP);

    /* Inconsistent family and sock type leads to error -7. */
    getaddrinfo_demo("localhost", "http", SOCK_STREAM, IPPROTO_UDP);
    getaddrinfo_demo("localhost", "http", SOCK_DGRAM, IPPROTO_TCP);
}

Here is the output.

$ gcc -std=c99 -D_POSIX_SOURCE -Wall -Wextra -pedantic foo.c && ./a.out 
ip: 127.0.0.1; port: 80; protocol: 6
ip: 127.0.0.1; port: 80; protocol: 6

ip: 127.0.0.1; port: 80; protocol: 17
ip: 127.0.0.1; port: 80; protocol: 17

Error -7: ai_socktype not supported

Error -7: ai_socktype not supported

As you can see if ai_socktype = AF_STREAM, then only ai_protocol = IPPROTO_TCP works. Specifying ai_protocol = IPPROTO_UDP leads to error. One might as well omit specifying ai_protocol in the hints if we can't specify any additional selection criteria via it.

So what is really the role of ai_protocol in hints? Can you give an example in which ai_socktype and ai_protocol both serve some purpose?

like image 470
Lone Learner Avatar asked Sep 13 '16 16:09

Lone Learner


1 Answers

How about these:

getaddrinfo_demo("localhost", "http", SOCK_STREAM, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "imap", SOCK_STREAM, IPPROTO_TCP);
getaddrinfo_demo("localhost", "imap", SOCK_STREAM, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "sbcap", SOCK_STREAM, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "sbcap", SOCK_SEQPACKET, IPPROTO_SCTP);
getaddrinfo_demo("localhost", "sbcap", SOCK_STREAM, IPPROTO_TCP);
getaddrinfo_demo("localhost", "http", SOCK_DGRAM, IPPROTO_UDPLITE);
getaddrinfo_demo("localhost", "syslog-tls", SOCK_DCCP, IPPROTO_DCCP);

Which give you:

ip: 127.0.0.1; port: 80; protocol: 132
ip: 127.0.0.1; port: 143; protocol: 6
Error -8: Servname not supported for ai_socktype
ip: 127.0.0.1; port: 29168; protocol: 132
ip: 127.0.0.1; port: 29168; protocol: 132
Error -8: Servname not supported for ai_socktype
Error -8: Servname not supported for ai_socktype
ip: 127.0.0.1; port: 6514; protocol: 33

So TCP is not the only streaming protocol, as well as UDP is not the only datagram protocol (although DCCP has its own SOCK_DCCP and UDP-Lite doesn't have any entries in the services DB (one might argue that it doesn't need to as UDP-Lite RFC explicitly says that "UDP-Lite uses the same set of port number values assigned by the IANA for use by UDP")).

We don't see it often in practice these days, but if we're talking about APIs like getaddrinfo(), they have to be designed to be somewhat future-proof, and that includes making some seemingly redundant things. Only time can tell whether these things really are redundant, or not. In this case it's not, it's exactly the opposite. If FreeBSD man page is correct, then

The implementation first appeared in WIDE Hydrangea IPv6 protocol stack kit.

There is still present an FAQ about this protocol stack on the Internet, from which we can guess that it was created around 1997--1998 (I'm not that old to remember such things and I don't see any other appropriate source, so correct me if I'm wrong). And SCTP was defined in 2000. As I've shown with examples above, we have SCTP using this API without any problems. Same story with DCCP that appeared around 2005--2006, fits into the same API perfectly.

like image 65
Roman Khimov Avatar answered Oct 20 '22 15:10

Roman Khimov