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
issockaddr_in.sin_family
- bytes 0-1 of
sockaddr.sa_data
aresockaddr_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) astruct in_addr
which has an alignment requirement of 4 which in turn meanssockaddr_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 requiresaddr
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)))
advancessa
bysa_len
multiplied with the size ofsockaddr
, 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 thefor 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
How to List Library Dependencies of a Non-Native Binary
How to Start Docker Service in Ubuntu 16.04
What Does "/Dev/Null" Mean at the End of Shell Commands
How to Set a Global Nofile Limit to Avoid "Many Open Files" Error
How to Upgrade Openssl in Centos 6.5/Linux/Unix from Source
How to Make a Computer Behave as a Bluetooth Hid Device
What Would Be the Equivalent of Win32 API in Linux
How to Install Node Binary Distribution Files on Linux
Linux Pipe Audio File to Microphone Input
How to Run Sudo Command in Winscp to Transfer Files from Windows to Linux
Bluetooth Low Energy: Use Bluez Stack as a Peripheral (With Custom Services and Characteristics)
Setting a Gdb Exit Breakpoint Not Working
Profiling a (Possibly I/O-Bound) Process to Reduce Latency