ObjFW  Check-in [857f8edc09]

Overview
Comment:Add OFSPXSocket
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 857f8edc09086abe9c8f74f7fb216e83a841711d22d6d505fc5c0c58dc899448
User & Date: js on 2020-04-29 23:59:08
Other Links: manifest | tags
Context
2020-05-01
11:09
OFSPXSocketTests: Handle IPX but not SPX support check-in: 13de836451 user: js tags: trunk
2020-04-29
23:59
Add OFSPXSocket check-in: 857f8edc09 user: js tags: trunk
2020-04-26
19:39
Remove redundant arguments from blocks check-in: d7ddb3dbc7 user: js tags: trunk
Changes

Modified configure.ac from [8421da9224] to [cd621d1afb].

  1450   1450   			#ifdef AF_IPX
  1451   1451   			egrep_cpp_yes
  1452   1452   			#endif
  1453   1453   		], [
  1454   1454   			AC_DEFINE(OF_HAVE_IPX, 1, [Whether we have IPX/SPX])
  1455   1455   			AC_SUBST(OFIPXSOCKET_M, OFIPXSocket.m)
  1456   1456   			AC_SUBST(OFIPXSOCKETTESTS_M, OFIPXSocketTests.m)
         1457  +			AC_SUBST(OFSPXSOCKET_M, OFSPXSocket.m)
         1458  +			AC_SUBST(OFSPXSOCKETTESTS_M, OFSPXSocketTests.m)
  1457   1459   		])
  1458   1460   	])
  1459   1461   
  1460   1462   	AC_CHECK_FUNCS(paccept accept4, break)
  1461   1463   
  1462   1464   	AC_CHECK_FUNCS(kqueue1 kqueue, [
  1463   1465   		AC_DEFINE(HAVE_KQUEUE, 1, [Whether we have kqueue])

Modified extra.mk.in from [d873097dd9] to [ceddd5b2ef].

    62     62   OFHTTPCLIENTTESTS_M = @OFHTTPCLIENTTESTS_M@
    63     63   OFIPXSOCKETTESTS_M = @OFIPXSOCKETTESTS_M@
    64     64   OFIPXSOCKET_M = @OFIPXSOCKET_M@
    65     65   OFKQUEUEKERNELEVENTOBSERVER_M = @OFKQUEUEKERNELEVENTOBSERVER_M@
    66     66   OFPOLLKERNELEVENTOBSERVER_M = @OFPOLLKERNELEVENTOBSERVER_M@
    67     67   OFPROCESS_M = @OFPROCESS_M@
    68     68   OFSELECTKERNELEVENTOBSERVER_M = @OFSELECTKERNELEVENTOBSERVER_M@
           69  +OFSPXSOCKETTESTS_M = @OFSPXSOCKETTESTS_M@
           70  +OFSPXSOCKET_M = @OFSPXSOCKET_M@
    69     71   OFSTDIOSTREAM_WIN32CONSOLE_M = @OFSTDIOSTREAM_WIN32CONSOLE_M@
    70     72   REEXPORT_RUNTIME = @REEXPORT_RUNTIME@
    71     73   REEXPORT_RUNTIME_FRAMEWORK = @REEXPORT_RUNTIME_FRAMEWORK@
    72     74   RUNTIME = @RUNTIME@
    73     75   RUNTIME_FRAMEWORK_LIBS = @RUNTIME_FRAMEWORK_LIBS@
    74     76   RUNTIME_LIBS = @RUNTIME_LIBS@
    75     77   RUN_TESTS = @RUN_TESTS@

Modified src/Makefile from [ccd861e5df] to [d03b7e35ac].

   142    142   	       OFHTTPClient.m			\
   143    143   	       OFHTTPCookie.m			\
   144    144   	       OFHTTPCookieManager.m		\
   145    145   	       OFHTTPRequest.m			\
   146    146   	       OFHTTPResponse.m			\
   147    147   	       OFHTTPServer.m			\
   148    148   	       ${OFIPXSOCKET_M}			\
          149  +	       ${OFSPXSOCKET_M}			\
   149    150   	       OFSequencedPacketSocket.m	\
   150    151   	       OFStreamSocket.m			\
   151    152   	       OFTCPSocket.m			\
   152    153   	       OFUDPSocket.m			\
   153    154   	       socket.m
   154    155   SRCS_THREADS = OFCondition.m		\
   155    156   	       OFMutex.m		\

Modified src/OFIPXSocket.h from [4f6d73d640] to [28e1d8cb6f].

    60     60    * @note The delegate is retained for as long as asynchronous operations are
    61     61    *	 still ongoing.
    62     62    */
    63     63   @property OF_NULLABLE_PROPERTY (assign, nonatomic)
    64     64       id <OFIPXSocketDelegate> delegate;
    65     65   
    66     66   /*!
    67         - * @brief Binds the socket to the specified network, node and port with the
           67  + * @brief Bind the socket to the specified network, node and port with the
    68     68    *	  specified packet type.
    69     69    *
    70     70    * @param port The port (sometimes called socket number) to bind to. 0 means to
    71     71    *	       pick one and return it.
    72     72    * @param packetType The packet type to use on the socket
    73     73    * @return The address on which this socket can be reached
    74     74    */
    75     75   - (of_socket_address_t)bindToPort: (uint16_t)port
    76     76   		       packetType: (uint8_t)packetType;
    77     77   @end
    78     78   
    79     79   OF_ASSUME_NONNULL_END

Modified src/OFRunLoop+Private.h from [72e897ec44] to [f46c29fa91].

    25     25   
    26     26   OF_ASSUME_NONNULL_BEGIN
    27     27   
    28     28   #ifdef OF_HAVE_SOCKETS
    29     29   @protocol OFRunLoopConnectDelegate <OFObject>
    30     30   - (void)of_socketDidConnect: (id)socket
    31     31   		  exception: (nullable id)exception;
           32  +- (id)of_connectionFailedExceptionForErrNo: (int)errNo;
    32     33   @end
    33     34   #endif
    34     35   
    35     36   @interface OFRunLoop ()
    36     37   + (void)of_setMainRunLoop: (OFRunLoop *)runLoop;
    37     38   #ifdef OF_HAVE_SOCKETS
    38     39   + (void)of_addAsyncReadForStream: (OFStream <OFReadyForReadingObserving> *)

Modified src/OFRunLoop.m from [964085efa2] to [8701d2b9f0].

    40     40   #endif
    41     41   #import "OFSortedList.h"
    42     42   #import "OFTimer.h"
    43     43   #import "OFTimer+Private.h"
    44     44   #import "OFDate.h"
    45     45   
    46     46   #import "OFObserveFailedException.h"
    47         -#ifdef OF_HAVE_SOCKETS
    48         -# import "OFConnectionFailedException.h"
    49         -#endif
    50     47   
    51     48   of_run_loop_mode_t of_run_loop_mode_default = @"of_run_loop_mode_default";
    52     49   static OFRunLoop *mainRunLoop = nil;
    53     50   
    54     51   @interface OFRunLoopState: OFObject
    55     52   #ifdef OF_HAVE_SOCKETS
    56     53       <OFKernelEventObserverDelegate>
................................................................................
   716    713   @implementation OFRunLoopConnectQueueItem
   717    714   - (bool)handleObject: (id)object
   718    715   {
   719    716   	id exception = nil;
   720    717   	int errNo;
   721    718   
   722    719   	if ((errNo = [object of_socketError]) != 0)
   723         -		exception = [OFConnectionFailedException
   724         -		    exceptionWithHost: nil
   725         -				 port: 0
   726         -			       socket: object
   727         -				errNo: errNo];
          720  +		exception =
          721  +		    [_delegate of_connectionFailedExceptionForErrNo: errNo];
   728    722   
   729    723   	if ([_delegate respondsToSelector:
   730    724   	    @selector(of_socketDidConnect:exception:)]) {
   731    725   		/*
   732    726   		 * Make sure we only call the delegate once we removed the
   733    727   		 * socket from the kernel event observer. This is necessary as
   734    728   		 * otherwise we could try to connect to the next address and it

Added src/OFSPXSocket.h version [514525c494].

            1  +/*
            2  + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
            3  + *               2018, 2019, 2020
            4  + *   Jonathan Schleifer <js@nil.im>
            5  + *
            6  + * All rights reserved.
            7  + *
            8  + * This file is part of ObjFW. It may be distributed under the terms of the
            9  + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
           10  + * the packaging of this file.
           11  + *
           12  + * Alternatively, it may be distributed under the terms of the GNU General
           13  + * Public License, either version 2 or 3, which can be found in the file
           14  + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
           15  + * file.
           16  + */
           17  +
           18  +#import "OFSequencedPacketSocket.h"
           19  +#import "OFRunLoop.h"
           20  +
           21  +#import "socket.h"
           22  +
           23  +OF_ASSUME_NONNULL_BEGIN
           24  +
           25  +/*! @file */
           26  +
           27  +@class OFSPXSocket;
           28  +@class OFString;
           29  +
           30  +#ifdef OF_HAVE_BLOCKS
           31  +/*!
           32  + * @brief A block which is called when the socket connected.
           33  + *
           34  + * @param exception An exception which occurred while connecting the socket or
           35  + *		    `nil` on success
           36  + */
           37  +typedef void (^of_spx_socket_async_connect_block_t)(id _Nullable exception);
           38  +#endif
           39  +
           40  +/*!
           41  + * @protocol OFSPXSocketDelegate OFSPXSocket.h ObjFW/OFSPXSocket.h
           42  + *
           43  + * A delegate for OFSPXSocket.
           44  + */
           45  +@protocol OFSPXSocketDelegate <OFSequencedPacketSocketDelegate>
           46  +@optional
           47  +/*!
           48  + * @brief A method which is called when a socket connected.
           49  + *
           50  + * @param socket The socket which connected
           51  + * @param node The node the socket connected to
           52  + * @param network The network of the node the socket connected to
           53  + * @param port The port of the node to which the socket connected
           54  + * @param exception An exception that occurred while connecting, or nil on
           55  + *		    success
           56  + */
           57  +-     (void)socket: (OFSPXSocket *)socket
           58  +  didConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
           59  +	   network: (uint32_t)network
           60  +	      port: (uint16_t)port
           61  +	 exception: (nullable id)exception;
           62  +@end
           63  +
           64  +/*!
           65  + * @class OFSPXSocket OFSPXSocket.h ObjFW/OFSPXSocket.h
           66  + *
           67  + * @brief A class which provides methods to create and use SPX sockets.
           68  + *
           69  + * To connect to a server, create a socket and connect it.
           70  + * To create a server, create a socket, bind it and listen on it.
           71  + */
           72  +@interface OFSPXSocket: OFSequencedPacketSocket
           73  +{
           74  +	OF_RESERVE_IVARS(4)
           75  +}
           76  +
           77  +/*!
           78  + * @brief The delegate for asynchronous operations on the socket.
           79  + *
           80  + * @note The delegate is retained for as long as asynchronous operations are
           81  + *	 still ongoing.
           82  + */
           83  +@property OF_NULLABLE_PROPERTY (assign, nonatomic)
           84  +    id <OFSPXSocketDelegate> delegate;
           85  +
           86  +/*!
           87  + * @brief Connect the OFSPXSocket to the specified destination.
           88  + *
           89  + * @param node The node to connect to
           90  + * @param network The network on which the node to connect to is
           91  + * @param port The port (sometimes also called socket number) on the node to
           92  + *	       connect to
           93  + */
           94  +- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
           95  +	      network: (uint32_t)network
           96  +		 port: (uint16_t)port;
           97  +
           98  +/*!
           99  + * @brief Asynchronously connect the OFSPXSocket to the specified destination.
          100  + *
          101  + * @param node The node to connect to
          102  + * @param network The network on which the node to connect to is
          103  + * @param port The port (sometimes also called socket number) on the node to
          104  + *	       connect to
          105  + */
          106  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          107  +		   network: (uint32_t)network
          108  +		      port: (uint16_t)port;
          109  +
          110  +/*!
          111  + * @brief Asynchronously connect the OFSPXSocket to the specified destination.
          112  + *
          113  + * @param node The node to connect to
          114  + * @param network The network on which the node to connect to is
          115  + * @param port The port (sometimes also called socket number) on the node to
          116  + *	       connect to
          117  + * @param runLoopMode The run loop mode in which to perform the async connect
          118  + */
          119  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          120  +		   network: (uint32_t)network
          121  +		      port: (uint16_t)port
          122  +	       runLoopMode: (of_run_loop_mode_t)runLoopMode;
          123  +
          124  +#ifdef OF_HAVE_BLOCKS
          125  +/*!
          126  + * @brief Asynchronously connect the OFSPXSocket to the specified destination.
          127  + *
          128  + * @param node The node to connect to
          129  + * @param network The network on which the node to connect to is
          130  + * @param port The port (sometimes also called socket number) on the node to
          131  + *	       connect to
          132  + * @param block The block to execute once the connection has been established
          133  + */
          134  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          135  +		   network: (uint32_t)network
          136  +		      port: (uint16_t)port
          137  +		     block: (of_spx_socket_async_connect_block_t)block;
          138  +
          139  +/*!
          140  + * @brief Asynchronously connect the OFSPXSocket to the specified destination.
          141  + *
          142  + * @param node The node to connect to
          143  + * @param network The network on which the node to connect to is
          144  + * @param port The port (sometimes also called socket number) on the node to
          145  + *	       connect to
          146  + * @param runLoopMode The run loop mode in which to perform the async connect
          147  + * @param block The block to execute once the connection has been established
          148  + */
          149  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          150  +		   network: (uint32_t)network
          151  +		      port: (uint16_t)port
          152  +	       runLoopMode: (of_run_loop_mode_t)runLoopMode
          153  +		     block: (of_spx_socket_async_connect_block_t)block;
          154  +#endif
          155  +
          156  +/*!
          157  + * @brief Bind the socket to the specified network, node and port with the
          158  + *	  specified packet type.
          159  + *
          160  + * @param port The port (sometimes called socket number) to bind to. 0 means to
          161  + *	       pick one and return it.
          162  + * @return The address on which this socket can be reached
          163  + */
          164  +- (of_socket_address_t)bindToPort: (uint16_t)port;
          165  +@end
          166  +
          167  +OF_ASSUME_NONNULL_END

Added src/OFSPXSocket.m version [0d3987e8b0].

            1  +/*
            2  + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
            3  + *               2018, 2019, 2020
            4  + *   Jonathan Schleifer <js@nil.im>
            5  + *
            6  + * All rights reserved.
            7  + *
            8  + * This file is part of ObjFW. It may be distributed under the terms of the
            9  + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
           10  + * the packaging of this file.
           11  + *
           12  + * Alternatively, it may be distributed under the terms of the GNU General
           13  + * Public License, either version 2 or 3, which can be found in the file
           14  + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
           15  + * file.
           16  + */
           17  +
           18  +#include "config.h"
           19  +
           20  +#include <errno.h>
           21  +
           22  +#import "OFSPXSocket.h"
           23  +#import "OFRunLoop.h"
           24  +#import "OFRunLoop+Private.h"
           25  +
           26  +#import "OFAlreadyConnectedException.h"
           27  +#import "OFBindFailedException.h"
           28  +#import "OFConnectionFailedException.h"
           29  +
           30  +#import "socket.h"
           31  +#import "socket_helpers.h"
           32  +
           33  +#ifndef NSPROTO_SPX
           34  +# define NSPROTO_SPX 0
           35  +#endif
           36  +
           37  +#define SPX_PACKET_TYPE 5
           38  +
           39  +@interface OFSPXSocketAsyncConnectDelegate: OFObject <OFRunLoopConnectDelegate>
           40  +{
           41  +	OFSPXSocket *_socket;
           42  +	unsigned char _node[IPX_NODE_LEN];
           43  +	uint32_t _network;
           44  +	uint16_t _port;
           45  +#ifdef OF_HAVE_BLOCKS
           46  +	of_spx_socket_async_connect_block_t _block;
           47  +#endif
           48  +}
           49  +
           50  +- (instancetype)initWithSocket: (OFSPXSocket *)socket
           51  +			  node: (unsigned char [IPX_NODE_LEN])node
           52  +		       network: (uint32_t)network
           53  +			  port: (uint16_t)port
           54  +#ifdef OF_HAVE_BLOCKS
           55  +			 block: (of_spx_socket_async_connect_block_t)block
           56  +#endif
           57  +;
           58  +- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
           59  +@end
           60  +
           61  +@implementation OFSPXSocketAsyncConnectDelegate
           62  +- (instancetype)initWithSocket: (OFSPXSocket *)sock
           63  +			  node: (unsigned char [IPX_NODE_LEN])node
           64  +		       network: (uint32_t)network
           65  +			  port: (uint16_t)port
           66  +#ifdef OF_HAVE_BLOCKS
           67  +			 block: (of_spx_socket_async_connect_block_t)block
           68  +#endif
           69  +{
           70  +	self = [super init];
           71  +
           72  +	@try {
           73  +		_socket = [sock retain];
           74  +		memcpy(_node, node, IPX_NODE_LEN);
           75  +		_network = network;
           76  +		_port = port;
           77  +#ifdef OF_HAVE_BLOCKS
           78  +		_block = [block copy];
           79  +#endif
           80  +	} @catch (id e) {
           81  +		[self release];
           82  +		@throw e;
           83  +	}
           84  +
           85  +	return self;
           86  +}
           87  +
           88  +- (void)dealloc
           89  +{
           90  +	[_socket release];
           91  +#ifdef OF_HAVE_BLOCKS
           92  +	[_block release];
           93  +#endif
           94  +
           95  +	[super dealloc];
           96  +}
           97  +
           98  +- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
           99  +{
          100  +	id exception = nil;
          101  +
          102  +	_socket.blocking = false;
          103  +
          104  +	@try {
          105  +		[_socket connectToNode: _node
          106  +			       network: _network
          107  +				  port: _port];
          108  +	} @catch (OFConnectionFailedException *e) {
          109  +		if (e.errNo == EINPROGRESS) {
          110  +			[OFRunLoop of_addAsyncConnectForSocket: _socket
          111  +							  mode: runLoopMode
          112  +						      delegate: self];
          113  +			return;
          114  +		}
          115  +
          116  +		exception = e;
          117  +	}
          118  +
          119  +	[self performSelector: @selector(of_socketDidConnect:exception:)
          120  +		   withObject: _socket
          121  +		   withObject: exception
          122  +		   afterDelay: 0];
          123  +}
          124  +
          125  +- (void)of_socketDidConnect: (id)sock
          126  +		  exception: (id)exception
          127  +{
          128  +	id <OFSPXSocketDelegate> delegate = ((OFSPXSocket *)sock).delegate;
          129  +
          130  +	if (exception == nil)
          131  +		((OFSPXSocket *)sock).blocking = true;
          132  +
          133  +#ifdef OF_HAVE_BLOCKS
          134  +	if (_block != NULL)
          135  +		_block(exception);
          136  +	else {
          137  +#endif
          138  +		if ([delegate respondsToSelector:
          139  +		    @selector(socket:didConnectToNode:network:port:exception:)])
          140  +			[delegate     socket: _socket
          141  +			    didConnectToNode: _node
          142  +				     network: _network
          143  +					port: _port
          144  +				   exception: exception];
          145  +#ifdef OF_HAVE_BLOCKS
          146  +	}
          147  +#endif
          148  +}
          149  +
          150  +- (id)of_connectionFailedExceptionForErrNo: (int)errNo
          151  +{
          152  +	return [OFConnectionFailedException exceptionWithNode: _node
          153  +						      network: _network
          154  +							 port: _port
          155  +						       socket: _socket
          156  +							errNo: errNo];
          157  +}
          158  +@end
          159  +
          160  +@implementation OFSPXSocket
          161  +@dynamic delegate;
          162  +
          163  +- (void)connectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          164  +	      network: (uint32_t)network
          165  +		 port: (uint16_t)port
          166  +{
          167  +	of_socket_address_t address =
          168  +	    of_socket_address_ipx(network, node, port);
          169  +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
          170  +	int flags;
          171  +#endif
          172  +
          173  +	if ((_socket = socket(address.sockaddr.ipx.sipx_family,
          174  +	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) == INVALID_SOCKET)
          175  +		@throw [OFConnectionFailedException
          176  +		    exceptionWithNode: node
          177  +			      network: network
          178  +				 port: port
          179  +			       socket: self
          180  +				errNo: of_socket_errno()];
          181  +
          182  +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
          183  +	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
          184  +		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
          185  +#endif
          186  +
          187  +	if (connect(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
          188  +		int errNo = of_socket_errno();
          189  +
          190  +		closesocket(_socket);
          191  +
          192  +		@throw [OFConnectionFailedException
          193  +		    exceptionWithNode: node
          194  +			      network: network
          195  +				 port: port
          196  +			       socket: self
          197  +				errNo: errNo];
          198  +	}
          199  +}
          200  +
          201  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          202  +		   network: (uint32_t)network
          203  +		      port: (uint16_t)port
          204  +{
          205  +	[self asyncConnectToNode: node
          206  +			 network: network
          207  +			    port: port
          208  +		     runLoopMode: of_run_loop_mode_default];
          209  +}
          210  +
          211  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          212  +		   network: (uint32_t)network
          213  +		      port: (uint16_t)port
          214  +	       runLoopMode: (of_run_loop_mode_t)runLoopMode
          215  +{
          216  +	void *pool = objc_autoreleasePoolPush();
          217  +
          218  +	[[[[OFSPXSocketAsyncConnectDelegate alloc]
          219  +	    initWithSocket: self
          220  +		      node: node
          221  +		   network: network
          222  +		      port: port
          223  +#ifdef OF_HAVE_BLOCKS
          224  +		     block: NULL
          225  +#endif
          226  +	    ] autorelease] startWithRunLoopMode: runLoopMode];
          227  +
          228  +	objc_autoreleasePoolPop(pool);
          229  +}
          230  +
          231  +#ifdef OF_HAVE_BLOCKS
          232  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          233  +		   network: (uint32_t)network
          234  +		      port: (uint16_t)port
          235  +		     block: (of_spx_socket_async_connect_block_t)block
          236  +{
          237  +	[self asyncConnectToNode: node
          238  +			 network: network
          239  +			    port: port
          240  +		     runLoopMode: of_run_loop_mode_default
          241  +			   block: block];
          242  +}
          243  +
          244  +- (void)asyncConnectToNode: (unsigned char [_Nonnull IPX_NODE_LEN])node
          245  +		   network: (uint32_t)network
          246  +		      port: (uint16_t)port
          247  +	       runLoopMode: (of_run_loop_mode_t)runLoopMode
          248  +		     block: (of_spx_socket_async_connect_block_t)block
          249  +{
          250  +	void *pool = objc_autoreleasePoolPush();
          251  +
          252  +	[[[[OFSPXSocketAsyncConnectDelegate alloc]
          253  +	    initWithSocket: self
          254  +		      node: node
          255  +		   network: network
          256  +		      port: port
          257  +		     block: block
          258  +	    ] autorelease] startWithRunLoopMode: runLoopMode];
          259  +
          260  +	objc_autoreleasePoolPop(pool);
          261  +}
          262  +#endif
          263  +
          264  +- (of_socket_address_t)bindToPort: (uint16_t)port
          265  +{
          266  +	const unsigned char zeroNode[IPX_NODE_LEN] = { 0 };
          267  +	of_socket_address_t address;
          268  +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
          269  +	int flags;
          270  +#endif
          271  +
          272  +	if (_socket != INVALID_SOCKET)
          273  +		@throw [OFAlreadyConnectedException exceptionWithSocket: self];
          274  +
          275  +	address = of_socket_address_ipx(0, zeroNode, port);
          276  +
          277  +	if ((_socket = socket(address.sockaddr.sockaddr.sa_family,
          278  +	    SOCK_SEQPACKET | SOCK_CLOEXEC, NSPROTO_SPX)) == INVALID_SOCKET)
          279  +		@throw [OFBindFailedException
          280  +		    exceptionWithPort: port
          281  +			   packetType: SPX_PACKET_TYPE
          282  +			       socket: self
          283  +				errNo: of_socket_errno()];
          284  +
          285  +	_blocking = true;
          286  +
          287  +#if SOCK_CLOEXEC == 0 && defined(HAVE_FCNTL_H) && defined(FD_CLOEXEC)
          288  +	if ((flags = fcntl(_socket, F_GETFD, 0)) != -1)
          289  +		fcntl(_socket, F_SETFD, flags | FD_CLOEXEC);
          290  +#endif
          291  +
          292  +	if (bind(_socket, &address.sockaddr.sockaddr, address.length) != 0) {
          293  +		int errNo = of_socket_errno();
          294  +
          295  +		if (errNo != EINPROGRESS) {
          296  +			closesocket(_socket);
          297  +			_socket = INVALID_SOCKET;
          298  +		}
          299  +
          300  +		@throw [OFBindFailedException exceptionWithPort: port
          301  +						     packetType: SPX_PACKET_TYPE
          302  +							 socket: self
          303  +							  errNo: errNo];
          304  +	}
          305  +
          306  +	memset(&address, 0, sizeof(address));
          307  +	address.family = OF_SOCKET_ADDRESS_FAMILY_IPX;
          308  +	address.length = (socklen_t)sizeof(address.sockaddr);
          309  +
          310  +	if (of_getsockname(_socket, &address.sockaddr.sockaddr,
          311  +	    &address.length) != 0) {
          312  +		int errNo = of_socket_errno();
          313  +
          314  +		closesocket(_socket);
          315  +		_socket = INVALID_SOCKET;
          316  +
          317  +		@throw [OFBindFailedException exceptionWithPort: port
          318  +						     packetType: SPX_PACKET_TYPE
          319  +							 socket: self
          320  +							  errNo: errNo];
          321  +	}
          322  +
          323  +	if (address.sockaddr.sockaddr.sa_family != AF_IPX) {
          324  +		closesocket(_socket);
          325  +		_socket = INVALID_SOCKET;
          326  +
          327  +		@throw [OFBindFailedException exceptionWithPort: port
          328  +						     packetType: SPX_PACKET_TYPE
          329  +							 socket: self
          330  +							  errNo: EAFNOSUPPORT];
          331  +	}
          332  +
          333  +	return address;
          334  +}
          335  +@end

Modified src/OFStream.h from [854f995c5b] to [849d120fde].

   188    188    *	 override these methods without the `lowlevel` prefix, you *will* break
   189    189    *	 caching and get broken results!
   190    190    */
   191    191   @interface OFStream: OFObject <OFCopying>
   192    192   {
   193    193   	bool _blocking;
   194    194   	id _Nullable _delegate;
   195         -#if !defined(OF_SEEKABLE_STREAM_M) && !defined(OF_TCP_SOCKET_M)
          195  +#ifndef OF_SEEKABLE_STREAM_M
   196    196   @private
   197    197   #endif
   198    198   	char *_Nullable _readBuffer, *_Nullable _readBufferMemory;
   199    199   	char *_Nullable _writeBuffer;
   200    200   	size_t _readBufferLength, _writeBufferLength;
   201    201   	bool _writeBuffered, _waitingForDelimiter;
   202    202   	OF_RESERVE_IVARS(4)

Modified src/OFTCPSocket.m from [36820b9499] to [ef285e844b].

    11     11    *
    12     12    * Alternatively, it may be distributed under the terms of the GNU General
    13     13    * Public License, either version 2 or 3, which can be found in the file
    14     14    * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
    15     15    * file.
    16     16    */
    17     17   
    18         -#define OF_TCP_SOCKET_M
    19     18   #define __NO_EXT_QNX
    20     19   
    21     20   #include "config.h"
    22     21   
    23     22   #include <assert.h>
    24     23   #include <errno.h>
    25     24   #include <stdio.h>
................................................................................
    99     98   }
   100     99   
   101    100   - (instancetype)initWithSocket: (OFTCPSocket *)sock
   102    101   			  host: (OFString *)host
   103    102   			  port: (uint16_t)port
   104    103   		    SOCKS5Host: (OFString *)SOCKS5Host
   105    104   		    SOCKS5Port: (uint16_t)SOCKS5Port
   106         -		      delegate: (id <OFTCPSocketDelegate>)delegate;
          105  +		      delegate: (id <OFTCPSocketDelegate>)delegate
   107    106   #ifdef OF_HAVE_BLOCKS
   108         -- (instancetype)initWithSocket: (OFTCPSocket *)sock
   109         -			  host: (OFString *)host
   110         -			  port: (uint16_t)port
   111         -		    SOCKS5Host: (OFString *)SOCKS5Host
   112         -		    SOCKS5Port: (uint16_t)SOCKS5Port
   113         -			 block: (of_tcp_socket_async_connect_block_t)block;
          107  +			 block: (of_tcp_socket_async_connect_block_t)block
   114    108   #endif
          109  +;
   115    110   - (void)didConnect;
   116    111   - (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
   117    112   - (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
   118    113   - (void)sendSOCKS5Request;
   119    114   @end
   120    115   
   121    116   @interface OFTCPSocketConnectDelegate: OFObject <OFTCPSocketDelegate>
................................................................................
   129    124   @implementation OFTCPSocketAsyncConnectDelegate
   130    125   - (instancetype)initWithSocket: (OFTCPSocket *)sock
   131    126   			  host: (OFString *)host
   132    127   			  port: (uint16_t)port
   133    128   		    SOCKS5Host: (OFString *)SOCKS5Host
   134    129   		    SOCKS5Port: (uint16_t)SOCKS5Port
   135    130   		      delegate: (id <OFTCPSocketDelegate>)delegate
          131  +#ifdef OF_HAVE_BLOCKS
          132  +			 block: (of_tcp_socket_async_connect_block_t)block
          133  +#endif
   136    134   {
   137    135   	self = [super init];
   138    136   
   139    137   	@try {
   140    138   		_socket = [sock retain];
   141    139   		_host = [host copy];
   142    140   		_port = port;
   143    141   		_SOCKS5Host = [SOCKS5Host copy];
   144    142   		_SOCKS5Port = SOCKS5Port;
   145    143   		_delegate = [delegate retain];
          144  +#ifdef OF_HAVE_BLOCKS
          145  +		_block = [block copy];
          146  +#endif
   146    147   
   147    148   		_socket.delegate = self;
   148    149   	} @catch (id e) {
   149    150   		[self release];
   150    151   		@throw e;
   151    152   	}
   152    153   
   153    154   	return self;
   154    155   }
   155    156   
   156         -#ifdef OF_HAVE_BLOCKS
   157         -- (instancetype)initWithSocket: (OFTCPSocket *)sock
   158         -			  host: (OFString *)host
   159         -			  port: (uint16_t)port
   160         -		    SOCKS5Host: (OFString *)SOCKS5Host
   161         -		    SOCKS5Port: (uint16_t)SOCKS5Port
   162         -			 block: (of_tcp_socket_async_connect_block_t)block
   163         -{
   164         -	self = [super init];
   165         -
   166         -	@try {
   167         -		_socket = [sock retain];
   168         -		_host = [host copy];
   169         -		_port = port;
   170         -		_SOCKS5Host = [SOCKS5Host copy];
   171         -		_SOCKS5Port = SOCKS5Port;
   172         -		_block = [block copy];
   173         -	} @catch (id e) {
   174         -		[self release];
   175         -		@throw e;
   176         -	}
   177         -
   178         -	return self;
   179         -}
   180         -#endif
   181         -
   182    157   - (void)dealloc
   183    158   {
   184    159   #ifdef OF_HAVE_BLOCKS
   185    160   	if (_block == NULL)
   186    161   #endif
   187    162   		if (_socket.delegate == self)
   188    163   			_socket.delegate = _delegate;
................................................................................
   263    238   	}
   264    239   
   265    240   	if (_SOCKS5Host != nil)
   266    241   		[self sendSOCKS5Request];
   267    242   	else
   268    243   		[self didConnect];
   269    244   }
          245  +
          246  +- (id)of_connectionFailedExceptionForErrNo: (int)errNo
          247  +{
          248  +	return [OFConnectionFailedException exceptionWithHost: _host
          249  +							 port: _port
          250  +						       socket: _socket
          251  +							errNo: errNo];
          252  +}
   270    253   
   271    254   - (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
   272    255   {
   273    256   	of_socket_address_t address = *(const of_socket_address_t *)
   274    257   	    [_socketAddresses itemAtIndex: _socketAddressesIndex++];
   275    258   	int errNo;
   276    259   
................................................................................
   699    682   
   700    683   - (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
   701    684   			    errNo: (int *)errNo
   702    685   {
   703    686   	if (_socket == INVALID_SOCKET)
   704    687   		@throw [OFNotOpenException exceptionWithObject: self];
   705    688   
   706         -	if (connect(_socket, (struct sockaddr *)&address->sockaddr.sockaddr,
          689  +	if (connect(_socket, &address->sockaddr.sockaddr,
   707    690   	    address->length) != 0) {
   708    691   		*errNo = of_socket_errno();
   709    692   		return false;
   710    693   	}
   711    694   
   712    695   	return true;
   713    696   }
................................................................................
   764    747   
   765    748   	[[[[OFTCPSocketAsyncConnectDelegate alloc]
   766    749   		  initWithSocket: self
   767    750   			    host: host
   768    751   			    port: port
   769    752   		      SOCKS5Host: _SOCKS5Host
   770    753   		      SOCKS5Port: _SOCKS5Port
   771         -			delegate: _delegate] autorelease]
   772         -	    startWithRunLoopMode: runLoopMode];
          754  +			delegate: _delegate
          755  +#ifdef OF_HAVE_BLOCKS
          756  +			   block: NULL
          757  +#endif
          758  +	    ] autorelease] startWithRunLoopMode: runLoopMode];
   773    759   
   774    760   	objc_autoreleasePoolPop(pool);
   775    761   }
   776    762   
   777    763   #ifdef OF_HAVE_BLOCKS
   778    764   - (void)asyncConnectToHost: (OFString *)host
   779    765   		      port: (uint16_t)port
................................................................................
   794    780   
   795    781   	[[[[OFTCPSocketAsyncConnectDelegate alloc]
   796    782   		  initWithSocket: self
   797    783   			    host: host
   798    784   			    port: port
   799    785   		      SOCKS5Host: _SOCKS5Host
   800    786   		      SOCKS5Port: _SOCKS5Port
          787  +			delegate: nil
   801    788   			   block: block] autorelease]
   802    789   	    startWithRunLoopMode: runLoopMode];
   803    790   
   804    791   	objc_autoreleasePoolPop(pool);
   805    792   }
   806    793   #endif
   807    794   

Modified src/ObjFW.h from [fc9f9015ac] to [923afa7ee5].

    76     76   # import "OFKernelEventObserver.h"
    77     77   # import "OFDNSQuery.h"
    78     78   # import "OFDNSResourceRecord.h"
    79     79   # import "OFDNSResponse.h"
    80     80   # import "OFDNSResolver.h"
    81     81   # ifdef OF_HAVE_IPX
    82     82   #  import "OFIPXSocket.h"
           83  +#  import "OFSPXSocket.h"
    83     84   # endif
    84     85   #endif
    85     86   #ifdef OF_HAVE_SOCKETS
    86     87   # ifdef OF_HAVE_THREADS
    87     88   #  import "OFHTTPClient.h"
    88     89   # endif
    89     90   # import "OFHTTPCookie.h"

Modified src/exceptions/OFConnectionFailedException.h from [e2e47e53e8] to [cd5a362f90].

    16     16    */
    17     17   
    18     18   #import "OFException.h"
    19     19   
    20     20   #ifndef OF_HAVE_SOCKETS
    21     21   # error No sockets available!
    22     22   #endif
           23  +
           24  +#import "socket.h"
    23     25   
    24     26   OF_ASSUME_NONNULL_BEGIN
    25     27   
    26     28   /*!
    27     29    * @class OFConnectionFailedException \
    28     30    *	  OFConnectionFailedException.h ObjFW/OFConnectionFailedException.h
    29     31    *
................................................................................
    30     32    * @brief An exception indicating that a connection could not be established.
    31     33    */
    32     34   @interface OFConnectionFailedException: OFException
    33     35   {
    34     36   	id _socket;
    35     37   	OFString *_host;
    36     38   	uint16_t _port;
           39  +	unsigned char _node[IPX_NODE_LEN];
           40  +	uint32_t _network;
    37     41   	int _errNo;
    38     42   }
    39     43   
    40     44   /*!
    41     45    * @brief The socket which could not connect.
    42     46    */
    43     47   @property (readonly, nonatomic) id socket;
................................................................................
    48     52   @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFString *host;
    49     53   
    50     54   /*!
    51     55    * @brief The port on the host to which the connection failed.
    52     56    */
    53     57   @property (readonly, nonatomic) uint16_t port;
    54     58   
           59  +/*!
           60  + * @brief The IPX node to which the connection failed.
           61  + */
           62  +@property (readonly, nonatomic) unsigned char *node;
           63  +
           64  +/*!
           65  + * @brief The IPX network of the node to which the connection failed.
           66  + */
           67  +@property (readonly, nonatomic) uint32_t network;
           68  +
    55     69   /*!
    56     70    * @brief The errno of the error that occurred.
    57     71    */
    58     72   @property (readonly, nonatomic) int errNo;
    59     73   
    60     74   + (instancetype)exception OF_UNAVAILABLE;
    61     75   
................................................................................
    69     83    * @return A new, autoreleased connection failed exception
    70     84    */
    71     85   + (instancetype)exceptionWithHost: (nullable OFString *)host
    72     86   			     port: (uint16_t)port
    73     87   			   socket: (id)socket
    74     88   			    errNo: (int)errNo;
    75     89   
           90  +/*!
           91  + * @brief Creates a new, autoreleased connection failed exception.
           92  + *
           93  + * @param node The node to which the connection failed
           94  + * @param network The IPX network of the node to which the connection failed
           95  + * @param port The port on the node to which the connection failed
           96  + * @param socket The socket which could not connect
           97  + * @param errNo The errno of the error that occurred
           98  + * @return A new, autoreleased connection failed exception
           99  + */
          100  ++ (instancetype)exceptionWithNode: (unsigned char [_Nullable IPX_NODE_LEN])node
          101  +			  network: (uint32_t)network
          102  +			     port: (uint16_t)port
          103  +			   socket: (id)socket
          104  +			    errNo: (int)errNo;
          105  +
    76    106   - (instancetype)init OF_UNAVAILABLE;
    77    107   
    78    108   /*!
    79    109    * @brief Initializes an already allocated connection failed exception.
    80    110    *
    81    111    * @param host The host to which the connection failed
    82    112    * @param port The port on the host to which the connection failed
................................................................................
    83    113    * @param socket The socket which could not connect
    84    114    * @param errNo The errno of the error that occurred
    85    115    * @return An initialized connection failed exception
    86    116    */
    87    117   - (instancetype)initWithHost: (nullable OFString *)host
    88    118   			port: (uint16_t)port
    89    119   		      socket: (id)socket
    90         -		       errNo: (int)errNo OF_DESIGNATED_INITIALIZER;
          120  +		       errNo: (int)errNo;
          121  +
          122  +/*!
          123  + * @brief Initializes an already allocated connection failed exception.
          124  + *
          125  + * @param node The node to which the connection failed
          126  + * @param network The IPX network of the node to which the connection failed
          127  + * @param port The port on the node to which the connection failed
          128  + * @param socket The socket which could not connect
          129  + * @param errNo The errno of the error that occurred
          130  + * @return An initialized connection failed exception
          131  + */
          132  +- (instancetype)initWithNode: (unsigned char [_Nullable IPX_NODE_LEN])node
          133  +		     network: (uint32_t)network
          134  +			port: (uint16_t)port
          135  +		      socket: (id)socket
          136  +		       errNo: (int)errNo;
    91    137   @end
    92    138   
    93    139   OF_ASSUME_NONNULL_END

Modified src/exceptions/OFConnectionFailedException.m from [19c84f566e] to [d44715253f].

    17     17   
    18     18   #include "config.h"
    19     19   
    20     20   #import "OFConnectionFailedException.h"
    21     21   #import "OFString.h"
    22     22   
    23     23   @implementation OFConnectionFailedException
    24         -@synthesize host = _host, port = _port, socket = _socket, errNo = _errNo;
           24  +@synthesize host = _host, port = _port, network = _network, socket = _socket;
           25  +@synthesize errNo = _errNo;
    25     26   
    26     27   + (instancetype)exception
    27     28   {
    28     29   	OF_UNRECOGNIZED_SELECTOR
    29     30   }
    30     31   
    31     32   + (instancetype)exceptionWithHost: (OFString *)host
    32     33   			     port: (uint16_t)port
    33     34   			   socket: (id)socket
    34     35   			    errNo: (int)errNo
    35     36   {
    36     37   	return [[[self alloc] initWithHost: host
    37     38   				      port: port
           39  +				    socket: socket
           40  +				     errNo: errNo] autorelease];
           41  +}
           42  +
           43  ++ (instancetype)exceptionWithNode: (unsigned char [IPX_NODE_LEN])node
           44  +			  network: (uint32_t)network
           45  +			     port: (uint16_t)port
           46  +			   socket: (id)socket
           47  +			    errNo: (int)errNo
           48  +{
           49  +	return [[[self alloc] initWithNode: node
           50  +				   network: network
           51  +				      port: port
    38     52   				    socket: socket
    39     53   				     errNo: errNo] autorelease];
    40     54   }
    41     55   
    42     56   - (instancetype)init
    43     57   {
    44     58   	OF_INVALID_INIT_METHOD
................................................................................
    49     63   		      socket: (id)socket
    50     64   		       errNo: (int)errNo
    51     65   {
    52     66   	self = [super init];
    53     67   
    54     68   	@try {
    55     69   		_host = [host copy];
           70  +		_port = port;
    56     71   		_socket = [socket retain];
           72  +		_errNo = errNo;
           73  +	} @catch (id e) {
           74  +		[self release];
           75  +		@throw e;
           76  +	}
           77  +
           78  +	return self;
           79  +}
           80  +
           81  +- (instancetype)initWithNode: (unsigned char [IPX_NODE_LEN])node
           82  +		     network: (uint32_t)network
           83  +			port: (uint16_t)port
           84  +		      socket: (id)socket
           85  +		       errNo: (int)errNo
           86  +{
           87  +	self = [super init];
           88  +
           89  +	@try {
           90  +		memcpy(_node, node, IPX_NODE_LEN);
           91  +		_network = network;
    57     92   		_port = port;
           93  +		_socket = [socket retain];
    58     94   		_errNo = errNo;
    59     95   	} @catch (id e) {
    60     96   		[self release];
    61     97   		@throw e;
    62     98   	}
    63     99   
    64    100   	return self;
................................................................................
    67    103   - (void)dealloc
    68    104   {
    69    105   	[_host release];
    70    106   	[_socket release];
    71    107   
    72    108   	[super dealloc];
    73    109   }
          110  +
          111  +- (unsigned char *)node
          112  +{
          113  +	return _node;
          114  +}
    74    115   
    75    116   - (OFString *)description
    76    117   {
    77    118   	if (_host != nil)
    78    119   		return [OFString stringWithFormat:
    79    120   		    @"A connection to %@ on port %" @PRIu16 @" could not be "
    80    121   		    @"established in socket of type %@: %@",
    81    122   		    _host, _port, [_socket class], of_strerror(_errNo)];
          123  +	else if (memcmp(_node, "\0\0\0\0\0", IPX_NODE_LEN) == 0)
          124  +		return [OFString stringWithFormat:
          125  +		    @"A connection to %02X%02X%02X%02X%02X%02X port %" @PRIu16
          126  +		    @" on network %" @PRIX32 " could not be established in "
          127  +		    @"socket of type %@: %@",
          128  +		    _node[0], _node[1], _node[2], _node[3], _node[4], _node[5],
          129  +		    _port, _network, [_socket class], of_strerror(_errNo)];
    82    130   	else
    83    131   		return [OFString stringWithFormat:
    84    132   		    @"A connection could not be established in socket of "
    85    133   		    @"type %@: %@",
    86    134   		    [_socket class], of_strerror(_errNo)];
    87    135   }
    88    136   @end

Modified tests/Makefile from [9e51c1dedf] to [d08febc49c].

    14     14          OFASN1DERValueTests.m		\
    15     15          OFArrayTests.m			\
    16     16          ${OFBLOCKTESTS_M}		\
    17     17          OFCharacterSetTests.m		\
    18     18          OFDataTests.m			\
    19     19          OFDateTests.m			\
    20     20          OFDictionaryTests.m		\
    21         -       ${OFIPXSOCKETTESTS_M}		\
    22     21          OFInvocationTests.m		\
    23     22          OFJSONTests.m			\
    24     23          OFListTests.m			\
    25     24          OFLocaleTests.m			\
    26     25          OFMethodSignatureTests.m		\
    27     26          OFNumberTests.m			\
    28     27          OFObjectTests.m			\
................................................................................
    56     55   	     OFSHA384HashTests.m	\
    57     56   	     OFSHA512HashTests.m
    58     57   SRCS_PLUGINS = OFPluginTests.m
    59     58   SRCS_SOCKETS = OFDNSResolverTests.m		\
    60     59   	       ${OFHTTPCLIENTTESTS_M}		\
    61     60   	       OFHTTPCookieTests.m		\
    62     61   	       OFHTTPCookieManagerTests.m	\
           62  +	       ${OFIPXSOCKETTESTS_M}		\
    63     63   	       OFKernelEventObserverTests.m	\
           64  +	       ${OFSPXSOCKETTESTS_M}		\
    64     65   	       OFTCPSocketTests.m		\
    65     66   	       OFUDPSocketTests.m		\
    66     67   	       SocketTests.m
    67     68   SRCS_THREADS = OFThreadTests.m
    68     69   SRCS_WINDOWS = OFWindowsRegistryKeyTests.m
    69     70   
    70     71   IOS_USER ?= mobile

Added tests/OFSPXSocketTests.m version [4b497d9d36].

            1  +/*
            2  + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
            3  + *               2018, 2019, 2020
            4  + *   Jonathan Schleifer <js@nil.im>
            5  + *
            6  + * All rights reserved.
            7  + *
            8  + * This file is part of ObjFW. It may be distributed under the terms of the
            9  + * Q Public License 1.0, which can be found in the file LICENSE.QPL included in
           10  + * the packaging of this file.
           11  + *
           12  + * Alternatively, it may be distributed under the terms of the GNU General
           13  + * Public License, either version 2 or 3, which can be found in the file
           14  + * LICENSE.GPLv2 or LICENSE.GPLv3 respectively included in the packaging of this
           15  + * file.
           16  + */
           17  +
           18  +#include "config.h"
           19  +
           20  +#include <errno.h>
           21  +
           22  +#import "TestsAppDelegate.h"
           23  +
           24  +static OFString *module = @"OFSPXSocket";
           25  +
           26  +@implementation TestsAppDelegate (OFSPXSocketTests)
           27  +- (void)SPXSocketTests
           28  +{
           29  +	void *pool = objc_autoreleasePoolPush();
           30  +	OFSPXSocket *sockClient, *sockServer, *sockAccepted;;
           31  +	of_socket_address_t address1;
           32  +	const of_socket_address_t *address2;
           33  +	unsigned char node[IPX_NODE_LEN], node2[IPX_NODE_LEN];
           34  +	uint32_t network;
           35  +	uint16_t port;
           36  +	char buffer[5];
           37  +
           38  +	TEST(@"+[socket]", (sockClient = [OFSPXSocket socket]) &&
           39  +	    (sockServer = [OFSPXSocket socket]))
           40  +
           41  +	@try {
           42  +		TEST(@"-[bindToPort:]",
           43  +		    R(address1 = [sockServer bindToPort: 0]))
           44  +	} @catch (OFBindFailedException *e) {
           45  +		switch (e.errNo) {
           46  +		case EAFNOSUPPORT:
           47  +			[self outputString: @"[OFSPXSocket] -[bindToPort:]: "
           48  +					    @"IPX unsupported, skipping tests\n"
           49  +				   inColor: GREEN];
           50  +			break;
           51  +		case EADDRNOTAVAIL:
           52  +			[self outputString: @"[OFSPXSocket] -[bindToPort:]: "
           53  +					    @"IPX not configured, skipping "
           54  +					    @"tests\n"
           55  +				   inColor: GREEN];
           56  +			break;
           57  +		default:
           58  +			@throw e;
           59  +		}
           60  +
           61  +		objc_autoreleasePoolPop(pool);
           62  +		return;
           63  +	}
           64  +
           65  +	of_socket_address_get_ipx_node(&address1, node);
           66  +	network = of_socket_address_get_ipx_network(&address1);
           67  +	port = of_socket_address_get_port(&address1);
           68  +
           69  +	TEST(@"-[listen]", R([sockServer listen]))
           70  +
           71  +	TEST(@"-[connectToNode:network:port:]",
           72  +	    R([sockClient connectToNode: node
           73  +				network: network
           74  +				   port: port]))
           75  +
           76  +	TEST(@"-[accept]", (sockAccepted = [sockServer accept]))
           77  +
           78  +	TEST(@"-[sendBuffer:length:]",
           79  +	    R([sockAccepted sendBuffer: "Hello"
           80  +				length: 5]))
           81  +
           82  +	TEST(@"-[receiveIntoBuffer:length:]",
           83  +	    [sockClient receiveIntoBuffer: buffer
           84  +				   length: 5] == 5 &&
           85  +	    memcmp(buffer, "Hello", 5) == 0)
           86  +
           87  +	TEST(@"-[remoteAddress]",
           88  +	    (address2 = sockAccepted.remoteAddress) &&
           89  +	    R(of_socket_address_get_ipx_node(address2, node2)) &&
           90  +	    memcmp(node, node2, IPX_NODE_LEN) == 0 &&
           91  +	    of_socket_address_get_ipx_network(address2) == network)
           92  +
           93  +	objc_autoreleasePoolPop(pool);
           94  +}
           95  +@end

Modified tests/TestsAppDelegate.h from [667b7617f0] to [3fbc6db155].

   225    225   @interface TestsAppDelegate (OFSHA384HashTests)
   226    226   - (void)SHA384HashTests;
   227    227   @end
   228    228   
   229    229   @interface TestsAppDelegate (OFSHA512HashTests)
   230    230   - (void)SHA512HashTests;
   231    231   @end
          232  +
          233  +@interface TestsAppDelegate (OFSPXSocketTests)
          234  +- (void)SPXSocketTests;
          235  +@end
   232    236   
   233    237   @interface TestsAppDelegate (OFSystemInfoTests)
   234    238   - (void)systemInfoTests;
   235    239   @end
   236    240   
   237    241   @interface TestsAppDelegate (OFHMACTests)
   238    242   - (void)HMACTests;

Modified tests/TestsAppDelegate.m from [6f16d81614] to [888148da42].

   413    413   #endif
   414    414   #ifdef OF_HAVE_SOCKETS
   415    415   	[self socketTests];
   416    416   	[self TCPSocketTests];
   417    417   	[self UDPSocketTests];
   418    418   # ifdef OF_HAVE_IPX
   419    419   	[self IPXSocketTests];
          420  +	[self SPXSocketTests];
   420    421   # endif
   421    422   	[self kernelEventObserverTests];
   422    423   #endif
   423    424   #ifdef OF_HAVE_THREADS
   424    425   	[self threadTests];
   425    426   #endif
   426    427   	[self URLTests];