Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -136,13 +136,11 @@ block: (nullable OFSCTPSocketAsyncReceiveBlock)block # endif delegate: (nullable id )delegate; + (void)of_addAsyncSendForSCTPSocket: (OFSCTPSocket *)socket data: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (OFSCTPMessageInfo)info mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (nullable OFSCTPSocketAsyncSendDataBlock)block # endif delegate: (nullable id )delegate; Index: src/OFRunLoop.m ================================================================== --- src/OFRunLoop.m +++ src/OFRunLoop.m @@ -220,13 +220,11 @@ @public # ifdef OF_HAVE_BLOCKS OFSCTPSocketAsyncSendDataBlock _block; # endif OFData *_data; - uint16_t _streamID; - uint32_t _PPID; - OFSCTPMessageFlags _flags; + OFSCTPMessageInfo _info; } @end # endif #endif @@ -1038,42 +1036,35 @@ # ifdef OF_HAVE_SCTP @implementation OFRunLoopSCTPReceiveQueueItem - (bool)handleObject: (id)object { size_t length; - uint16_t streamID; - uint32_t PPID; - OFSCTPMessageFlags flags; + OFSCTPMessageInfo info; id exception = nil; @try { length = [object receiveIntoBuffer: _buffer length: _length - streamID: &streamID - PPID: &PPID - flags: &flags]; + info: &info]; } @catch (id e) { length = 0; exception = e; } # ifdef OF_HAVE_BLOCKS if (_block != NULL) - return _block(length, streamID, PPID, flags, exception); + return _block(length, info, exception); else { # endif - if (![_delegate respondsToSelector: @selector(socket: - didReceiveIntoBuffer:length:streamID:PPID:flags: - exception:)]) + if (![_delegate respondsToSelector: @selector( + socket:didReceiveIntoBuffer:length:info:exception:)]) return false; return [_delegate socket: object didReceiveIntoBuffer: _buffer length: length - streamID: streamID - PPID: PPID - flags: flags + info: info exception: exception]; # ifdef OF_HAVE_BLOCKS } # endif } @@ -1095,13 +1086,11 @@ OFData *newData, *oldData; @try { [object sendBuffer: _data.items length: _data.count * _data.itemSize - streamID: _streamID - PPID: _PPID - flags: _flags]; + info: _info]; } @catch (id e) { exception = e; } # ifdef OF_HAVE_BLOCKS @@ -1116,19 +1105,17 @@ [oldData release]; return true; } else { # endif - if (![_delegate respondsToSelector: @selector(socket: - didSendData:streamID:PPID:flags:exception:)]) + if (![_delegate respondsToSelector: @selector( + socket:didSendData:info:exception:)]) return false; newData = [_delegate socket: object didSendData: _data - streamID: _streamID - PPID: _PPID - flags: _flags + info: _info exception: exception]; if (newData == nil) return false; @@ -1146,10 +1133,11 @@ { [_data release]; # ifdef OF_HAVE_BLOCKS [_block release]; # endif + [_info release]; [super dealloc]; } @end # endif @@ -1484,13 +1472,11 @@ QUEUE_ITEM } + (void)of_addAsyncSendForSCTPSocket: (OFSCTPSocket *)sock data: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (OFSCTPMessageInfo)info mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (OFSCTPSocketAsyncSendDataBlock)block # endif delegate: (id )delegate @@ -1500,13 +1486,11 @@ queueItem->_delegate = [delegate retain]; # ifdef OF_HAVE_BLOCKS queueItem->_block = [block copy]; # endif queueItem->_data = [data copy]; - queueItem->_streamID = streamID; - queueItem->_PPID = PPID; - queueItem->_flags = flags; + queueItem->_info = [info copy]; QUEUE_ITEM } # endif # undef NEW_READ Index: src/OFSCTPSocket.h ================================================================== --- src/OFSCTPSocket.h +++ src/OFSCTPSocket.h @@ -22,20 +22,57 @@ OF_ASSUME_NONNULL_BEGIN /** @file */ +@class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFSCTPSocket; @class OFString; /** - * @brief Flags for an SCTP message. + * @brief A key for the SCTP message info. + * + * Possible values are: + * + * * @ref OFSCTPStreamID + * * @ref OFSCTPPPID + * * @ref OFSCTPUnordered + */ +typedef OFConstantString *OFSCTPMessageInfoKey; + +/** + * @brief A dictionary mapping keys of type @ref OFSCTPMessageInfoKey to their + * values. + */ +typedef OFDictionary OF_GENERIC(OFSCTPMessageInfoKey, id) *OFSCTPMessageInfo; + +#ifdef __cplusplus +extern "C" { +#endif +/** + * @brief The SCTP stream ID for which the message was send / received. + * + * This is an `uint16_t` wrapped in an @ref OFNumber. + */ +extern const OFSCTPMessageInfoKey OFSCTPStreamID; + +/** + * @brief The Payload Protocol Identifier for the message. + * + * This is an `uint32_t` wrapped in an @ref OFNumber. + */ +extern const OFSCTPMessageInfoKey OFSCTPPPID; + +/** + * @brief Whether the message is send / received out of order. + * + * Possible values are an @ref OFNumber with either `true` or `false`. */ -typedef enum { - /** The message is sent / received out of order. */ - OFSCTPMessageUnordered = 1 -} OFSCTPMessageFlags; +extern const OFSCTPMessageInfoKey OFSCTPUnordered; +#ifdef __cplusplus +} +#endif #ifdef OF_HAVE_BLOCKS /** * @brief A block which is called when the socket connected. * @@ -46,19 +83,17 @@ /** * @brief A block which is called when a message has been received. * * @param length The length of the message - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message + * @param info Information about the message, see @ref OFSCTPMessageInfo * @param exception An exception which occurred while receiving or `nil` on * success * @return A bool whether the same block should be used for the next receive */ -typedef bool (^OFSCTPSocketAsyncReceiveBlock)(size_t length, uint16_t streamID, - uint32_t PPID, OFSCTPMessageFlags flags, id _Nullable exception); +typedef bool (^OFSCTPSocketAsyncReceiveBlock)(size_t length, + OFSCTPMessageInfo info, id _Nullable exception); /** * @brief A block which is called when a message has been sent. * * @param exception An exception which occurred while reading or `nil` on @@ -94,41 +129,33 @@ * @brief This method is called when a message has been received. * * @param socket The SCTP socket which received a message * @param buffer The buffer the message has been written to * @param length The length of the message - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message + * @param info Information about the message, see @ref OFSCTPMessageInfo * @param exception An exception that occurred while receiving, or nil on * success * @return A bool whether the same block should be used for the next receive */ - (bool)socket: (OFSCTPSocket *)socket didReceiveIntoBuffer: (void *)buffer length: (size_t)length - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (nullable OFSCTPMessageInfo)info exception: (nullable id)exception; /** * @brief This method is called when a message has been sent. * * @param socket The SCTP socket which sent a message * @param data The data which was sent - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message + * @param info Information about the message, see @ref OFSCTPMessageInfo * @param exception An exception that occurred while sending, or nil on success * @return The data to repeat the send with or nil if it should not repeat */ - (nullable OFData *)socket: (OFSCTPSocket *)socket didSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (nullable OFSCTPMessageInfo)info exception: (nullable id)exception; @end /** * @class OFSCTPSocket OFSCTPSocket.h ObjFW/OFSCTPSocket.h @@ -236,26 +263,23 @@ * * If the buffer is too small, the message is truncated. * * @param buffer The buffer to write the message to * @param length The length of the buffer - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message + * @param info Information about the message, see @ref OFSCTPMessageInfo * @return The length of the received message * @throw OFReadFailedException Receiving failed * @throw OFNotOpenException The socket is not open */ - (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length - streamID: (nullable uint16_t *)streamID - PPID: (nullable uint32_t *)PPID - flags: (nullable OFSCTPMessageFlags *)flags; + info: (__autoreleasing _Nullable OFSCTPMessageInfo + *_Nullable)info; /** - * @brief Asynchronously receives a message with stream ID and PPID and stores - * it into the specified buffer. + * @brief Asynchronously receives a message and stores it into the specified + * buffer. * * If the buffer is too small, the message is truncated. * * @param buffer The buffer to write the message to * @param length The length of the buffer @@ -262,12 +286,12 @@ */ - (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer length: (size_t)length; /** - * @brief Asynchronously receives a message with stream ID and PPID and stores - * it into the specified buffer. + * @brief Asynchronously receives a message and stores it into the specified + * buffer. * * If the buffer is too small, the message is truncated. * * @param buffer The buffer to write the message to * @param length The length of the buffer @@ -278,12 +302,12 @@ length: (size_t)length runLoopMode: (OFRunLoopMode)runLoopMode; #ifdef OF_HAVE_BLOCKS /** - * @brief Asynchronously receives a message with stream ID and PPID and stores - * it into the specified buffer. + * @brief Asynchronously receives a message and stores it into the specified + * buffer. * * If the buffer is too small, the message is truncated. * * @param buffer The buffer to write the message to * @param length The length of the buffer @@ -296,12 +320,12 @@ - (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer length: (size_t)length block: (OFSCTPSocketAsyncReceiveBlock)block; /** - * @brief Asynchronously receives a message with stream ID and PPID and stores - * it into the specified buffer. + * @brief Asynchronously receives a message and stores it into the specified + * buffer. * * If the buffer is too small, the message is truncated. * * @param buffer The buffer to write the message to * @param length The length of the buffer @@ -322,87 +346,66 @@ /** * @brief Sends the specified message on the specified stream. * * @param buffer The buffer to send as a message * @param length The length of the buffer - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message - * @throw OFWriteFailedException Sending failed - * @throw OFNotOpenException The socket is not open - */ -- (void)sendBuffer: (const void *)buffer - length: (size_t)length - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags; - -/** - * @brief Asynchronously sends the specified message on the specified stream. - * - * @param data The data to send as a message - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message - */ -- (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags; - -/** - * @brief Asynchronously sends the specified message on the specified stream. - * - * @param data The data to send as a message - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message - * @param runLoopMode The run loop mode in which to perform the asynchronous - * send - */ -- (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + * @param info Information about the message, see @ref OFSCTPMessageInfo + * @throw OFWriteFailedException Sending failed + * @throw OFNotOpenException The socket is not open + */ +- (void)sendBuffer: (const void *)buffer + length: (size_t)length + info: (nullable OFSCTPMessageInfo)info; + +/** + * @brief Asynchronously sends the specified message on the specified stream. + * + * @param data The data to send as a message + * @param info Information about the message, see @ref OFSCTPMessageInfo + */ +- (void)asyncSendData: (OFData *)data info: (nullable OFSCTPMessageInfo)info; + +/** + * @brief Asynchronously sends the specified message on the specified stream. + * + * @param data The data to send as a message + * @param info Information about the message, see @ref OFSCTPMessageInfo + * @param runLoopMode The run loop mode in which to perform the asynchronous + * send + */ +- (void)asyncSendData: (OFData *)data + info: (nullable OFSCTPMessageInfo)info runLoopMode: (OFRunLoopMode)runLoopMode; #ifdef OF_HAVE_BLOCKS /** * @brief Asynchronously sends the specified message on the specified stream. * * @param data The data to send as a message - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message + * @param info Information about the message, see @ref OFSCTPMessageInfo * @param block The block to call when the message has been sent. It should * return the data for the next send with the same callback or nil * if it should not repeat. */ - (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (nullable OFSCTPMessageInfo)info block: (OFSCTPSocketAsyncSendDataBlock)block; /** * @brief Asynchronously sends the specified message on the specified stream. * * @param data The data to send as a message - * @param streamID The stream ID for the message - * @param PPID The Payload Protocol Identifier for the message - * @param flags Flags for the message + * @param info Information about the message, see @ref OFSCTPMessageInfo * @param runLoopMode The run loop mode in which to perform the asynchronous * send * @param block The block to call when the message has been sent. It should * return the data for the next send with the same callback or nil * if it should not repeat. */ - (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (nullable OFSCTPMessageInfo)info runLoopMode: (OFRunLoopMode)runLoopMode block: (OFSCTPSocketAsyncSendDataBlock)block; #endif @end OF_ASSUME_NONNULL_END Index: src/OFSCTPSocket.m ================================================================== --- src/OFSCTPSocket.m +++ src/OFSCTPSocket.m @@ -31,10 +31,12 @@ #import "OFSCTPSocket.h" #import "OFAsyncIPSocketConnector.h" #import "OFDNSResolver.h" #import "OFData.h" #import "OFDate.h" +#import "OFDictionary.h" +#import "OFNumber.h" #import "OFRunLoop.h" #import "OFRunLoop+Private.h" #import "OFSocket.h" #import "OFSocket+Private.h" #import "OFString.h" @@ -48,10 +50,14 @@ #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" #import "OFSetOptionFailedException.h" #import "OFWriteFailedException.h" +const OFSCTPMessageInfoKey OFSCTPStreamID = @"OFSCTPStreamID"; +const OFSCTPMessageInfoKey OFSCTPPPID = @"OFSCTPPPID"; +const OFSCTPMessageInfoKey OFSCTPUnordered = @"OFSCTPUnordered"; + static const OFRunLoopMode connectRunLoopMode = @"OFSCTPSocketConnectRunLoopMode"; @interface OFSCTPSocket () @end @@ -343,22 +349,16 @@ } #endif - (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length { - return [self receiveIntoBuffer: buffer - length: length - streamID: NULL - PPID: NULL - flags: NULL]; + return [self receiveIntoBuffer: buffer length: length info: NULL]; } - (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length - streamID: (uint16_t *)streamID - PPID: (uint32_t *)PPID - flags: (OFSCTPMessageFlags *)flags + info: (OFSCTPMessageInfo *)info { ssize_t ret; struct iovec iov = { .iov_base = buffer, .iov_len = length @@ -375,33 +375,26 @@ @throw [OFReadFailedException exceptionWithObject: self requestedLength: length errNo: _OFSocketErrNo()]; - if (streamID != NULL) { - if (infotype == SCTP_RECVV_RCVINFO && - rcvinfoSize >= (socklen_t)sizeof(rcvinfo)) - *streamID = rcvinfo.rcv_sid; - else - *streamID = 0; - } - - if (PPID != NULL) { - if (infotype == SCTP_RECVV_RCVINFO && - rcvinfoSize >= (socklen_t)sizeof(rcvinfo)) - *PPID = rcvinfo.rcv_ppid; - else - *PPID = 0; - } - - if (flags != NULL) { - *flags = 0; - - if (infotype == SCTP_RECVV_RCVINFO && - rcvinfoSize >= (socklen_t)sizeof(rcvinfo) && - rcvinfo.rcv_flags & SCTP_UNORDERED) - *flags |= OFSCTPMessageUnordered; + if (info != NULL) { + if (infotype == SCTP_RECVV_RCVINFO && + rcvinfoSize >= (socklen_t)sizeof(rcvinfo)) { + OFNumber *streamID = [OFNumber numberWithUnsignedShort: + rcvinfo.rcv_sid]; + OFNumber *PPID = [OFNumber numberWithUnsignedLong: + rcvinfo.rcv_ppid]; + OFNumber *unordered = [OFNumber numberWithBool: + (rcvinfo.rcv_flags & SCTP_UNORDERED)]; + + *info = [OFDictionary dictionaryWithKeysAndObjects: + OFSCTPStreamID, streamID, + OFSCTPPPID, PPID, + OFSCTPUnordered, unordered, nil]; + } else + *info = [OFDictionary dictionary]; } return ret; } @@ -453,29 +446,29 @@ } #endif - (void)sendBuffer: (const void *)buffer length: (size_t)length { - [self sendBuffer: buffer length: length streamID: 0 PPID: 0 flags: 0]; + [self sendBuffer: buffer length: length info: nil]; } - (void)sendBuffer: (const void *)buffer length: (size_t)length - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (OFSCTPMessageInfo)info { ssize_t bytesWritten; struct iovec iov = { .iov_base = (void *)buffer, .iov_len = length }; struct sctp_sndinfo sndinfo = { - .snd_sid = streamID, - .snd_ppid = PPID, + .snd_sid = (uint16_t) + [[info objectForKey: OFSCTPStreamID] unsignedShortValue], + .snd_ppid = (uint32_t) + [[info objectForKey: OFSCTPPPID] unsignedLongValue], .snd_flags = - ((flags & OFSCTPMessageUnordered) ? SCTP_UNORDERED : 0), + [[info objectForKey: OFSCTPUnordered] boolValue] }; if (_socket == OFInvalidSocketHandle) @throw [OFNotOpenException exceptionWithObject: self]; @@ -495,67 +488,48 @@ requestedLength: length bytesWritten: bytesWritten errNo: 0]; } -- (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags -{ - [self asyncSendData: data - streamID: streamID - PPID: PPID - flags: flags - runLoopMode: OFDefaultRunLoopMode]; +- (void)asyncSendData: (OFData *)data info: (OFSCTPMessageInfo)info +{ + [self asyncSendData: data info: nil runLoopMode: OFDefaultRunLoopMode]; } - (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (OFSCTPMessageInfo)info runLoopMode: (OFRunLoopMode)runLoopMode { [OFRunLoop of_addAsyncSendForSCTPSocket: self data: data - streamID: streamID - PPID: PPID - flags: flags + info: info mode: runLoopMode # ifdef OF_HAVE_BLOCKS block: NULL # endif delegate: _delegate]; } #ifdef OF_HAVE_BLOCKS - (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (OFSCTPMessageInfo)info block: (OFSCTPSocketAsyncSendDataBlock)block { [self asyncSendData: data - streamID: streamID - PPID: PPID - flags: flags + info: info runLoopMode: OFDefaultRunLoopMode block: block]; } - (void)asyncSendData: (OFData *)data - streamID: (uint16_t)streamID - PPID: (uint32_t)PPID - flags: (OFSCTPMessageFlags)flags + info: (OFSCTPMessageInfo)info runLoopMode: (OFRunLoopMode)runLoopMode block: (OFSCTPSocketAsyncSendDataBlock)block { [OFRunLoop of_addAsyncSendForSCTPSocket: self data: data - streamID: streamID - PPID: PPID - flags: flags + info: info mode: runLoopMode block: block delegate: nil]; } #endif Index: tests/OFSCTPSocketTests.m ================================================================== --- tests/OFSCTPSocketTests.m +++ tests/OFSCTPSocketTests.m @@ -32,13 +32,12 @@ - (void)testSCTPSocket { OFSCTPSocket *server, *client, *accepted; OFSocketAddress address; char buffer[6]; - uint16_t streamID; - uint32_t PPID; - OFSCTPMessageFlags flags; + OFNumber *streamID, *PPID, *unordered; + OFSCTPMessageInfo sendInfo, receiveInfo; server = [OFSCTPSocket socket]; client = [OFSCTPSocket socket]; @try { @@ -59,22 +58,23 @@ accepted = [server accept]; OTAssertEqualObjects(OFSocketAddressString(accepted.remoteAddress), @"127.0.0.1"); + streamID = [OFNumber numberWithUnsignedShort: 1]; + PPID = [OFNumber numberWithUnsignedLong: 1234]; + unordered = [OFNumber numberWithBool: true]; + sendInfo = [OFDictionary dictionaryWithKeysAndObjects: + OFSCTPStreamID, streamID, + OFSCTPPPID, PPID, + OFSCTPUnordered, unordered, nil]; [client sendBuffer: "Hello!" length: 6 - streamID: 1 - PPID: 1234 - flags: OFSCTPMessageUnordered]; + info: sendInfo]; [accepted receiveIntoBuffer: buffer length: 6 - streamID: &streamID - PPID: &PPID - flags: &flags]; + info: &receiveInfo]; OTAssertEqual(memcmp(buffer, "Hello!", 6), 0); - OTAssertEqual(streamID, 1); - OTAssertEqual(PPID, 1234); - OTAssertTrue(flags & OFSCTPMessageUnordered); + OTAssertEqualObjects(receiveInfo, sendInfo); } @end