Lone Learner Lone Learner - 2 months ago 16
C Question

What is the correct way to convert a struct sockaddr * to struct sockaddr_in6 * with valid C code?

Here is a simple program that shows how we normally type cast

struct sockaddr *
to
struct sockaddr_in *
or
struct sockaddr_in6 *
while writing socket programs.

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

int main()
{
struct addrinfo *ai;

printf("sizeof (struct sockaddr): %zu\n", sizeof (struct sockaddr));
printf("sizeof (struct sockaddr_in): %zu\n", sizeof (struct sockaddr_in));
printf("sizeof (struct sockaddr_in6): %zu\n", sizeof (struct sockaddr_in6));

if (getaddrinfo("localhost", "http", NULL, &ai) != 0) {
printf("error\n");
return EXIT_FAILURE;
}

if (ai->ai_family == AF_INET) {
struct sockaddr_in *addr = (struct sockaddr_in *) ai->ai_addr;
printf("IPv4 port: %d\n", addr->sin_port);
} else if (ai->ai_family == AF_INET6) {
struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai->ai_addr;
printf("IPv6 port: %d\n", addr->sin6_port);
}

return 0;
}


Beej's Guide to Network Programming also recommends this in page 10.


To deal with struct sockaddr, programmers created a parallel structure: struct sockaddr_in (“in” for “Internet”) to be used with IPv4.

And this is the important bit: a pointer to a struct sockaddr_in can be cast to a pointer to a struct sockaddr and vice-versa. So even though connect() wants a struct sockaddr*, you can still use a struct sockaddr_in and cast it at the last minute!


But from the discussion at another question, it appears that this is just a hack, not valid C code as per the C standard.

In particular, see AnT's answer that mentions,


As for the popular technique with casts between struct sockaddr *, struct sockaddr_in * and struct sockaddr_in6 * - these are just hacks that have nothing to do with C language. They just work in practice, but as far as C language is concerned, the technique is invalid.


So if this technique we use to do socket programming (and what is also recommended by the books) is invalid, what is the valid way to rewrite the above code so that it is also a valid C code as per the C standard?

Answer

So if the way we do socket programming (and what is also recommended by the books) is a hack, what is the correct way to rewrite the above code so that it is also a valid C code as per the C standard?

TL;DR: continue to do what you present in your example.

The code you presented appears to be syntactically correct. It may or may not exhibit undefined behavior under some circumstances. Whether or not it does depends on the behavior of getaddrinfo().

There is no way to do this in C that meets all the functional requirements and is any better protected against undefined behavior than the standard technique you've presented. That's why it's the standard technique. The issue here is that the function must support all conceivable address types, including types that have not yet been defined. It could declare the socket address pointer as a void *, which would not require casting, but that wouldn't actually change anything about whether any given program exhibits undefined behavior.

For its part, getaddrinfo() is designed with exactly such usage in mind, so it is its problem if using the expected cast on the result allows for misbehavior. Moreover, getaddrinfo() is not part of the C standard library -- it is standardized (only) by POSIX, which also incorporates the C standard. Analyzing that function in the light of C alone therefore demonstrates an inappropriate hyperfocus. Though the casts raise some concern in light of C alone, you should expect that in the context of getaddrinfo() and other POSIX networking functions using struct sockaddr *, casting to the correct specific address type and accessing the referenced object produces reliable results.

Additionally, I think AnT's answer to your other question is oversimplified and overly negative. I'm considering whether to write a contrasting answer.