Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -294,10 +294,12 @@ 4B6C8ADE17BD5C2E00B194F2 /* OFZIPArchiveEntry+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B6C8AD717BD5C2E00B194F2 /* OFZIPArchiveEntry+Private.h */; }; 4B7161AD17A6FC7600B74970 /* OFHTTPResponse.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7161AB17A6FC7600B74970 /* OFHTTPResponse.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B7161AE17A6FC7600B74970 /* OFHTTPResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B7161AC17A6FC7600B74970 /* OFHTTPResponse.m */; }; 4B745BA5168B25E600A6C20E /* OFSystemInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B745BA3168B25E600A6C20E /* OFSystemInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B745BA6168B25E600A6C20E /* OFSystemInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B745BA4168B25E600A6C20E /* OFSystemInfo.m */; }; + 4B7769ED1895C07D00D12284 /* resolver.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7769EB1895C07D00D12284 /* resolver.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B7769EE1895C07D00D12284 /* resolver.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B7769EC1895C07D00D12284 /* resolver.m */; }; 4B7DD58218942FE200990FD6 /* socket_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7DD58118942FE200990FD6 /* socket_helpers.h */; }; 4B7DD5851894358500990FD6 /* OFMoveItemFailedException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7DD5831894358400990FD6 /* OFMoveItemFailedException.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B7DD5861894358500990FD6 /* OFMoveItemFailedException.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B7DD5841894358400990FD6 /* OFMoveItemFailedException.m */; }; 4B7DD58818943D4A00990FD6 /* socket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7DD58718943D4A00990FD6 /* socket.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4B7FF3B4133CED6200000324 /* OFConditionStillWaitingException.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7FF3B2133CED6100000324 /* OFConditionStillWaitingException.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -732,10 +734,12 @@ 4B6EF685123535C80076B512 /* test.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = test.m; path = tests/objc_sync/test.m; sourceTree = SOURCE_ROOT; }; 4B7161AB17A6FC7600B74970 /* OFHTTPResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFHTTPResponse.h; path = src/OFHTTPResponse.h; sourceTree = ""; }; 4B7161AC17A6FC7600B74970 /* OFHTTPResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFHTTPResponse.m; path = src/OFHTTPResponse.m; sourceTree = ""; }; 4B745BA3168B25E600A6C20E /* OFSystemInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFSystemInfo.h; path = src/OFSystemInfo.h; sourceTree = ""; }; 4B745BA4168B25E600A6C20E /* OFSystemInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFSystemInfo.m; path = src/OFSystemInfo.m; sourceTree = ""; }; + 4B7769EB1895C07D00D12284 /* resolver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = resolver.h; path = src/resolver.h; sourceTree = ""; }; + 4B7769EC1895C07D00D12284 /* resolver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = resolver.m; path = src/resolver.m; sourceTree = ""; }; 4B7DD58118942FE200990FD6 /* socket_helpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = socket_helpers.h; path = src/socket_helpers.h; sourceTree = ""; }; 4B7DD5831894358400990FD6 /* OFMoveItemFailedException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFMoveItemFailedException.h; path = src/exceptions/OFMoveItemFailedException.h; sourceTree = ""; }; 4B7DD5841894358400990FD6 /* OFMoveItemFailedException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFMoveItemFailedException.m; path = src/exceptions/OFMoveItemFailedException.m; sourceTree = ""; }; 4B7DD58718943D4A00990FD6 /* socket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = socket.h; path = src/socket.h; sourceTree = ""; }; 4B7DD58918944A7900990FD6 /* apple-forwarding-arm64.S */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.asm; name = "apple-forwarding-arm64.S"; path = "src/forwarding/apple-forwarding-arm64.S"; sourceTree = ""; }; @@ -1350,10 +1354,12 @@ 4BF1BCBF11C9663F0025511F /* objfw-defs.h.in */, 4BB50DCF12F863C700C9393F /* of_asprintf.h */, 4BB50DD012F863C700C9393F /* of_asprintf.m */, 4BA355BC14879BF700442EF4 /* of_strptime.h */, 4BA355B914879BDD00442EF4 /* of_strptime.m */, + 4B7769EB1895C07D00D12284 /* resolver.h */, + 4B7769EC1895C07D00D12284 /* resolver.m */, 4B7DD58718943D4A00990FD6 /* socket.h */, 4B7DD58118942FE200990FD6 /* socket_helpers.h */, 4B67998B1099E7C50041064A /* threading.h */, 4B67998C1099E7C50041064A /* unicode.h */, 4BFBDD1610A0724800051AFB /* unicode.m */, @@ -1559,10 +1565,11 @@ 4B837D7A16829C5F007A3E83 /* instance.h in Headers */, 4B3D23E81337FCB000DD29B8 /* macros.h in Headers */, 4BD98C03133814220048DD5B /* objfw-defs.h in Headers */, 4B3D23E91337FCB000DD29B8 /* of_asprintf.h in Headers */, 4BA355BD14879BF700442EF4 /* of_strptime.h in Headers */, + 4B7769ED1895C07D00D12284 /* resolver.h in Headers */, 4B7DD58818943D4A00990FD6 /* socket.h in Headers */, 4B3D23EA1337FCB000DD29B8 /* threading.h in Headers */, 4B3D23EB1337FCB000DD29B8 /* unicode.h in Headers */, 4B90B79E133AD87D00BD33CB /* OFAcceptFailedException.h in Headers */, 4B90B7A0133AD87D00BD33CB /* OFAddressTranslationFailedException.h in Headers */, @@ -1927,10 +1934,11 @@ 4BB52CC717B8EA7F00B7EBF5 /* codepage_437.m in Sources */, 4B3D23B41337FC0D00DD29B8 /* iso_8859_15.m in Sources */, 4B3D23B51337FC0D00DD29B8 /* foundation-compat.m in Sources */, 4B3D23EE1337FFD000DD29B8 /* of_asprintf.m in Sources */, 4BA355BA14879BDD00442EF4 /* of_strptime.m in Sources */, + 4B7769EE1895C07D00D12284 /* resolver.m in Sources */, 4B3D23B91337FC0D00DD29B8 /* unicode.m in Sources */, 4B3D23BA1337FC0D00DD29B8 /* windows_1252.m in Sources */, 4B90B79F133AD87D00BD33CB /* OFAcceptFailedException.m in Sources */, 4B90B7A1133AD87D00BD33CB /* OFAddressTranslationFailedException.m in Sources */, 4B17FF80133A2D17003E6DCD /* OFAllocFailedException.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -83,11 +83,12 @@ OFHTTPRequest.m \ OFHTTPResponse.m \ OFHTTPServer.m \ OFStreamObserver.m \ OFStreamSocket.m \ - OFTCPSocket.m + OFTCPSocket.m \ + resolver.m SRCS_THREADS = OFCondition.m \ OFMutex.m \ OFRecursiveMutex.m \ OFThreadPool.m Index: src/OFTCPSocket.h ================================================================== --- src/OFTCPSocket.h +++ src/OFTCPSocket.h @@ -55,12 +55,12 @@ * To create a server, create a socket, bind it and listen on it. */ @interface OFTCPSocket: OFStreamSocket { bool _listening; - struct sockaddr_storage _sockAddr; - socklen_t _sockAddrLen; + struct sockaddr *_address; + socklen_t _addressLength; OFString *_SOCKS5Host; uint16_t _SOCKS5Port; } #ifdef OF_HAVE_PROPERTIES Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -20,10 +20,12 @@ #define __NO_EXT_QNX #include #include #include + +#include #import "OFTCPSocket.h" #import "OFTCPSocket+SOCKS5.h" #import "OFString.h" #import "OFThread.h" @@ -31,30 +33,24 @@ #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFAcceptFailedException.h" #import "OFAlreadyConnectedException.h" -#import "OFAddressTranslationFailedException.h" #import "OFBindFailedException.h" #import "OFConnectionFailedException.h" #import "OFInvalidArgumentException.h" #import "OFListenFailedException.h" #import "OFNotConnectedException.h" #import "OFNotImplementedException.h" +#import "OFOutOfMemoryException.h" #import "OFSetOptionFailedException.h" #import "autorelease.h" #import "macros.h" +#import "resolver.h" #import "socket_helpers.h" -#if defined(OF_HAVE_THREADS) && !defined(HAVE_THREADSAFE_GETADDRINFO) -# import "OFMutex.h" -# import "OFDataArray.h" - -static OFMutex *mutex = nil; -#endif - /* References for static linking */ void _references_to_categories_of_OFTCPSocket(void) { _OFTCPSocket_SOCKS5_reference = 1; } @@ -201,18 +197,10 @@ } @end #endif @implementation OFTCPSocket -#if defined(OF_HAVE_THREADS) && !defined(HAVE_THREADSAFE_GETADDRINFO) -+ (void)initialize -{ - if (self == [OFTCPSocket class]) - mutex = [[OFMutex alloc] init]; -} -#endif - + (void)setSOCKS5Host: (OFString*)host { id old = defaultSOCKS5Host; defaultSOCKS5Host = [host copy]; [old release]; @@ -237,11 +225,10 @@ { self = [super init]; @try { _socket = INVALID_SOCKET; - _sockAddrLen = sizeof(struct sockaddr_storage); _SOCKS5Host = [defaultSOCKS5Host copy]; _SOCKS5Port = defaultSOCKS5Port; } @catch (id e) { [self release]; @throw e; @@ -280,10 +267,11 @@ - (void)connectToHost: (OFString*)host port: (uint16_t)port { OFString *destinationHost = host; uint16_t destinationPort = port; + of_resolver_result_t **results, **iter; if (_socket != INVALID_SOCKET) @throw [OFAlreadyConnectedException exceptionWithSocket: self]; if (_SOCKS5Host != nil) { @@ -290,143 +278,30 @@ /* Connect to the SOCKS5 proxy instead */ host = _SOCKS5Host; port = _SOCKS5Port; } -#ifdef HAVE_THREADSAFE_GETADDRINFO - struct addrinfo hints, *res, *res0; - char portCString[7]; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV; - snprintf(portCString, 7, "%" PRIu16, port); - - if (getaddrinfo([host UTF8String], portCString, &hints, &res0)) - @throw [OFAddressTranslationFailedException - exceptionWithHost: host - socket: self]; - - for (res = res0; res != NULL; res = res->ai_next) { - if ((_socket = socket(res->ai_family, res->ai_socktype, - res->ai_protocol)) == INVALID_SOCKET) + results = of_resolve_host(host, port, SOCK_STREAM); + + for (iter = results; *iter != NULL; iter++) { + of_resolver_result_t *result = *iter; + + if ((_socket = socket(result->family, result->type, + result->protocol)) == INVALID_SOCKET) continue; - if (connect(_socket, res->ai_addr, res->ai_addrlen) == -1) { + if (connect(_socket, result->address, + result->addressLength) == -1) { close(_socket); _socket = INVALID_SOCKET; continue; } break; } - freeaddrinfo(res0); -#else - bool connected = false; - struct hostent *he; - struct sockaddr_in addr; - char **ip; -# ifdef OF_HAVE_THREADS - OFDataArray *addrlist; -# endif - - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = OF_BSWAP16_IF_LE(port); - - if ((addr.sin_addr.s_addr = inet_addr([host UTF8String])) != - (in_addr_t)(-1)) { - if ((_socket = socket(AF_INET, SOCK_STREAM, - 0)) == INVALID_SOCKET) { - @throw [OFConnectionFailedException - exceptionWithHost: host - port: port - socket: self]; - } - - if (connect(_socket, (struct sockaddr*)&addr, - sizeof(addr)) == -1) { - close(_socket); - _socket = INVALID_SOCKET; - @throw [OFConnectionFailedException - exceptionWithHost: host - port: port - socket: self]; - } - - if (_SOCKS5Host != nil) - [self OF_SOCKS5ConnectToHost: destinationHost - port: destinationPort]; - - return; - } - -# ifdef OF_HAVE_THREADS - addrlist = [[OFDataArray alloc] initWithItemSize: sizeof(char**)]; - [mutex lock]; -# endif - - if ((he = gethostbyname([host UTF8String])) == NULL) { -# ifdef OF_HAVE_THREADS - [addrlist release]; - [mutex unlock]; -# endif - @throw [OFAddressTranslationFailedException - exceptionWithHost: host - socket: self]; - } - - if (he->h_addrtype != AF_INET || - (_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { -# ifdef OF_HAVE_THREADS - [addrlist release]; - [mutex unlock]; -# endif - @throw [OFConnectionFailedException exceptionWithHost: host - port: port - socket: self]; - } - -# ifdef OF_HAVE_THREADS - @try { - for (ip = he->h_addr_list; *ip != NULL; ip++) - [addrlist addItem: ip]; - - /* Add the terminating NULL */ - [addrlist addItem: ip]; - } @catch (id e) { - [addrlist release]; - @throw e; - } @finally { - [mutex unlock]; - } - - for (ip = [addrlist items]; *ip != NULL; ip++) { -# else - for (ip = he->h_addr_list; *ip != NULL; ip++) { -# endif - memcpy(&addr.sin_addr.s_addr, *ip, he->h_length); - - if (connect(_socket, (struct sockaddr*)&addr, - sizeof(addr)) == -1) - continue; - - connected = true; - break; - } - -# ifdef OF_HAVE_THREADS - [addrlist release]; -# endif - - if (!connected) { - close(_socket); - _socket = INVALID_SOCKET; - } -#endif + of_resolver_free(results); if (_socket == INVALID_SOCKET) @throw [OFConnectionFailedException exceptionWithHost: host port: port socket: self]; @@ -475,19 +350,20 @@ #endif - (uint16_t)bindToHost: (OFString*)host port: (uint16_t)port { + of_resolver_result_t **results; const int one = 1; +#ifndef __wii__ union { struct sockaddr_storage storage; struct sockaddr_in in; -#ifdef AF_INET6 +# ifdef AF_INET6 struct sockaddr_in6 in6; -#endif +# endif } addr; -#ifndef __wii__ socklen_t addrLen; #endif if (_socket != INVALID_SOCKET) @throw [OFAlreadyConnectedException exceptionWithSocket: self]; @@ -499,96 +375,34 @@ #ifdef __wii__ if (port == 0) port = freePort--; #endif -#ifdef HAVE_THREADSAFE_GETADDRINFO - struct addrinfo hints, *res; - char portCString[7]; - - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE; - snprintf(portCString, 7, "%" PRIu16, port); - - if (getaddrinfo([host UTF8String], portCString, &hints, &res)) - @throw [OFAddressTranslationFailedException - exceptionWithHost: host - socket: self]; - - if ((_socket = socket(res->ai_family, SOCK_STREAM, - 0)) == INVALID_SOCKET) - @throw [OFBindFailedException exceptionWithHost: host - port: port - socket: self]; - - if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, - sizeof(one))) - @throw [OFSetOptionFailedException exceptionWithStream: self]; - - if (bind(_socket, res->ai_addr, res->ai_addrlen) == -1) { - freeaddrinfo(res); - close(_socket); - _socket = INVALID_SOCKET; - @throw [OFBindFailedException exceptionWithHost: host - port: port - socket: self]; - } - - freeaddrinfo(res); -#else - memset(&addr, 0, sizeof(addr)); - addr.in.sin_family = AF_INET; - addr.in.sin_port = OF_BSWAP16_IF_LE(port); - - if ((addr.in.sin_addr.s_addr = inet_addr([host UTF8String])) == - (in_addr_t)(-1)) { -# ifdef OF_HAVE_THREADS - [mutex lock]; - @try { -# endif - struct hostent *he; - - if ((he = gethostbyname([host UTF8String])) == NULL) - @throw [OFAddressTranslationFailedException - exceptionWithHost: host - socket: self]; - - if (he->h_addrtype != AF_INET || - he->h_addr_list[0] == NULL) { - @throw [OFAddressTranslationFailedException - exceptionWithHost: host - socket: self]; - } - - memcpy(&addr.in.sin_addr.s_addr, he->h_addr_list[0], - he->h_length); -# ifdef OF_HAVE_THREADS - } @finally { - [mutex unlock]; - } -# endif - } - - if ((_socket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) - @throw [OFBindFailedException exceptionWithHost: host - port: port - socket: self]; - - if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, - sizeof(one))) - @throw [OFSetOptionFailedException exceptionWithStream: self]; - - if (bind(_socket, (struct sockaddr*)&addr.in, sizeof(addr.in)) == -1) { - close(_socket); - _socket = INVALID_SOCKET; - @throw [OFBindFailedException exceptionWithHost: host - port: port - socket: self]; - } -#endif + results = of_resolve_host(host, port, SOCK_STREAM); + @try { + if ((_socket = socket(results[0]->family, results[0]->type, + results[0]->protocol)) == INVALID_SOCKET) + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self]; + + if (setsockopt(_socket, SOL_SOCKET, SO_REUSEADDR, + (const char*)&one, sizeof(one))) + @throw [OFSetOptionFailedException + exceptionWithStream: self]; + + if (bind(_socket, results[0]->address, + results[0]->addressLength) == -1) { + close(_socket); + _socket = INVALID_SOCKET; + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self]; + } + } @finally { + of_resolver_free(results); + } if (port > 0) return port; #ifndef __wii__ @@ -635,14 +449,29 @@ - (instancetype)accept { OFTCPSocket *client = [[[[self class] alloc] init] autorelease]; - if ((client->_socket = accept(_socket, - (struct sockaddr*)&client->_sockAddr, - &client->_sockAddrLen)) == INVALID_SOCKET) + client->_address = [client + allocMemoryWithSize: sizeof(struct sockaddr_storage)]; + client->_addressLength = sizeof(struct sockaddr_storage); + + if ((client->_socket = accept(_socket, client->_address, + &client->_addressLength)) == INVALID_SOCKET) @throw [OFAcceptFailedException exceptionWithSocket: self]; + + assert(client->_addressLength <= sizeof(struct sockaddr_storage)); + + if (client->_addressLength != sizeof(struct sockaddr_storage)) { + @try { + client->_address = [client + resizeMemory: client->_address + size: client->_addressLength]; + } @catch (OFOutOfMemoryException *e) { + /* We don't care, as we only made it smaller */ + } + } return client; } - (void)asyncAcceptWithTarget: (id)target @@ -669,50 +498,17 @@ @throw [OFSetOptionFailedException exceptionWithStream: self]; } - (OFString*)remoteAddress { - char *host; - if (_socket == INVALID_SOCKET) @throw [OFNotConnectedException exceptionWithSocket: self]; -#ifdef HAVE_THREADSAFE_GETADDRINFO - host = [self allocMemoryWithSize: NI_MAXHOST]; - - @try { - if (getnameinfo((struct sockaddr*)&_sockAddr, _sockAddrLen, - host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV)) - @throw [OFAddressTranslationFailedException - exceptionWithSocket: self]; - - return [OFString stringWithUTF8String: host]; - } @finally { - [self freeMemory: host]; - } -#else -# ifdef OF_HAVE_THREADS - [mutex lock]; - - @try { -# endif - host = inet_ntoa(((struct sockaddr_in*)&_sockAddr)->sin_addr); - - if (host == NULL) - @throw [OFAddressTranslationFailedException - exceptionWithSocket: self]; - - return [OFString stringWithUTF8String: host]; -# ifdef OF_HAVE_THREADS - } @finally { - [mutex unlock]; - } -# endif -#endif - - /* Get rid of a warning, never reached anyway */ - OF_ENSURE(0); + if (_address == NULL) + @throw [OFInvalidArgumentException exception]; + + return of_address_to_string(_address, _addressLength); } - (bool)isListening { return _listening; @@ -721,8 +517,7 @@ - (void)close { [super close]; _listening = false; - _sockAddrLen = sizeof(struct sockaddr_storage); } @end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -180,5 +180,8 @@ #import "autorelease.h" #import "asprintf.h" #import "base64.h" #import "of_asprintf.h" #import "of_strptime.h" +#ifdef OF_HAVE_SOCKETS +# import "resolver.h" +#endif Index: src/exceptions/OFAddressTranslationFailedException.h ================================================================== --- src/exceptions/OFAddressTranslationFailedException.h +++ src/exceptions/OFAddressTranslationFailedException.h @@ -25,89 +25,44 @@ /*! * @brief An exception indicating the translation of an address failed. */ @interface OFAddressTranslationFailedException: OFException { - id _socket; OFString *_host; int _errNo; } #ifdef OF_HAVE_PROPERTIES @property (readonly, copy) OFString *host; -@property (readonly, retain) id socket; @property (readonly) int errNo; #endif -/*! - * @brief Creates a new, autoreleased address translation failed exception. - * - * @param socket The socket which could not translate the address - * @return A new, autoreleased address translation failed exception - */ -+ (instancetype)exceptionWithSocket: (id)socket; - /*! * @brief Creates a new, autoreleased address translation failed exception. * * @param host The host for which translation was requested * @return A new, autoreleased address translation failed exception */ + (instancetype)exceptionWithHost: (OFString*)host; -/*! - * @brief Creates a new, autoreleased address translation failed exception. - * - * @param host The host for which translation was requested - * @param socket The socket which could not translate the address - * @return A new, autoreleased address translation failed exception - */ -+ (instancetype)exceptionWithHost: (OFString*)host - socket: (id)socket; - -/*! - * @brief Initializes an already allocated address translation failed exception. - * - * @param socket The socket which could not translate the address - * @return An initialized address translation failed exception - */ -- initWithSocket: (id)socket; - /*! * @brief Initializes an already allocated address translation failed exception. * * @param host The host for which translation was requested * @return An initialized address translation failed exception */ - initWithHost: (OFString*)host; -/*! - * @brief Initializes an already allocated address translation failed exception. - * - * @param host The host for which translation was requested - * @param socket The socket which could not translate the address - * @return An initialized address translation failed exception - */ -- initWithHost: (OFString*)host - socket: (id)socket; - /*! * @brief Returns the host for which the address translation was requested. * * @return The host for which the address translation was requested */ - (OFString*)host; -/*! - * @brief Returns the socket which could not translate the address. - * - * @return The socket which could not translate the address - */ -- (id)socket; - /*! * @brief Returns the errno from when the exception was created. * * @return The errno from when the exception was created */ - (int)errNo; @end Index: src/exceptions/OFAddressTranslationFailedException.m ================================================================== --- src/exceptions/OFAddressTranslationFailedException.m +++ src/exceptions/OFAddressTranslationFailedException.m @@ -21,53 +21,23 @@ #import "common.h" #import "macros.h" @implementation OFAddressTranslationFailedException -+ (instancetype)exceptionWithSocket: (id)socket -{ - return [[[self alloc] initWithSocket: socket] autorelease]; -} - + (instancetype)exceptionWithHost: (OFString*)host { return [[[self alloc] initWithHost: host] autorelease]; } -+ (instancetype)exceptionWithHost: (OFString*)host - socket: (id)socket -{ - return [[[self alloc] initWithHost: host - socket: socket] autorelease]; -} - -- init -{ - OF_INVALID_INIT_METHOD -} - -- initWithSocket: (id)socket -{ - return [self initWithHost: nil - socket: socket]; -} - -- initWithHost: (OFString*)host -{ - return [self initWithHost: host - socket: nil]; -} - -- initWithHost: (OFString*)host - socket: (id)socket + +- initWithHost: (OFString*)host { self = [super init]; @try { - _host = [host copy]; - _socket = [socket retain]; - _errNo = GET_AT_ERRNO; + _host = [host copy]; + _errNo = GET_AT_ERRNO; } @catch (id e) { [self release]; @throw e; } @@ -75,31 +45,17 @@ } - (void)dealloc { [_host release]; - [_socket release]; [super dealloc]; } - (OFString*)description { - if (_host != nil && _socket != nil) - return [OFString stringWithFormat: - @"The host %@ could not be translated to an address for a " - @"socket of type %@. This means that either the host was " - @"not found, there was a problem with the name server, " - @"there was a problem with your network connection or you " - @"specified an invalid host. " ERRFMT, _host, - [_socket class], AT_ERRPARAM]; - else if (_socket != nil) - return [OFString stringWithFormat: - @"An address could not be translated for a socket of type " - @"%@! " ERRFMT, [_socket class], - AT_ERRPARAM]; - else if (_host != nil) + if (_host != nil) return [OFString stringWithFormat: @"The host %@ could not be translated to an address. This " @"means that either the host was not found, there was a " @"problem with the name server, there was a problem with " @"your network connection or you specified an invalid " @@ -113,19 +69,14 @@ - (OFString*)host { OF_GETTER(_host, true) } -- (id)socket -{ - OF_GETTER(_socket, true) -} - - (int)errNo { #ifdef _WIN32 return of_wsaerr_to_errno(_errNo); #else return _errNo; #endif } @end ADDED src/resolver.h Index: src/resolver.h ================================================================== --- src/resolver.h +++ src/resolver.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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" + +/*! @file */ + +/*! + * @brief A struct representing one result from the resolver. + */ +typedef struct of_resolver_result_t { + 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 + * free'd after use. + */ +extern of_resolver_result_t** of_resolve_host(OFString *host, uint16_t port, + int protocol); + +/*! + * @brief Converts the specified address to a string. + * + * @param address The address to convert to a string + * @param addressLength The length of the address to convert to a string + * @return The address as a string + */ +extern OFString* of_address_to_string(struct sockaddr *address, + socklen_t addressLength); + +/*! + * @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 **results); +#ifdef __cplusplus +} +#endif ADDED src/resolver.m Index: src/resolver.m ================================================================== --- src/resolver.m +++ src/resolver.m @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 + * 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" + +#include +#include +#include +#include + +#import "macros.h" +#import "resolver.h" + +#if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) +# include "OFMutex.h" +#endif + +#import "OFAddressTranslationFailedException.h" +#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 "socket_helpers.h" + +#if !defined(HAVE_THREADSAFE_GETADDRINFO) && defined(OF_HAVE_THREADS) +static of_mutex_t mutex; + +static void __attribute__((constructor)) +init(void) +{ + 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_THREADSAFE_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 (getaddrinfo([host UTF8String], portCString, &hints, &res0)) + @throw [OFAddressTranslationFailedException + exceptionWithHost: host]; + + count = 0; + for (res = res0; res != NULL; res = res->ai_next) + count++; + + if (count == 0) { + freeaddrinfo(res0); + @throw [OFAddressTranslationFailedException + exceptionWithHost: host]; + } + + if ((ret = calloc(count + 1, sizeof(*ret))) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: (count + 1) * sizeof(*ret)]; + + if ((results = malloc(count * sizeof(*results))) == NULL) + @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 = res->ai_addrlen; + resultsIter->private = NULL; + + *retIter = resultsIter; + } + *retIter = NULL; + + ret[0]->private = res0; +#else + struct hostent *he; + in_addr_t s_addr; + 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([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) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: sizeof(*tmp)]; + + if ((addr = calloc(1, sizeof(*addr))) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: sizeof(*addr)]; + + 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; + tmp->addressLength = sizeof(*addr); + + ret[0] = tmp; + ret[1] = NULL; + + return ret; + } + +# ifdef OF_HAVE_THREADS + if (!of_mutex_lock(&mutex)) + @throw [OFLockFailedException exception]; +# endif + + if ((he = gethostbyname([host UTF8String])) == NULL || + he->h_addrtype != AF_INET) { +# ifdef OF_HAVE_THREADS + if (!of_mutex_unlock(&mutex)) + @throw [OFUnlockFailedException exception]; +# endif + + @throw [OFAddressTranslationFailedException + exceptionWithHost: host]; + } + + count = 0; + for (ip = he->h_addr_list; *ip != NULL; ip++) + count++; + + if (count == 0) + @throw [OFAddressTranslationFailedException + exceptionWithHost: host]; + + if ((ret = calloc(count + 1, sizeof(*ret))) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: (count + 1) * sizeof(*ret)]; + + if ((results = malloc(count * sizeof(*results))) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: count * sizeof(*results)]; + + if ((addrs = calloc(count, sizeof(*addrs))) == NULL) + @throw [OFOutOfMemoryException + exceptionWithRequestedSize: count * sizeof(*addrs)]; + + for (retIter = ret, resultsIter = results, addrsIter = addrs, + ip = 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 (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 + if (!of_mutex_unlock(&mutex)) + @throw [OFUnlockFailedException exception]; +# endif +#endif + + return ret; +} + +OFString* +of_address_to_string(struct sockaddr *address, socklen_t addressLength) +{ +#ifdef HAVE_THREADSAFE_GETADDRINFO + char host[NI_MAXHOST]; + + if (getnameinfo(address, addressLength, host, NI_MAXHOST, NULL, 0, + NI_NUMERICHOST | NI_NUMERICSERV)) + @throw [OFAddressTranslationFailedException exception]; + + return [OFString stringWithUTF8String: host]; +#else + OFString *ret; + char *host; + + if (address->sa_family != AF_INET) + @throw [OFInvalidArgumentException exception]; + +# if OF_HAVE_THREADS + if (!of_mutex_lock(&mutex)) + @throw [OFLockFailedException exception]; +# endif + + host = inet_ntoa(((struct sockaddr_in*)(void*)address)->sin_addr); + if (host == NULL) + @throw [OFAddressTranslationFailedException exception]; + + ret = [OFString stringWithUTF8String: host]; + +# if OF_HAVE_THREADS + if (!of_mutex_unlock(&mutex)) + @throw [OFUnlockFailedException exception]; +# endif + + return ret; +#endif +} + +void +of_resolver_free(of_resolver_result_t **results) +{ +#ifdef HAVE_THREADSAFE_GETADDRINFO + freeaddrinfo(results[0]->private); +#else + free(results[0]->address); +#endif + free(results[0]); + free(results); +} Index: src/socket_helpers.h ================================================================== --- src/socket_helpers.h +++ src/socket_helpers.h @@ -36,10 +36,14 @@ # endif # ifndef AI_NUMERICHOST # define AI_NUMERICHOST 0 # endif #endif + +#ifndef INADDR_NONE +# define INADDR_NONE ((in_addr_t)-1) +#endif #ifndef SOMAXCONN # define SOMAXCONN 32 #endif