Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reasoning behind C sockets sockaddr and sockaddr_storage

Tags:

c

unix

sockets

I'm looking at functions such as connect() and bind() in C sockets and notice that they take a pointer to a sockaddr struct. I've been reading and to make your application AF-Independent, it is useful to use the sockaddr_storage struct pointer and cast it to a sockaddr pointer because of all the extra space it has for larger addresses.

What I am wondering is how functions like connect() and bind() that ask for a sockaddr pointer go about accessing the data from a pointer that points at a larger structure than the one it is expecting. Sure, you pass it the size of the structure you are providing it, but what is the actual syntax that the functions use to get the IP Address off the pointers to larger structures that you have cast to struct *sockaddr?

It's probably because I come from OOP languages, but it seems like kind of a hack and a bit messy.

like image 373
Matt Vaughan Avatar asked Apr 15 '13 08:04

Matt Vaughan


People also ask

What is Sockaddr in C?

The “sockaddr_in” structure is very commonly used in socket programming in the C programming language. This structure allows you to bind a socket with the desired address so that a server can listen to the clients' connection requests.

What is the data size of the sockaddr structure?

With IPv4 (what basically everyone in 2005 still uses), the struct s_addr is a 4-byte number that represents one digit in an IP address per byte. (You won't ever see an IP address with a number in it greater than 255.)


1 Answers

Functions that expect a pointer to struct sockaddr probably typecast the pointer you send them to sockaddr when you send them a pointer to struct sockaddr_storage. In that way, they access it as if it was a struct sockaddr.

struct sockaddr_storage is designed to fit in both a struct sockaddr_in and struct sockaddr_in6

You don't create your own struct sockaddr, you usually create a struct sockaddr_in or a struct sockaddr_in6 depending on what IP version you're using. In order to avoid trying to know what IP version you will be using, you can use a struct sockaddr_storage which can hold either. This will in turn be typecasted to struct sockaddr by the connect(), bind(), etc functions and accessed that way.

You can see all of these structs below (the padding is implementation specific, for alignment purposes):

struct sockaddr {    unsigned short    sa_family;    // address family, AF_xxx    char              sa_data[14];  // 14 bytes of protocol address };   struct sockaddr_in {     short            sin_family;   // e.g. AF_INET, AF_INET6     unsigned short   sin_port;     // e.g. htons(3490)     struct in_addr   sin_addr;     // see struct in_addr, below     char             sin_zero[8];  // zero this if you want to };   struct sockaddr_in6 {     u_int16_t       sin6_family;   // address family, AF_INET6     u_int16_t       sin6_port;     // port number, Network Byte Order     u_int32_t       sin6_flowinfo; // IPv6 flow information     struct in6_addr sin6_addr;     // IPv6 address     u_int32_t       sin6_scope_id; // Scope ID };  struct sockaddr_storage {     sa_family_t  ss_family;     // address family      // all this is padding, implementation specific, ignore it:     char      __ss_pad1[_SS_PAD1SIZE];     int64_t   __ss_align;     char      __ss_pad2[_SS_PAD2SIZE]; }; 

So as you can see, if the function expects an IPv4 address, it will just read the first 4 bytes (because it assumes the struct is of type struct sockaddr. Otherwise it will read the full 16 bytes for IPv6).

like image 139
theprole Avatar answered Sep 22 '22 19:09

theprole