Why How to Cast Sockaddr to Sockaddr_In

how do you cast sockaddr structure to a sockaddr_in - C++ networking sockets ubuntu UDP

I would point out that if this is actually C++ the idiomatic way to do this would be:

sockaddr *sa = ...; // struct not needed in C++
char ip[INET6_ADDRSTRLEN] = {0};

switch (sa->sa_family) {
case AF_INET: {
// use of reinterpret_cast preferred to C style cast
sockaddr_in *sin = reinterpret_cast<sockaddr_in*>(sa);
inet_ntop(AF_INET, &sin->sin_addr, ip, INET6_ADDRSTRLEN);
break;
}
case AF_INET6: {
sockaddr_in6 *sin = reinterpret_cast<sockaddr_in6*>(sa);
// inet_ntoa should be considered deprecated
inet_ntop(AF_INET6, &sin->sin6_addr, ip, INET6_ADDRSTRLEN);
break;
}
default:
abort();
}

This sample code handles IPv4 and IPv6 addresses and also would be considered more C++ idiomatic than either of the suggested implementations.

Why can we cast sockaddr to sockaddr_in

It is possible because you normally cast pointers, not the structures themselves. You do what in natural language means "please treat this pointer to a socket structure as a pointer to an internet socket structure instead". Compiler has no problems to re-interpret the pointer.

Here is more detailed description taken up from comments:

A sockaddr is 16 bytes in size - the first two bytes are the sa_family, and the remaining 14 bytes are the sa_data which is arbitrary data. A sockaddr_in is also 16 bytes in size - the first 2 bytes are the sin_family (always AF_INET), the next 2 bytes are the sin_port, the next 4 bytes are the sin_addr (IP address), and the last 8 bytes are the sin_zero which is unused in IPv4 and provided only to ensure 16 bytes. This way, you can look at sockaddr.sa_family first, and if it is AF_INET then interpret the entire sockaddr as a sockaddr_in.

A sockaddr_in is not stored inside of sockaddr.sa_data field. The entire sockaddr is the entire sockaddr_in (when sockaddr.sa_family is AF_INET, that is). If you take a sockaddr* pointer and cast it to a sockaddr_in* pointer, then:

  • sockaddr.sa_family is sockaddr_in.sin_family
  • bytes 0-1 of sockaddr.sa_data are sockaddr_in.sin_port
  • bytes 2-5 are sockaddr_in.sin_addr
  • bytes 6-13 are sockaddr_in.sin_zero.

Why do we cast sockaddr_in to sockaddr when calling bind()?

No, it's not just convention.

sockaddr is a generic descriptor for any kind of socket operation, whereas sockaddr_in is a struct specific to IP-based communication (IIRC, "in" stands for "InterNet"). As far as I know, this is a kind of "polymorphism" : the bind() function pretends to take a struct sockaddr *, but in fact, it will assume that the appropriate type of structure is passed in; i. e. one that corresponds to the type of socket you give it as the first argument.

Why does a cast from sockaddr_in to sockaddr work

The sockaddr struct basically has only one field, the address family. The code that receives this structure can use this field to determine what is the actual type of the structure. All the structures that are really used also have this field as the first one and therefore the value is deterministic.

The implementations also make the structures the same size with padding, so the memory usage is also completely deterministic. This makes it work properly.

For example Microsoft defines the sockaddr structure in Visual Studio 2017 as

struct sockaddr {  
unsigned short sa_family;
char sa_data[14];
};

sa_data

Maximum size of all the different socket address structures.

So any “child” struct that may be sent must have 14 bytes of data in it, no more or less.

Whereas sockaddr_in is

struct sockaddr_in{  
short sin_family;
unsigned short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};

Here the port and in_addr require six bytes in total so 8 bytes of padding is used to keep the size the same as sockaddr.

Of course it would be possible to create for example sockaddr_un, set its address family to claim it’s sockaddr_in and any code receiving the structure would cast it wrong and get completely wrong values.

Socket Programming, Casting sockaddr_in to sockaddr. Why?

What is the purpose of this casting here which casted sockaddr_in to sockaddr.

sockaddr_in is not casted to sockaddr. sockaddr_in* is casted to sockaddr*.

That cast is done because the function connect which is being called doesn't accept a sockaddr_in* parameter, but a sockaddr*.


P.S. I recommend to not manually create a sockaddr_in, but instead to use getaddrinfo to create it.

P.P.S char *message = "A message from Client !"; is ill-formed in C++. The code that you've posted seems to be C rather than C++.

cast from sockaddr * to sockaddr_in * increases required alignment

TLDR: This warning doesn't indicate an error in your code, but you can avoid it by using a poper c++ reinterpret_cast (thanks to @Kurt Stutsman).


Explanation:

Reason for the warning:

  • sockaddr consists of a unsigned short (usually 16 bit) and a char array, so its alignment requirement is 2.
  • sockaddr_in contains (among other things) a struct in_addr which has an alignment requirement of 4 which in turn means sockaddr_in also must be aligned to a 4 Byte boundary.

For that reason, casting an arbitrary sockaddr* to an sockaddr_in* changes the alignment requirement, and accessing the object via the new pointer would even violate aliasing rules and result in undefined behavior.

Why you can ignore it:

In your case, the object, p->ai_addr is pointing to, most likely is a sockaddr_in or sockaddr_in6 object anyway (as determined by checking ai_family) and so the operation is safe. However you compiler doesn't know that and produces a warning.

It is essentially the same thing as using a static_cast to cast a pointer to a base class to a pointer to a derived class - it is unsafe in the general case, but if you know the correct dynamic type extrinsically, it is well defined.

Solution:
I don't know a clean way around this (other than suppress the warning), which is not unusual with warnings enabled by -Weverything . You could copy the object pointed to by p->ai_addr byte by byte to an object of the appropriate type, but then you could (most likely) no longer use addr the same way as before, as it would now point to a different (e.g. local) variable.
-Weverything isn't something I would use for my usual builds anyway, because it adds far too much noise, but if you want to keep it, @Kurt Stutsman mentioned a good solution in the comments:

clang++ (g++ doesn't emit a warning in any case) doesn't emit a warning, if you use a reinterpret_cast instead of the c style cast (which you shouldn't use anyway), although both have (in this case) exactly the same functionality. Maybe because reinterpret_cast explicitly tells the compiler: "Trust me, I know, what I'm doing" .


On a side Note: In c++ code you don't need the struct keywords.

Casting between sockaddr and sockaddr_in6

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.

How to cast sockadd to sockaddr_in in swift

You have to take the address of addr (which requires
addr to be a variable), use withMemoryRebound() to
temporarily rebind it to an sockaddr_in pointer, which can then
be dereferenced:

var addr: sockaddr = ...
let addr_in = withUnsafePointer(to: &addr) {
$0.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee
}
}

There are some problems in your defaultGatewayAddress() method:

  • sa = sa!.advanced(by: ROUNDUP(a: Int(sa!.pointee.sa_len))) advances sa by
    sa_len multiplied with the size of sockaddr, which is not what you intend.
  • The test if ((rt!.pointee.rtm_addrs & (RTA_DST|RTA_GATEWAY)) == (RTA_DST|RTA_GATEWAY))
    must be done after the for i in 0..<RTAX_MAX loop.
  • With sa_tab.insert(sa!.pointee, at: Int(i)) you insert new elements into the
    array instead of replacing them.

Note also that

rt = p!.withMemoryRebound(to: rt_msghdr.self, capacity: 1, {$0})

might work here, but it generally unsafe: The $0 pointer is only valid for
the execution of the closure and must not be passed to the outside.

Here is a working version of your code. It is essentially a translation of
How can I determine the default gateway on iPhone?
to Swift (which in turn seems it built on https://github.com/getlantern/libnatpmp/blob/master/getgateway.c).

I have also simplified it a bit and modified to avoid all forced unwraps.

func defaultGatewayAddress() -> in_addr? {

var defaultGateway: in_addr?

var mib:[Int32] = [CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_GATEWAY]
var len = 0
if sysctl(&mib, u_int(mib.count), nil, &len, nil, 0) < 0 {
return nil
}
let buffer = UnsafeMutablePointer<CChar>.allocate(capacity: len)
defer {
buffer.deallocate(capacity: len)
}
if sysctl(&mib, u_int(mib.count), buffer, &len, nil, 0) < 0 {
return nil
}

var sa_tab = [UnsafePointer<sockaddr>?](repeating: nil, count: Int(RTAX_MAX))

var ptr = buffer
while ptr < buffer + len {
ptr.withMemoryRebound(to: rt_msghdr.self, capacity: 1) { rt in
var sa = UnsafeMutableRawPointer(rt + 1).assumingMemoryBound(to: sockaddr.self)
for i in 0..<RTAX_MAX {
if rt.pointee.rtm_addrs & (1 << i) != 0 {
sa_tab[Int(i)] = UnsafePointer(sa)
sa = (UnsafeMutableRawPointer(sa) + Int(sa.pointee.sa_len)).assumingMemoryBound(to: sockaddr.self)
} else {
sa_tab[Int(i)] = nil
}
}

if let dst = sa_tab[Int(RTAX_DST)],
dst.pointee.sa_family == sa_family_t(AF_INET),
let gateway = sa_tab[Int(RTAX_GATEWAY)],
gateway.pointee.sa_family == sa_family_t(AF_INET)
{
dst.withMemoryRebound(to: sockaddr_in.self, capacity: 1) { addr in
if addr.pointee.sin_addr.s_addr == 0 {

var name = [CChar](repeating: CChar(0), count: Int(IFNAMSIZ) + 1)
if_indextoname(UInt32((rt.pointee.rtm_index)), &name)
if String(cString: name) == "en0" {
defaultGateway = gateway.withMemoryRebound(to: sockaddr_in.self, capacity: 1) {
$0.pointee.sin_addr
}
}
}
}

}
ptr += Int(rt.pointee.rtm_msglen)
}
}

return defaultGateway
}


Related Topics



Leave a reply



Submit