Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typecasting structures with different definitions

I came across this in socket programming:

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

struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};

These are the two structures with different type and this is how I was made to use them

Client Side:

connect(sfd,(struct sockaddr *)&caddr,clen; //type casted one

Server Side:

bind(sfd,(struct sockaddr *)&saddr,slen);
accept(sfd,(struct sockaddr *)&caddr,&clen);

Here structures with different definitions are being type casted how does this affects the variable?

Even though I typecast I can access the variables like this:

printf("File Descriptor : %d\n", fd);
char *p = inet_ntoa(caddr.sin_addr);
unsigned short port_no = ntohs(caddr.sin_port);

printf("Ip address : %s\n", p);
printf("Ephimeral port : %d\n", port_no);

What is the use of this kind of typecasting? Even though I have have typecasted it how can I access those members of other structures (addr_in here)? I want to know how these operations take place and understand the need for typecasting different structures.

like image 347
Anjaneyulu Avatar asked Nov 06 '16 06:11

Anjaneyulu


People also ask

What is Typedef and typecast in C?

In typecasting, a programmer converts the data type from one form to another. In typedef, a compiler converts the data type from one form to another. 2. Type cast applies to all the data types, whether they may be compatible or incompatible. Typedef is only applicable to compatible data types.

What happens in typecasting?

Typecasting is making a variable of one type, such as an int, act like another type, a char, for one single operation. To typecast something, simply put the type of variable you want the actual variable to act as inside parentheses in front of the actual variable. (char)a will make 'a' function as a char.


2 Answers

Note that socket operations are not standard C, but are standardized by POSIX.1, also known as IEEE Std. 1003-1. Thus, the posix tag added by the OP is important to notice.

In particular, the IEEE Std. 1003-1 definitions for <sys/socket.h> and socket() require implementations to behave in a very specific way, regardless of whether the C standard declares such behaviour implementation defined or even undefined behaviour.

The POSIX.1 definition for getaddrinfo() has an example of a program that looks up an IPv4 or IPv6 socket address (struct sockaddr_in and struct sockaddr_in6 types, respectively) for UDP. As explained in the <sys/socket.h> definition, a struct sockaddr_storage type can be used for static storage, when the socket type is unknown.

Originally, struct sockaddr was used as an opaque type, to simplify the socket interface, while keeping minimal type checking. The form shown in the question are from ANSI C (ISO C89) era. Due to added pointer rules in later versions of the ISO C standard, the actual structures used by POSIX.1 implementations are slightly different; the struct sockaddr is actually a structure containing an union nowadays.

If the sockets API used a void pointer, void *, for the socket address structure, there would be no type checking. With a generic type, developers must cast their socket address structure pointer to struct sockaddr * to avoid a warning (or error, depending on compiler options used), which is hopefully enough to avoid the grossest mistakes -- like supplying e.g. a string instead, and wonder why it does not work, even though the compiler does not complain at all.

In general, this approach -- using a "generic-ish" type instead of a specific type -- is very useful in many situations in C. It allows you to create data-specific types, while keeping the interface simple, but retain at least some type checking. With well-designed structures, you can do things like generic binary tree structures for any kind of data, while only implementing one set of functions (compare to e.g. qsort() function in C). Because of this, I show later on how to define such structures/unions without invoking Undefined Behaviour in standard C.

What is the use of this kind of typecasting?

A function that takes a pointer argument has two options. If the pointer argument is of type void *, the compiler will happily convert any object pointer to void * without warnings or complaints. If we want to only accept certain types of pointers, we need to specify exactly one type.

There are many types of socket addresses, and each socket address type has their own structure type. There is no way to tell the compiler to accept a pointer to one of maybe a dozen types of structures. Therefore, the pointer has to be cast, or type-punned, into the "generic" type, struct sockaddr in this instance.

Again, this approach does not lead to Undefined Behaviour in standard C in general, as long as the structures (the "generic" type in particular) are defined in a C standard compliant manner. It's just that the ones shown by OP are historical, not current, and cannot be really used as-is in current C due to strict aliasing requirements. I will explain later on how to do this.

In short, such type-punning is useful, when the function accepts pointers to certain types, and you wish to ensure only those types are supplied. In my opinion, the cast acts as a reminder for the developer to ensure they are using the correct type.

How can I access members of the other types?

Well, you cannot.

Thing is, every socket address structure type has a common sa_family_t field, that is set to a value corresponding to the type of the socket address it defines. If you use a sockaddr_in, the value is AF_INET; if you use a sockaddr_in6, the value is AF_INET6; if sockaddr_un, the value is AF_UNIX (or AF_LOCAL, which evaluates to the same value as AF_UNIX), and so on.

You can only examine this common field, to determine the type. However, you can examine it through any type supported by the struct sockaddr type.

For example, if you have struct sockaddr *foo, you can use ((struct sockaddr_storage *)foo)->ss_family (or even ((struct sockaddr_in *)foo)->sin_family) to examine the type of the structure. If it is the type that contains the member you are interested in, then you can access it.

For example, to return the uint32_t corresponding to the IPv4 address in network byte order (most significant byte first), you can use

uint32_t ip_address_of(const struct sockaddr *addr, uint32_t none)
{
    /* NULL pointer is invalid. */
    if (!addr)
        return none;

    /* If IPv4 address, return the s_addr member of the sin_addr member. */
    if (((const struct sockaddr_storage *)addr)->ss_family == AF_INET)
        return ((const struct sockaddr_in *)addr)->sin_addr.s_addr;

    /* The pointer did not point to an IPv4 address structure. */
    return none;
}

The second parameter, none, is returned if a NULL pointer or a pointer to a non-IPv4 socket address structure was specified. Usually (but not in all use cases), a value corresponding to broadcast addresses (0U or 0xFFFFFFFFU) can be used.


Historical background:

Using the structures shown in the question is not Undefined Behaviour in ANSI C -- the C standard of the era when they were widely used --, because 3.5.2.1 says

A pointer to a structure object, suitably cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may therefore be unnamed holes within a structure object, but not at its beginning, as necessary to achieve the appropriate alignment.

and ANSI C has relaxed rules with respect to type punning than later C standards (C99 and C11), allowing back-and-forth casting between pointer types without issues. In particular, 3.3.4,

It is guaranteed, however, that a pointer to an object of a given alignment may be converted to a pointer to an object of the same alignment or a less strict alignment and back again; the result shall compare equal to the original pointer.

This means that there are no issues in ANSI C when casting a socket address structure pointer to or from a struct sockaddr *; no information is lost in the casting.

(It is not a problem that different socket address structures might have different alignment requirements. The initial member is safe to access in any case, because the pointer to the structure points to the initial member. It is mostly an issue for users who wish to support multiple different socket types using the same code; they must use e.g. an union, or allocate memory dynamically, for the socket address structures.)


In the current era, we need to define the structures (struct sockaddr, to be precise) a bit differently, to ensure compatibility with the C standard.

Note that this means the following approach is valid even on non-POSIX systems that support standard C.

First, there are no changes needed to the individual socket address structures. (This also means there are no issues with backwards compatibility.) For example, in the GNU C library, a struct sockaddr_in and struct sockaddr_in6 are defined essentially as

struct sockaddr_in {
    sa_family_t     sin_family;    /* address family: AF_INET */
    in_port_t       sin_port;      /* port in network byte order */
    struct in_addr  sin_addr;      /* internet address */
};

struct sockaddr_in6 {
    sa_family_t     sin6_family;   /* address family: AF_INET6 */
    in_port_t       sin6_port;     /* port in network byte order */
    uint32_t        sin6_flowinfo; /* IPv6 flow information */
    struct in6_addr sin6_addr;     /* IPv6 address */
    uint32_t        sin6_scope_id; /* IPv6 scope-id */
};

The only important change needed is that struct sockaddr must contain a single union (preferably anonymous union for simplicity, but it requires C11 or at least anonymous union support from the C compiler used, and not many support the current C standard fully yet in 2016):

struct sockaddr {
    union {
        struct sockaddr_in   sa_in;
        struct sockaddr_in6  sa_in6;

        /* ... other sockaddr_ types ... */

    }  u;
};

The above lets the POSIX.1 socket interface work in standard C (from ANSI C or ISO C89 to C99 to C11 revisions).

You see, the ANSI C 3.3.2.3 says that "If a union contains several structures that share a common initial sequence, and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them" with later standards adding "anywhere that a declaration of the completed type of the union is visible". The standards continue, "Two structures share a common initial sequence if corresponding members have compatible types for a sequence of one or more initial members."

Above, the sin_family and sin6_family members (of type sa_family_t) are such a common initial part, and may be inspected via any of the members in the struct sockaddr.

ANSI C 3.5.2.1 says that "A pointer to a union object, suitably cast, points to each of its members, [..] and vice versa." The later revisions of the C standard has the same (or similar enough) language.

This means that if you have an interface that can use a pointer to any of the struct sockaddr_ types, you can use struct sockaddr * as a "generic pointer" instead. If you have, say struct sockaddr *sa, you could use sa->u.sa_in.sin_family or sa->u.sa_in6.sin6_family to access the common initial member (that specifies the type of the socket address in question). Because struct sockaddr is an union (or rather, because it is a structure with the union as its initial member), you can also use ((struct sockaddr_in *)sa)->sin_family or ((struct sockaddr_in6 *)sa)->sin6_family to access the family type. Because the family is the common initial member, you can do that using any type; just remember, that the other members are only accessible if the family matches the type the members belong to.

For current C, you can make the union anonymous (by dropping the u name near the end), in which case the above would be sa->sa_in.sin_family or sa->sa_in6.sin_family.

As to how this union-based struct sockaddr works on the other side, let's examine a possible implementation of bind():

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
    /* Clearly invalid sockfd? */
    if (sockfd == -1) {
        errno = EBADF;
        return -1;
    }

    /* Clearly invalid addr or addrlen? */
    if (addr == NULL || addrlen == 0) {
        errno = EINVAL;
        return -1;
    }

    switch (addr->u.sin_family) {

    case AF_INET:
        if (addrlen != sizeof (struct sockaddr_in)) {
            errno = EINVAL;
            return -1;
        }
        return bind_inet(sockfd, (struct sockaddr_in *)addr);

    case AF_INET6:
        if (addrlen != sizeof (struct sockaddr_in6)) {
            errno = EINVAL;
            return -1;
        }
        return bind_inet6(sockfd, (struct sockaddr_in6 *)addr);

    default:
        errno = EINVAL;
        return -1;
    }
}

The socket type dependent bind calls could be written equivalently as

        return bind_inet(sockfd, &(addr->u.sa_in));

and

        return bind_inet6(sockfd, &(addr->u.sa_in6));

i.e. taking the address of the union member, instead of just casting the pointer to the entire union.


When designing your own multi-subtype structures, there are four things to really remember, to stay standard C compliant:

  1. Use an union type containing all of the subtypes as members as the "generic" type.

  2. The union contains only one one subtype at a time; the one used to assign to it.

  3. Optionally, add a subtype for accessing the type (and possibly the other members that are common to all subtypes) with an easy name, and use it consistently in the documentation.

  4. Always examine the member that corresponds to the actual type first.

For example, if you are building an abstract binary tree of some sort -- perhaps a calculator? -- with different types of data stored at each node, you could use

/* Our "generic" type is 'node'. */
typedef  struct node  node;

typedef enum {
    DATA_NONE = 0,
    DATA_LONG,
    DATA_DOUBLE,
} node_data;

/* The minimal node type; no data payload. */
struct node_minimal {
    node      *left;
    node      *right;
    node_data  data;
};

struct node_long {
    node      *left;
    node      *right;
    node_data  data;   /* = DATA_LONG */
    long       value;
};

struct node_double {
    node      *left;
    node      *right;
    node_data  data;   /* = DATA_DOUBLE */
    double     value;
};

/* The generic type. */
struct node {
    union {
        struct node_minimal  of;
        struct node_long     long_data;
        struct node_double   double_data;
    } type;
};

To traverse such a tree, recursively, one could use e.g.

int node_traverse(const node *root,
                  int (*preorder)(const node *, void *),
                  int (*inorder)(const node *, void *),
                  int (*postorder)(const node *, void *),
                  void *custom)
{
    int retval;

    if (!root)
        return 0;

    if (preorder) {
        retval = preorder(root, custom);
        if (retval)
            return retval;
    }

    if (root->type.of.left) {
        retval = node_traverse(root->type.of.left, preorder, inorder, postorder, custom);
        if (retval)
            return retval;
    }

    if (inorder) {
        retval = inorder(root, custom);
        if (retval)
            return retval;
    }

    if (root->type.of.right) {
        retval = node_traverse(root->type.of.right, preorder, inorder, postorder, custom);
        if (retval)
            return retval;
    }

    if (postorder) {
        retval = postorder(root, custom);
        if (retval)
            return retval;
    }

    return 0;
}

where you supply the function called on each node in one (or more of) preorder, inorder, postorder parameters; custom being there only if you wish to provide the functions some context.

Note that with node *root:

  • root->type refers to the union of all subtypes.

  • root->type.of refers to the union member having type struct node_minimal; I named it so just to be playful. The intent is that you use this to access nodes of unknown type.

  • root->type.of.data depends only on the type actually used for the node, one of the DATA_ enums.

  • root->type.of.left and root->type.of.right are also available regardless of the type of the node, and are used when you just traverse the tree and don't care about the exact types of nodes.

  • root->type.long_data refers to the union member having type struct node_long (but you should only attempt to access it if root->type.of.data == DATA_LONG). Therefore, root->type.long_data.value is the long value member of a struct node_long.

  • root->type.double_data refers to the union member having type struct node_double (but you should only attempt to access it if root->type.of.data == DATA_DOUBLE). Therefore, root->type.double_data.value is the double value member of a struct node_long.

  • root->type.of.data == root->type.long_data.data == root->type.double_data.data, root->type.of.left == root->type.long_data.left == root->type.double_data.left, and root->type.of.right == root->type.long_data.right == root->type.double_data.right, because these are all common initial members, and it is explicitly allowed in C to access their value through any of the types in the union.

Note that the above traversal function is just an example; it uses a lot of stack for deep trees, and does not even try to detect loops. So, there is a lot of enhancements possible to make it a "library-worthy" function.

To print the value of a node, one could use for example

int node_print(const node *n, void *out)
{
    if (!out || !n) {
        errno = EINVAL;
        return -1;
    }

    if (n->type.of.data == DATA_DOUBLE)
        return fprintf((FILE *)out, "%.16g", n->type.double_data.value);

    if (n->type.of.data == DATA_LONG)
        return fprintf((FILE *)out, "%lu", n->type.long_data.value);

    /* Unknown type, so ... return -1 with errno == 0, I guess? */
    errno = 0;
    return -1;
}

which is designed to work with the tree traversal function. You could print the values of tree tree in order (from left to right) to standard output using

node_traverse(tree, NULL, node_print, NULL, stdout);

Hopefully the above example shows you enough to give you ideas, but also enough to make you careful and think hard about the kind of interface you are designing.

If you think (many do) that I am incorrect in my reading of the C standard, please point out the section you believe contradicts the above. My point is not to be popular, but I do want to be corrected when I am wrong.

Note: Rewritten on 2016-17-11.

like image 186
Nominal Animal Avatar answered Sep 29 '22 05:09

Nominal Animal


The perfect term will be "type punning" instead of calling it as typecasting.

sockaddr_in is for IP based communication in which we specify type of protocol, IP addr, port etc and sockaddr is a generic struct used in socket operation. The bind() uses sockaddr thus type punning is required.

You can search for type punning and can get more information.

like image 33
Akshay Patole Avatar answered Sep 29 '22 06:09

Akshay Patole