ObjFW  Check-in [0d6380c626]

Overview
Comment:OFTCPSocket: Move out async connecting
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: 0d6380c6266d7558a962876791b30d6492124d303c9d32f68b5fc0fda2fa1ffb
User & Date: js on 2020-05-05 00:36:41
Other Links: manifest | tags
Context
2020-05-05
01:14
Add OFSCTPSocket check-in: 4592b16fab user: js tags: trunk
00:36
OFTCPSocket: Move out async connecting check-in: 0d6380c626 user: js tags: trunk
00:06
OFTCPSocket: Move out SOCKS5 handling check-in: b5cd23da2b user: js tags: trunk
Changes

Modified src/Makefile from [45e34e66a3] to [eabf64dd6c].

   214    214   	OFUTF8String.m			\
   215    215   	${LIBBASES_M}
   216    216   SRCS_FILES += OFFileURLHandler.m	\
   217    217   	      OFINIFileSettings.m
   218    218   SRCS_SOCKETS += OFDNSResolverSettings.m			\
   219    219   		OFHTTPURLHandler.m			\
   220    220   		OFHostAddressResolver.m			\
          221  +		OFIPSocketAsyncConnector.m		\
   221    222   		OFKernelEventObserver.m			\
   222    223   		${OF_EPOLL_KERNEL_EVENT_OBSERVER_M}	\
   223    224   		${OF_KQUEUE_KERNEL_EVENT_OBSERVER_M}	\
   224    225   		${OF_POLL_KERNEL_EVENT_OBSERVER_M}	\
   225    226   		${OF_SELECT_KERNEL_EVENT_OBSERVER_M}	\
   226    227   		OFTCPSocketSOCKS5Connector.m
   227    228   

Added src/OFIPSocketAsyncConnector.h version [7e8d3c9896].

            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 "OFDNSResolver.h"
           19  +#import "OFRunLoop.h"
           20  +#import "OFRunLoop+Private.h"
           21  +
           22  +OF_ASSUME_NONNULL_BEGIN
           23  +
           24  +@protocol OFIPSocketAsyncConnecting
           25  +- (bool)of_createSocketForAddress: (const of_socket_address_t *)address
           26  +			    errNo: (int *)errNo;
           27  +- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
           28  +			    errNo: (int *)errNo;
           29  +- (void)of_closeSocket;
           30  +@end
           31  +
           32  +@interface OFIPSocketAsyncConnector: OFObject <OFRunLoopConnectDelegate,
           33  +    OFDNSResolverHostDelegate>
           34  +{
           35  +	id _socket;
           36  +	OFString *_host;
           37  +	uint16_t _port;
           38  +	id _Nullable _delegate;
           39  +	id _Nullable _block;
           40  +	id _Nullable _exception;
           41  +	OFData *_Nullable _socketAddresses;
           42  +	size_t _socketAddressesIndex;
           43  +}
           44  +
           45  +- (instancetype)initWithSocket: (OFTCPSocket *)sock
           46  +			  host: (OFString *)host
           47  +			  port: (uint16_t)port
           48  +		      delegate: (nullable id)delegate
           49  +			 block: (nullable id)block;
           50  +- (void)didConnect;
           51  +- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
           52  +- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
           53  +@end
           54  +
           55  +OF_ASSUME_NONNULL_END

Added src/OFIPSocketAsyncConnector.m version [92d6cb022d].

            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 "OFIPSocketAsyncConnector.h"
           23  +#import "OFData.h"
           24  +#import "OFTCPSocket.h"
           25  +#import "OFThread.h"
           26  +#import "OFTimer.h"
           27  +
           28  +#import "OFConnectionFailedException.h"
           29  +#import "OFInvalidFormatException.h"
           30  +
           31  +@implementation OFIPSocketAsyncConnector
           32  +- (instancetype)initWithSocket: (id)sock
           33  +			  host: (OFString *)host
           34  +			  port: (uint16_t)port
           35  +		      delegate: (id)delegate
           36  +			 block: (id)block
           37  +{
           38  +	self = [super init];
           39  +
           40  +	@try {
           41  +		_socket = [sock retain];
           42  +		_host = [host copy];
           43  +		_port = port;
           44  +		_delegate = [delegate retain];
           45  +		_block = [block copy];
           46  +	} @catch (id e) {
           47  +		[self release];
           48  +		@throw e;
           49  +	}
           50  +
           51  +	return self;
           52  +}
           53  +
           54  +- (void)dealloc
           55  +{
           56  +	[_socket release];
           57  +	[_host release];
           58  +	[_delegate release];
           59  +	[_block release];
           60  +	[_exception release];
           61  +	[_socketAddresses release];
           62  +
           63  +	[super dealloc];
           64  +}
           65  +
           66  +- (void)didConnect
           67  +{
           68  +	if (_exception == nil)
           69  +		[_socket setBlocking: true];
           70  +
           71  +#ifdef OF_HAVE_BLOCKS
           72  +	if (_block != NULL) {
           73  +		if ([_socket isKindOfClass: [OFTCPSocket class]])
           74  +			((of_tcp_socket_async_connect_block_t)_block)(
           75  +			    _exception);
           76  +		else
           77  +			OF_ENSURE(0);
           78  +	} else {
           79  +#endif
           80  +		if ([_delegate respondsToSelector:
           81  +		    @selector(socket:didConnectToHost:port:exception:)])
           82  +			[_delegate    socket: _socket
           83  +			    didConnectToHost: _host
           84  +					port: _port
           85  +				   exception: _exception];
           86  +#ifdef OF_HAVE_BLOCKS
           87  +	}
           88  +#endif
           89  +}
           90  +
           91  +- (void)of_socketDidConnect: (id)sock
           92  +		  exception: (id)exception
           93  +{
           94  +	if (exception != nil) {
           95  +		/*
           96  +		 * self might be retained only by the pending async requests,
           97  +		 * which we're about to cancel.
           98  +		 */
           99  +		[[self retain] autorelease];
          100  +
          101  +		[sock cancelAsyncRequests];
          102  +		[sock of_closeSocket];
          103  +
          104  +		if (_socketAddressesIndex >= _socketAddresses.count) {
          105  +			_exception = [exception retain];
          106  +			[self didConnect];
          107  +		} else {
          108  +			/*
          109  +			 * We must not call it before returning, as otherwise
          110  +			 * the new socket would be removed from the queue upon
          111  +			 * return.
          112  +			 */
          113  +			OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
          114  +			SEL selector =
          115  +			    @selector(tryNextAddressWithRunLoopMode:);
          116  +			OFTimer *timer = [OFTimer
          117  +			    timerWithTimeInterval: 0
          118  +					   target: self
          119  +					 selector: selector
          120  +					   object: runLoop.currentMode
          121  +					  repeats: false];
          122  +			[runLoop addTimer: timer
          123  +				  forMode: runLoop.currentMode];
          124  +		}
          125  +
          126  +		return;
          127  +	}
          128  +
          129  +	[self didConnect];
          130  +}
          131  +
          132  +- (id)of_connectionFailedExceptionForErrNo: (int)errNo
          133  +{
          134  +	return [OFConnectionFailedException exceptionWithHost: _host
          135  +							 port: _port
          136  +						       socket: _socket
          137  +							errNo: errNo];
          138  +}
          139  +
          140  +- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
          141  +{
          142  +	of_socket_address_t address = *(const of_socket_address_t *)
          143  +	    [_socketAddresses itemAtIndex: _socketAddressesIndex++];
          144  +	int errNo;
          145  +
          146  +	of_socket_address_set_port(&address, _port);
          147  +
          148  +	if (![_socket of_createSocketForAddress: &address
          149  +					  errNo: &errNo]) {
          150  +		if (_socketAddressesIndex >= _socketAddresses.count) {
          151  +			_exception = [[OFConnectionFailedException alloc]
          152  +			    initWithHost: _host
          153  +				    port: _port
          154  +				  socket: _socket
          155  +				   errNo: errNo];
          156  +			[self didConnect];
          157  +			return;
          158  +		}
          159  +
          160  +		[self tryNextAddressWithRunLoopMode: runLoopMode];
          161  +		return;
          162  +	}
          163  +
          164  +#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
          165  +	/*
          166  +	 * On Wii and 3DS, connect() fails if non-blocking is enabled.
          167  +	 *
          168  +	 * Additionally, on Wii, there is no getsockopt(), so it would not be
          169  +	 * possible to get the error (or success) after connecting anyway.
          170  +	 *
          171  +	 * So for now, connecting is blocking on Wii and 3DS.
          172  +	 *
          173  +	 * FIXME: Use a different thread as a work around.
          174  +	 */
          175  +	[_socket setBlocking: true];
          176  +#else
          177  +	[_socket setBlocking: false];
          178  +#endif
          179  +
          180  +	if (![_socket of_connectSocketToAddress: &address
          181  +					  errNo: &errNo]) {
          182  +#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
          183  +		if (errNo == EINPROGRESS) {
          184  +			[OFRunLoop of_addAsyncConnectForSocket: _socket
          185  +							  mode: runLoopMode
          186  +						      delegate: self];
          187  +			return;
          188  +		} else {
          189  +#endif
          190  +			[_socket of_closeSocket];
          191  +
          192  +			if (_socketAddressesIndex >= _socketAddresses.count) {
          193  +				_exception = [[OFConnectionFailedException
          194  +				    alloc] initWithHost: _host
          195  +						   port: _port
          196  +						 socket: _socket
          197  +						  errNo: errNo];
          198  +				[self didConnect];
          199  +				return;
          200  +			}
          201  +
          202  +			[self tryNextAddressWithRunLoopMode: runLoopMode];
          203  +			return;
          204  +#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
          205  +		}
          206  +#endif
          207  +	}
          208  +
          209  +#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
          210  +	[_socket setBlocking: false];
          211  +#endif
          212  +
          213  +	[self didConnect];
          214  +}
          215  +
          216  +- (void)resolver: (OFDNSResolver *)resolver
          217  +  didResolveHost: (OFString *)host
          218  +       addresses: (OFData *)addresses
          219  +       exception: (id)exception
          220  +{
          221  +	if (exception != nil) {
          222  +		_exception = [exception retain];
          223  +		[self didConnect];
          224  +		return;
          225  +	}
          226  +
          227  +	_socketAddresses = [addresses copy];
          228  +
          229  +	[self tryNextAddressWithRunLoopMode:
          230  +	    [OFRunLoop currentRunLoop].currentMode];
          231  +}
          232  +
          233  +- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
          234  +{
          235  +	@try {
          236  +		of_socket_address_t address =
          237  +		    of_socket_address_parse_ip(_host, _port);
          238  +
          239  +		_socketAddresses = [[OFData alloc]
          240  +		    initWithItems: &address
          241  +			 itemSize: sizeof(address)
          242  +			    count: 1];
          243  +
          244  +		[self tryNextAddressWithRunLoopMode: runLoopMode];
          245  +		return;
          246  +	} @catch (OFInvalidFormatException *e) {
          247  +	}
          248  +
          249  +	[[OFThread DNSResolver]
          250  +	    asyncResolveAddressesForHost: _host
          251  +			   addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY
          252  +			     runLoopMode: runLoopMode
          253  +				delegate: self];
          254  +}
          255  +@end

Modified src/OFTCPSocket.m from [4f94254b4b] to [9be04ec18b].

    28     28   # include <fcntl.h>
    29     29   #endif
    30     30   
    31     31   #import "OFTCPSocket.h"
    32     32   #import "OFDNSResolver.h"
    33     33   #import "OFData.h"
    34     34   #import "OFDate.h"
           35  +#import "OFIPSocketAsyncConnector.h"
    35     36   #import "OFRunLoop.h"
    36     37   #import "OFRunLoop+Private.h"
    37     38   #import "OFString.h"
    38     39   #import "OFTCPSocketSOCKS5Connector.h"
    39     40   #import "OFThread.h"
    40         -#import "OFTimer.h"
    41     41   
    42     42   #import "OFAlreadyConnectedException.h"
    43     43   #import "OFBindFailedException.h"
    44         -#import "OFConnectionFailedException.h"
    45     44   #import "OFGetOptionFailedException.h"
    46         -#import "OFInvalidFormatException.h"
    47     45   #import "OFNotImplementedException.h"
    48     46   #import "OFNotOpenException.h"
    49         -#import "OFOutOfMemoryException.h"
    50         -#import "OFOutOfRangeException.h"
    51     47   #import "OFSetOptionFailedException.h"
    52     48   
    53     49   #import "socket.h"
    54     50   #import "socket_helpers.h"
    55     51   
    56     52   static const of_run_loop_mode_t connectRunLoopMode =
    57     53       @"of_tcp_socket_connect_mode";
    58     54   
    59     55   Class of_tls_socket_class = Nil;
    60     56   
    61     57   static OFString *defaultSOCKS5Host = nil;
    62     58   static uint16_t defaultSOCKS5Port = 1080;
    63     59   
    64         -@interface OFTCPSocket ()
    65         -- (bool)of_createSocketForAddress: (const of_socket_address_t *)address
    66         -			    errNo: (int *)errNo;
    67         -- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address
    68         -			    errNo: (int *)errNo;
    69         -- (void)of_closeSocket;
    70         -@end
    71         -
    72         -@interface OFTCPSocketAsyncConnectDelegate: OFObject <OFTCPSocketDelegate,
    73         -    OFRunLoopConnectDelegate, OFDNSResolverHostDelegate>
    74         -{
    75         -	OFTCPSocket *_socket;
    76         -	OFString *_host;
    77         -	uint16_t _port;
    78         -	id <OFTCPSocketDelegate> _delegate;
    79         -#ifdef OF_HAVE_BLOCKS
    80         -	of_tcp_socket_async_connect_block_t _block;
    81         -#endif
    82         -	id _exception;
    83         -	OFData *_socketAddresses;
    84         -	size_t _socketAddressesIndex;
    85         -}
    86         -
    87         -- (instancetype)initWithSocket: (OFTCPSocket *)sock
    88         -			  host: (OFString *)host
    89         -			  port: (uint16_t)port
    90         -		      delegate: (id <OFTCPSocketDelegate>)delegate
    91         -#ifdef OF_HAVE_BLOCKS
    92         -			 block: (of_tcp_socket_async_connect_block_t)block
    93         -#endif
    94         -;
    95         -- (void)didConnect;
    96         -- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
    97         -- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode;
           60  +@interface OFTCPSocket () <OFIPSocketAsyncConnecting>
    98     61   @end
    99     62   
   100     63   @interface OFTCPSocketConnectDelegate: OFObject <OFTCPSocketDelegate>
   101     64   {
   102     65   @public
   103     66   	bool _done;
   104     67   	id _exception;
   105     68   }
   106         -@end
   107         -
   108         -@implementation OFTCPSocketAsyncConnectDelegate
   109         -- (instancetype)initWithSocket: (OFTCPSocket *)sock
   110         -			  host: (OFString *)host
   111         -			  port: (uint16_t)port
   112         -		      delegate: (id <OFTCPSocketDelegate>)delegate
   113         -#ifdef OF_HAVE_BLOCKS
   114         -			 block: (of_tcp_socket_async_connect_block_t)block
   115         -#endif
   116         -{
   117         -	self = [super init];
   118         -
   119         -	@try {
   120         -		_socket = [sock retain];
   121         -		_host = [host copy];
   122         -		_port = port;
   123         -		_delegate = [delegate retain];
   124         -#ifdef OF_HAVE_BLOCKS
   125         -		_block = [block copy];
   126         -#endif
   127         -	} @catch (id e) {
   128         -		[self release];
   129         -		@throw e;
   130         -	}
   131         -
   132         -	return self;
   133         -}
   134         -
   135         -- (void)dealloc
   136         -{
   137         -	[_socket release];
   138         -	[_host release];
   139         -	[_delegate release];
   140         -#ifdef OF_HAVE_BLOCKS
   141         -	[_block release];
   142         -#endif
   143         -	[_exception release];
   144         -	[_socketAddresses release];
   145         -
   146         -	[super dealloc];
   147         -}
   148         -
   149         -- (void)didConnect
   150         -{
   151         -	if (_exception == nil)
   152         -		_socket.blocking = true;
   153         -
   154         -#ifdef OF_HAVE_BLOCKS
   155         -	if (_block != NULL)
   156         -		_block(_exception);
   157         -	else {
   158         -#endif
   159         -		if ([_delegate respondsToSelector:
   160         -		    @selector(socket:didConnectToHost:port:exception:)])
   161         -			[_delegate    socket: _socket
   162         -			    didConnectToHost: _host
   163         -					port: _port
   164         -				   exception: _exception];
   165         -#ifdef OF_HAVE_BLOCKS
   166         -	}
   167         -#endif
   168         -}
   169         -
   170         -- (void)of_socketDidConnect: (id)sock
   171         -		  exception: (id)exception
   172         -{
   173         -	if (exception != nil) {
   174         -		/*
   175         -		 * self might be retained only by the pending async requests,
   176         -		 * which we're about to cancel.
   177         -		 */
   178         -		[[self retain] autorelease];
   179         -
   180         -		[sock cancelAsyncRequests];
   181         -		[sock of_closeSocket];
   182         -
   183         -		if (_socketAddressesIndex >= _socketAddresses.count) {
   184         -			_exception = [exception retain];
   185         -			[self didConnect];
   186         -		} else {
   187         -			/*
   188         -			 * We must not call it before returning, as otherwise
   189         -			 * the new socket would be removed from the queue upon
   190         -			 * return.
   191         -			 */
   192         -			OFRunLoop *runLoop = [OFRunLoop currentRunLoop];
   193         -			SEL selector =
   194         -			    @selector(tryNextAddressWithRunLoopMode:);
   195         -			OFTimer *timer = [OFTimer
   196         -			    timerWithTimeInterval: 0
   197         -					   target: self
   198         -					 selector: selector
   199         -					   object: runLoop.currentMode
   200         -					  repeats: false];
   201         -			[runLoop addTimer: timer
   202         -				  forMode: runLoop.currentMode];
   203         -		}
   204         -
   205         -		return;
   206         -	}
   207         -
   208         -	[self didConnect];
   209         -}
   210         -
   211         -- (id)of_connectionFailedExceptionForErrNo: (int)errNo
   212         -{
   213         -	return [OFConnectionFailedException exceptionWithHost: _host
   214         -							 port: _port
   215         -						       socket: _socket
   216         -							errNo: errNo];
   217         -}
   218         -
   219         -- (void)tryNextAddressWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
   220         -{
   221         -	of_socket_address_t address = *(const of_socket_address_t *)
   222         -	    [_socketAddresses itemAtIndex: _socketAddressesIndex++];
   223         -	int errNo;
   224         -
   225         -	of_socket_address_set_port(&address, _port);
   226         -
   227         -	if (![_socket of_createSocketForAddress: &address
   228         -					  errNo: &errNo]) {
   229         -		if (_socketAddressesIndex >= _socketAddresses.count) {
   230         -			_exception = [[OFConnectionFailedException alloc]
   231         -			    initWithHost: _host
   232         -				    port: _port
   233         -				  socket: _socket
   234         -				   errNo: errNo];
   235         -			[self didConnect];
   236         -			return;
   237         -		}
   238         -
   239         -		[self tryNextAddressWithRunLoopMode: runLoopMode];
   240         -		return;
   241         -	}
   242         -
   243         -#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
   244         -	/*
   245         -	 * On Wii and 3DS, connect() fails if non-blocking is enabled.
   246         -	 *
   247         -	 * Additionally, on Wii, there is no getsockopt(), so it would not be
   248         -	 * possible to get the error (or success) after connecting anyway.
   249         -	 *
   250         -	 * So for now, connecting is blocking on Wii and 3DS.
   251         -	 *
   252         -	 * FIXME: Use a different thread as a work around.
   253         -	 */
   254         -	_socket.blocking = true;
   255         -#else
   256         -	_socket.blocking = false;
   257         -#endif
   258         -
   259         -	if (![_socket of_connectSocketToAddress: &address
   260         -					  errNo: &errNo]) {
   261         -#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
   262         -		if (errNo == EINPROGRESS) {
   263         -			[OFRunLoop of_addAsyncConnectForSocket: _socket
   264         -							  mode: runLoopMode
   265         -						      delegate: self];
   266         -			return;
   267         -		} else {
   268         -#endif
   269         -			[_socket of_closeSocket];
   270         -
   271         -			if (_socketAddressesIndex >= _socketAddresses.count) {
   272         -				_exception = [[OFConnectionFailedException
   273         -				    alloc] initWithHost: _host
   274         -						   port: _port
   275         -						 socket: _socket
   276         -						  errNo: errNo];
   277         -				[self didConnect];
   278         -				return;
   279         -			}
   280         -
   281         -			[self tryNextAddressWithRunLoopMode: runLoopMode];
   282         -			return;
   283         -#if !defined(OF_NINTENDO_3DS) && !defined(OF_WII)
   284         -		}
   285         -#endif
   286         -	}
   287         -
   288         -#if defined(OF_NINTENDO_3DS) || defined(OF_WII)
   289         -	_socket.blocking = false;
   290         -#endif
   291         -
   292         -	[self didConnect];
   293         -}
   294         -
   295         -- (void)resolver: (OFDNSResolver *)resolver
   296         -  didResolveHost: (OFString *)host
   297         -       addresses: (OFData *)addresses
   298         -       exception: (id)exception
   299         -{
   300         -	if (exception != nil) {
   301         -		_exception = [exception retain];
   302         -		[self didConnect];
   303         -		return;
   304         -	}
   305         -
   306         -	_socketAddresses = [addresses copy];
   307         -
   308         -	[self tryNextAddressWithRunLoopMode:
   309         -	    [OFRunLoop currentRunLoop].currentMode];
   310         -}
   311         -
   312         -- (void)startWithRunLoopMode: (of_run_loop_mode_t)runLoopMode
   313         -{
   314         -	@try {
   315         -		of_socket_address_t address =
   316         -		    of_socket_address_parse_ip(_host, _port);
   317         -
   318         -		_socketAddresses = [[OFData alloc]
   319         -		    initWithItems: &address
   320         -			 itemSize: sizeof(address)
   321         -			    count: 1];
   322         -
   323         -		[self tryNextAddressWithRunLoopMode: runLoopMode];
   324         -		return;
   325         -	} @catch (OFInvalidFormatException *e) {
   326         -	}
   327         -
   328         -	[[OFThread DNSResolver]
   329         -	    asyncResolveAddressesForHost: _host
   330         -			   addressFamily: OF_SOCKET_ADDRESS_FAMILY_ANY
   331         -			     runLoopMode: runLoopMode
   332         -				delegate: self];
   333         -}
   334     69   @end
   335     70   
   336     71   @implementation OFTCPSocketConnectDelegate
   337     72   - (void)dealloc
   338     73   {
   339     74   	[_exception release];
   340     75   
................................................................................
   487    222   - (void)asyncConnectToHost: (OFString *)host
   488    223   		      port: (uint16_t)port
   489    224   	       runLoopMode: (of_run_loop_mode_t)runLoopMode
   490    225   {
   491    226   	void *pool = objc_autoreleasePoolPush();
   492    227   	id <OFTCPSocketDelegate> delegate;
   493    228   
          229  +	if (_socket != INVALID_SOCKET)
          230  +		@throw [OFAlreadyConnectedException exceptionWithSocket: self];
          231  +
   494    232   	if (_SOCKS5Host != nil) {
   495    233   		delegate = [[[OFTCPSocketSOCKS5Connector alloc]
   496    234   		    initWithSocket: self
   497    235   			      host: host
   498    236   			      port: port
   499    237   			  delegate: _delegate
   500    238   #ifdef OF_HAVE_BLOCKS
................................................................................
   502    240   #endif
   503    241   		    ] autorelease];
   504    242   		host = _SOCKS5Host;
   505    243   		port = _SOCKS5Port;
   506    244   	} else
   507    245   		delegate = _delegate;
   508    246   
   509         -	[[[[OFTCPSocketAsyncConnectDelegate alloc]
          247  +	[[[[OFIPSocketAsyncConnector alloc]
   510    248   		  initWithSocket: self
   511    249   			    host: host
   512    250   			    port: port
   513    251   			delegate: delegate
   514         -#ifdef OF_HAVE_BLOCKS
   515    252   			   block: NULL
   516         -#endif
   517    253   	    ] autorelease] startWithRunLoopMode: runLoopMode];
   518    254   
   519    255   	objc_autoreleasePoolPop(pool);
   520    256   }
   521    257   
   522    258   #ifdef OF_HAVE_BLOCKS
   523    259   - (void)asyncConnectToHost: (OFString *)host
................................................................................
   534    270   		      port: (uint16_t)port
   535    271   	       runLoopMode: (of_run_loop_mode_t)runLoopMode
   536    272   		     block: (of_tcp_socket_async_connect_block_t)block
   537    273   {
   538    274   	void *pool = objc_autoreleasePoolPush();
   539    275   	id <OFTCPSocketDelegate> delegate = nil;
   540    276   
          277  +	if (_socket != INVALID_SOCKET)
          278  +		@throw [OFAlreadyConnectedException exceptionWithSocket: self];
          279  +
   541    280   	if (_SOCKS5Host != nil) {
   542    281   		delegate = [[[OFTCPSocketSOCKS5Connector alloc]
   543    282   		    initWithSocket: self
   544    283   			      host: host
   545    284   			      port: port
   546    285   			  delegate: nil
   547    286   			     block: block] autorelease];
   548    287   		host = _SOCKS5Host;
   549    288   		port = _SOCKS5Port;
   550    289   	}
   551    290   
   552         -	[[[[OFTCPSocketAsyncConnectDelegate alloc]
          291  +	[[[[OFIPSocketAsyncConnector alloc]
   553    292   		  initWithSocket: self
   554    293   			    host: host
   555    294   			    port: port
   556    295   			delegate: delegate
   557    296   			   block: (delegate == nil ? block : NULL)] autorelease]
   558    297   	    startWithRunLoopMode: runLoopMode];
   559    298