Index: ObjFW.xcodeproj/project.pbxproj ================================================================== --- ObjFW.xcodeproj/project.pbxproj +++ ObjFW.xcodeproj/project.pbxproj @@ -296,10 +296,12 @@ 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 */; }; + 4B7769F11895ED0C00D12284 /* OFUDPSocket.h in Headers */ = {isa = PBXBuildFile; fileRef = 4B7769EF1895ED0C00D12284 /* OFUDPSocket.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4B7769F21895ED0C00D12284 /* OFUDPSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B7769F01895ED0C00D12284 /* OFUDPSocket.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, ); }; }; @@ -736,10 +738,12 @@ 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 = ""; }; + 4B7769EF1895ED0C00D12284 /* OFUDPSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OFUDPSocket.h; path = src/OFUDPSocket.h; sourceTree = ""; }; + 4B7769F01895ED0C00D12284 /* OFUDPSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = OFUDPSocket.m; path = src/OFUDPSocket.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 = ""; }; @@ -1309,10 +1313,12 @@ 4B9361A71511000C00DCD16B /* OFThreadPool.m */, 4B325EDB1605F3A0007836CA /* OFTimer.h */, 4B325EDC1605F3A0007836CA /* OFTimer.m */, 4B6C8AD617BD5C2E00B194F2 /* OFTimer+Private.h */, 4BA02BA015041F5900002F84 /* OFTLSSocket.h */, + 4B7769EF1895ED0C00D12284 /* OFUDPSocket.h */, + 4B7769F01895ED0C00D12284 /* OFUDPSocket.m */, 4B4A61F212DF5EA20048F3F2 /* OFURL.h */, 4B4A61F312DF5EA20048F3F2 /* OFURL.m */, 4BF1BCCE11C9663F0025511F /* OFXMLAttribute.h */, 4BF1BCCF11C9663F0025511F /* OFXMLAttribute.m */, 4B49EA67143B3A090005BBC6 /* OFXMLCDATA.h */, @@ -1541,10 +1547,11 @@ 4B3D23DD1337FCB000DD29B8 /* OFTCPSocket.h in Headers */, 4B3D23DE1337FCB000DD29B8 /* OFThread.h in Headers */, 4B9361A81511000C00DCD16B /* OFThreadPool.h in Headers */, 4B325EDF1605F3A0007836CA /* OFTimer.h in Headers */, 4BA02BA215041F5900002F84 /* OFTLSSocket.h in Headers */, + 4B7769F11895ED0C00D12284 /* OFUDPSocket.h in Headers */, 4B3D23DF1337FCB000DD29B8 /* OFURL.h in Headers */, 4B3D23E01337FCB000DD29B8 /* OFXMLAttribute.h in Headers */, 4B49EA6D143B3A090005BBC6 /* OFXMLCDATA.h in Headers */, 4B49EA6F143B3A090005BBC6 /* OFXMLCharacters.h in Headers */, 4B49EA71143B3A090005BBC6 /* OFXMLComment.h in Headers */, @@ -1915,10 +1922,11 @@ 4B3D23AB1337FC0D00DD29B8 /* OFTCPSocket.m in Sources */, 4BD653C6143B8489006182F0 /* OFTCPSocket+SOCKS5.m in Sources */, 4B3D23AC1337FC0D00DD29B8 /* OFThread.m in Sources */, 4B9361A91511000C00DCD16B /* OFThreadPool.m in Sources */, 4B325EE01605F3A0007836CA /* OFTimer.m in Sources */, + 4B7769F21895ED0C00D12284 /* OFUDPSocket.m in Sources */, 4B3D23AD1337FC0D00DD29B8 /* OFURL.m in Sources */, 4B3D23AE1337FC0D00DD29B8 /* OFXMLAttribute.m in Sources */, 4B49EA6E143B3A090005BBC6 /* OFXMLCDATA.m in Sources */, 4B49EA70143B3A090005BBC6 /* OFXMLCharacters.m in Sources */, 4B49EA72143B3A090005BBC6 /* OFXMLComment.m in Sources */, Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -84,10 +84,11 @@ OFHTTPResponse.m \ OFHTTPServer.m \ OFStreamObserver.m \ OFStreamSocket.m \ OFTCPSocket.m \ + OFUDPSocket.m \ resolver.m SRCS_THREADS = OFCondition.m \ OFMutex.m \ OFRecursiveMutex.m \ OFThreadPool.m Index: src/OFTCPSocket.h ================================================================== --- src/OFTCPSocket.h +++ src/OFTCPSocket.h @@ -166,14 +166,14 @@ block: (of_tcpsocket_async_connect_block_t)block; # endif #endif /*! - * @brief Bind the socket on the specified port and host. + * @brief Bind 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. + * @param host The host to bind to. Use `@"0.0.0.0"` for IPv4 or `@"::"` for + * IPv6 to bind to all. * @param port The port to bind to. If the port is 0, an unused port will be * chosen, which can be obtained using the return value. * @return The port the socket was bound to */ - (uint16_t)bindToHost: (OFString*)host ADDED src/OFUDPSocket.h Index: src/OFUDPSocket.h ================================================================== --- src/OFUDPSocket.h +++ src/OFUDPSocket.h @@ -0,0 +1,146 @@ +/* + * 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 "OFObject.h" + +#import "socket.h" + +/*! @file */ + +/*! + * @brief A struct which represents a host / port pair for a UDP socket. + */ +typedef struct { + struct sockaddr_storage address; + socklen_t length; +} of_udp_socket_address_t; + +/*! + * @brief A class which provides functions to create and use UDP sockets. + * + * Addresses are of type @ref of_udp_socket_address_t. You can use + * @ref resolveAddressForHost:port:address: to create an address for a host / + * port pair and @ref hostForAddress:port: to get the host / port pair for an + * address. If you want to compare two addresses, you can use + * @ref of_udp_socket_address_equal and you can use + * @ref of_udp_socket_address_hash to get a hash to use in e.g. @ref OFMapTable. + */ +@interface OFUDPSocket: OFObject +{ + int _socket; +} + +/*! + * @brief Returns a new, autoreleased OFUDPSocket. + * + * @return A new, autoreleased OFUDPSocket + */ ++ (instancetype)socket; + +/*! + * @brief Resolves the specified host and creates a host / port pair together + * with the specified port. + * + * @param host The host to resolve + * @param port The port for the resulting host / port pair + * @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_udp_socket_address_t*)address; + +/*! + * @brief Returns the host for the specified address and optionally the port. + * + * @param address The address for which the host and (optionally) the port + * should be returned + * @param port A pointer to an uint16_t. If it is not NULL, the port of the + * host / port pair will be written to it. + * @return The host of the host / port pair + */ ++ (OFString*)hostForAddress: (of_udp_socket_address_t*)address + port: (uint16_t*)port; + +/*! + * @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. + * @param port The port to bind to. If the port is 0, an unused port will be + * chosen, which can be obtained using the return value. + * @return The port the socket was bound to + */ +- (uint16_t)bindToHost: (OFString*)host + port: (uint16_t)port; + +/*! + * @brief Receives a datagram and stores it into the specified buffer. + * + * If the buffer is too small, the datagram is truncated. + * + * @param buffer The buffer to write the datagram to + * @param length The length of the buffer + * @param sender A pointer to an @ref of_udp_socket_address_t, which will be + * set to the address of the sender + * @return The length of the received datagram + */ +- (size_t)receiveIntoBuffer: (void*)buffer + length: (size_t)length + sender: (of_udp_socket_address_t*)sender; + +/*! + * @brief Sends the specified datagram to the specified address. + * + * @param buffer The buffer to send as a datagram + * @param length The length of the buffer + * @param receiver A pointer to an @ref of_udp_socket_address_t to which the + * datagram should be sent + */ +- (void)sendBuffer: (const void*)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t*)receiver; + +/*! + * @brief Closes the socket so that it can neither receive nor send any more + * datagrams. + */ +- (void)close; +@end + +#ifdef __cplusplus +extern "C" { +#endif +/*! + * @brief Compares two of_udp_socket_address_t for equality. + * + * @param address1 The address to compare with the second address + * @param address2 The second address + * @return Whether the two addresses are equal + */ +extern bool of_udp_socket_address_equal(of_udp_socket_address_t *address1, + of_udp_socket_address_t *address2); + +/*! + * @brief Returns the hash for the specified of_udp_socket_address_t. + * + * @param address The address to hash + * @return The hash for the specified of_udp_socket_address_t + */ +extern uint32_t of_udp_socket_address_hash(of_udp_socket_address_t *address); +#ifdef __cplusplus +} +#endif ADDED src/OFUDPSocket.m Index: src/OFUDPSocket.m ================================================================== --- src/OFUDPSocket.m +++ src/OFUDPSocket.m @@ -0,0 +1,296 @@ +/* + * 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 + +#import "OFUDPSocket.h" + +#import "OFBindFailedException.h" +#import "OFInvalidArgumentException.h" +#import "OFNotConnectedException.h" +#import "OFReadFailedException.h" +#import "OFWriteFailedException.h" + +#import "macros.h" +#import "resolver.h" +#import "socket_helpers.h" + +#ifdef __wii__ +static uint16_t freePort = 65532; +#endif + +bool +of_udp_socket_address_equal(of_udp_socket_address_t *address1, + of_udp_socket_address_t *address2) +{ + struct sockaddr_in *sin_1, *sin_2; +#ifdef AF_INET6 + struct sockaddr_in6 *sin6_1, *sin6_2; +#endif + + if (address1->address.ss_family != address2->address.ss_family) + return false; + + switch (address1->address.ss_family) { + case AF_INET: + if (address1->length < sizeof(struct sockaddr_in) || + address2->length < sizeof(struct sockaddr_in)) + @throw [OFInvalidArgumentException exception]; + + sin_1 = (struct sockaddr_in*)&address1->address; + sin_2 = (struct sockaddr_in*)&address2->address; + + if (sin_1->sin_port != sin_2->sin_port) + return false; + if (sin_1->sin_addr.s_addr != sin_2->sin_addr.s_addr) + return false; + + break; +#ifdef AF_INET6 + case AF_INET6: + if (address1->length < sizeof(struct sockaddr_in6) || + address2->length < sizeof(struct sockaddr_in6)) + @throw [OFInvalidArgumentException exception]; + + sin6_1 = (struct sockaddr_in6*)&address1->address; + sin6_2 = (struct sockaddr_in6*)&address2->address; + + if (sin6_1->sin6_port != sin6_2->sin6_port) + return false; + if (memcmp(sin6_1->sin6_addr.s6_addr, + sin6_2->sin6_addr.s6_addr, + sizeof(sin6_1->sin6_addr.s6_addr))) + return false; + + break; +#endif + default: + @throw [OFInvalidArgumentException exception]; + } + + return true; +} + +uint32_t +of_udp_socket_address_hash(of_udp_socket_address_t *address) +{ + uint32_t hash = of_hash_seed; + struct sockaddr_in *sin; +#ifdef AF_INET6 + struct sockaddr_in6 *sin6; + uint32_t subhash; + size_t i; +#endif + + hash += address->address.ss_family; + + switch (address->address.ss_family) { + case AF_INET: + if (address->length < sizeof(struct sockaddr_in)) + @throw [OFInvalidArgumentException exception]; + + sin = (struct sockaddr_in*)&address->address; + + hash += (sin->sin_port << 1); + hash ^= sin->sin_addr.s_addr; + + break; +#ifdef AF_INET6 + case AF_INET6: + if (address->length < sizeof(struct sockaddr_in6)) + @throw [OFInvalidArgumentException exception]; + + sin6 = (struct sockaddr_in6*)&address->address; + + hash += (sin6->sin6_port << 1); + + OF_HASH_INIT(subhash); + + for (i = 0; i < sizeof(sin6->sin6_addr.s6_addr); i++) + OF_HASH_ADD(subhash, sin6->sin6_addr.s6_addr[i]); + + OF_HASH_FINALIZE(subhash); + + hash ^= subhash; + + break; +#endif + default: + @throw [OFInvalidArgumentException exception]; + } + + return hash; +} + +@implementation OFUDPSocket ++ (instancetype)socket +{ + return [[[self alloc] init] autorelease]; +} + ++ (void)resolveAddressForHost: (OFString*)host + port: (uint16_t)port + address: (of_udp_socket_address_t*)address +{ + of_resolver_result_t **results = + of_resolve_host(host, port, SOCK_DGRAM); + + assert(results[0]->addressLength <= sizeof(address->address)); + + memcpy(&address->address, results[0]->address, + results[0]->addressLength); + address->length = results[0]->addressLength; + + of_resolver_free(results); +} + ++ (OFString*)hostForAddress: (of_udp_socket_address_t*)address + port: (uint16_t*)port +{ + return of_address_to_string_and_port( + (struct sockaddr*)&address->address, address->length, port); +} + +- init +{ + self = [super init]; + + _socket = INVALID_SOCKET; + + return self; +} + +- (void)dealloc +{ + if (_socket != INVALID_SOCKET) + [self close]; + + [super dealloc]; +} + +- (uint16_t)bindToHost: (OFString*)host + port: (uint16_t)port +{ + of_resolver_result_t **results; +#ifndef __wii__ + union { + struct sockaddr_storage storage; + struct sockaddr_in in; +# ifdef AF_INET6 + struct sockaddr_in6 in6; +# endif + } addr; + socklen_t addrLen; +#endif + +#ifdef __wii__ + if (port == 0) + port = freePort--; +#endif + + results = of_resolve_host(host, port, SOCK_DGRAM); + @try { + if ((_socket = socket(results[0]->family, results[0]->type, + results[0]->protocol)) == INVALID_SOCKET) + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: 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__ + addrLen = sizeof(addr.storage); + if (getsockname(_socket, (struct sockaddr*)&addr.storage, &addrLen)) { + close(_socket); + _socket = INVALID_SOCKET; + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self]; + } + + if (addr.storage.ss_family == AF_INET) + return OF_BSWAP16_IF_LE(addr.in.sin_port); +# ifdef AF_INET6 + if (addr.storage.ss_family == AF_INET6) + return OF_BSWAP16_IF_LE(addr.in6.sin6_port); +# endif +#endif + + close(_socket); + _socket = INVALID_SOCKET; + @throw [OFBindFailedException exceptionWithHost: host + port: port + socket: self]; +} + +- (size_t)receiveIntoBuffer: (void*)buffer + length: (size_t)length + sender: (of_udp_socket_address_t*)sender +{ + ssize_t ret; + + if (_socket == INVALID_SOCKET) + @throw [OFNotConnectedException exceptionWithSocket: self]; + + sender->length = sizeof(sender->address); + + if ((ret = recvfrom(_socket, buffer, length, 0, + (struct sockaddr*)&sender->address, &sender->length)) < 0) + @throw [OFReadFailedException exceptionWithObject: self + requestedLength: length]; + + return ret; +} + +- (void)sendBuffer: (const void*)buffer + length: (size_t)length + receiver: (of_udp_socket_address_t*)receiver +{ + if (_socket == INVALID_SOCKET) + @throw [OFNotConnectedException exceptionWithSocket: self]; + + if (sendto(_socket, buffer, length, 0, + (struct sockaddr*)&receiver->address, receiver->length) < length) + @throw [OFWriteFailedException exceptionWithObject: self + requestedLength: length]; +} + +- (void)close +{ + if (_socket == INVALID_SOCKET) + @throw [OFNotConnectedException exceptionWithSocket: self]; + + close(_socket); + _socket = INVALID_SOCKET; +} +@end Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -53,10 +53,11 @@ # import "OFZIPArchiveEntry.h" #endif #ifdef OF_HAVE_SOCKETS # import "OFStreamSocket.h" # import "OFTCPSocket.h" +# import "OFUDPSocket.h" # import "OFTLSSocket.h" # import "OFStreamObserver.h" # import "OFHTTPRequest.h" # import "OFHTTPResponse.h" Index: src/exceptions/OFReadOrWriteFailedException.m ================================================================== --- src/exceptions/OFReadOrWriteFailedException.m +++ src/exceptions/OFReadOrWriteFailedException.m @@ -18,10 +18,11 @@ #import "OFReadOrWriteFailedException.h" #import "OFString.h" #ifdef OF_HAVE_SOCKETS # import "OFStreamSocket.h" +# import "OFUDPSocket.h" #endif #import "common.h" #import "macros.h" @@ -45,11 +46,12 @@ _object = [object retain]; _requestedLength = requestedLength; #ifdef OF_HAVE_SOCKETS - if ([object isKindOfClass: [OFStreamSocket class]]) + if ([object isKindOfClass: [OFStreamSocket class]] || + [object isKindOfClass: [OFUDPSocket class]]) _errNo = GET_SOCK_ERRNO; else #endif _errNo = GET_ERRNO; Index: tests/Makefile ================================================================== --- tests/Makefile +++ tests/Makefile @@ -29,11 +29,12 @@ ${OFHTTPCLIENTTESTS_M} SRCS_FILES = OFMD5HashTests.m \ OFSerializationTests.m \ OFSHA1HashTests.m SRCS_PLUGINS = OFPluginTests.m -SRCS_SOCKETS = OFTCPSocketTests.m +SRCS_SOCKETS = OFTCPSocketTests.m \ + OFUDPSocketTests.m SRCS_THREADS = OFThreadTests.m IOS_USER ?= mobile IOS_TMP ?= /tmp/objfw-test ADDED tests/OFUDPSocketTests.m Index: tests/OFUDPSocketTests.m ================================================================== --- tests/OFUDPSocketTests.m +++ tests/OFUDPSocketTests.m @@ -0,0 +1,81 @@ +/* + * 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 + +#import "OFUDPSocket.h" +#import "OFString.h" +#import "OFAutoreleasePool.h" + +#import "TestsAppDelegate.h" + +static OFString *module = @"OFUDPSocket"; + +@implementation TestsAppDelegate (OFUDPSocketTests) +- (void)UDPSocketTests +{ + OFAutoreleasePool *pool = [[OFAutoreleasePool alloc] init]; + OFUDPSocket *sock; + uint16_t port1, port2; + of_udp_socket_address_t addr1, addr2, addr3; + char buf[6]; + + TEST(@"+[socket]", (sock = [OFUDPSocket socket])) + + 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])) + + TEST(@"-[sendBuffer:length:receiver:]", + R([sock sendBuffer: "Hello" + length: 6 + receiver: &addr1])) + + TEST(@"-[receiveIntoBuffer:length:sender:]", + [sock receiveIntoBuffer: buf + length: 6 + sender: &addr2] == 6 && + !memcmp(buf, "Hello", 6)) + + TEST(@"+[hostForAddress:port:]", + [[OFUDPSocket hostForAddress: &addr2 + port: &port2] isEqual: @"127.0.0.1"] && + port2 == port1) + + [OFUDPSocket resolveAddressForHost: @"127.0.0.1" + port: port1 + 1 + address: &addr3]; + + TEST(@"of_udp_socket_address_equal()", + of_udp_socket_address_equal(&addr1, &addr2) && + !of_udp_socket_address_equal(&addr1, &addr3)) + + TEST(@"of_udp_socket_address_hash()", + of_udp_socket_address_hash(&addr1) == + of_udp_socket_address_hash(&addr2) && + of_udp_socket_address_hash(&addr1) != + of_udp_socket_address_hash(&addr3)) + + [pool drain]; +} +@end Index: tests/TestsAppDelegate.h ================================================================== --- tests/TestsAppDelegate.h +++ tests/TestsAppDelegate.h @@ -149,10 +149,14 @@ @end @interface TestsAppDelegate (OFTCPSocketTests) - (void)TCPSocketTests; @end + +@interface TestsAppDelegate (OFUDPSocketTests) +- (void)UDPSocketTests; +@end @interface TestsAppDelegate (OFThreadTests) - (void)threadTests; @end Index: tests/TestsAppDelegate.m ================================================================== --- tests/TestsAppDelegate.m +++ tests/TestsAppDelegate.m @@ -312,10 +312,11 @@ [self dateTests]; [self numberTests]; [self streamTests]; #ifdef OF_HAVE_SOCKETS [self TCPSocketTests]; + [self UDPSocketTests]; #endif #ifdef OF_HAVE_THREADS [self threadTests]; #endif [self URLTests];