@@ -1,7 +1,7 @@ /* - * Copyright (c) 2008-2022 Jonathan Schleifer + * Copyright (c) 2008-2024 Jonathan Schleifer * * All rights reserved. * * This file is part of ObjFW. It may be distributed under the terms of the * Q Public License 1.0, which can be found in the file LICENSE.QPL included in @@ -51,11 +51,13 @@ #ifdef HAVE_NET_IF_H # include #endif #ifdef OF_AMIGAOS +# define Class IntuitionClass # include +# undef Class #endif #ifdef OF_NINTENDO_3DS # include <3ds/types.h> # include <3ds/services/soc.h> @@ -428,10 +430,38 @@ 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(); @@ -465,10 +495,13 @@ } 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]; @@ -555,10 +588,13 @@ 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 @@ -904,21 +940,84 @@ [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: - @throw [OFInvalidArgumentException exception]; + return OFSocketAddressString(address); } } void OFSocketAddressSetIPPort(OFSocketAddress *address, uint16_t port) @@ -960,13 +1059,10 @@ for (socklen_t i = 0; i < length; i++) if (address->sockaddr.un.sun_path[i] == 0) length = i; - if (length <= 0) - return nil; - return [OFString stringWithCString: address->sockaddr.un.sun_path encoding: [OFLocale encoding] length: length]; }