11

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?

Lone Learner
  • 18,088
  • 20
  • 102
  • 200
  • _/usr/include/bits/socket.h_ contains a bunch of `PF_*` defines. Sockets are not necessarily bound to network (even if that is their most popular usage). – CristiFati Sep 13 '16 at 17:15

1 Answers1

15

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.

Community
  • 1
  • 1
Roman Khimov
  • 4,807
  • 1
  • 27
  • 35
  • From your examples, it appears that then `ai_socktype` is redundant. The `ai_protocol` alone is sufficient to decide whether it is a streaming protocol or datagram protocol. Is there any value of `ai_protocol` that can be used both with `ai_socktype = SOCK_STREAM` as well as with `ai_socktype = SOCK_DGRAM`? If yes, then we can conclude that `ai_socktype` is not redundant. – Lone Learner Sep 18 '16 at 01:28
  • @LoneLearner: take a look at SCTP with STREAM or SEQPACKET, in theory they could have some difference in result. We don't have anything like that at the moment, but then again, who knows what future holds? – Roman Khimov Sep 18 '16 at 06:31
  • @LoneLearner: again, mostly that's an API design question. If you assume that there is only one protocol for one socket type --- that's one thing, if you assume that every protocol only belongs to one socket type --- that's another one. The designers of this API decided that there could be different relations between these two things and, again, are now proven by SCTP to be right (even though there is little difference in the end result at the moment). – Roman Khimov Sep 18 '16 at 07:40
  • Can you share some details about which Operating System you ran the code on? When I run `getaddrinfo_demo("localhost", "sbcap", SOCK_STREAM, IPPROTO_SCTP);` or `getaddrinfo_demo("localhost", "sbcap", SOCK_SEQPACKET, IPPROTO_SCTP);` on my Debian 8.3 system, I get `Error -8: Servname not supported for ai_socktype`. Also, where did you figure out the service name `sbcap` from? How can I find out what are the supported service names? – Lone Learner Sep 18 '16 at 12:17
  • @LoneLearner: take a look at `/etc/services`, it's all there. My OS is openSUSE 42.1. Debian is probably a bit behind with this definitions, but sid probably has it already. – Roman Khimov Sep 18 '16 at 16:32