Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1346,10 +1346,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; @@ -1467,10 +1470,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/objfw-defs.h.in ================================================================== --- src/objfw-defs.h.in +++ src/objfw-defs.h.in @@ -33,15 +33,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 Index: src/socket.h ================================================================== --- src/socket.h +++ src/socket.h @@ -36,10 +36,13 @@ # include #endif #ifdef OF_HAVE_NETIPX_IPX_H # include #endif +#ifdef OF_HAVE_SYS_UN_H +# include +#endif #include "platform.h" #ifdef OF_WINDOWS # include @@ -92,10 +95,12 @@ OF_SOCKET_ADDRESS_FAMILY_IPV4, /** IPv6 */ OF_SOCKET_ADDRESS_FAMILY_IPV6, /** IPX */ OF_SOCKET_ADDRESS_FAMILY_IPX, + /** UNIX */ + OF_SOCKET_ADDRESS_FAMILY_UNIX, /** Any address family */ OF_SOCKET_ADDRESS_FAMILY_ANY = 255 } of_socket_address_family_t; #ifndef OF_HAVE_IPV6 @@ -126,10 +131,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 of_socket_address_t socket.h ObjFW/socket.h * * @brief A struct which represents a host / port pair for a socket. */ @@ -145,10 +157,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; }; typedef struct of_socket_address_t of_socket_address_t; @@ -184,20 +197,29 @@ */ extern of_socket_address_t of_socket_address_parse_ipv6( 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 of_socket_address_t of_socket_address_ipx( 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 of_socket_address_t of_socket_address_unix(OFString *path); + /** * @brief Compares two of_socket_address_t for equality. * * @param address1 The address to compare with the second address * @param address2 The second address @@ -283,10 +305,19 @@ */ extern void of_socket_address_get_ipx_node( const of_socket_address_t *_Nonnull address, unsigned char node[_Nonnull IPX_NODE_LEN]); +/** + * @brief Gets the UNIX socket path of the specified of_socket_address_t. + * + * @param address The address on which to get the UNIX socket path + * @return The UNIX socket path + */ +extern OFString *_Nullable of_socket_get_unix_path( + const of_socket_address_t *_Nonnull address); + extern bool of_socket_init(void); #if defined(OF_HAVE_THREADS) && defined(OF_AMIGAOS) && !defined(OF_MORPHOS) extern void of_socket_deinit(void); #endif extern int of_socket_errno(void); Index: src/socket.m ================================================================== --- src/socket.m +++ src/socket.m @@ -37,10 +37,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" #import "socket.h" #import "socket_helpers.h" #ifdef OF_HAVE_THREADS @@ -536,18 +537,50 @@ sizeof(ret.sockaddr.ipx.sipx_network)); ret.sockaddr.ipx.sipx_port = OF_BSWAP16_IF_LE(port); return ret; } + +of_socket_address_t +of_socket_unix(OFString *path) +{ + void *pool = objc_autoreleasePoolPush(); + of_string_encoding_t encoding = [OFLocale encoding]; + size_t length = [path cStringLengthWithEncoding: encoding]; + of_socket_address_t ret; + + if (length > sizeof(ret.sockaddr.un.sun_path)) + @throw [OFOutOfRangeException exception]; + + memset(&ret, '\0', sizeof(ret)); + ret.family = OF_SOCKET_ADDRESS_FAMILY_UNIX; + 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 of_socket_address_equal(const of_socket_address_t *address1, const of_socket_address_t *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) { @@ -567,11 +600,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 OF_SOCKET_ADDRESS_FAMILY_IPV6: if (address1->length < (socklen_t)sizeof(struct sockaddr_in6) || address2->length < (socklen_t)sizeof(struct sockaddr_in6)) @throw [OFInvalidArgumentException exception]; @@ -583,11 +616,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 OF_SOCKET_ADDRESS_FAMILY_IPX: if (address1->length < (socklen_t)sizeof(struct sockaddr_ipx) || address2->length < (socklen_t)sizeof(struct sockaddr_ipx)) @throw [OFInvalidArgumentException exception]; @@ -601,22 +634,50 @@ return false; if (memcmp(addrIPX1->sipx_node, addrIPX2->sipx_node, IPX_NODE_LEN) != 0) return false; - break; + return true; + case OF_SOCKET_ADDRESS_FAMILY_UNIX: + /* + * 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 = of_socket_get_unix_path(address1); + path2 = of_socket_get_unix_path(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 of_socket_address_hash(const of_socket_address_t *address) { - uint32_t hash; + unsigned long hash; OF_HASH_INIT(hash); OF_HASH_ADD(hash, address->family); switch (address->family) { @@ -667,10 +728,32 @@ OF_HASH_ADD(hash, network[i]); for (size_t i = 0; i < IPX_NODE_LEN; i++) OF_HASH_ADD(hash, address->sockaddr.ipx.sipx_node[i]); + break; + case OF_SOCKET_ADDRESS_FAMILY_UNIX:; + /* + * 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 = of_socket_get_unix_path(address); + + if (path != nil) { + hash = [path hash]; + objc_autoreleasePoolPop(pool); + return hash; + } + + objc_autoreleasePoolPop(pool); + + for (socklen_t i = 0; i < address->length; i++) + OF_HASH_ADD(hash, + ((const char *)&address->sockaddr.un)[i]); + break; default: @throw [OFInvalidArgumentException exception]; } @@ -854,5 +937,26 @@ if (address->family != OF_SOCKET_ADDRESS_FAMILY_IPX) @throw [OFInvalidArgumentException exception]; memcpy(node, address->sockaddr.ipx.sipx_node, IPX_NODE_LEN); } + +OFString * +of_socket_get_unix_path(const of_socket_address_t *_Nonnull address) +{ + socklen_t maxLength = (socklen_t)sizeof(address->sockaddr.un); + size_t length; + + if (address->family != OF_SOCKET_ADDRESS_FAMILY_UNIX || + 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]; +}