Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -1306,102 +1306,10 @@ "OFKernelEventObserver_select.m") ]) ;; esac - AC_MSG_CHECKING(for getaddrinfo) - AC_TRY_COMPILE([ - #include - #ifdef OF_HAVE_SYS_TYPES_H - # include - #endif - #ifdef HAVE_SYS_SOCKET_H - # include - #endif - #ifdef HAVE_NETDB_H - # include - #endif - #ifdef _WIN32 - typedef unsigned char BOOL; - # include - #endif - ], [ - struct addrinfo ai; - getaddrinfo(NULL, NULL, NULL, NULL); - ], [ - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_GETADDRINFO, 1, [Whether we have getaddrinfo()]) - - AS_IF([test x"$enable_threads" != x"no"], [ - AC_MSG_CHECKING(whether getaddrinfo is thread-safe) - - case "$host_os" in - darwin[[12345]].*) - have_threadsafe_getaddrinfo="no" - ;; - darwin*) - have_threadsafe_getaddrinfo="yes" - ;; - freebsd[[1234]].* | freebsd5.[[1234]]*) - have_threadsafe_getaddrinfo="no" - ;; - freebsd*) - have_threadsafe_getaddrinfo="yes" - ;; - netbsd[[123]].*) - have_threadsafe_getaddrinfo="no" - ;; - netbsd*) - have_threadsafe_getaddrinfo="yes" - ;; - solaris*) - have_threadsafe_getaddrinfo="yes" - ;; - *) - have_threadsafe_getaddrinfo="unknown" - ;; - esac - - AS_IF([test x"$have_threadsafe_getaddrinfo" = \ - x"unknown"], [ - AC_EGREP_CPP(egrep_cpp_yes, [ - #ifdef OF_HAVE_SYS_TYPES_H - # include - #endif - #ifdef HAVE_SYS_SOCKET_H - # include - #endif - #ifdef HAVE_NETDB_H - # include - #endif - #ifdef _WIN32 - # define _WIN32_WINNT 0x0501 - # include - # include - #endif - - #ifdef h_errno - egrep_cpp_yes - #end - ], [ - have_threadsafe_getaddrinfo="yes" - ], [ - have_threadsafe_getaddrinfo="no" - ]) - ]) - - AS_IF([test x"$have_threadsafe_getaddrinfo" = x"yes"], [ - AC_DEFINE(HAVE_THREADSAFE_GETADDRINFO, 1, - [Whether getaddrinfo is thread-safe]) - ]) - - AC_MSG_RESULT($have_threadsafe_getaddrinfo) - ]) - ], [ - AC_MSG_RESULT(no) - ]) - AS_IF([test x"$enable_threads" != x"no"], [ AC_SUBST(OFHTTPCLIENTTESTS_M, "OFHTTPClientTests.m") ]) AC_SUBST(OFDNS, "ofdns") Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -132,11 +132,10 @@ OFHTTPResponse.m \ OFHTTPServer.m \ OFStreamSocket.m \ OFTCPSocket.m \ OFUDPSocket.m \ - resolver.m \ socket.m SRCS_THREADS = OFCondition.m \ OFMutex.m \ OFRecursiveMutex.m \ OFThreadPool.m \ Index: src/OFDNSResolver.m ================================================================== --- src/OFDNSResolver.m +++ src/OFDNSResolver.m @@ -32,10 +32,11 @@ #import "OFNumber.h" #import "OFPair.h" #import "OFString.h" #import "OFTimer.h" #import "OFUDPSocket.h" +#import "OFUDPSocket+Private.h" #ifdef OF_WINDOWS # import "OFWindowsRegistryKey.h" #endif #import "OFInitializationFailedException.h" @@ -1782,24 +1783,28 @@ switch (query->_usedNameServer.family) { #ifdef OF_HAVE_IPV6 case OF_SOCKET_ADDRESS_FAMILY_IPV6: if (_IPv6Socket == nil) { + of_socket_address_t address = + of_socket_address_parse_ip(@"::", 0); + _IPv6Socket = [[OFUDPSocket alloc] init]; - [_IPv6Socket bindToHost: @"::" - port: 0]; + [_IPv6Socket of_bindToAddress: &address]; [_IPv6Socket setBlocking: false]; } sock = _IPv6Socket; break; #endif case OF_SOCKET_ADDRESS_FAMILY_IPV4: if (_IPv4Socket == nil) { + of_socket_address_t address = + of_socket_address_parse_ip(@"0.0.0.0", 0); + _IPv4Socket = [[OFUDPSocket alloc] init]; - [_IPv4Socket bindToHost: @"0.0.0.0" - port: 0]; + [_IPv4Socket of_bindToAddress: &address]; [_IPv4Socket setBlocking: false]; } sock = _IPv4Socket; break; @@ -2071,10 +2076,32 @@ selector: (SEL)selector context: (id)userContext { void *pool = objc_autoreleasePoolPush(); OFDNSResolver_AsyncResolveSocketAddressesContext *context; + + @try { + of_socket_address_t address = + of_socket_address_parse_ip(host, 0); + OFData *addresses; + void (*method)(id, SEL, OFDNSResolver *, OFString *, OFData *, + id, id); + + if (addressFamily != OF_SOCKET_ADDRESS_FAMILY_ANY && + address.family != addressFamily) + @throw [OFInvalidArgumentException exception]; + + addresses = [OFData dataWithItems: &address + itemSize: sizeof(address) + count: 1]; + + method = (void (*)(id, SEL, OFDNSResolver *, OFString *, + OFData *, id, id))[target methodForSelector: selector]; + method(target, selector, self, host, addresses, userContext, + nil); + } @catch (OFInvalidFormatException *e) { + } context = [[[OFDNSResolver_AsyncResolveSocketAddressesContext alloc] initWithHost: host target: target selector: selector Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -1139,10 +1139,11 @@ - (void)runMode: (of_run_loop_mode_t)mode beforeDate: (OFDate *)deadline { void *pool = objc_autoreleasePoolPush(); + of_run_loop_mode_t previousMode = _currentMode; OFRunLoop_State *state = [self of_stateForMode: mode create: false]; if (state == nil) return; @@ -1249,11 +1250,11 @@ #endif } objc_autoreleasePoolPop(pool); } @finally { - _currentMode = nil; + _currentMode = previousMode; } } - (void)stop { Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -55,11 +55,10 @@ #import "OFOutOfRangeException.h" #import "OFSetOptionFailedException.h" #import "socket.h" #import "socket_helpers.h" -#import "resolver.h" Class of_tls_socket_class = Nil; static of_run_loop_mode_t connectRunLoopMode = @"of_tcp_socket_connect_mode"; static OFString *defaultSOCKS5Host = nil; @@ -864,53 +863,83 @@ #endif - (uint16_t)bindToHost: (OFString *)host port: (uint16_t)port { - of_resolver_result_t **results; const int one = 1; -#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) + void *pool = objc_autoreleasePoolPush(); + OFData *socketAddresses; of_socket_address_t address; +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + int flags; #endif if (_socket != INVALID_SOCKET) @throw [OFAlreadyConnectedException exceptionWithSocket: self]; if (_SOCKS5Host != nil) @throw [OFNotImplementedException exceptionWithSelector: _cmd object: self]; - results = of_resolve_host(host, port, SOCK_STREAM); - @try { -#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - int flags; -#endif - - if ((_socket = socket(results[0]->family, - results[0]->type | SOCK_CLOEXEC, - results[0]->protocol)) == INVALID_SOCKET) - @throw [OFBindFailedException - exceptionWithHost: host - port: port - socket: self - errNo: of_socket_errno()]; - - _blocking = true; - -#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) - fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); -#endif - - setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, - (const char *)&one, (socklen_t)sizeof(one)); - -#if defined(OF_WII) || defined(OF_NINTENDO_3DS) - if (port != 0) { -#endif - if (bind(_socket, results[0]->address, - results[0]->addressLength) != 0) { + socketAddresses = [[OFThread DNSResolver] + resolveSocketAddressesForHost: host + addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY]; + + address = *(of_socket_address_t *)[socketAddresses itemAtIndex: 0]; + of_socket_address_set_port(&address, port); + + if ((_socket = socket(address.sockaddr.sockaddr.sa_family, + SOCK_STREAM | SOCK_CLOEXEC, 0)) == INVALID_SOCKET) + @throw [OFBindFailedException + exceptionWithHost: host + port: port + socket: self + errNo: of_socket_errno()]; + + _blocking = true; + +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) + fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); +#endif + + setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, + (const char *)&one, (socklen_t)sizeof(one)); + +#if defined(OF_WII) || defined(OF_NINTENDO_3DS) + if (port != 0) { +#endif + if (bind(_socket, &address.sockaddr.sockaddr, + address.length) != 0) { + int errNo = of_socket_errno(); + + closesocket(_socket); + _socket = INVALID_SOCKET; + + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self + errNo: errNo]; + } +#if defined(OF_WII) || defined(OF_NINTENDO_3DS) + } else { + for (;;) { + uint16_t rnd = 0; + int ret; + + while (rnd < 1024) + rnd = (uint16_t)rand(); + + of_socket_address_set_port(&address, rnd); + + if ((ret = bind(_socket, &address.sockaddr.sockaddr, + address.length)) == 0) { + port = rnd; + break; + } + + if (of_socket_errno() != EADDRINUSE) { int errNo = of_socket_errno(); closesocket(_socket); _socket = INVALID_SOCKET; @@ -918,68 +947,22 @@ exceptionWithHost: host port: port socket: self errNo: errNo]; } -#if defined(OF_WII) || defined(OF_NINTENDO_3DS) - } else { - for (;;) { - uint16_t rnd = 0; - int ret; - - while (rnd < 1024) - rnd = (uint16_t)rand(); - - switch (results[0]->family) { - case AF_INET: - ((struct sockaddr_in *) - results[0]->address)->sin_port = - OF_BSWAP16_IF_LE(rnd); - break; -# ifdef OF_HAVE_IPV6 - case AF_INET6: - ((struct sockaddr_in6 *) - results[0]->address)->sin6_port = - OF_BSWAP16_IF_LE(rnd); - break; -# endif - default: - @throw [OFInvalidArgumentException - exception]; - } - - ret = bind(_socket, results[0]->address, - results[0]->addressLength); - - if (ret == 0) { - port = rnd; - break; - } - - if (of_socket_errno() != EADDRINUSE) { - int errNo = of_socket_errno(); - - closesocket(_socket); - _socket = INVALID_SOCKET; - - @throw [OFBindFailedException - exceptionWithHost: host - port: port - socket: self - errNo: errNo]; - } - } - } -#endif - } @finally { - of_resolver_free(results); - } + } + } +#endif + + objc_autoreleasePoolPop(pool); if (port > 0) return port; #if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) + memset(&address, 0, sizeof(address)); + address.length = (socklen_t)sizeof(address.sockaddr); if (of_getsockname(_socket, &address.sockaddr.sockaddr, &address.length) != 0) { int errNo = of_socket_errno(); @@ -993,21 +976,30 @@ } if (address.sockaddr.sockaddr.sa_family == AF_INET) return OF_BSWAP16_IF_LE(address.sockaddr.in.sin_port); # ifdef OF_HAVE_IPV6 - if (address.sockaddr.sockaddr.sa_family == AF_INET6) + else if (address.sockaddr.sockaddr.sa_family == AF_INET6) return OF_BSWAP16_IF_LE(address.sockaddr.in6.sin6_port); # endif + else { + closesocket(_socket); + _socket = INVALID_SOCKET; + + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self + errNo: EAFNOSUPPORT]; + } #endif closesocket(_socket); _socket = INVALID_SOCKET; @throw [OFBindFailedException exceptionWithHost: host port: port socket: self - errNo: EAFNOSUPPORT]; + errNo: EADDRNOTAVAIL]; } - (void)listen { [self listenWithBacklog: SOMAXCONN]; ADDED src/OFUDPSocket+Private.h Index: src/OFUDPSocket+Private.h ================================================================== --- src/OFUDPSocket+Private.h +++ src/OFUDPSocket+Private.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018 + * 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 + * the packaging of this file. + * + * Alternatively, it may be distributed under the terms of the GNU General + * Public License, either version 2 or 3, which can be found in the file + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this + * file. + */ + +#import "OFUDPSocket.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFUDPSocket () +- (uint16_t)of_bindToAddress: (of_socket_address_t *)address; +@end + +OF_ASSUME_NONNULL_END Index: src/OFUDPSocket.h ================================================================== --- src/OFUDPSocket.h +++ src/OFUDPSocket.h @@ -26,23 +26,10 @@ /*! @file */ @class OFUDPSocket; #ifdef OF_HAVE_BLOCKS -/*! - * @brief A block which is called when the host / port pair for the UDP socket - * has been resolved. - * - * @param host The host that has been resolved - * @param port The port of the host / port pair - * @param address The address of the resolved host / port pair - * @param exception An exception which occurred while resolving or `nil` on - * success - */ -typedef void (^of_udp_socket_async_resolve_block_t)(OFString *host, - uint16_t port, of_socket_address_t address, id _Nullable exception); - /*! * @brief A block which is called when a packet has been received. * * @param socket The UDP socket which received a packet * @param buffer The buffer the packet has been written to @@ -82,16 +69,16 @@ /*! * @class OFUDPSocket OFUDPSocket.h ObjFW/OFUDPSocket.h * * @brief A class which provides methods to create and use UDP sockets. * - * Addresses are of type @ref of_socket_address_t. You can use - * @ref resolveAddressForHost:port:address: to create an address for a host / - * port pair and @ref of_socket_address_ip_string to get the IP string / port - * pair for an address. If you want to compare two addresses, you can use - * @ref of_socket_address_equal and you can use @ref of_socket_address_hash to - * get a hash to use in e.g. @ref OFMapTable. + * Addresses are of type @ref of_socket_address_t. You can use the current + * thread's @ref OFDNSResolver to create an address for a host / port pair and + * @ref of_socket_address_ip_string to get the IP string / port pair for an + * address. If you want to compare two addresses, you can use @ref + * of_socket_address_equal and you can use @ref of_socket_address_hash to get a + * hash to use in e.g. @ref OFMapTable. * * @warning Even though the OFCopying protocol is implemented, it does *not* * return an independent copy of the socket, but instead retains it. * This is so that the socket can be used as a key for a dictionary, * so context can be associated with a socket. Using a socket in more @@ -120,58 +107,10 @@ * * @return A new, autoreleased OFUDPSocket */ + (instancetype)socket; -/*! - * @brief Resolves the specified host and creates a an address for the host / - * port pair. - * - * @param host The host to resolve - * @param port The port for the resulting address - * @param address A pointer to the address that should be filled with the - * host / port pair - */ -+ (void)resolveAddressForHost: (OFString *)host - port: (uint16_t)port - address: (of_socket_address_t *)address; - -#ifdef OF_HAVE_THREADS -/*! - * @brief Asynchronously resolves the specified host and creates an address for - * the host / port pair. - * - * @param host The host to resolve - * @param port The port for the resulting address - * @param target The target on which to call the selector once the host has been - * resolved - * @param selector The selector to call on the target. The signature must be - * `void (OFString *host, uint16_t port, - * of_socket_address_t address, id context, id exception)`. - * @param context A context object to pass along to the target - */ -+ (void)asyncResolveAddressForHost: (OFString *)host - port: (uint16_t)port - target: (id)target - selector: (SEL)selector - context: (nullable id)context; - -# ifdef OF_HAVE_BLOCKS -/*! - * @brief Asynchronously resolves the specified host and creates an address for - * the host / port pair. - * - * @param host The host to resolve - * @param port The port for the resulting address - * @param block The block to execute once the host has been resolved - */ -+ (void)asyncResolveAddressForHost: (OFString *)host - port: (uint16_t)port - block: (of_udp_socket_async_resolve_block_t)block; -# endif -#endif - /*! * @brief Binds the socket to the specified host and port. * * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for * IPv6 to bind to all. Index: src/OFUDPSocket.m ================================================================== --- src/OFUDPSocket.m +++ src/OFUDPSocket.m @@ -24,165 +24,30 @@ #ifdef HAVE_FCNTL_H # include #endif #import "OFUDPSocket.h" -#ifdef OF_HAVE_THREADS -# import "OFThread.h" -#endif +#import "OFUDPSocket+Private.h" +#import "OFDNSResolver.h" +#import "OFData.h" +#import "OFRunLoop+Private.h" #import "OFRunLoop.h" -#import "OFRunLoop+Private.h" +#import "OFThread.h" +#import "OFAlreadyConnectedException.h" #import "OFBindFailedException.h" #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" +#import "OFInvalidFormatException.h" #import "OFNotOpenException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFSetOptionFailedException.h" #import "OFWriteFailedException.h" #import "socket.h" #import "socket_helpers.h" -#import "resolver.h" - -#ifdef OF_HAVE_THREADS -@interface OFUDPSocket_ResolveThread: OFThread -{ - OFThread *_sourceThread; - OFString *_host; - uint16_t _port; - id _target; - SEL _selector; - id _context; -# ifdef OF_HAVE_BLOCKS - of_udp_socket_async_resolve_block_t _block; -# endif - of_socket_address_t _address; - id _exception; -} - -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - host: (OFString *)host - port: (uint16_t)port - target: (id)target - selector: (SEL)selector - context: (id)context; -# ifdef OF_HAVE_BLOCKS -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - host: (OFString *)host - port: (uint16_t)port - block: (of_udp_socket_async_resolve_block_t) - block; -# endif -@end - -@implementation OFUDPSocket_ResolveThread -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - host: (OFString *)host - port: (uint16_t)port - target: (id)target - selector: (SEL)selector - context: (id)context -{ - self = [super init]; - - @try { - _sourceThread = [sourceThread retain]; - _host = [host retain]; - _port = port; - _target = [target retain]; - _selector = selector; - _context = [context retain]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} - -# ifdef OF_HAVE_BLOCKS -- (instancetype)initWithSourceThread: (OFThread *)sourceThread - host: (OFString *)host - port: (uint16_t)port - block: (of_udp_socket_async_resolve_block_t)block -{ - self = [super init]; - - @try { - _sourceThread = [sourceThread retain]; - _host = [host copy]; - _port = port; - _block = [block copy]; - } @catch (id e) { - [self release]; - @throw e; - } - - return self; -} -# endif - -- (void)dealloc -{ - [_sourceThread release]; - [_host release]; - [_target release]; - [_context release]; -# ifdef OF_HAVE_BLOCKS - [_block release]; -# endif - [_exception release]; - - [super dealloc]; -} - -- (void)didResolve -{ - [self join]; - -# ifdef OF_HAVE_BLOCKS - if (_block != NULL) - _block(_host, _port, _address, _exception); - else { -# endif - void (*func)(id, SEL, OFString *, uint16_t, - of_socket_address_t, id, id) = - (void (*)(id, SEL, OFString *, uint16_t, - of_socket_address_t, id, id)) - [_target methodForSelector: _selector]; - - func(_target, _selector, _host, _port, _address, _context, - _exception); -# ifdef OF_HAVE_BLOCKS - } -# endif -} - -- (id)main -{ - void *pool = objc_autoreleasePoolPush(); - - @try { - [OFUDPSocket resolveAddressForHost: _host - port: _port - address: &_address]; - } @catch (id e) { - _exception = e; - } - - [self performSelector: @selector(didResolve) - onThread: _sourceThread - waitUntilDone: false]; - - objc_autoreleasePoolPop(pool); - - return nil; -} -@end -#endif @implementation OFUDPSocket + (void)initialize { if (self != [OFUDPSocket class]) @@ -196,79 +61,10 @@ + (instancetype)socket { return [[[self alloc] init] autorelease]; } -+ (void)resolveAddressForHost: (OFString *)host - port: (uint16_t)port - address: (of_socket_address_t *)address -{ - of_resolver_result_t **results = - of_resolve_host(host, port, SOCK_DGRAM); - - assert(results[0]->addressLength <= - (socklen_t)sizeof(address->sockaddr)); - - memcpy(&address->sockaddr, results[0]->address, - results[0]->addressLength); - address->length = results[0]->addressLength; - - switch (results[0]->address->sa_family) { - case AF_INET: - address->family = OF_SOCKET_ADDRESS_FAMILY_IPV4; - break; -#ifdef OF_HAVE_IPV6 - case AF_INET6: - address->family = OF_SOCKET_ADDRESS_FAMILY_IPV6; - break; -#endif - default: - address->family = OF_SOCKET_ADDRESS_FAMILY_UNKNOWN; - break; - } - - of_resolver_free(results); -} - -#ifdef OF_HAVE_THREADS -+ (void)asyncResolveAddressForHost: (OFString *)host - port: (uint16_t)port - target: (id)target - selector: (SEL)selector - context: (id)context -{ - void *pool = objc_autoreleasePoolPush(); - - [[[[OFUDPSocket_ResolveThread alloc] - initWithSourceThread: [OFThread currentThread] - host: host - port: port - target: target - selector: selector - context: context] autorelease] start]; - - objc_autoreleasePoolPop(pool); -} - -# ifdef OF_HAVE_BLOCKS -+ (void)asyncResolveAddressForHost: (OFString *)host - port: (uint16_t)port - block: (of_udp_socket_async_resolve_block_t)block -{ - void *pool = objc_autoreleasePoolPush(); - - [[[[OFUDPSocket_ResolveThread alloc] - initWithSourceThread: [OFThread currentThread] - host: host - port: port - block: block] autorelease] start]; - - objc_autoreleasePoolPop(pool); -} -# endif -#endif - - (instancetype)init { self = [super init]; _socket = INVALID_SOCKET; @@ -326,142 +122,160 @@ #else OF_UNRECOGNIZED_SELECTOR #endif } -- (uint16_t)bindToHost: (OFString *)host - port: (uint16_t)port -{ - of_resolver_result_t **results; -#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) - of_socket_address_t address; -#endif - - results = of_resolve_host(host, port, SOCK_DGRAM); - @try { -#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - int flags; -#endif - - if ((_socket = socket(results[0]->family, - results[0]->type | SOCK_CLOEXEC, - results[0]->protocol)) == INVALID_SOCKET) - @throw [OFBindFailedException - exceptionWithHost: host - port: port - socket: self - errNo: of_socket_errno()]; - - _blocking = true; - -#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) - fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); -#endif - -#if defined(OF_WII) || defined(OF_NINTENDO_3DS) - if (port != 0) { -#endif - if (bind(_socket, results[0]->address, - results[0]->addressLength) != 0) { +- (uint16_t)of_bindToAddress: (of_socket_address_t *)address +{ + void *pool = objc_autoreleasePoolPush(); + OFString *host; + uint16_t port; +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + int flags; +#endif + + if ((_socket = socket(address->sockaddr.sockaddr.sa_family, + SOCK_DGRAM | SOCK_CLOEXEC, 0)) == INVALID_SOCKET) { + host = of_socket_address_ip_string(address, &port); + @throw [OFBindFailedException + exceptionWithHost: host + port: port + socket: self + errNo: of_socket_errno()]; + } + + _blocking = true; + +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + if ((flags = fcntl(_socket, F_GETFD, 0)) != -1) + fcntl(_socket, F_SETFD, flags | FD_CLOEXEC); +#endif + +#if defined(OF_WII) || defined(OF_NINTENDO_3DS) + if (port != 0) { +#endif + if (bind(_socket, &address->sockaddr.sockaddr, + address->length) != 0) { + int errNo = of_socket_errno(); + + closesocket(_socket); + _socket = INVALID_SOCKET; + + host = of_socket_address_ip_string(address, &port); + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self + errNo: errNo]; + } +#if defined(OF_WII) || defined(OF_NINTENDO_3DS) + } else { + for (;;) { + uint16_t rnd = 0; + int ret; + + while (rnd < 1024) + rnd = (uint16_t)rand(); + + of_socket_address_set_port(address, rnd); + + if ((ret = bind(_socket, &address->sockaddr.sockaddr, + address->length)) == 0) { + port = rnd; + break; + } + + if (of_socket_errno() != EADDRINUSE) { int errNo = of_socket_errno(); closesocket(_socket); _socket = INVALID_SOCKET; + host = of_socket_address_ip_string( + address, &port); @throw [OFBindFailedException exceptionWithHost: host port: port socket: self errNo: errNo]; } -#if defined(OF_WII) || defined(OF_NINTENDO_3DS) - } else { - for (;;) { - uint16_t rnd = 0; - int ret; - - while (rnd < 1024) - rnd = (uint16_t)rand(); - - switch (results[0]->family) { - case AF_INET: - ((struct sockaddr_in *) - results[0]->address)->sin_port = - OF_BSWAP16_IF_LE(rnd); - break; -# ifdef OF_HAVE_IPV6 - case AF_INET6: - ((struct sockaddr_in6 *) - results[0]->address)->sin6_port = - OF_BSWAP16_IF_LE(rnd); - break; -# endif - default: - @throw [OFInvalidArgumentException - exception]; - } - - ret = bind(_socket, results[0]->address, - results[0]->addressLength); - - if (ret == 0) { - port = rnd; - break; - } - - if (of_socket_errno() != EADDRINUSE) { - int errNo = of_socket_errno(); - - closesocket(_socket); - _socket = INVALID_SOCKET; - - @throw [OFBindFailedException - exceptionWithHost: host - port: port - socket: self - errNo: errNo]; - } - } - } -#endif - } @finally { - of_resolver_free(results); - } - - if (port > 0) - return port; - -#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) - address.length = (socklen_t)sizeof(address.sockaddr); - if (of_getsockname(_socket, &address.sockaddr.sockaddr, - &address.length) != 0) { - int errNo = of_socket_errno(); - - closesocket(_socket); - _socket = INVALID_SOCKET; - + } + } +#endif + + objc_autoreleasePoolPop(pool); + + if ((port = of_socket_address_get_port(address)) > 0) + return port; + +#if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) + memset(address, 0, sizeof(*address)); + + address->length = (socklen_t)sizeof(address->sockaddr); + if (of_getsockname(_socket, &address->sockaddr.sockaddr, + &address->length) != 0) { + int errNo = of_socket_errno(); + + closesocket(_socket); + _socket = INVALID_SOCKET; + + host = of_socket_address_ip_string(address, &port); @throw [OFBindFailedException exceptionWithHost: host port: port socket: self errNo: errNo]; } - if (address.sockaddr.sockaddr.sa_family == AF_INET) - return OF_BSWAP16_IF_LE(address.sockaddr.in.sin_port); + if (address->sockaddr.sockaddr.sa_family == AF_INET) + return OF_BSWAP16_IF_LE(address->sockaddr.in.sin_port); # ifdef OF_HAVE_IPV6 - if (address.sockaddr.sockaddr.sa_family == AF_INET6) - return OF_BSWAP16_IF_LE(address.sockaddr.in6.sin6_port); + else if (address->sockaddr.sockaddr.sa_family == AF_INET6) + return OF_BSWAP16_IF_LE(address->sockaddr.in6.sin6_port); # endif + else { + closesocket(_socket); + _socket = INVALID_SOCKET; + + host = of_socket_address_ip_string(address, &port); + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self + errNo: EAFNOSUPPORT]; + } #endif closesocket(_socket); _socket = INVALID_SOCKET; + + host = of_socket_address_ip_string(address, &port); @throw [OFBindFailedException exceptionWithHost: host port: port socket: self - errNo: EAFNOSUPPORT]; + errNo: EADDRNOTAVAIL]; +} + +- (uint16_t)bindToHost: (OFString *)host + port: (uint16_t)port +{ + void *pool = objc_autoreleasePoolPush(); + OFData *socketAddresses; + of_socket_address_t address; + + if (_socket != INVALID_SOCKET) + @throw [OFAlreadyConnectedException exceptionWithSocket: self]; + + socketAddresses = [[OFThread DNSResolver] + resolveSocketAddressesForHost: host + addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY]; + + address = *(of_socket_address_t *)[socketAddresses itemAtIndex: 0]; + of_socket_address_set_port(&address, port); + + port = [self of_bindToAddress: &address]; + + objc_autoreleasePoolPop(pool); + + return port; } - (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length sender: (of_socket_address_t *)sender Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -241,11 +241,8 @@ #import "crc32.h" #import "huffman_tree.h" #import "instance.h" #import "of_asprintf.h" #import "of_strptime.h" -#ifdef OF_HAVE_SOCKETS -# import "resolver.h" -#endif #import "pbkdf2.h" #import "scrypt.h" #import "unicode.h" DELETED src/resolver.h Index: src/resolver.h ================================================================== --- src/resolver.h +++ src/resolver.h @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018 - * 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 - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#import "OFString.h" - -#import "socket.h" - -OF_ASSUME_NONNULL_BEGIN - -/*! @file */ - -/*! - * @struct of_resolver_result_t resolver.h ObjFW/resolver.h - * - * @brief A struct representing one result from the resolver. - */ -typedef struct { - int family, type, protocol; - struct sockaddr *address; - socklen_t addressLength; - void *private_; -} of_resolver_result_t; - -#ifdef __cplusplus -extern "C" { -#endif -/*! - * @brief Resolves the specified host. - * - * @param host The host to resolve - * @param port The port that should be inserted into the resulting address - * struct - * @param protocol The protocol that should be inserted into the resulting - * address struct - * - * @return An array of results. The list is terminated by NULL and should be - * freed after use. - */ -extern of_resolver_result_t *_Nullable *_Nonnull of_resolve_host(OFString *host, - uint16_t port, int protocol); - -/*! - * @brief Frees the results returned by @ref of_resolve_host. - * - * @param results The results returned by @ref of_resolve_host - */ -extern void of_resolver_free( - of_resolver_result_t *_Nullable *_Nonnull results); -#ifdef __cplusplus -} -#endif - -OF_ASSUME_NONNULL_END DELETED src/resolver.m Index: src/resolver.m ================================================================== --- src/resolver.m +++ src/resolver.m @@ -1,275 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018 - * 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 - * the packaging of this file. - * - * Alternatively, it may be distributed under the terms of the GNU General - * Public License, either version 2 or 3, which can be found in the file - * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this - * file. - */ - -#include "config.h" - -#define _WIN32_WINNT 0x0501 - -#include -#include -#include - -#import "OFString.h" - -#import "resolver.h" -#import "macros.h" - -#if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) -# include "threading.h" -#endif - -#import "OFInitializationFailedException.h" -#import "OFInvalidArgumentException.h" -#import "OFOutOfMemoryException.h" -#import "OFOutOfRangeException.h" -#if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) -# import "OFLockFailedException.h" -# import "OFUnlockFailedException.h" -#endif -#import "OFResolveHostFailedException.h" - -#import "socket_helpers.h" - -#if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) -static of_mutex_t mutex; - -OF_CONSTRUCTOR() -{ - if (!of_mutex_new(&mutex)) - @throw [OFInitializationFailedException exception]; -} -#endif - -of_resolver_result_t ** -of_resolve_host(OFString *host, uint16_t port, int type) -{ - of_resolver_result_t **ret, **retIter; - of_resolver_result_t *results, *resultsIter; - size_t count; -#ifdef HAVE_GETADDRINFO - struct addrinfo hints = { 0 }, *res, *res0; - char portCString[7]; - - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = type; - hints.ai_flags = AI_NUMERICSERV; - snprintf(portCString, 7, "%" PRIu16, port); - -# if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) - if (!of_mutex_lock(&mutex)) - @throw [OFLockFailedException exception]; - - @try { -# endif - if (getaddrinfo([host UTF8String], portCString, &hints, - &res0) != 0) - @throw [OFResolveHostFailedException - exceptionWithHost: host - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: 0 - error: OF_DNS_RESOLVER_ERROR_UNKNOWN]; - - count = 0; - for (res = res0; res != NULL; res = res->ai_next) - count++; - - if (count == 0) { - freeaddrinfo(res0); - @throw [OFResolveHostFailedException - exceptionWithHost: host - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: 0 - error: OF_DNS_RESOLVER_ERROR_NO_RESULT]; - } - - if ((ret = calloc(count + 1, sizeof(*ret))) == NULL) { - freeaddrinfo(res0); - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: (count + 1) * - sizeof(*ret)]; - } - - if ((results = malloc(count * sizeof(*results))) == NULL) { - freeaddrinfo(res0); - free(ret); - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: count * - sizeof(*results)]; - } - - for (retIter = ret, resultsIter = results, res = res0; - res != NULL; retIter++, resultsIter++, res = res->ai_next) { - resultsIter->family = res->ai_family; - resultsIter->type = res->ai_socktype; - resultsIter->protocol = res->ai_protocol; - resultsIter->address = res->ai_addr; - resultsIter->addressLength = (socklen_t)res->ai_addrlen; - - *retIter = resultsIter; - } - *retIter = NULL; - - ret[0]->private_ = res0; -# if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) - } @finally { - if (!of_mutex_unlock(&mutex)) - @throw [OFUnlockFailedException exception]; - } -# endif -#else -# ifdef OF_HAVE_THREADS - if (!of_mutex_lock(&mutex)) - @throw [OFLockFailedException exception]; - - @try { -# endif - in_addr_t s_addr; - struct hostent *he; - char **ip; - struct sockaddr_in *addrs, *addrsIter; - - /* - * If the host is an IP address, don't try resolving it. On the - * Wii for example, the resolver will return an error if you - * specify an IP address. - */ - if ((s_addr = inet_addr((const void *)[host UTF8String])) != - INADDR_NONE) { - of_resolver_result_t *tmp; - struct sockaddr_in *addr; - - if ((ret = calloc(2, sizeof(*ret))) == NULL) - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: 2 * - sizeof(*ret)]; - - if ((tmp = malloc(sizeof(*tmp))) == NULL) { - free(ret); - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: sizeof(*tmp)]; - } - - if ((addr = calloc(1, sizeof(*addr))) == NULL) { - free(ret); - free(tmp); - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: sizeof(*addr)]; - } - -#ifdef OF_WII - addr->sin_len = 8; -#endif - addr->sin_family = AF_INET; - addr->sin_port = OF_BSWAP16_IF_LE(port); - addr->sin_addr.s_addr = s_addr; - - tmp->family = AF_INET; - tmp->type = type; - tmp->protocol = 0; - tmp->address = (struct sockaddr *)addr; -#ifndef OF_WII - tmp->addressLength = sizeof(*addr); -#else - tmp->addressLength = 8; -#endif - - ret[0] = tmp; - ret[1] = NULL; - - return ret; - } - - if ((he = gethostbyname((const void *)[host UTF8String])) == - NULL || he->h_addrtype != AF_INET) - @throw [OFResolveHostFailedException - exceptionWithHost: host - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: 0 - error: OF_DNS_RESOLVER_ERROR_UNKNOWN]; - - count = 0; - for (ip = (char **)he->h_addr_list; *ip != NULL; ip++) - count++; - - if (count == 0) - @throw [OFResolveHostFailedException - exceptionWithHost: host - recordClass: OF_DNS_RESOURCE_RECORD_CLASS_IN - recordType: 0 - error: OF_DNS_RESOLVER_ERROR_NO_RESULT]; - - if ((ret = calloc(count + 1, sizeof(*ret))) == NULL) - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: (count + 1) * - sizeof(*ret)]; - - if ((results = malloc(count * sizeof(*results))) == NULL) { - free(ret); - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: count * - sizeof(*results)]; - } - - if ((addrs = calloc(count, sizeof(*addrs))) == NULL) { - free(ret); - free(results); - @throw [OFOutOfMemoryException - exceptionWithRequestedSize: count * sizeof(*addrs)]; - } - - for (retIter = ret, resultsIter = results, addrsIter = addrs, - ip = (char **)he->h_addr_list; *ip != NULL; - retIter++, resultsIter++, addrsIter++, ip++) { - addrsIter->sin_family = he->h_addrtype; - addrsIter->sin_port = OF_BSWAP16_IF_LE(port); - - if ((size_t)he->h_length > - sizeof(addrsIter->sin_addr.s_addr)) - @throw [OFOutOfRangeException exception]; - - memcpy(&addrsIter->sin_addr.s_addr, *ip, he->h_length); - - resultsIter->family = he->h_addrtype; - resultsIter->type = type; - resultsIter->protocol = 0; - resultsIter->address = (struct sockaddr *)addrsIter; - resultsIter->addressLength = sizeof(*addrsIter); - - *retIter = resultsIter; - } -# ifdef OF_HAVE_THREADS - } @finally { - if (!of_mutex_unlock(&mutex)) - @throw [OFUnlockFailedException exception]; - } -# endif -#endif - - return ret; -} - -void -of_resolver_free(of_resolver_result_t **results) -{ -#ifdef HAVE_GETADDRINFO - freeaddrinfo(results[0]->private_); -#else - free(results[0]->address); -#endif - free(results[0]); - free(results); -} Index: src/socket.h ================================================================== --- src/socket.h +++ src/socket.h @@ -205,10 +205,20 @@ * @param address The address on which to set the port * @param port The port to set on the address */ extern void of_socket_address_set_port(of_socket_address_t *_Nonnull address, uint16_t port); + +/*! + * @brief Returns the port of the specified of_socket_address_t, independent of + * the address family used. + * + * @param address The address on which to get the port + * @return The port of the address + */ +extern uint16_t of_socket_address_get_port( + const of_socket_address_t *_Nonnull address); #ifdef __cplusplus } #endif OF_ASSUME_NONNULL_END Index: src/socket.m ================================================================== --- src/socket.m +++ src/socket.m @@ -606,5 +606,18 @@ break; default: @throw [OFInvalidArgumentException exception]; } } + +uint16_t +of_socket_address_get_port(const of_socket_address_t *address) +{ + switch (address->family) { + case OF_SOCKET_ADDRESS_FAMILY_IPV4: + return OF_BSWAP16_IF_LE(address->sockaddr.in.sin_port); + case OF_SOCKET_ADDRESS_FAMILY_IPV6: + return OF_BSWAP16_IF_LE(address->sockaddr.in6.sin6_port); + default: + @throw [OFInvalidArgumentException exception]; + } +} Index: src/socket_helpers.h ================================================================== --- src/socket_helpers.h +++ src/socket_helpers.h @@ -30,19 +30,10 @@ #ifndef INVALID_SOCKET # define INVALID_SOCKET -1 #endif -#ifdef HAVE_GETADDRINFO -# ifndef AI_NUMERICSERV -# define AI_NUMERICSERV 0 -# endif -# ifndef AI_NUMERICHOST -# define AI_NUMERICHOST 0 -# endif -#endif - #ifndef INADDR_NONE # define INADDR_NONE ((in_addr_t)-1) #endif #ifndef SOMAXCONN @@ -75,11 +66,10 @@ # define bind(sock, addr, addrlen) net_bind(sock, addr, addrlen) # define closesocket(sock) net_close(sock) # define connect(sock, addr, addrlen) \ net_connect(sock, (struct sockaddr *)addr, addrlen) # define fcntl(fd, cmd, flags) net_fcntl(fd, cmd, flags) -# define gethostbyname(name) net_gethostbyname(name) # define getsockopt(sock, level, name, value, len) \ net_getsockopt(sock, level, name, value, len) # define h_errno 0 # define hstrerror(err) "unknown (no hstrerror)" # define listen(sock, backlog) net_listen(sock, backlog) Index: tests/OFUDPSocketTests.m ================================================================== --- tests/OFUDPSocketTests.m +++ tests/OFUDPSocketTests.m @@ -41,14 +41,11 @@ TEST(@"-[bindToHost:port:]", (port1 = [sock bindToHost: @"127.0.0.1" port: 0])) - TEST(@"+[resolveAddressForHost:port:address:]", - R([OFUDPSocket resolveAddressForHost: @"127.0.0.1" - port: port1 - address: &addr1])) + addr1 = of_socket_address_parse_ip(@"127.0.0.1", port1); TEST(@"-[sendBuffer:length:receiver:]", R([sock sendBuffer: "Hello" length: 6 receiver: &addr1])) @@ -59,13 +56,11 @@ sender: &addr2] == 6 && !memcmp(buf, "Hello", 6) && (host = of_socket_address_ip_string(&addr2, &port2)) && [host isEqual: @"127.0.0.1"] && port2 == port1) - [OFUDPSocket resolveAddressForHost: @"127.0.0.1" - port: port1 + 1 - address: &addr3]; + addr3 = of_socket_address_parse_ip(@"127.0.0.1", port1 + 1); /* * TODO: Move those tests elsewhere as soon as the DNS resolving part * is no longer in OFUDPSocket. */