the_Foundation [main]
Address: More robust way to get a UDP broadcast address
5795b11aec103314a593eee00dd861b2d93acea1
[1mdiff --git a/include/the_Foundation/address.h b/include/the_Foundation/address.h[m
[1mindex e2e4edf..b715d98 100644[m
[1m--- a/include/the_Foundation/address.h[m
[1m+++ b/include/the_Foundation/address.h[m
[36m@@ -47,8 +47,18 @@[m [menum iSocketType {[m
udp_SocketType,[m
};[m
[m
[31m-iAddress * newBroadcast_Address(uint16_t port);[m
[31m-iAddress * newSockAddr_Address (const void *sockAddr, size_t sockAddrSize, enum iSocketType socketType);[m
[32m+[m[32miAddress * newBroadcast_Address (uint16_t port); /* 255.255.255.255 */[m
[32m+[m[32miAddress * newSockAddr_Address (const void *sockAddr, size_t sockAddrSize, enum iSocketType socketType);[m
[32m+[m
[32m+[m[32m/**[m
[32m+[m[32m * Makes a UDP broadcast address for the interface that owns this address.[m
[32m+[m[32m *[m
[32m+[m[32m * @param port UDP port number for broadcasting.[m
[32m+[m[32m *[m
[32m+[m[32m * @return A new Address instance (ownership given to caller), or NULL if not applicable (IPv6,[m
[32m+[m[32m * loopback, point-to-point).[m
[32m+[m[32m */[m
[32m+[m[32miAddress * makeBroadcast_Address (const iAddress *, uint16_t port);[m
[m
void init_Address (iAddress *);[m
void deinit_Address (iAddress *);[m
[1mdiff --git a/src/address.c b/src/address.c[m
[1mindex a4c28d0..9d07354 100644[m
[1m--- a/src/address.c[m
[1m+++ b/src/address.c[m
[36m@@ -46,6 +46,7 @@[m [mSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</small>[m
# include <arpa/inet.h>[m
# include <netinet/in.h>[m
# include <ifaddrs.h>[m
[32m+[m[32m# include <net/if.h>[m
#endif[m
[m
enum iAddressFlag {[m
[36m@@ -161,6 +162,39 @@[m [miAddress *newBroadcast_Address(uint16_t port) {[m
return d;[m
}[m
[m
[32m+[m[32miAddress *makeBroadcast_Address(const iAddress *d, uint16_t port) {[m
[32m+[m[32m#if !defined (iPlatformWindows)[m
[32m+[m[32m waitForFinished_Address(d);[m
[32m+[m[32m if (!d->info || d->info->ai_family != AF_INET) {[m
[32m+[m[32m return NULL; /* not an IPv4 address */[m
[32m+[m[32m }[m
[32m+[m[32m /* Find the network interface that owns this address and use its broadcast address.[m
[32m+[m[32m On macOS/BSD, sending to 255.255.255.255 from an INADDR_ANY socket fails with[m
[32m+[m[32m EADDRNOTAVAIL because the kernel requires a specific source interface for limited[m
[32m+[m[32m broadcast. On Linux it works, but using per-interface broadcast addresses is[m
[32m+[m[32m more correct on multi-homed hosts regardless of platform. */[m
[32m+[m[32m const struct in_addr inputAddr =[m
[32m+[m[32m ((const struct sockaddr_in *) d->info->ai_addr)->sin_addr;[m
[32m+[m[32m struct ifaddrs *ifs = NULL;[m
[32m+[m[32m if (getifaddrs(&ifs) == 0) {[m
[32m+[m[32m for (const struct ifaddrs *i = ifs; i; i = i->ifa_next) {[m
[32m+[m[32m if (!i->ifa_addr || i->ifa_addr->sa_family != AF_INET) continue;[m
[32m+[m[32m if (!(i->ifa_flags & IFF_BROADCAST) || !i->ifa_broadaddr) continue;[m
[32m+[m[32m const struct in_addr ifAddr =[m
[32m+[m[32m ((const struct sockaddr_in *) i->ifa_addr)->sin_addr;[m
[32m+[m[32m if (ifAddr.s_addr != inputAddr.s_addr) continue;[m
[32m+[m[32m struct sockaddr_in ba = *(const struct sockaddr_in *) i->ifa_broadaddr;[m
[32m+[m[32m ba.sin_port = htons(port);[m
[32m+[m[32m freeifaddrs(ifs);[m
[32m+[m[32m return newSockAddr_Address(&ba, sizeof(ba), udp_SocketType);[m
[32m+[m[32m }[m
[32m+[m[32m freeifaddrs(ifs);[m
[32m+[m[32m }[m
[32m+[m[32m return NULL; /* no broadcast-capable interface found for this address */[m
[32m+[m[32m#endif[m
[32m+[m[32m return NULL;[m
[32m+[m[32m}[m
[32m+[m
iAddress *newSockAddr_Address(const void * sockAddr,[m
size_t sockAddrSize,[m
enum iSocketType socketType) {[m
[1mdiff --git a/tests/t_network.c b/tests/t_network.c[m
[1mindex 02bc00b..e9521cd 100644[m
[1m--- a/tests/t_network.c[m
[1m+++ b/tests/t_network.c[m
[36m@@ -279,8 +279,12 @@[m [mint main(int argc, char *argv[]) {[m
printf("%zu network interfaces:\n", size_ObjectList(ifs));[m
iConstForEach(ObjectList, i, ifs) {[m
iString *str = toString_Address(i.object);[m
[31m- printf("- %s\n", cstr_String(str));[m
[32m+[m[32m iAddress *broad = makeBroadcast_Address(i.object, 0);[m
[32m+[m[32m printf("- %s (broadcast: %s)\n",[m
[32m+[m[32m cstr_String(str),[m
[32m+[m[32m broad ? cstrCollect_String(toString_Address(broad)) : "n/a");[m
delete_String(str);[m
[32m+[m[32m iRelease(broad);[m
}[m
iRelease(ifs);[m
}[m