struct type conversion in C - c

In the below snippet , I found a sockaddr_in is type converted as sockaddr as '(struct sockaddr *) &sin' . I just want to know what are the variables that are in sockaddr_in will be mapped correspondingly to sockaddr .
Below is the snippet.
struct sockaddr {
unsigned short sa_family; // address family, AF_xxx
char sa_data[14]; // 14 bytes of protocol address
};
// IPv4 AF_INET sockets:
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_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(floodport);
sin.sin_addr.s_addr = inet_addr(argv[1]);
//type conversion
(struct sockaddr *) &sin // what values of sockaddr_in would be mapped to sockaddr ?
this conversion is used in sendto() in socket programming as below .
sendto(s, datagram, iph->tot_len,0, (struct sockaddr *) &sin,sizeof(sin))
Thanks in advance .

The only purpose for struct sockaddr is to have one type to pass around to functions like sendto() etc.
In fact, you don't use it for other purposes, there you have other structs such as
struct sockaddr_in for the legacy IPv4
struct sockaddr_in6 for IPv6
struct sockaddr_un for Unix sockets
struct sockaddr_bth for Bluetooth
struct sockaddr_storage which is as large as the largest in your
architecture. Can neatly be used for storing addresses whose type you
don't know.

The functions that use struct sockaddr will only read sa_family and do the opposite cast internally (if they understand what's inside sa_family).

Related

Casting between sockaddr and sockaddr_in6

I've been reading Beej's Guide to Network Programming and in one of his examples, he casts a pointer to struct sockaddr to a struct sockaddr_in6 pointer like shown below.
void *addr;
char *ipver;
// get the pointer to the address itself,
// different fields in IPv4 and IPv6:
if (p->ai_family == AF_INET) { // IPv4
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
ipver = "IPv4";
} else { // IPv6
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
ipver = "IPv6";
}
How is this possible, since the sizes of the structs are different?
ai_family and ai_addr are fields of the addrinfo struct, so presumably the code you are quoting had called getaddrinfo() beforehand.
The result of getaddrinfo() is a NULL-terminated linked list of addrinfo structs, where the addrinfo::ai_addr field is a pointer to an allocated memory block that is of sufficient size to hold a socket address of the reported addrinfo::ai_family type. The size of the address is reported in the addrinfo::ai_addrlen field.
For AF_INET, the addrinfo::ai_addr field is pointing at a memory block containing a sockaddr_in struct.
For AF_INET6, the addrinfo::ai_addr field is pointing at a memory block containing a sockaddr_in6 struct.
That is why the type-casts work.
The addrinfo::ai_addr field is declared as struct sockaddr* so it can be passed as-is to the addr parameter of the bind() and connect() functions without type-casting. The addrinfo::ai_addrlen field can be passed as-is to their addrlen parameter.

UNIX sendto() without destination port

I'm new to unix socket programming. I have some questions about unix sendto() function.
ssize_t sendto(int sockfd,
const void *buf,
size_t len,
int flags,
const struct sockaddr *dest_addr,
socklen_t addrlen);
My questions are:
(1) If port number in dest_addr is not set, how would the receiver host deal with the packet?
(2) How does this functions process info in dest_addr? I send IPv6 packet with this function, I have to use sockaddr_in6 struct which is a totally different with sockaddr_in struct:
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
};
and why do we need a cast to struct sockaddr in sendto()? Looks like only sa_family is meaningful to this function. Then what about other fields?
Regarding 1: there is no special "undefined" value. Whatever value happens to be stored in this memory location will be taken as the desired port number.
Regarding 2: each struct sockaddr starts with an address family identifier. If the family is set to AF_INET, all functions will know to expect a struct sockaddr_in. Likewise for the other families. In that sense, I believe the example you posted (which seems to imply AF_INET6 can be a valid value of sin_family in a struct sockaddr_in) to be misleading.

casting sockaddr_storage as sockaddr_in for inet_ntop

I am trying to cast a sockaddr_storage to a sockadd_in, so that i can print out the source ip address of a datagram packet,
i do not seem to be able to get the cast correct,
struct sockaddr_storage peer_addr;
getnameinfo((struct sockaddr *) &peer_add
peer_addrlen,
hostbuff, sizeof(hostbuff),
NULL, 0, NI_NAMEREQD);
inet_ntop(AF_INET, (((struct sockaddr_in *)peer_addr).sin_addr), ipbuff, INET_ADDRSTRLEN);
when i try to cast the structure to a sockaddr_in, i either get 'cannot convert to pointer', or when i remove the dereferance i get 'conversion to non scaler type requested'.
I have tried alot of combinations and simply don't understand where i am going wrong.
inet_ntop(peer_addr->ss_family, &(((struct sockaddr_in *)peer_addr)->sin_addr), ipbuff, INET_ADDRSTRLEN);
should work. But consider to use getnameinfo() instead, which is the more modern
interface:
char host[NI_MAXHOST];
getnameinfo((struct sockaddr *)peer_addr, peer_addr->ss_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
works for both IPv4 and IPv6 addresses.
Update according to the changed types in the question: This is a complete example
that should compile without warnings:
int socket = ...;
struct sockaddr_storage peer_addr;
socklen_t peer_addrlen;
char host[NI_MAXHOST];
ssize_t amount;
char buffer[1000];
amount = recvfrom(socket, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer_addr, &peer_addrlen);
getnameinfo((struct sockaddr *)&peer_addr, peer_addrlen, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
Or, using inet_ntop():
char ipbuff[INET_ADDRSTRLEN];
inet_ntop(peer_addr.ss_family, &(((struct sockaddr_in *)&peer_addr)->sin_addr), ipbuff, INET_ADDRSTRLEN);
struct sockaddr_storage * peer_addr;
getnameinfo((struct sockaddr *) &peer_add
peer_addrlen,
hostbuff, sizeof(hostbuff),
NULL, 0, NI_NAMEREQD);
Here you are mixing up stuff.
getnameinfo() indeed takes a struct sockaddr* as its first parameter, but what you try to do here won't work: peer_addr is a struct sockaddr_storage *, you take its address - which is a struct sockaddr_storage **, and try to cast this. That won't work.
I don't know where your peer_addr comes from, but
either it should be a struct sockaddr_storage (I don't think there is a need to have a pointer to a struct sockaddr_storage somewhere)
or it is really a pointer, and you should pass (struct sockaddr *) peer_addr - without the & - to getnameinfo().
Another point: The second parameter for getnameinfo() is supposed to be the "real" size of the address struct you are inputting.

Reasoning behind C sockets sockaddr and sockaddr_storage

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.
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).
In C++ classes with at least one virtual function are given a TAG. That tag allows you to dynamic_cast<>() to any of the classes your class derives from and vice versa. The TAG is what allows dynamic_cast<>() to work. More or less, this can be a number or a string...
In C we are limited to structures. However, structures can also be assigned a TAG. In fact, if you look at all the structures that theprole posted in his answer, you will notice that they all start with 2 bytes (an unsigned short) which represents what we call the family of the address. This defines exactly what the structure is and thus its size, fields, etc.
Therefore you can do something like this:
int bind(int fd, struct sockaddr *in, socklen_t len)
{
switch(in->sa_family)
{
case AF_INET:
if(len < sizeof(struct sockaddr_in))
{
errno = EINVAL; // wrong size
return -1;
}
{
struct sockaddr_in *p = (struct sockaddr_in *) in;
...
}
break;
case AF_INET6:
if(len < sizeof(struct sockaddr_in6))
{
errno = EINVAL; // wrong size
return -1;
}
{
struct sockaddr_in6 *p = (struct sockaddr_in6 *) in;
...
}
break;
[...other cases...]
default:
errno = EINVAL; // family not supported
return -1;
}
}
As you can see, the function can check the len parameter to make sure that the length is enough to fit the expected structure and therefore they can reinterpret_cast<>() (as it would be called in C++) your pointer. Whether the data is correct in the structure is up to the caller. There is not much choice on that end. These functions are expected to verify all sorts of things before it uses the data and return -1 and errno whenever a problem is found.
So in effect, you have a struct sockaddr_in or struct sockaddr_in6 that you (reinterpret) cast to a struct sockaddr and the bind() function (and others) cast that pointer back to a struct sockaddr_in or struct sockaddr_in6 after they checked the sa_family member and verified the size.

getnameinfo prototype asks for sockaddr not sockaddr_in?

The getnameinfo prototype asks for sockaddr but I have only seen examples using sockaddr_in. Can this example be re-written for sockaddr ? sin_family becomes sa_family but what about sin_port and sin_addr ? How are they included in sa_data ?
struct sockaddr{
unsigned short sa_family;
char sa_data[14];
};
struct sockaddr_in{
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr(IPvar);
sin.sin_port = 0; // If 0, port is chosen by system
getnameinfo( (struct sockaddr *)&sin, sizeof(sin), buffervar, sizeof(buffervar), NULL, 0, 0);
struct sockaddr is a "super-class" of the concrete protocol address structures like struct sockaddr_in, struct sockaddr_in6, and struct sockaddr_un, etc. The getnameinfo(3) dispatches to a specific execution path based of the address family (the sa_family member.)
As far as memory is concerned - the three members of struct sockaddr_in are overlaid with the struct sockaddr's sa_data member. Take a look at Chapter 3 of the UnP book.

Resources