Index: src/OFHTTPServer.m ================================================================== --- src/OFHTTPServer.m +++ src/OFHTTPServer.m @@ -57,25 +57,25 @@ @interface OFHTTPServer () @end @interface OFHTTPServerResponse: OFHTTPResponse { - OFTCPSocket *_socket; + OFStreamSocket *_socket; OFHTTPServer *_server; OFHTTPRequest *_request; bool _chunked, _headersSent; } -- (instancetype)initWithSocket: (OFTCPSocket *)sock +- (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server request: (OFHTTPRequest *)request; @end @interface OFHTTPServerConnection: OFObject { @public - OFTCPSocket *_socket; + OFStreamSocket *_socket; OFHTTPServer *_server; OFTimer *_timer; enum { AWAITING_PROLOG, PARSING_HEADERS, @@ -88,27 +88,27 @@ OFMutableDictionary *_headers; size_t _contentLength; OFStream *_requestBody; } -- (instancetype)initWithSocket: (OFTCPSocket *)sock +- (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server; - (bool)parseProlog: (OFString *)line; - (bool)parseHeaders: (OFString *)line; - (bool)sendErrorAndClose: (short)statusCode; - (void)createResponse; @end @interface OFHTTPServerRequestBodyStream: OFStream { - OFTCPSocket *_socket; + OFStreamSocket *_socket; bool _chunked; intmax_t _toRead; bool _atEndOfStream, _setAtEndOfStream; } -- (instancetype)initWithSocket: (OFTCPSocket *)sock +- (instancetype)initWithSocket: (OFStreamSocket *)sock chunked: (bool)chunked contentLength: (uintmax_t)contentLength; @end #ifdef OF_HAVE_THREADS @@ -146,11 +146,11 @@ return [OFString stringWithUTF8StringNoCopy: cString freeWhenDone: true]; } @implementation OFHTTPServerResponse -- (instancetype)initWithSocket: (OFTCPSocket *)sock +- (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server request: (OFHTTPRequest *)request { self = [super init]; @@ -281,11 +281,11 @@ return _socket.fileDescriptorForWriting; } @end @implementation OFHTTPServerConnection -- (instancetype)initWithSocket: (OFTCPSocket *)sock +- (instancetype)initWithSocket: (OFStreamSocket *)sock server: (OFHTTPServer *)server { self = [super init]; @try { @@ -577,11 +577,11 @@ objc_autoreleasePoolPop(pool); } @end @implementation OFHTTPServerRequestBodyStream -- (instancetype)initWithSocket: (OFTCPSocket *)sock +- (instancetype)initWithSocket: (OFStreamSocket *)sock chunked: (bool)chunked contentLength: (uintmax_t)contentLength { self = [super init]; @@ -974,22 +974,22 @@ [_threadPool release]; _threadPool = nil; #endif } -- (void)of_handleAcceptedSocket: (OFTCPSocket *)acceptedSocket +- (void)of_handleAcceptedSocket: (OFStreamSocket *)acceptedSocket { OFHTTPServerConnection *connection = [[[OFHTTPServerConnection alloc] initWithSocket: acceptedSocket server: self] autorelease]; acceptedSocket.delegate = connection; [acceptedSocket asyncReadLine]; } -- (bool)socket: (OFTCPSocket *)sock - didAcceptSocket: (OFTCPSocket *)acceptedSocket +- (bool)socket: (OFStreamSocket *)sock + didAcceptSocket: (OFStreamSocket *)acceptedSocket exception: (id)exception { if (exception != nil) { if (![_delegate respondsToSelector: @selector(server:didReceiveExceptionOnListeningSocket:)]) Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -18,18 +18,18 @@ #import "OFRunLoop.h" #import "OFStream.h" #ifdef OF_HAVE_SOCKETS # import "OFDatagramSocket.h" # import "OFSequencedPacketSocket.h" -# import "OFTCPSocket.h" +# import "OFStreamSocket.h" #endif OF_ASSUME_NONNULL_BEGIN #ifdef OF_HAVE_SOCKETS -@protocol OFTCPSocketDelegate_Private -- (void)of_socketDidConnect: (OFTCPSocket *)socket +@protocol OFRunLoopConnectDelegate +- (void)of_socketDidConnect: (id)socket exception: (nullable id)exception; @end #endif @interface OFRunLoop () @@ -82,14 +82,13 @@ of_stream_async_write_string_block_t) block # endif delegate: (nullable id )delegate; # if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) -+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)socket - mode: (of_run_loop_mode_t)mode - delegate: (id ) - delegate; ++ (void)of_addAsyncConnectForSocket: (id)socket + mode: (of_run_loop_mode_t)mode + delegate: (id )delegate; # endif + (void)of_addAsyncAcceptForSocket: (id)socket mode: (of_run_loop_mode_t)mode block: (nullable id)block delegate: (nullable id)delegate; Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -27,12 +27,13 @@ #import "OFDictionary.h" #ifdef OF_HAVE_SOCKETS # import "OFKernelEventObserver.h" # import "OFDatagramSocket.h" # import "OFSequencedPacketSocket.h" -# import "OFTCPSocket.h" -# import "OFTCPSocket+Private.h" +# import "OFSequencedPacketSocket+Private.h" +# import "OFStreamSocket.h" +# import "OFStreamSocket+Private.h" #endif #import "OFThread.h" #ifdef OF_HAVE_THREADS # import "OFMutex.h" # import "OFCondition.h" @@ -767,12 +768,12 @@ exception = e; } # ifdef OF_HAVE_BLOCKS if (_block != NULL) { - if ([object isKindOfClass: [OFTCPSocket class]]) - return ((of_tcp_socket_async_accept_block_t) + if ([object isKindOfClass: [OFStreamSocket class]]) + return ((of_stream_socket_async_accept_block_t) _block)(object, acceptedSocket, exception); else if ([object isKindOfClass: [OFSequencedPacketSocket class]]) return ((of_sequenced_packet_socket_async_accept_block_t) @@ -1182,16 +1183,15 @@ QUEUE_ITEM } # if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) -+ (void)of_addAsyncConnectForTCPSocket: (OFTCPSocket *)stream - mode: (of_run_loop_mode_t)mode - delegate: (id ) - delegate ++ (void)of_addAsyncConnectForSocket: (id)sock + mode: (of_run_loop_mode_t)mode + delegate: (id )delegate { - NEW_WRITE(OFRunLoopConnectQueueItem, stream, mode) + NEW_WRITE(OFRunLoopConnectQueueItem, sock, mode) queueItem->_delegate = [delegate retain]; QUEUE_ITEM } ADDED src/OFSequencedPacketSocket+Private.h Index: src/OFSequencedPacketSocket+Private.h ================================================================== --- src/OFSequencedPacketSocket+Private.h +++ src/OFSequencedPacketSocket+Private.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * 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 "OFSequencedPacketSocket.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFSequencedPacketSocket () +#ifndef OF_WII +@property (readonly, nonatomic) int of_socketError; +#endif +@end + +OF_ASSUME_NONNULL_END Index: src/OFSequencedPacketSocket.m ================================================================== --- src/OFSequencedPacketSocket.m +++ src/OFSequencedPacketSocket.m @@ -23,10 +23,11 @@ #ifdef HAVE_FCNTL_H # include #endif #import "OFSequencedPacketSocket.h" +#import "OFSequencedPacketSocket+Private.h" #import "OFData.h" #import "OFRunLoop+Private.h" #import "OFRunLoop.h" #import "OFAcceptFailedException.h" @@ -85,10 +86,24 @@ if (_socket != INVALID_SOCKET) [self close]; [super dealloc]; } + +#ifndef OF_WII +- (int)of_socketError +{ + int errNo; + socklen_t len = sizeof(errNo); + + if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo, + &len) != 0) + return of_socket_errno(); + + return errNo; +} +#endif - (id)copy { return [self retain]; } @@ -418,15 +433,10 @@ @throw [OFOutOfRangeException exception]; return &_remoteAddress; } -- (bool)isListening -{ - return _listening; -} - - (void)cancelAsyncRequests { [OFRunLoop of_cancelAsyncRequestsForObject: self mode: of_run_loop_mode_default]; } ADDED src/OFStreamSocket+Private.h Index: src/OFStreamSocket+Private.h ================================================================== --- src/OFStreamSocket+Private.h +++ src/OFStreamSocket+Private.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, + * 2018, 2019, 2020 + * 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 "OFStreamSocket.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OFStreamSocket () +#ifndef OF_WII +@property (readonly, nonatomic) int of_socketError; +#endif +@end + +OF_ASSUME_NONNULL_END Index: src/OFStreamSocket.h ================================================================== --- src/OFStreamSocket.h +++ src/OFStreamSocket.h @@ -19,27 +19,143 @@ #import "socket.h" OF_ASSUME_NONNULL_BEGIN +/*! @file */ + +@class OFStreamSocket; + +#ifdef OF_HAVE_BLOCKS +/*! + * @brief A block which is called when the socket accepted a connection. + * + * @param socket The socket which accepted the connection + * @param acceptedSocket The socket which has been accepted + * @param exception An exception which occurred while accepting the socket or + * `nil` on success + * @return A bool whether the same block should be used for the next incoming + * connection + */ +typedef bool (^of_stream_socket_async_accept_block_t)(OFStreamSocket *socket, + OFStreamSocket *acceptedSocket, id _Nullable exception); +#endif + +/*! + * @protocol OFStreamSocketDelegate OFStreamSocket.h ObjFW/OFStreamSocket.h + * + * A delegate for OFStreamSocket. + */ +@protocol OFStreamSocketDelegate +@optional +/*! + * @brief A method which is called when a socket accepted a connection. + * + * @param socket The socket which accepted the connection + * @param acceptedSocket The socket which has been accepted + * @param exception An exception that occurred while accepting, or nil on + * success + * @return A bool whether to accept the next incoming connection + */ +- (bool)socket: (OFStreamSocket *)socket + didAcceptSocket: (OFStreamSocket *)acceptedSocket + exception: (nullable id)exception; +@end + /*! * @class OFStreamSocket OFStreamSocket.h ObjFW/OFStreamSocket.h * * @brief A class which provides methods to create and use stream sockets. */ @interface OFStreamSocket: OFStream { of_socket_t _socket; - bool _atEndOfStream; + bool _atEndOfStream, _listening; + of_socket_address_t _remoteAddress; OF_RESERVE_IVARS(4) } /*! - * @brief Returns a new, autoreleased OFTCPSocket. + * @brief Whether the socket is a listening socket. + */ +@property (readonly, nonatomic, getter=isListening) bool listening; + +/*! + * @brief The remote address. + * + * @note This only works for accepted sockets! + */ +@property (readonly, nonatomic) const of_socket_address_t *remoteAddress; + +/*! + * @brief The delegate for asynchronous operations on the socket. + * + * @note The delegate is retained for as long as asynchronous operations are + * still ongoing. + */ +@property OF_NULLABLE_PROPERTY (assign, nonatomic) + id delegate; + +/*! + * @brief Returns a new, autoreleased OFStreamSocket. * - * @return A new, autoreleased OFTCPSocket + * @return A new, autoreleased OFStreamSocket */ + (instancetype)socket; + +/*! + * @brief Listen on the socket. + * + * @param backlog Maximum length for the queue of pending connections. + */ +- (void)listenWithBacklog: (int)backlog; + +/*! + * @brief Listen on the socket. + */ +- (void)listen; + +/*! + * @brief Accept an incoming connection. + * + * @return An autoreleased OFStreamSocket for the accepted connection. + */ +- (instancetype)accept; + +/*! + * @brief Asynchronously accept an incoming connection. + */ +- (void)asyncAccept; + +/*! + * @brief Asynchronously accept an incoming connection. + * + * @param runLoopMode The run loop mode in which to perform the async accept + */ +- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode; + +#ifdef OF_HAVE_BLOCKS +/*! + * @brief Asynchronously accept an incoming connection. + * + * @param block The block to execute when a new connection has been accepted. + * Returns whether the next incoming connection should be accepted + * by the specified block as well. + */ +- (void)asyncAcceptWithBlock: (of_stream_socket_async_accept_block_t)block; + +/*! + * @brief Asynchronously accept an incoming connection. + * + * @param runLoopMode The run loop mode in which to perform the async accept + * @param block The block to execute when a new connection has been accepted. + * Returns whether the next incoming connection should be accepted + * by the specified block as well. + */ +- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_socket_async_accept_block_t) + block; +#endif @end OF_ASSUME_NONNULL_END Index: src/OFStreamSocket.m ================================================================== --- src/OFStreamSocket.m +++ src/OFStreamSocket.m @@ -17,16 +17,23 @@ #define __NO_EXT_QNX #include "config.h" +#include #include #include #import "OFStreamSocket.h" +#import "OFStreamSocket+Private.h" +#import "OFRunLoop.h" +#import "OFRunLoop+Private.h" +#import "OFAcceptFailedException.h" #import "OFInitializationFailedException.h" +#import "OFInvalidArgumentException.h" +#import "OFListenFailedException.h" #import "OFNotImplementedException.h" #import "OFNotOpenException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFSetOptionFailedException.h" @@ -33,10 +40,13 @@ #import "OFWriteFailedException.h" #import "socket_helpers.h" @implementation OFStreamSocket +@dynamic delegate; +@synthesize listening = _listening; + + (void)initialize { if (self != [OFStreamSocket class]) return; @@ -66,10 +76,18 @@ @throw e; } return self; } + +- (void)dealloc +{ + if (_socket != INVALID_SOCKET) + [self close]; + + [super dealloc]; +} - (bool)lowlevelIsAtEndOfStream { if (_socket == INVALID_SOCKET) @throw [OFNotOpenException exceptionWithObject: self]; @@ -188,27 +206,166 @@ @throw [OFOutOfRangeException exception]; return (int)_socket; #endif } + +#ifndef OF_WII +- (int)of_socketError +{ + int errNo; + socklen_t len = sizeof(errNo); + + if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo, + &len) != 0) + return of_socket_errno(); + + return errNo; +} +#endif + +- (void)listen +{ + [self listenWithBacklog: SOMAXCONN]; +} + +- (void)listenWithBacklog: (int)backlog +{ + if (_socket == INVALID_SOCKET) + @throw [OFNotOpenException exceptionWithObject: self]; + + if (listen(_socket, backlog) == -1) + @throw [OFListenFailedException + exceptionWithSocket: self + backlog: backlog + errNo: of_socket_errno()]; + + _listening = true; +} + +- (instancetype)accept +{ + OFStreamSocket *client = [[[[self class] alloc] init] autorelease]; +#if (!defined(HAVE_PACCEPT) && !defined(HAVE_ACCEPT4)) || !defined(SOCK_CLOEXEC) +# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + int flags; +# endif +#endif + + client->_remoteAddress.length = + (socklen_t)sizeof(client->_remoteAddress.sockaddr); + +#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC) + if ((client->_socket = paccept(_socket, + &client->_remoteAddress.sockaddr.sockaddr, + &client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) == + INVALID_SOCKET) + @throw [OFAcceptFailedException + exceptionWithSocket: self + errNo: of_socket_errno()]; +#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) + if ((client->_socket = accept4(_socket, + &client->_remoteAddress.sockaddr.sockaddr, + &client->_remoteAddress.length, SOCK_CLOEXEC)) == INVALID_SOCKET) + @throw [OFAcceptFailedException + exceptionWithSocket: self + errNo: of_socket_errno()]; +#else + if ((client->_socket = accept(_socket, + &client->_remoteAddress.sockaddr.sockaddr, + &client->_remoteAddress.length)) == INVALID_SOCKET) + @throw [OFAcceptFailedException + exceptionWithSocket: self + errNo: of_socket_errno()]; + +# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC) + if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1) + fcntl(client->_socket, F_SETFD, flags | FD_CLOEXEC); +# endif +#endif + + assert(client->_remoteAddress.length <= + (socklen_t)sizeof(client->_remoteAddress.sockaddr)); + + switch (client->_remoteAddress.sockaddr.sockaddr.sa_family) { + case AF_INET: + client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV4; + break; +#ifdef OF_HAVE_IPV6 + case AF_INET6: + client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV6; + break; +#endif +#ifdef OF_HAVE_IPX + case AF_IPX: + client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPX; + break; +#endif + default: + client->_remoteAddress.family = + OF_SOCKET_ADDRESS_FAMILY_UNKNOWN; + break; + } + + return client; +} + +- (void)asyncAccept +{ + [self asyncAcceptWithRunLoopMode: of_run_loop_mode_default]; +} + +- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode +{ + [OFRunLoop of_addAsyncAcceptForSocket: self + mode: runLoopMode + block: NULL + delegate: _delegate]; +} + +#ifdef OF_HAVE_BLOCKS +- (void)asyncAcceptWithBlock: (of_stream_socket_async_accept_block_t)block +{ + [self asyncAcceptWithRunLoopMode: of_run_loop_mode_default + block: block]; +} + +- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode + block: (of_stream_socket_async_accept_block_t)block +{ + [OFRunLoop of_addAsyncAcceptForSocket: self + mode: runLoopMode + block: block + delegate: nil]; +} +#endif + +- (const of_socket_address_t *)remoteAddress +{ + if (_socket == INVALID_SOCKET) + @throw [OFNotOpenException exceptionWithObject: self]; + + if (_remoteAddress.length == 0) + @throw [OFInvalidArgumentException exception]; + + if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr)) + @throw [OFOutOfRangeException exception]; + + return &_remoteAddress; +} - (void)close { if (_socket == INVALID_SOCKET) @throw [OFNotOpenException exceptionWithObject: self]; + _listening = false; + memset(&_remoteAddress, 0, sizeof(_remoteAddress)); + closesocket(_socket); _socket = INVALID_SOCKET; _atEndOfStream = false; [super close]; } - -- (void)dealloc -{ - if (_socket != INVALID_SOCKET) - [self close]; - - [super dealloc]; -} @end DELETED src/OFTCPSocket+Private.h Index: src/OFTCPSocket+Private.h ================================================================== --- src/OFTCPSocket+Private.h +++ src/OFTCPSocket+Private.h @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, - * 2018, 2019, 2020 - * 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 "OFTCPSocket.h" - -OF_ASSUME_NONNULL_BEGIN - -@interface OFTCPSocket () -#ifndef OF_WII -@property (readonly, nonatomic) int of_socketError; -#endif - -- (bool)of_createSocketForAddress: (const of_socket_address_t *)address - errNo: (int *)errNo; -- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address - errNo: (int *)errNo; -- (void)of_closeSocket; -@end - -OF_ASSUME_NONNULL_END Index: src/OFTCPSocket.h ================================================================== --- src/OFTCPSocket.h +++ src/OFTCPSocket.h @@ -35,31 +35,18 @@ * @param exception An exception which occurred while connecting the socket or * `nil` on success */ typedef void (^of_tcp_socket_async_connect_block_t)(OFTCPSocket *socket, id _Nullable exception); - -/*! - * @brief A block which is called when the socket accepted a connection. - * - * @param socket The socket which accepted the connection - * @param acceptedSocket The socket which has been accepted - * @param exception An exception which occurred while accepting the socket or - * `nil` on success - * @return A bool whether the same block should be used for the next incoming - * connection - */ -typedef bool (^of_tcp_socket_async_accept_block_t)(OFTCPSocket *socket, - OFTCPSocket *acceptedSocket, id _Nullable exception); #endif /*! * @protocol OFTCPSocketDelegate OFTCPSocket.h ObjFW/OFTCPSocket.h * * A delegate for OFTCPSocket. */ -@protocol OFTCPSocketDelegate +@protocol OFTCPSocketDelegate @optional /*! * @brief A method which is called when a socket connected. * * @param socket The socket which connected @@ -70,23 +57,10 @@ */ - (void)socket: (OFTCPSocket *)socket didConnectToHost: (OFString *)host port: (uint16_t)port exception: (nullable id)exception; - -/*! - * @brief A method which is called when a socket accepted a connection. - * - * @param socket The socket which accepted the connection - * @param acceptedSocket The socket which has been accepted - * @param exception An exception that occurred while accepting, or nil on - * success - * @return A bool whether to accept the next incoming connection - */ -- (bool)socket: (OFTCPSocket *)socket - didAcceptSocket: (OFTCPSocket *)acceptedSocket - exception: (nullable id)exception; @end /*! * @class OFTCPSocket OFTCPSocket.h ObjFW/OFTCPSocket.h * @@ -95,12 +69,10 @@ * To connect to a server, create a socket and connect it. * To create a server, create a socket, bind it and listen on it. */ @interface OFTCPSocket: OFStreamSocket { - bool _listening; - of_socket_address_t _remoteAddress; OFString *_Nullable _SOCKS5Host; uint16_t _SOCKS5Port; #ifdef OF_WII uint16_t _port; #endif @@ -110,22 +82,10 @@ #ifdef OF_HAVE_CLASS_PROPERTIES @property (class, nullable, copy, nonatomic) OFString *SOCKS5Host; @property (class, nonatomic) uint16_t SOCKS5Port; #endif -/*! - * @brief Whether the socket is a listening socket. - */ -@property (readonly, nonatomic, getter=isListening) bool listening; - -/*! - * @brief The remote address. - * - * @note This only works for accepted sockets! - */ -@property (readonly, nonatomic) const of_socket_address_t *remoteAddress; - #if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) /*! * @brief Whether keep alives are enabled for the connection. * * @warning This is not available on the Wii or Nintendo 3DS! @@ -137,11 +97,11 @@ /*! * @brief Whether TCP_NODELAY is enabled for the connection * * @warning This is not available on the Wii! */ -@property (nonatomic, getter=isTCPNoDelayEnabled) bool TCPNoDelayEnabled; +@property (nonatomic, getter=isNoDelayEnabled) bool noDelayEnabled; #endif /*! * @brief The host to use as a SOCKS5 proxy. */ @@ -254,63 +214,10 @@ * 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 Listen on the socket. - * - * @param backlog Maximum length for the queue of pending connections. - */ -- (void)listenWithBacklog: (int)backlog; - -/*! - * @brief Listen on the socket. - */ -- (void)listen; - -/*! - * @brief Accept an incoming connection. - * - * @return An autoreleased OFTCPSocket for the accepted connection. - */ -- (instancetype)accept; - -/*! - * @brief Asynchronously accept an incoming connection. - */ -- (void)asyncAccept; - -/*! - * @brief Asynchronously accept an incoming connection. - * - * @param runLoopMode The run loop mode in which to perform the async accept - */ -- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode; - -#ifdef OF_HAVE_BLOCKS -/*! - * @brief Asynchronously accept an incoming connection. - * - * @param block The block to execute when a new connection has been accepted. - * Returns whether the next incoming connection should be accepted - * by the specified block as well. - */ -- (void)asyncAcceptWithBlock: (of_tcp_socket_async_accept_block_t)block; - -/*! - * @brief Asynchronously accept an incoming connection. - * - * @param runLoopMode The run loop mode in which to perform the async accept - * @param block The block to execute when a new connection has been accepted. - * Returns whether the next incoming connection should be accepted - * by the specified block as well. - */ -- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode - block: (of_tcp_socket_async_accept_block_t)block; -#endif @end #ifdef __cplusplus extern "C" { #endif Index: src/OFTCPSocket.m ================================================================== --- src/OFTCPSocket.m +++ src/OFTCPSocket.m @@ -29,28 +29,24 @@ #ifdef HAVE_FCNTL_H # include #endif #import "OFTCPSocket.h" -#import "OFTCPSocket+Private.h" #import "OFDate.h" #import "OFDNSResolver.h" #import "OFData.h" #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFString.h" #import "OFThread.h" #import "OFTimer.h" -#import "OFAcceptFailedException.h" #import "OFAlreadyConnectedException.h" #import "OFBindFailedException.h" #import "OFConnectionFailedException.h" #import "OFGetOptionFailedException.h" -#import "OFInvalidArgumentException.h" #import "OFInvalidFormatException.h" -#import "OFListenFailedException.h" #import "OFNotImplementedException.h" #import "OFNotOpenException.h" #import "OFOutOfMemoryException.h" #import "OFOutOfRangeException.h" #import "OFSetOptionFailedException.h" @@ -63,13 +59,21 @@ Class of_tls_socket_class = Nil; static OFString *defaultSOCKS5Host = nil; static uint16_t defaultSOCKS5Port = 1080; + +@interface OFTCPSocket () +- (bool)of_createSocketForAddress: (const of_socket_address_t *)address + errNo: (int *)errNo; +- (bool)of_connectSocketToAddress: (const of_socket_address_t *)address + errNo: (int *)errNo; +- (void)of_closeSocket; +@end @interface OFTCPSocketAsyncConnectDelegate: OFObject + OFRunLoopConnectDelegate, OFDNSResolverHostDelegate> { OFTCPSocket *_socket; OFString *_host; uint16_t _port; OFString *_SOCKS5Host; @@ -218,11 +222,11 @@ #ifdef OF_HAVE_BLOCKS } #endif } -- (void)of_socketDidConnect: (OFTCPSocket *)sock +- (void)of_socketDidConnect: (id)sock exception: (id)exception { if (exception != nil) { /* * self might be retained only by the pending async requests, @@ -309,13 +313,13 @@ if (![_socket of_connectSocketToAddress: &address errNo: &errNo]) { #if !defined(OF_NINTENDO_3DS) && !defined(OF_WII) if (errNo == EINPROGRESS) { - [OFRunLoop of_addAsyncConnectForTCPSocket: _socket - mode: runLoopMode - delegate: self]; + [OFRunLoop of_addAsyncConnectForSocket: _socket + mode: runLoopMode + delegate: self]; return; } else { #endif [_socket of_closeSocket]; @@ -712,24 +716,10 @@ { closesocket(_socket); _socket = INVALID_SOCKET; } -#ifndef OF_WII -- (int)of_socketError -{ - int errNo; - socklen_t len = sizeof(errNo); - - if (getsockopt(_socket, SOL_SOCKET, SO_ERROR, (char *)&errNo, - &len) != 0) - return of_socket_errno(); - - return errNo; -} -#endif - - (void)connectToHost: (OFString *)host port: (uint16_t)port { void *pool = objc_autoreleasePoolPush(); id delegate = [_delegate retain]; @@ -951,140 +941,10 @@ socket: self errNo: EADDRNOTAVAIL]; #endif } -- (void)listen -{ - [self listenWithBacklog: SOMAXCONN]; -} - -- (void)listenWithBacklog: (int)backlog -{ - if (_socket == INVALID_SOCKET) - @throw [OFNotOpenException exceptionWithObject: self]; - - if (listen(_socket, backlog) == -1) - @throw [OFListenFailedException - exceptionWithSocket: self - backlog: backlog - errNo: of_socket_errno()]; - - _listening = true; -} - -- (instancetype)accept -{ - OFTCPSocket *client = [[[[self class] alloc] init] autorelease]; -#if (!defined(HAVE_PACCEPT) && !defined(HAVE_ACCEPT4)) || !defined(SOCK_CLOEXEC) -# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - int flags; -# endif -#endif - - client->_remoteAddress.length = - (socklen_t)sizeof(client->_remoteAddress.sockaddr); - -#if defined(HAVE_PACCEPT) && defined(SOCK_CLOEXEC) - if ((client->_socket = paccept(_socket, - &client->_remoteAddress.sockaddr.sockaddr, - &client->_remoteAddress.length, NULL, SOCK_CLOEXEC)) == - INVALID_SOCKET) - @throw [OFAcceptFailedException - exceptionWithSocket: self - errNo: of_socket_errno()]; -#elif defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) - if ((client->_socket = accept4(_socket, - &client->_remoteAddress.sockaddr.sockaddr, - &client->_remoteAddress.length, SOCK_CLOEXEC)) == INVALID_SOCKET) - @throw [OFAcceptFailedException - exceptionWithSocket: self - errNo: of_socket_errno()]; -#else - if ((client->_socket = accept(_socket, - &client->_remoteAddress.sockaddr.sockaddr, - &client->_remoteAddress.length)) == INVALID_SOCKET) - @throw [OFAcceptFailedException - exceptionWithSocket: self - errNo: of_socket_errno()]; - -# if defined(HAVE_FCNTL) && defined(FD_CLOEXEC) - if ((flags = fcntl(client->_socket, F_GETFD, 0)) != -1) - fcntl(client->_socket, F_SETFD, flags | FD_CLOEXEC); -# endif -#endif - - assert(client->_remoteAddress.length <= - (socklen_t)sizeof(client->_remoteAddress.sockaddr)); - - switch (client->_remoteAddress.sockaddr.sockaddr.sa_family) { - case AF_INET: - client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV4; - break; -#ifdef OF_HAVE_IPV6 - case AF_INET6: - client->_remoteAddress.family = OF_SOCKET_ADDRESS_FAMILY_IPV6; - break; -#endif - default: - client->_remoteAddress.family = - OF_SOCKET_ADDRESS_FAMILY_UNKNOWN; - break; - } - - return client; -} - -- (void)asyncAccept -{ - [self asyncAcceptWithRunLoopMode: of_run_loop_mode_default]; -} - -- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode -{ - [OFRunLoop of_addAsyncAcceptForSocket: self - mode: runLoopMode - block: NULL - delegate: _delegate]; -} - -#ifdef OF_HAVE_BLOCKS -- (void)asyncAcceptWithBlock: (of_tcp_socket_async_accept_block_t)block -{ - [self asyncAcceptWithRunLoopMode: of_run_loop_mode_default - block: block]; -} - -- (void)asyncAcceptWithRunLoopMode: (of_run_loop_mode_t)runLoopMode - block: (of_tcp_socket_async_accept_block_t)block -{ - [OFRunLoop of_addAsyncAcceptForSocket: self - mode: runLoopMode - block: block - delegate: nil]; -} -#endif - -- (const of_socket_address_t *)remoteAddress -{ - if (_socket == INVALID_SOCKET) - @throw [OFNotOpenException exceptionWithObject: self]; - - if (_remoteAddress.length == 0) - @throw [OFInvalidArgumentException exception]; - - if (_remoteAddress.length > (socklen_t)sizeof(_remoteAddress.sockaddr)) - @throw [OFOutOfRangeException exception]; - - return &_remoteAddress; -} - -- (bool)isListening -{ - return _listening; -} - #if !defined(OF_WII) && !defined(OF_NINTENDO_3DS) - (void)setKeepAliveEnabled: (bool)enabled { int v = enabled; @@ -1109,11 +969,11 @@ return v; } #endif #ifndef OF_WII -- (void)setTCPNoDelayEnabled: (bool)enabled +- (void)setNoDelayEnabled: (bool)enabled { int v = enabled; if (setsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, (char *)&v, (socklen_t)sizeof(v)) != 0) @@ -1120,11 +980,11 @@ @throw [OFSetOptionFailedException exceptionWithObject: self errNo: of_socket_errno()]; } -- (bool)isTCPNoDelayEnabled +- (bool)isNoDelayEnabled { int v; socklen_t len = sizeof(v); if (getsockopt(_socket, IPPROTO_TCP, TCP_NODELAY, @@ -1137,16 +997,12 @@ } #endif - (void)close { - _listening = false; - - memset(&_remoteAddress, 0, sizeof(_remoteAddress)); - #ifdef OF_WII _port = 0; #endif [super close]; } @end