Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1331,10 +1331,13 @@ AC_CHECK_HEADERS([arpa/inet.h netdb.h]) AC_CHECK_HEADER(netipx/ipx.h, [ AC_DEFINE(OF_HAVE_NETIPX_IPX_H, 1, [Whether we have netipx/ipx.h]) ]) + AC_CHECK_HEADERS(sys/un.h, [ + AC_DEFINE(OF_HAVE_SYS_UN_H, 1, [Whether we have sys/un.h]) + ]) AC_CHECK_MEMBER([struct sockaddr_in6.sin6_addr], [ AC_EGREP_CPP(egrep_cpp_yes, [ #ifdef _WIN32 typedef int BOOL; @@ -1455,10 +1458,19 @@ ], [ AC_DEFINE(OF_HAVE_IPX, 1, [Whether we have IPX/SPX]) AC_SUBST(USE_SRCS_IPX, '${SRCS_IPX}') ]) ]) + + AC_CHECK_MEMBER(struct sockaddr_un.sun_path, [ + AC_DEFINE(OF_HAVE_UNIX_SOCKETS, 1, + [Whether we have UNIX sockets]) + ], [], [ + #ifdef OF_HAVE_SYS_UN_H + # include + #endif + ]) AC_CHECK_FUNCS(paccept accept4, break) AC_CHECK_FUNCS(kqueue1 kqueue, [ AC_DEFINE(HAVE_KQUEUE, 1, [Whether we have kqueue]) Index: src/OFSocket.h ================================================================== --- src/OFSocket.h +++ src/OFSocket.h @@ -36,10 +36,13 @@ # include #endif #ifdef OF_HAVE_NETIPX_IPX_H # include #endif +#ifdef OF_HAVE_SYS_UN_H +# include +#endif #ifdef OF_WINDOWS # include # include # ifdef OF_HAVE_IPX @@ -66,10 +69,14 @@ static const OFSocketHandle OFInvalidSocketHandle = -1; #else typedef SOCKET OFSocketHandle; static const OFSocketHandle OFInvalidSocketHandle = INVALID_SOCKET; #endif + +#ifdef OF_WINDOWS +typedef short sa_family_t; +#endif #ifdef OF_WII typedef u8 sa_family_t; #endif @@ -89,10 +96,12 @@ OFSocketAddressFamilyIPv4, /** IPv6 */ OFSocketAddressFamilyIPv6, /** IPX */ OFSocketAddressFamilyIPX, + /** UNIX */ + OFSocketAddressFamilyUNIX, /** Any address family */ OFSocketAddressFamilyAny = 255 } OFSocketAddressFamily; #ifndef OF_HAVE_IPV6 @@ -123,10 +132,17 @@ # define sipx_network sa_netnum # define sipx_node sa_nodenum # define sipx_port sa_socket #endif +#ifndef OF_HAVE_UNIX_SOCKETS +struct sockaddr_un { + sa_family_t sun_family; + char sun_path[108]; +}; +#endif + /** * @struct OFSocketAddress OFSocket.h ObjFW/OFSocket.h * * @brief A struct which represents a host / port pair for a socket. */ @@ -142,10 +158,11 @@ union { struct sockaddr sockaddr; struct sockaddr_in in; struct sockaddr_in6 in6; struct sockaddr_ipx ipx; + struct sockaddr_un un; } sockaddr; socklen_t length; } OFSocketAddress; #ifdef __cplusplus @@ -178,20 +195,29 @@ * @return The parsed IPv6 and port as an OFSocketAddress */ extern OFSocketAddress OFSocketAddressParseIPv6(OFString *IP, uint16_t port); /** - * @brief Creates an IPX address for the specified network, node and port. + * @brief Creates an IPX address for the specified node, network and port. * * @param node The node in the IPX network * @param network The IPX network * @param port The IPX port (sometimes called socket number) on the node + * @return An IPX socket address with the specified node, network and port. */ extern OFSocketAddress OFSocketAddressMakeIPX( const unsigned char node[_Nonnull IPX_NODE_LEN], uint32_t network, uint16_t port); +/** + * @brief Creates a UNIX socket address from the specified path. + * + * @param path The path of the UNIX socket + * @return A UNIX socket address with the specified path + */ +extern OFSocketAddress OFSocketAddressMakeUNIX(OFString *path); + /** * @brief Compares two OFSocketAddress for equality. * * @param address1 The address to compare with the second address * @param address2 The second address @@ -271,10 +297,19 @@ * @param node A byte array to store the IPX node of the address */ extern void OFSocketAddressIPXNode(const OFSocketAddress *_Nonnull address, unsigned char node[_Nonnull IPX_NODE_LEN]); +/** + * @brief Gets the UNIX socket path of the specified @ref OFSocketAddress. + * + * @param address The address on which to get the UNIX socket path + * @return The UNIX socket path + */ +extern OFString *_Nullable OFSocketAddressUNIXPath( + const OFSocketAddress *_Nonnull address); + extern bool OFSocketInit(void); #if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS) extern void OFSocketDeinit(void); #endif extern int OFSocketErrNo(void); Index: src/OFSocket.m ================================================================== --- src/OFSocket.m +++ src/OFSocket.m @@ -43,10 +43,11 @@ #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 OF_AMIGAOS # include #endif @@ -534,18 +535,50 @@ sizeof(ret.sockaddr.ipx.sipx_network)); ret.sockaddr.ipx.sipx_port = OFToBigEndian16(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)(sizeof(ret.sockaddr.un) - + (sizeof(ret.sockaddr.un.sun_path) - length)); + +#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; +} 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; + void *pool; + OFString *path1, *path2; + bool ret; if (address1->family != address2->family) return false; switch (address1->family) { @@ -565,11 +598,11 @@ if (addrIn1->sin_port != addrIn2->sin_port) return false; if (addrIn1->sin_addr.s_addr != addrIn2->sin_addr.s_addr) return false; - break; + return true; case OFSocketAddressFamilyIPv6: if (address1->length < (socklen_t)sizeof(struct sockaddr_in6) || address2->length < (socklen_t)sizeof(struct sockaddr_in6)) @throw [OFInvalidArgumentException exception]; @@ -581,11 +614,11 @@ if (memcmp(addrIn6_1->sin6_addr.s6_addr, addrIn6_2->sin6_addr.s6_addr, sizeof(addrIn6_1->sin6_addr.s6_addr)) != 0) return false; - break; + return true; case OFSocketAddressFamilyIPX: if (address1->length < (socklen_t)sizeof(struct sockaddr_ipx) || address2->length < (socklen_t)sizeof(struct sockaddr_ipx)) @throw [OFInvalidArgumentException exception]; @@ -599,16 +632,44 @@ return false; if (memcmp(addrIPX1->sipx_node, addrIPX2->sipx_node, IPX_NODE_LEN) != 0) return false; - break; + return true; + case OFSocketAddressFamilyUNIX: + /* + * This is a bit tricky. The only thing that is well-defined is + * the path. So compare the path, and if both don't have a + * path, compare bytes. + */ + + if (address1->length != address2->length) + return false; + + pool = objc_autoreleasePoolPush(); + + path1 = OFSocketAddressUNIXPath(address1); + path2 = OFSocketAddressUNIXPath(address2); + + if (path1 == nil && path2 == nil) { + objc_autoreleasePoolPop(pool); + + return (memcmp(&address1->sockaddr.un, + &address2->sockaddr.un, address1->length) == 0); + } + + if (path1 == nil || path2 == nil) + ret = false; + else + ret = [path1 isEqual: path2]; + + objc_autoreleasePoolPop(pool); + + return ret; default: @throw [OFInvalidArgumentException exception]; } - - return true; } unsigned long OFSocketAddressHash(const OFSocketAddress *address) { @@ -665,10 +726,32 @@ OFHashAdd(&hash, network[i]); for (size_t i = 0; i < IPX_NODE_LEN; i++) OFHashAdd(&hash, address->sockaddr.ipx.sipx_node[i]); + break; + case OFSocketAddressFamilyUNIX:; + /* + * This is a bit tricky. The only thing that is well-defined is + * the path. So hash the path if we have one, otherwise the + * bytes. + */ + void *pool = objc_autoreleasePoolPush(); + OFString *path = OFSocketAddressUNIXPath(address); + + if (path != nil) { + hash = path.hash; + objc_autoreleasePoolPop(pool); + return hash; + } + + objc_autoreleasePoolPop(pool); + + for (socklen_t i = 0; i < address->length; i++) + OFHashAdd(&hash, + ((const char *)&address->sockaddr.un)[i]); + break; default: @throw [OFInvalidArgumentException exception]; } @@ -845,5 +928,26 @@ if (address->family != OFSocketAddressFamilyIPX) @throw [OFInvalidArgumentException exception]; memcpy(node, address->sockaddr.ipx.sipx_node, IPX_NODE_LEN); } + +OFString * +OFSocketAddressUNIXPath(const OFSocketAddress *_Nonnull address) +{ + socklen_t maxLength = (socklen_t)sizeof(address->sockaddr.un); + size_t length; + + if (address->family != OFSocketAddressFamilyUNIX || + address->length > maxLength) + @throw [OFInvalidArgumentException exception]; + + length = sizeof(address->sockaddr.un.sun_path) - + (maxLength - address->length); + + if (length == 0) + return nil; + + return [OFString stringWithCString: address->sockaddr.un.sun_path + encoding: [OFLocale encoding] + length: length]; +} Index: src/objfw-defs.h.in ================================================================== --- src/objfw-defs.h.in +++ src/objfw-defs.h.in @@ -32,15 +32,17 @@ #undef OF_HAVE_STDNORETURN #undef OF_HAVE_SYMLINK #undef OF_HAVE_SYNC_BUILTINS #undef OF_HAVE_SYS_SOCKET_H #undef OF_HAVE_SYS_TYPES_H +#undef OF_HAVE_SYS_UN_H #undef OF_HAVE_THREADS #undef OF_HAVE_UNICODE_TABLES +#undef OF_HAVE_UNIX_SOCKETS #undef OF_HAVE__THREAD_LOCAL #undef OF_HAVE___THREAD #undef OF_NINTENDO_3DS #undef OF_NINTENDO_DS #undef OF_NO_SHARED #undef OF_OBJFW_RUNTIME #undef OF_UNIVERSAL #undef OF_WII