/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#ifndef _XOPEN_SOURCE_EXTENDED
# define _XOPEN_SOURCE_EXTENDED
#endif
#define _HPUX_ALT_XOPEN_SOCKET_API
#ifdef OF_NINTENDO_3DS
# include <malloc.h> /* For memalign() */
#endif
#include <errno.h>
#import "OFArray.h"
#import "OFCharacterSet.h"
#import "OFLocale.h"
#ifdef OF_HAVE_THREADS
# import "OFMutex.h"
#endif
#import "OFOnce.h"
#import "OFSocket.h"
#import "OFSocket+Private.h"
#import "OFString.h"
#ifdef OF_HAVE_THREADS
# import "OFTLSKey.h"
#endif
#import "OFException.h" /* For some E* -> WSAE* defines */
#import "OFInitializationFailedException.h"
#import "OFInvalidArgumentException.h"
#import "OFInvalidFormatException.h"
#import "OFLockFailedException.h"
#import "OFOutOfRangeException.h"
#import "OFUnlockFailedException.h"
#ifdef HAVE_NET_IF_H
# include <net/if.h>
#endif
#ifdef OF_AMIGAOS
# define Class IntuitionClass
# include <proto/exec.h>
# undef Class
#endif
#ifdef OF_NINTENDO_3DS
# include <3ds/types.h>
# include <3ds/services/soc.h>
#endif
#ifdef OF_NINTENDO_SWITCH
# define id nx_id
# include <switch.h>
# undef id
#endif
#if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
static OFMutex *mutex;
static void
releaseMutex(void)
{
[mutex release];
}
#endif
#if !defined(OF_AMIGAOS) || defined(OF_MORPHOS) || !defined(OF_HAVE_THREADS)
static bool initSuccessful = false;
#endif
#ifdef OF_AMIGAOS
# if defined(OF_HAVE_THREADS) && !defined(OF_MORPHOS)
OFTLSKey _OFSocketBaseKey;
# ifdef OF_AMIGAOS4
OFTLSKey _OFSocketInterfaceKey;
# endif
# else
struct Library *SocketBase;
# ifdef OF_AMIGAOS4
struct SocketIFace *ISocket = NULL;
# endif
# endif
#endif
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
OF_CONSTRUCTOR()
{
if (OFTLSKeyNew(&_OFSocketBaseKey) != 0)
@throw [OFInitializationFailedException exception];
# ifdef OF_AMIGAOS4
if (OFTLSKeyNew(&_OFSocketInterfaceKey) != 0)
@throw [OFInitializationFailedException exception];
# endif
}
#endif
#if !defined(OF_AMIGAOS) || defined(OF_MORPHOS) || !defined(OF_HAVE_THREADS)
static void
init(void)
{
# if defined(OF_WINDOWS)
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 0), &wsa))
return;
# elif defined(OF_AMIGAOS)
if ((SocketBase = OpenLibrary("bsdsocket.library", 4)) == NULL)
return;
# ifdef OF_AMIGAOS4
if ((ISocket = (struct SocketIFace *)
GetInterface(SocketBase, "main", 1, NULL)) == NULL) {
CloseLibrary(SocketBase);
return;
}
# endif
# elif defined(OF_WII)
if (net_init() < 0)
return;
# elif defined(OF_NINTENDO_3DS)
void *ctx;
if ((ctx = memalign(0x1000, 0x100000)) == NULL)
return;
if (socInit(ctx, 0x100000) != 0)
return;
atexit((void (*)(void))socExit);
# elif defined(OF_NINTENDO_SWITCH)
if (R_FAILED(socketInitializeDefault()))
return;
atexit(socketExit);
# endif
# if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
mutex = [[OFMutex alloc] init];
atexit(releaseMutex);
# endif
initSuccessful = true;
}
OF_DESTRUCTOR()
{
# ifdef OF_AMIGAOS
# ifdef OF_AMIGAOS4
if (ISocket != NULL)
DropInterface((struct Interface *)ISocket);
# endif
if (SocketBase != NULL)
CloseLibrary(SocketBase);
# endif
}
#endif
bool
_OFSocketInit(void)
{
#if !defined(OF_AMIGAOS) || defined(OF_MORPHOS) || !defined(OF_HAVE_THREADS)
static OFOnceControl onceControl = OFOnceControlInitValue;
OFOnce(&onceControl, init);
return initSuccessful;
#else
struct Library *socketBase;
# ifdef OF_AMIGAOS4
struct SocketIFace *socketInterface;
# endif
# ifdef OF_AMIGAOS4
if ((socketInterface = OFTLSKeyGet(_OFSocketInterfaceKey)) != NULL)
# else
if ((socketBase = OFTLSKeyGet(_OFSocketBaseKey)) != NULL)
# endif
return true;
if ((socketBase = OpenLibrary("bsdsocket.library", 4)) == NULL)
return false;
# ifdef OF_AMIGAOS4
if ((socketInterface = (struct SocketIFace *)
GetInterface(socketBase, "main", 1, NULL)) == NULL) {
CloseLibrary(socketBase);
return false;
}
# endif
if (OFTLSKeySet(_OFSocketBaseKey, socketBase) != 0) {
CloseLibrary(socketBase);
# ifdef OF_AMIGAOS4
DropInterface((struct Interface *)socketInterface);
# endif
return false;
}
# ifdef OF_AMIGAOS4
if (OFTLSKeySet(_OFSocketInterfaceKey, socketInterface) != 0) {
CloseLibrary(socketBase);
DropInterface((struct Interface *)socketInterface);
return false;
}
# endif
return true;
#endif
}
#if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS)
void
_OFSocketDeinit(void)
{
struct Library *socketBase = OFTLSKeyGet(_OFSocketBaseKey);
# ifdef OF_AMIGAOS4
struct SocketIFace *socketInterface =
OFTLSKeyGet(_OFSocketInterfaceKey);
if (socketInterface != NULL)
DropInterface((struct Interface *)socketInterface);
# endif
if (socketBase != NULL)
CloseLibrary(socketBase);
}
#endif
int
_OFSocketErrNo(void)
{
#if defined(OF_WINDOWS)
switch (WSAGetLastError()) {
case WSAEACCES:
return EACCES;
case WSAEADDRINUSE:
return EADDRINUSE;
case WSAEADDRNOTAVAIL:
return EADDRNOTAVAIL;
case WSAEAFNOSUPPORT:
return EAFNOSUPPORT;
case WSAEALREADY:
return EALREADY;
case WSAEBADF:
return EBADF;
case WSAECONNABORTED:
return ECONNABORTED;
case WSAECONNREFUSED:
return ECONNREFUSED;
case WSAECONNRESET:
return ECONNRESET;
case WSAEDESTADDRREQ:
return EDESTADDRREQ;
case WSAEDISCON:
return EPIPE;
case WSAEDQUOT:
return EDQUOT;
case WSAEFAULT:
return EFAULT;
case WSAEHOSTDOWN:
return EHOSTDOWN;
case WSAEHOSTUNREACH:
return EHOSTUNREACH;
case WSAEINPROGRESS:
return EINPROGRESS;
case WSAEINTR:
return EINTR;
case WSAEINVAL:
return EINVAL;
case WSAEISCONN:
return EISCONN;
case WSAELOOP:
return ELOOP;
case WSAEMSGSIZE:
return EMSGSIZE;
case WSAENAMETOOLONG:
return ENAMETOOLONG;
case WSAENETDOWN:
return ENETDOWN;
case WSAENETRESET:
return ENETRESET;
case WSAENETUNREACH:
return ENETUNREACH;
case WSAENOBUFS:
return ENOBUFS;
case WSAENOPROTOOPT:
return ENOPROTOOPT;
case WSAENOTCONN:
return ENOTCONN;
case WSAENOTEMPTY:
return ENOTEMPTY;
case WSAENOTSOCK:
return ENOTSOCK;
case WSAEOPNOTSUPP:
return EOPNOTSUPP;
case WSAEPFNOSUPPORT:
return EPFNOSUPPORT;
case WSAEPROCLIM:
return EPROCLIM;
case WSAEPROTONOSUPPORT:
return EPROTONOSUPPORT;
case WSAEPROTOTYPE:
return EPROTOTYPE;
case WSAEREMOTE:
return EREMOTE;
case WSAESHUTDOWN:
return ESHUTDOWN;
case WSAESOCKTNOSUPPORT:
return ESOCKTNOSUPPORT;
case WSAESTALE:
return ESTALE;
case WSAETIMEDOUT:
return ETIMEDOUT;
case WSAETOOMANYREFS:
return ETOOMANYREFS;
case WSAEUSERS:
return EUSERS;
case WSAEWOULDBLOCK:
return EWOULDBLOCK;
}
return 0;
#elif defined(OF_AMIGAOS)
return Errno();
#else
return errno;
#endif
}
#ifndef OF_WII
int
_OFGetSockName(OFSocketHandle sock, struct sockaddr *restrict addr,
socklen_t *restrict addrLen)
{
int ret;
# if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
[mutex lock];
# endif
ret = getsockname(sock, addr, addrLen);
# if defined(OF_HAVE_THREADS) && (!defined(OF_AMIGAOS) || defined(OF_MORPHOS))
[mutex unlock];
# endif
return ret;
}
#endif
OFSocketAddress
OFSocketAddressParseIPv4(OFString *IPv4, uint16_t port)
{
void *pool = objc_autoreleasePoolPush();
OFCharacterSet *whitespaceCharacterSet =
[OFCharacterSet whitespaceCharacterSet];
OFSocketAddress ret;
struct sockaddr_in *addrIn = &ret.sockaddr.in;
OFArray OF_GENERIC(OFString *) *components;
uint32_t addr;
memset(&ret, '\0', sizeof(ret));
ret.family = OFSocketAddressFamilyIPv4;
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
ret.length = 8;
#else
ret.length = sizeof(ret.sockaddr.in);
#endif
addrIn->sin_family = AF_INET;
addrIn->sin_port = OFToBigEndian16(port);
#ifdef OF_WII
addrIn->sin_len = ret.length;
#endif
components = [IPv4 componentsSeparatedByString: @"."];
if (components.count != 4)
@throw [OFInvalidFormatException exception];
addr = 0;
for (OFString *component in components) {
unsigned long long number;
if (component.length == 0)
@throw [OFInvalidFormatException exception];
if ([component indexOfCharacterFromSet:
whitespaceCharacterSet] != OFNotFound)
@throw [OFInvalidFormatException exception];
number = component.unsignedLongLongValue;
if (number > UINT8_MAX)
@throw [OFInvalidFormatException exception];
addr = (addr << 8) | ((uint32_t)number & 0xFF);
}
addrIn->sin_addr.s_addr = OFToBigEndian32(addr);
objc_autoreleasePoolPop(pool);
return ret;
}
static uint16_t
parseIPv6Component(OFString *component)
{
unsigned long long number;
if ([component indexOfCharacterFromSet:
[OFCharacterSet whitespaceCharacterSet]] != OFNotFound)
@throw [OFInvalidFormatException exception];
number = [component unsignedLongLongValueWithBase: 16];
if (number > UINT16_MAX)
@throw [OFInvalidFormatException exception];
return (uint16_t)number;
}
static OFString *
transformEmbeddedIPv4(OFString *IPv6)
{
size_t lastColon = [IPv6
rangeOfString: @":"
options: OFStringSearchBackwards].location;
OFString *IPv4;
OFSocketAddress address;
const struct sockaddr_in *addrIn;
uint32_t addr;
if (lastColon == OFNotFound)
@throw [OFInvalidFormatException exception];
IPv4 = [IPv6 substringWithRange:
OFMakeRange(lastColon + 1, IPv6.length - lastColon - 1)];
IPv6 = [IPv6 substringWithRange: OFMakeRange(0, lastColon + 1)];
address = OFSocketAddressParseIPv4(IPv4, 0);
addrIn = &address.sockaddr.in;
addr = OFFromBigEndian32(addrIn->sin_addr.s_addr);
return [IPv6 stringByAppendingString:
[OFString stringWithFormat: @"%x%02x:%x%02x",
(addr & 0xFF000000) >> 24, (addr & 0x00FF0000) >> 16,
(addr & 0x0000FF00) >> 8, addr & 0x000000FF]];
}
OFSocketAddress
OFSocketAddressParseIPv6(OFString *IPv6, uint16_t port)
{
void *pool = objc_autoreleasePoolPush();
OFSocketAddress ret;
struct sockaddr_in6 *addrIn6 = &ret.sockaddr.in6;
size_t doubleColon, percent;
memset(&ret, '\0', sizeof(ret));
ret.family = OFSocketAddressFamilyIPv6;
ret.length = sizeof(ret.sockaddr.in6);
#ifdef AF_INET6
addrIn6->sin6_family = AF_INET6;
#else
addrIn6->sin6_family = AF_UNSPEC;
#endif
addrIn6->sin6_port = OFToBigEndian16(port);
if ((percent = [IPv6 rangeOfString: @"%"].location) != OFNotFound) {
OFString *interface = [IPv6 substringFromIndex: percent + 1];
IPv6 = [IPv6 substringToIndex: percent];
@try {
addrIn6->sin6_scope_id = (uint32_t)[interface
unsignedLongLongValueWithBase: 10];
} @catch (OFInvalidFormatException *e) {
#if defined(HAVE_IF_NAMETOINDEX) && !defined(OF_WINDOWS)
addrIn6->sin6_scope_id = if_nametoindex([interface
cStringWithEncoding: [OFLocale encoding]]);
#endif
}
if (addrIn6->sin6_scope_id == 0)
@throw [OFInvalidArgumentException exception];
}
if ([IPv6 rangeOfString: @"."].location != OFNotFound)
IPv6 = transformEmbeddedIPv4(IPv6);
doubleColon = [IPv6 rangeOfString: @"::"].location;
if (doubleColon != OFNotFound) {
OFString *left = [IPv6 substringToIndex: doubleColon];
OFString *right = [IPv6 substringFromIndex: doubleColon + 2];
OFArray OF_GENERIC(OFString *) *leftComponents;
OFArray OF_GENERIC(OFString *) *rightComponents;
size_t i;
if ([right hasPrefix: @":"] || [right containsString: @"::"])
@throw [OFInvalidFormatException exception];
leftComponents = [left componentsSeparatedByString: @":"];
rightComponents = [right componentsSeparatedByString: @":"];
if (leftComponents.count + rightComponents.count > 7)
@throw [OFInvalidFormatException exception];
i = 0;
for (OFString *component in leftComponents) {
uint16_t number = parseIPv6Component(component);
addrIn6->sin6_addr.s6_addr[i++] = number >> 8;
addrIn6->sin6_addr.s6_addr[i++] = number;
}
i = 16;
for (OFString *component in rightComponents.reversedArray) {
uint16_t number = parseIPv6Component(component);
addrIn6->sin6_addr.s6_addr[--i] = number;
addrIn6->sin6_addr.s6_addr[--i] = number >> 8;
}
} else {
OFArray OF_GENERIC(OFString *) *components =
[IPv6 componentsSeparatedByString: @":"];
size_t i;
if (components.count != 8)
@throw [OFInvalidFormatException exception];
i = 0;
for (OFString *component in components) {
uint16_t number;
if (component.length == 0)
@throw [OFInvalidFormatException exception];
number = parseIPv6Component(component);
addrIn6->sin6_addr.s6_addr[i++] = number >> 8;
addrIn6->sin6_addr.s6_addr[i++] = number;
}
}
objc_autoreleasePoolPop(pool);
return ret;
}
OFSocketAddress
OFSocketAddressParseIP(OFString *IP, uint16_t port)
{
OFSocketAddress ret;
@try {
ret = OFSocketAddressParseIPv6(IP, port);
} @catch (OFInvalidFormatException *e) {
ret = OFSocketAddressParseIPv4(IP, port);
}
return ret;
}
OFSocketAddress
OFSocketAddressMakeUNIX(OFString *path)
{
void *pool = objc_autoreleasePoolPush();
OFStringEncoding encoding = [OFLocale encoding];
size_t length = [path cStringLengthWithEncoding: encoding];
OFSocketAddress ret;
if (length > sizeof(ret.sockaddr.un.sun_path))
@throw [OFOutOfRangeException exception];
memset(&ret, '\0', sizeof(ret));
ret.family = OFSocketAddressFamilyUNIX;
ret.length = (socklen_t)
(offsetof(struct sockaddr_un, sun_path) + length);
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
ret.sockaddr.un.sun_len = (uint8_t)length;
#endif
#ifdef AF_UNIX
ret.sockaddr.un.sun_family = AF_UNIX;
#else
ret.sockaddr.un.sun_family = AF_UNSPEC;
#endif
memcpy(ret.sockaddr.un.sun_path,
[path cStringWithEncoding: encoding], length);
objc_autoreleasePoolPop(pool);
return ret;
}
OFSocketAddress
OFSocketAddressMakeIPX(uint32_t network, const unsigned char node[IPX_NODE_LEN],
uint16_t port)
{
OFSocketAddress ret;
memset(&ret, '\0', sizeof(ret));
ret.family = OFSocketAddressFamilyIPX;
ret.length = sizeof(ret.sockaddr.ipx);
#ifdef AF_IPX
ret.sockaddr.ipx.sipx_family = AF_IPX;
#else
ret.sockaddr.ipx.sipx_family = AF_UNSPEC;
#endif
network = OFToBigEndian32(network);
memcpy(&ret.sockaddr.ipx.sipx_network, &network,
sizeof(ret.sockaddr.ipx.sipx_network));
memcpy(ret.sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
ret.sockaddr.ipx.sipx_port = OFToBigEndian16(port);
return ret;
}
OFSocketAddress
OFSocketAddressMakeAppleTalk(uint16_t network, uint8_t node, uint8_t port)
{
OFSocketAddress ret;
memset(&ret, '\0', sizeof(ret));
ret.family = OFSocketAddressFamilyAppleTalk;
ret.length = sizeof(ret.sockaddr.at);
#ifdef AF_APPLETALK
ret.sockaddr.at.sat_family = AF_APPLETALK;
#else
ret.sockaddr.at.sat_family = AF_UNSPEC;
#endif
#ifdef OF_WINDOWS
ret.sockaddr.at.sat_net = network;
#else
ret.sockaddr.at.sat_net = OFToBigEndian16(network);
#endif
ret.sockaddr.at.sat_node = node;
ret.sockaddr.at.sat_port = port;
return ret;
}
bool
OFSocketAddressEqual(const OFSocketAddress *address1,
const OFSocketAddress *address2)
{
const struct sockaddr_in *addrIn1, *addrIn2;
const struct sockaddr_in6 *addrIn6_1, *addrIn6_2;
const struct sockaddr_ipx *addrIPX1, *addrIPX2;
const struct sockaddr_at *addrAT1, *addrAT2;
void *pool;
OFString *path1, *path2;
bool ret;
if (address1->family != address2->family)
return false;
switch (address1->family) {
case OFSocketAddressFamilyIPv4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
if (address1->length < 8 || address2->length < 8)
@throw [OFInvalidArgumentException exception];
#else
if (address1->length < (socklen_t)sizeof(struct sockaddr_in) ||
address2->length < (socklen_t)sizeof(struct sockaddr_in))
@throw [OFInvalidArgumentException exception];
#endif
addrIn1 = &address1->sockaddr.in;
addrIn2 = &address2->sockaddr.in;
if (addrIn1->sin_port != addrIn2->sin_port)
return false;
if (addrIn1->sin_addr.s_addr != addrIn2->sin_addr.s_addr)
return false;
return true;
case OFSocketAddressFamilyIPv6:
if (address1->length < (socklen_t)sizeof(struct sockaddr_in6) ||
address2->length < (socklen_t)sizeof(struct sockaddr_in6))
@throw [OFInvalidArgumentException exception];
addrIn6_1 = &address1->sockaddr.in6;
addrIn6_2 = &address2->sockaddr.in6;
if (addrIn6_1->sin6_port != addrIn6_2->sin6_port)
return false;
if (memcmp(addrIn6_1->sin6_addr.s6_addr,
addrIn6_2->sin6_addr.s6_addr,
sizeof(addrIn6_1->sin6_addr.s6_addr)) != 0)
return false;
return true;
case OFSocketAddressFamilyUNIX:
pool = objc_autoreleasePoolPush();
path1 = OFSocketAddressUNIXPath(address1);
path2 = OFSocketAddressUNIXPath(address2);
if (path1 == nil || path2 == nil) {
objc_autoreleasePoolPop(pool);
return false;
}
ret = [path1 isEqual: path2];
objc_autoreleasePoolPop(pool);
return ret;
case OFSocketAddressFamilyIPX:
if (address1->length < (socklen_t)sizeof(struct sockaddr_ipx) ||
address2->length < (socklen_t)sizeof(struct sockaddr_ipx))
@throw [OFInvalidArgumentException exception];
addrIPX1 = &address1->sockaddr.ipx;
addrIPX2 = &address2->sockaddr.ipx;
if (addrIPX1->sipx_port != addrIPX2->sipx_port)
return false;
if (memcmp(&addrIPX1->sipx_network, &addrIPX2->sipx_network,
4) != 0)
return false;
if (memcmp(addrIPX1->sipx_node, addrIPX2->sipx_node,
IPX_NODE_LEN) != 0)
return false;
return true;
case OFSocketAddressFamilyAppleTalk:
if (address1->length < (socklen_t)sizeof(struct sockaddr_at) ||
address2->length < (socklen_t)sizeof(struct sockaddr_at))
@throw [OFInvalidArgumentException exception];
addrAT1 = &address1->sockaddr.at;
addrAT2 = &address2->sockaddr.at;
if (addrAT1->sat_net != addrAT2->sat_net)
return false;
if (addrAT1->sat_node != addrAT2->sat_node)
return false;
if (addrAT1->sat_port != addrAT2->sat_port)
return false;
return true;
default:
@throw [OFInvalidArgumentException exception];
}
}
unsigned long
OFSocketAddressHash(const OFSocketAddress *address)
{
unsigned long hash;
OFHashInit(&hash);
OFHashAddByte(&hash, address->family);
switch (address->family) {
case OFSocketAddressFamilyIPv4:
#if defined(OF_WII) || defined(OF_NINTENDO_3DS)
if (address->length < 8)
@throw [OFInvalidArgumentException exception];
#else
if (address->length < (socklen_t)sizeof(struct sockaddr_in))
@throw [OFInvalidArgumentException exception];
#endif
OFHashAddByte(&hash, address->sockaddr.in.sin_port >> 8);
OFHashAddByte(&hash, address->sockaddr.in.sin_port);
OFHashAddByte(&hash,
address->sockaddr.in.sin_addr.s_addr >> 24);
OFHashAddByte(&hash,
address->sockaddr.in.sin_addr.s_addr >> 16);
OFHashAddByte(&hash, address->sockaddr.in.sin_addr.s_addr >> 8);
OFHashAddByte(&hash, address->sockaddr.in.sin_addr.s_addr);
break;
case OFSocketAddressFamilyIPv6:
if (address->length < (socklen_t)sizeof(struct sockaddr_in6))
@throw [OFInvalidArgumentException exception];
OFHashAddByte(&hash, address->sockaddr.in6.sin6_port >> 8);
OFHashAddByte(&hash, address->sockaddr.in6.sin6_port);
for (size_t i = 0;
i < sizeof(address->sockaddr.in6.sin6_addr.s6_addr); i++)
OFHashAddByte(&hash,
address->sockaddr.in6.sin6_addr.s6_addr[i]);
break;
case OFSocketAddressFamilyUNIX:;
void *pool = objc_autoreleasePoolPush();
OFString *path = OFSocketAddressUNIXPath(address);
hash = path.hash;
objc_autoreleasePoolPop(pool);
return hash;
case OFSocketAddressFamilyIPX:;
unsigned char network[
sizeof(address->sockaddr.ipx.sipx_network)];
if (address->length < (socklen_t)sizeof(struct sockaddr_ipx))
@throw [OFInvalidArgumentException exception];
OFHashAddByte(&hash, address->sockaddr.ipx.sipx_port >> 8);
OFHashAddByte(&hash, address->sockaddr.ipx.sipx_port);
memcpy(network, &address->sockaddr.ipx.sipx_network,
sizeof(network));
for (size_t i = 0; i < sizeof(network); i++)
OFHashAddByte(&hash, network[i]);
for (size_t i = 0; i < IPX_NODE_LEN; i++)
OFHashAddByte(&hash,
address->sockaddr.ipx.sipx_node[i]);
break;
case OFSocketAddressFamilyAppleTalk:
if (address->length < (socklen_t)sizeof(struct sockaddr_at))
@throw [OFInvalidArgumentException exception];
OFHashAddByte(&hash, address->sockaddr.at.sat_net >> 8);
OFHashAddByte(&hash, address->sockaddr.at.sat_net);
OFHashAddByte(&hash, address->sockaddr.at.sat_port);
break;
default:
@throw [OFInvalidArgumentException exception];
}
OFHashFinalize(&hash);
return hash;
}
static OFString *
IPv4String(const OFSocketAddress *address)
{
const struct sockaddr_in *addrIn = &address->sockaddr.in;
uint32_t addr = OFFromBigEndian32(addrIn->sin_addr.s_addr);
OFString *string;
string = [OFString stringWithFormat: @"%u.%u.%u.%u",
(addr & 0xFF000000) >> 24, (addr & 0x00FF0000) >> 16,
(addr & 0x0000FF00) >> 8, addr & 0x000000FF];
return string;
}
static OFString *
IPv6String(const OFSocketAddress *address)
{
OFMutableString *string = [OFMutableString string];
const struct sockaddr_in6 *addrIn6 = &address->sockaddr.in6;
int_fast8_t zerosStart = -1, maxZerosStart = -1;
uint_fast8_t zerosCount = 0, maxZerosCount = 0;
bool first = true;
for (uint_fast8_t i = 0; i < 16; i += 2) {
if (addrIn6->sin6_addr.s6_addr[i] == 0 &&
addrIn6->sin6_addr.s6_addr[i + 1] == 0) {
if (zerosStart >= 0)
zerosCount++;
else {
zerosStart = i;
zerosCount = 1;
}
} else {
if (zerosCount > maxZerosCount) {
maxZerosStart = zerosStart;
maxZerosCount = zerosCount;
}
zerosStart = -1;
}
}
if (zerosCount > maxZerosCount) {
maxZerosStart = zerosStart;
maxZerosCount = zerosCount;
}
if (maxZerosCount >= 2) {
for (int_fast8_t i = 0; i < maxZerosStart; i += 2) {
[string appendFormat:
(first ? @"%x" : @":%x"),
(addrIn6->sin6_addr.s6_addr[(uint_fast8_t)i] << 8) |
addrIn6->sin6_addr.s6_addr[(uint_fast8_t)i + 1]];
first = false;
}
[string appendString: @"::"];
first = true;
for (int_fast8_t i = maxZerosStart + (maxZerosCount * 2);
i < 16; i += 2) {
[string appendFormat:
(first ? @"%x" : @":%x"),
(addrIn6->sin6_addr.s6_addr[(uint_fast8_t)i] << 8) |
addrIn6->sin6_addr.s6_addr[(uint_fast8_t)i + 1]];
first = false;
}
} else {
for (uint_fast8_t i = 0; i < 16; i += 2) {
[string appendFormat:
(first ? @"%x" : @":%x"),
(addrIn6->sin6_addr.s6_addr[i] << 8) |
addrIn6->sin6_addr.s6_addr[i + 1]];
first = false;
}
}
if (addrIn6->sin6_scope_id != 0) {
#if defined(HAVE_IF_INDEXTONAME) && !defined(OF_WINDOWS)
char interface[IF_NAMESIZE];
if (if_indextoname(addrIn6->sin6_scope_id, interface) != NULL)
[string appendFormat: @"%%%s", interface];
else
# endif
[string appendFormat: @"%%%u", addrIn6->sin6_scope_id];
}
[string makeImmutable];
return string;
}
static OFString *
IPXString(const OFSocketAddress *address)
{
const struct sockaddr_ipx *addrIPX = &address->sockaddr.ipx;
uint32_t network;
uint64_t node;
memcpy(&network, &addrIPX->sipx_network, sizeof(addrIPX->sipx_network));
node = ((uint64_t)addrIPX->sipx_node[0] << 40) |
((uint64_t)addrIPX->sipx_node[1] << 32) |
((uint64_t)addrIPX->sipx_node[2] << 24) |
((uint64_t)addrIPX->sipx_node[3] << 16) |
((uint64_t)addrIPX->sipx_node[4] << 8) |
(uint64_t)addrIPX->sipx_node[5];
return [OFString stringWithFormat: @"%" PRIX32 ".%" PRIX64,
OFFromBigEndian32(network), node];
}
static OFString *
appleTalkString(const OFSocketAddress *address)
{
const struct sockaddr_at *addrAT = &address->sockaddr.at;
return [OFString stringWithFormat: @"%" PRIu8 ".%" PRIu8,
OFFromBigEndian16(addrAT->sat_net), addrAT->sat_node];
}
OFString *
OFSocketAddressString(const OFSocketAddress *address)
{
switch (address->family) {
case OFSocketAddressFamilyIPv4:
return IPv4String(address);
case OFSocketAddressFamilyIPv6:
return IPv6String(address);
case OFSocketAddressFamilyUNIX:
return OFSocketAddressUNIXPath(address);
case OFSocketAddressFamilyIPX:
return IPXString(address);
case OFSocketAddressFamilyAppleTalk:
return appleTalkString(address);
default:
@throw [OFInvalidArgumentException exception];
}
}
OFString *
OFSocketAddressDescription(const OFSocketAddress *address)
{
switch (address->family) {
case OFSocketAddressFamilyIPv4:
return [OFString
stringWithFormat: @"%@:%" PRIu16,
IPv4String(address),
OFSocketAddressIPPort(address)];
case OFSocketAddressFamilyIPv6:
return [OFString
stringWithFormat: @"[%@]:%" PRIu16,
IPv6String(address),
OFSocketAddressIPPort(address)];
case OFSocketAddressFamilyIPX:
return [OFString
stringWithFormat: @"%@.%" PRIX16,
IPXString(address),
OFSocketAddressIPXPort(address)];
case OFSocketAddressFamilyAppleTalk:
return [OFString
stringWithFormat: @"%@." PRIu8,
appleTalkString(address),
OFSocketAddressAppleTalkPort(address)];
default:
return OFSocketAddressString(address);
}
}
void
OFSocketAddressSetIPPort(OFSocketAddress *address, uint16_t port)
{
switch (address->family) {
case OFSocketAddressFamilyIPv4:
address->sockaddr.in.sin_port = OFToBigEndian16(port);
break;
case OFSocketAddressFamilyIPv6:
address->sockaddr.in6.sin6_port = OFToBigEndian16(port);
break;
default:
@throw [OFInvalidArgumentException exception];
}
}
uint16_t
OFSocketAddressIPPort(const OFSocketAddress *address)
{
switch (address->family) {
case OFSocketAddressFamilyIPv4:
return OFFromBigEndian16(address->sockaddr.in.sin_port);
case OFSocketAddressFamilyIPv6:
return OFFromBigEndian16(address->sockaddr.in6.sin6_port);
default:
@throw [OFInvalidArgumentException exception];
}
}
OFString *
OFSocketAddressUNIXPath(const OFSocketAddress *_Nonnull address)
{
socklen_t length;
if (address->family != OFSocketAddressFamilyUNIX)
@throw [OFInvalidArgumentException exception];
length = address->length - offsetof(struct sockaddr_un, sun_path);
for (socklen_t i = 0; i < length; i++)
if (address->sockaddr.un.sun_path[i] == 0)
length = i;
return [OFString stringWithCString: address->sockaddr.un.sun_path
encoding: [OFLocale encoding]
length: length];
}
void
OFSocketAddressSetIPXNetwork(OFSocketAddress *address, uint32_t network)
{
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
network = OFToBigEndian32(network);
memcpy(&address->sockaddr.ipx.sipx_network, &network,
sizeof(address->sockaddr.ipx.sipx_network));
}
uint32_t
OFSocketAddressIPXNetwork(const OFSocketAddress *address)
{
uint32_t network;
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
memcpy(&network, &address->sockaddr.ipx.sipx_network, sizeof(network));
return OFFromBigEndian32(network);
}
void
OFSocketAddressSetIPXNode(OFSocketAddress *address,
const unsigned char node[IPX_NODE_LEN])
{
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
memcpy(address->sockaddr.ipx.sipx_node, node, IPX_NODE_LEN);
}
void
OFSocketAddressGetIPXNode(const OFSocketAddress *address,
unsigned char node[IPX_NODE_LEN])
{
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
memcpy(node, address->sockaddr.ipx.sipx_node, IPX_NODE_LEN);
}
void
OFSocketAddressSetIPXPort(OFSocketAddress *address, uint16_t port)
{
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
address->sockaddr.ipx.sipx_port = OFToBigEndian16(port);
}
uint16_t
OFSocketAddressIPXPort(const OFSocketAddress *address)
{
if (address->family != OFSocketAddressFamilyIPX)
@throw [OFInvalidArgumentException exception];
return OFFromBigEndian16(address->sockaddr.ipx.sipx_port);
}
void
OFSocketAddressSetAppleTalkNetwork(OFSocketAddress *address, uint16_t network)
{
if (address->family != OFSocketAddressFamilyAppleTalk)
@throw [OFInvalidArgumentException exception];
#ifdef OF_WINDOWS
address->sockaddr.at.sat_net = network;
#else
address->sockaddr.at.sat_net = OFToBigEndian16(network);
#endif
}
uint16_t
OFSocketAddressAppleTalkNetwork(const OFSocketAddress *address)
{
if (address->family != OFSocketAddressFamilyAppleTalk)
@throw [OFInvalidArgumentException exception];
#ifdef OF_WINDOWS
return address->sockaddr.at.sat_net;
#else
return OFFromBigEndian16(address->sockaddr.at.sat_net);
#endif
}
void
OFSocketAddressSetAppleTalkNode(OFSocketAddress *address, uint8_t node)
{
if (address->family != OFSocketAddressFamilyAppleTalk)
@throw [OFInvalidArgumentException exception];
address->sockaddr.at.sat_node = node;
}
uint8_t
OFSocketAddressAppleTalkNode(const OFSocketAddress *address)
{
if (address->family != OFSocketAddressFamilyAppleTalk)
@throw [OFInvalidArgumentException exception];
return address->sockaddr.at.sat_node;
}
void
OFSocketAddressSetAppleTalkPort(OFSocketAddress *address, uint8_t port)
{
if (address->family != OFSocketAddressFamilyAppleTalk)
@throw [OFInvalidArgumentException exception];
address->sockaddr.at.sat_port = port;
}
uint8_t
OFSocketAddressAppleTalkPort(const OFSocketAddress *address)
{
if (address->family != OFSocketAddressFamilyAppleTalk)
@throw [OFInvalidArgumentException exception];
return address->sockaddr.at.sat_port;
}