Question

Context

I am self-learning how sockets work. Admitted, I am not a C guru, but learn fast.

I read this page :

http://publib.boulder.ibm.com/infocenter/iseries/v5r3/topic/rzab6/rzab6xafunixsrv.htm

Problem

I am stuck at this line :

rc = bind(sd, (struct sockaddr *)&serveraddr, SUN_LEN(&serveraddr));

I just cannot figure what we get from this cast (struct sockaddr *)&serveraddr.

My test so far :

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>


int main(void)
{

/*JUST TESTING THE CAST THING NOTHING ELSE HERE*/

struct sockaddr_in localaddr ;
struct sockaddr_in * mi;
struct sockaddr * toto;

localaddr.sin_family = AF_INET;

localaddr.sin_addr.s_addr = htonl(INADDR_ANY);
localaddr.sin_port = 38999;

/* DID I DEFINED MI CORRECTLY ? */
mi = (struct sockaddr*)&localaddr;
toto = (struct sockaddr*)&localaddr;

printf("mi %d\n",mi->sin_family);
printf("mi %d\n",mi->sin_port);
printf("toto %d\n",toto->sa_family);
/*ERROR*/
printf("toto %d\n",toto->sa_port);

}

SUM UP

Could someone please tell me what is really passed to the bind function concerning the structure cast ?

What members do we have in that structure ?

How can I check it ?

Thanks

Was it helpful?

Solution

Here's struct sockaddr:

struct sockaddr {
    uint8_t      sa_len;
    sa_family_t  sa_family;
    char         sa_data[14];
};

and, for instance, here's struct sockaddr_in:

struct sockaddr_in {
    uint8_t        sa_len;
    sa_family_t    sa_family;
    in_port_t      sin_port;
    struct in_addr sin_addr;
    char           sin_zero[8];
};

and struct sockaddr_in6:

struct sockaddr_in6 {
    uint8_t         sa_len;
    sa_family_t     sa_family;
    in_port_t       sin_port;
    uint32_t        sin6_flowinfo;
    struct in6_addr sin6_addr;
};

You'll note that they all share the first two members in common. Thus, functions like bind() can accept a pointer to a generic struct sockaddr and know that, regardless of what specific struct it actually points to, it'll have sa_len and sa_family in common (and "in common" here means "laid out the same way in memory", so there won't be any weirdness where both structs have an sa_family member, but they're in totally different places in the two different structs. Technically sa_len is optional, but if it's not there, none of the structs will have it, so sa_family will still be aligned in the same way, and often the datatype of sa_family_t will be increased to make up the difference in size). So, it can access sa_family and determine exactly what type of struct it is, and proceed accordingly, e.g. something like:

int bind(int socket, const struct sockaddr *address, socklen_t address_len) {
    if ( address->sa_family == AF_INET ) {
        struct sockaddr_in * real_struct = (struct sockaddr_in *)address;

        /*  Do stuff with IPv4 socket  */

    }
    else if ( address->sa_family == AF_INET6 ) {
        struct sockaddr_in6 * real_struct = (struct sockaddr_in6 *)address;

        /*  Do stuff with IPv6 socket  */

    }

    /*  etc  */
}

(Pedantic note: technically, according to the C standard [section 6.5.2.3.6 of C11], you're only supposed to inspect common initial parts of structs like this if you embed them within a union, but in practice it'll almost always work without one, and for simplicity I haven't used one in the above code).

It's basically a way of getting polymorphism when you don't actually have real OOP constructs. In other words, it means you don't have to have a bunch of functions like bind_in(), bind_in6(), and all the rest of it, one single bind() function can handle them all because it can figure out what type of struct you actually have (provided that you set the sa_family member correctly, of course).

The reason you need the actual cast is because C's type system requires it. You have a generic pointer in void *, but beyond that everything has to match, so if a function accepts a struct sockaddr * it just won't let you pass anything else, including a struct sockaddr_in *. The cast essentially tells the compiler "I know what I'm doing, here, trust me", and it'll relax the rules for you. bind() could have been written to accept a void * instead of a struct sockaddr * and a cast would not have been necessary, but it wasn't written that way, because:

  1. It's semantically more meaningful - bind() isn't written to accept any pointer whatsoever, just one to a struct which is "derived" from struct sockaddr; and

  2. The original sockets API was released in 1983, which was before the ANSI C standard in 1989, and its predecessor - K&R C - just didn't have void *, so you were going to have to cast it to something in any case.

OTHER TIPS

Casts appear in socket code because C doesn't have inheritance.

struct sockaddr is an abstract supertype of struct sockaddr_in and friends. The syscalls take the abstract type, you want to pass an actual instance of a derived type, and C doesn't know that converting a struct sockaddr_in * to a struct sockaddr * is automatically safe because it has no idea of the relationship between them.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top