Index: src/OFRunLoop+Private.h ================================================================== --- src/OFRunLoop+Private.h +++ src/OFRunLoop+Private.h @@ -138,10 +138,11 @@ delegate: (nullable id )delegate; + (void)of_addAsyncSendForSCTPSocket: (OFSCTPSocket *)socket data: (OFData *)data streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags 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 @@ -222,10 +222,11 @@ OFSCTPSocketAsyncSendDataBlock _block; # endif OFData *_data; uint16_t _streamID; uint32_t _PPID; + OFSCTPPacketFlags _flags; } @end # endif #endif @@ -1039,36 +1040,40 @@ - (bool)handleObject: (id)object { size_t length; uint16_t streamID; uint32_t PPID; + OFSCTPPacketFlags flags; id exception = nil; @try { length = [object receiveIntoBuffer: _buffer length: _length streamID: &streamID - PPID: &PPID]; + PPID: &PPID + flags: &flags]; } @catch (id e) { length = 0; exception = e; } # ifdef OF_HAVE_BLOCKS if (_block != NULL) - return _block(length, streamID, PPID, exception); + return _block(length, streamID, PPID, flags, exception); else { # endif if (![_delegate respondsToSelector: @selector(socket: - didReceiveIntoBuffer:length:streamID:PPID:exception:)]) + didReceiveIntoBuffer:length:streamID:PPID:flags: + exception:)]) return false; return [_delegate socket: object didReceiveIntoBuffer: _buffer length: length streamID: streamID PPID: PPID + flags: flags exception: exception]; # ifdef OF_HAVE_BLOCKS } # endif } @@ -1091,11 +1096,12 @@ @try { [object sendBuffer: _data.items length: _data.count * _data.itemSize streamID: _streamID - PPID: _PPID]; + PPID: _PPID + flags: _flags]; } @catch (id e) { exception = e; } # ifdef OF_HAVE_BLOCKS @@ -1110,18 +1116,19 @@ [oldData release]; return true; } else { # endif - if (![_delegate respondsToSelector: - @selector(socket:didSendData:streamID:PPID:exception:)]) + if (![_delegate respondsToSelector: @selector(socket: + didSendData:streamID:PPID:flags:exception:)]) return false; newData = [_delegate socket: object didSendData: _data streamID: _streamID PPID: _PPID + flags: _flags exception: exception]; if (newData == nil) return false; @@ -1479,10 +1486,11 @@ + (void)of_addAsyncSendForSCTPSocket: (OFSCTPSocket *)sock data: (OFData *)data streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags mode: (OFRunLoopMode)mode # ifdef OF_HAVE_BLOCKS block: (OFSCTPSocketAsyncSendDataBlock)block # endif delegate: (id )delegate @@ -1494,10 +1502,11 @@ queueItem->_block = [block copy]; # endif queueItem->_data = [data copy]; queueItem->_streamID = streamID; queueItem->_PPID = PPID; + queueItem->_flags = flags; QUEUE_ITEM } # endif # undef NEW_READ Index: src/OFSCTPSocket.h ================================================================== --- src/OFSCTPSocket.h +++ src/OFSCTPSocket.h @@ -25,10 +25,18 @@ /** @file */ @class OFSCTPSocket; @class OFString; +/** + * @brief Flags for an SCTP packet. + */ +typedef enum { + /** The packet is sent / received out of order. */ + OFSCTPPacketUnordered = 1 +} OFSCTPPacketFlags; + #ifdef OF_HAVE_BLOCKS /** * @brief A block which is called when the socket connected. * * @param exception An exception which occurred while connecting the socket or @@ -40,16 +48,17 @@ * @brief A block which is called when a packet has been received. * * @param length The length of the packet * @param streamID The stream ID for the message * @param PPID The Payload Protocol Identifier for the message + * @param flags Flags for the packet * @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, id _Nullable exception); + uint32_t PPID, OFSCTPPacketFlags flags, id _Nullable exception); /** * @brief A block which is called when a packet has been sent. * * @param exception An exception which occurred while reading or `nil` on @@ -87,35 +96,39 @@ * @param socket The sequenced packet socket which received a packet * @param buffer The buffer the packet has been written to * @param length The length of the packet * @param streamID The stream ID for the message * @param PPID The Payload Protocol Identifier for the message + * @param flags Flags for the packet * @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: (OFSCTPPacketFlags)flags exception: (nullable id)exception; /** * @brief This method is called when a packet has been sent. * * @param socket The sequenced packet socket which sent a packet * @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 packet * @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: (OFSCTPPacketFlags)flags exception: (nullable id)exception; @end /** * @class OFSCTPSocket OFSCTPSocket.h ObjFW/OFSCTPSocket.h @@ -225,18 +238,20 @@ * * @param buffer The buffer to write the packet 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 packet * @return The length of the received packet * @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; + PPID: (nullable uint32_t *)PPID + flags: (nullable OFSCTPPacketFlags *)flags; /** * @brief Asynchronously receives a packet with stream ID and PPID and stores * it into the specified buffer. * @@ -309,75 +324,85 @@ * * @param buffer The buffer to send as a packet * @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 packet * @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; + PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags; /** * @brief Asynchronously sends the specified packet on the specified stream. * * @param data The data to send as a packet * @param streamID The stream ID for the message * @param PPID The Payload Protocol Identifier for the message + * @param flags Flags for the packet */ - (void)asyncSendData: (OFData *)data streamID: (uint16_t)streamID - PPID: (uint32_t)PPID; + PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags; /** * @brief Asynchronously sends the specified packet on the specified stream. * * @param data The data to send as a packet * @param streamID The stream ID for the message * @param PPID The Payload Protocol Identifier for the message + * @param flags Flags for the packet * @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: (OFSCTPPacketFlags)flags runLoopMode: (OFRunLoopMode)runLoopMode; #ifdef OF_HAVE_BLOCKS /** * @brief Asynchronously sends the specified packet on the specified stream. * * @param data The data to send as a packet * @param streamID The stream ID for the message * @param PPID The Payload Protocol Identifier for the message + * @param flags Flags for the packet * @param block The block to call when the packet 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: (OFSCTPPacketFlags)flags block: (OFSCTPSocketAsyncSendDataBlock)block; /** * @brief Asynchronously sends the specified packet on the specified stream. * * @param data The data to send as a packet * @param streamID The stream ID for the message * @param PPID The Payload Protocol Identifier for the message + * @param flags Flags for the packet * @param runLoopMode The run loop mode in which to perform the asynchronous * send * @param block The block to call when the packet 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: (OFSCTPPacketFlags)flags runLoopMode: (OFRunLoopMode)runLoopMode block: (OFSCTPSocketAsyncSendDataBlock)block; #endif @end OF_ASSUME_NONNULL_END Index: src/OFSCTPSocket.m ================================================================== --- src/OFSCTPSocket.m +++ src/OFSCTPSocket.m @@ -346,17 +346,19 @@ - (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length { return [self receiveIntoBuffer: buffer length: length streamID: NULL - PPID: NULL]; + PPID: NULL + flags: NULL]; } - (size_t)receiveIntoBuffer: (void *)buffer length: (size_t)length streamID: (uint16_t *)streamID PPID: (uint32_t *)PPID + flags: (OFSCTPPacketFlags *)flags { ssize_t ret; struct iovec iov = { .iov_base = buffer, .iov_len = length @@ -388,10 +390,19 @@ 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 |= OFSCTPPacketUnordered; + } return ret; } - (void)asyncReceiveWithInfoIntoBuffer: (void *)buffer @@ -442,26 +453,29 @@ } #endif - (void)sendBuffer: (const void *)buffer length: (size_t)length { - [self sendBuffer: buffer length: length streamID: 0 PPID: 0]; + [self sendBuffer: buffer length: length streamID: 0 PPID: 0 flags: 0]; } - (void)sendBuffer: (const void *)buffer length: (size_t)length streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags { ssize_t bytesWritten; struct iovec iov = { .iov_base = (void *)buffer, .iov_len = length }; struct sctp_sndinfo sndinfo = { .snd_sid = streamID, - .snd_ppid = PPID + .snd_ppid = PPID, + .snd_flags = + ((flags & OFSCTPPacketUnordered) ? SCTP_UNORDERED : 0), }; if (_socket == OFInvalidSocketHandle) @throw [OFNotOpenException exceptionWithObject: self]; @@ -484,26 +498,30 @@ } - (void)asyncSendData: (OFData *)data streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags { [self asyncSendData: data streamID: streamID PPID: PPID + flags: flags runLoopMode: OFDefaultRunLoopMode]; } - (void)asyncSendData: (OFData *)data streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags runLoopMode: (OFRunLoopMode)runLoopMode { [OFRunLoop of_addAsyncSendForSCTPSocket: self data: data streamID: streamID PPID: PPID + flags: flags mode: runLoopMode # ifdef OF_HAVE_BLOCKS block: NULL # endif delegate: _delegate]; @@ -511,29 +529,33 @@ #ifdef OF_HAVE_BLOCKS - (void)asyncSendData: (OFData *)data streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags block: (OFSequencedPacketSocketAsyncSendDataBlock)block { [self asyncSendData: data streamID: streamID PPID: PPID + flags: flags runLoopMode: OFDefaultRunLoopMode block: block]; } - (void)asyncSendData: (OFData *)data streamID: (uint16_t)streamID PPID: (uint32_t)PPID + flags: (OFSCTPPacketFlags)flags runLoopMode: (OFRunLoopMode)runLoopMode block: (OFSequencedPacketSocketAsyncSendDataBlock)block { [OFRunLoop of_addAsyncSendForSCTPSocket: self data: data streamID: streamID PPID: PPID + flags: flags mode: runLoopMode block: block delegate: nil]; } #endif Index: src/test/OTAssert.h ================================================================== --- src/test/OTAssert.h +++ src/test/OTAssert.h @@ -42,21 +42,21 @@ * @param condition The condition to check * @param ... An optional format string to print if the assertion failed, * followed by optional arguments */ #define OTAssertTrue(condition, ...) \ - OTAssert(condition == true, ## __VA_ARGS__) + OTAssert((condition) == true, ## __VA_ARGS__) /** * @brief Asserts that the specified condition is false. * * @param condition The condition to check * @param ... An optional format string to print if the assertion failed, * followed by optional arguments */ #define OTAssertFalse(condition, ...) \ - OTAssert(condition == false, ## __VA_ARGS__) + OTAssert((condition) == false, ## __VA_ARGS__) /** * @brief Asserts that the two values are equal. * * @param a The value to check Index: tests/OFSCTPSocketTests.m ================================================================== --- tests/OFSCTPSocketTests.m +++ tests/OFSCTPSocketTests.m @@ -34,10 +34,11 @@ OFSCTPSocket *server, *client, *accepted; OFSocketAddress address; char buffer[6]; uint16_t streamID; uint32_t PPID; + OFSCTPPacketFlags flags; server = [OFSCTPSocket socket]; client = [OFSCTPSocket socket]; @try { @@ -58,16 +59,22 @@ accepted = [server accept]; OTAssertEqualObjects(OFSocketAddressString(accepted.remoteAddress), @"127.0.0.1"); - [client sendBuffer: "Hello!" length: 6 streamID: 1 PPID: 1234]; + [client sendBuffer: "Hello!" + length: 6 + streamID: 1 + PPID: 1234 + flags: OFSCTPPacketUnordered]; [accepted receiveIntoBuffer: buffer length: 6 streamID: &streamID - PPID: &PPID]; + PPID: &PPID + flags: &flags]; OTAssertEqual(memcmp(buffer, "Hello!", 6), 0); OTAssertEqual(streamID, 1); OTAssertEqual(PPID, 1234); + OTAssertTrue(flags & OFSCTPPacketUnordered); } @end