diff --git a/ChangeLog b/ChangeLog index b1fc200..47c38ad 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +ENet 1.3.2 (May 31, 2011): + +* added support for unreliable packet fragmenting via the packet flag +ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT +* fixed regression in unreliable packet queuing +* added check against received port to limit some forms of IP-spoofing + ENet 1.3.1 (February 10, 2011): * fixed bug in tracking of reliable data in transit @@ -21,6 +28,15 @@ Caveats: This version is not protocol compatible with the 1.2 series or earlier. The enet_host_connect and enet_host_create API functions require supplying additional parameters. +ENet 1.2.4 (May 31, 2011): + +* fixed regression in unreliable packet queuing +* added check against received port to limit some forms of IP-spoofing + +ENet 1.2.3 (February 10, 2011): + +* fixed bug in tracking reliable data in transit + ENet 1.2.2 (June 5, 2010): * checksum functionality is now enabled by setting a checksum callback diff --git a/Makefile.am b/Makefile.am index 4f39688..da792bc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,7 +16,7 @@ enetinclude_HEADERS = \ lib_LTLIBRARIES = libenet.la libenet_la_SOURCES = callbacks.c compress.c host.c list.c packet.c peer.c protocol.c unix.c win32.c # see info '(libtool) Updating version info' before making a release -libenet_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:1:0 +libenet_la_LDFLAGS = $(AM_LDFLAGS) -version-info 1:2:0 INCLUDES = -Iinclude ACLOCAL_AMFLAGS = -Im4 diff --git a/configure.ac b/configure.ac index 468a4b4..dc6da4f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([libenet], [1.3.1]) +AC_INIT([libenet], [1.3.2]) AC_CONFIG_SRCDIR([include/enet/enet.h]) AM_INIT_AUTOMAKE([foreign]) diff --git a/docs/mainpage.dox b/docs/mainpage.dox index 1ea5b4e..3b6234a 100755 --- a/docs/mainpage.dox +++ b/docs/mainpage.dox @@ -36,8 +36,8 @@ portable, and easily embeddable. You can retrieve the source to ENet by downloading it in either .tar.gz form or accessing the cvs distribution directly. -The most recent stable release (1.3.1) can be downloaded here. -The last release that is protocol compatible with the 1.2 series or earlier (1.2.3) can be downloaded here +The most recent stable release (1.3.2) can be downloaded here. +The last release that is protocol compatible with the 1.2 series or earlier (1.2.4) can be downloaded here To access ENet via anonymous CVS, you must use the CVSROOT :pserver:anonymous\@bespin.org:/var/lib/cvs/enet with an empty diff --git a/host.c b/host.c index 8bb2433..c44f2ed 100644 --- a/host.c +++ b/host.c @@ -210,6 +210,7 @@ enet_host_connect (ENetHost * host, const ENetAddress * address, size_t channelC channel -> outgoingReliableSequenceNumber = 0; channel -> outgoingUnreliableSequenceNumber = 0; channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; enet_list_clear (& channel -> incomingReliableCommands); enet_list_clear (& channel -> incomingUnreliableCommands); diff --git a/include/enet/enet.h b/include/enet/enet.h index b572590..02dc2c0 100644 --- a/include/enet/enet.h +++ b/include/enet/enet.h @@ -25,7 +25,7 @@ extern "C" #define ENET_VERSION_MAJOR 1 #define ENET_VERSION_MINOR 3 -#define ENET_VERSION_PATCH 1 +#define ENET_VERSION_PATCH 2 #define ENET_VERSION_CREATE(major, minor, patch) (((major)<<16) | ((minor)<<8) | (patch)) #define ENET_VERSION ENET_VERSION_CREATE(ENET_VERSION_MAJOR, ENET_VERSION_MINOR, ENET_VERSION_PATCH) @@ -96,7 +96,10 @@ typedef enum _ENetPacketFlag */ ENET_PACKET_FLAG_UNSEQUENCED = (1 << 1), /** packet will not allocate data, and user must supply it instead */ - ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2) + ENET_PACKET_FLAG_NO_ALLOCATE = (1 << 2), + /** packet will be fragmented using unreliable (instead of reliable) sends + * if it exceeds the MTU */ + ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT = (1 << 3) } ENetPacketFlag; struct _ENetPacket; @@ -218,6 +221,7 @@ typedef struct _ENetChannel enet_uint16 usedReliableWindows; enet_uint16 reliableWindows [ENET_PEER_RELIABLE_WINDOWS]; enet_uint16 incomingReliableSequenceNumber; + enet_uint16 incomingUnreliableSequenceNumber; ENetList incomingReliableCommands; ENetList incomingUnreliableCommands; } ENetChannel; diff --git a/include/enet/protocol.h b/include/enet/protocol.h index 19f7e45..faef917 100644 --- a/include/enet/protocol.h +++ b/include/enet/protocol.h @@ -33,7 +33,8 @@ typedef enum _ENetProtocolCommand ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED = 9, ENET_PROTOCOL_COMMAND_BANDWIDTH_LIMIT = 10, ENET_PROTOCOL_COMMAND_THROTTLE_CONFIGURE = 11, - ENET_PROTOCOL_COMMAND_COUNT = 12, + ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT = 12, + ENET_PROTOCOL_COMMAND_COUNT = 13, ENET_PROTOCOL_COMMAND_MASK = 0x0F } ENetProtocolCommand; diff --git a/peer.c b/peer.c index e1c117f..5047a47 100644 --- a/peer.c +++ b/peer.c @@ -113,13 +113,26 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) if (packet -> dataLength > fragmentLength) { - enet_uint16 startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); enet_uint32 fragmentCount = ENET_HOST_TO_NET_32 ((packet -> dataLength + fragmentLength - 1) / fragmentLength), fragmentNumber, fragmentOffset; + enet_uint8 commandNumber; + enet_uint16 startSequenceNumber; ENetList fragments; ENetOutgoingCommand * fragment; + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT)) == ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT && + channel -> outgoingUnreliableSequenceNumber < 0xFFFF) + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); + } + else + { + commandNumber = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + startSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingReliableSequenceNumber + 1); + } + enet_list_clear (& fragments); for (fragmentNumber = 0, @@ -147,7 +160,7 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) fragment -> fragmentOffset = fragmentOffset; fragment -> fragmentLength = fragmentLength; fragment -> packet = packet; - fragment -> command.header.command = ENET_PROTOCOL_COMMAND_SEND_FRAGMENT | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; + fragment -> command.header.command = commandNumber; fragment -> command.header.channelID = channelID; fragment -> command.sendFragment.startSequenceNumber = startSequenceNumber; fragment -> command.sendFragment.dataLength = ENET_HOST_TO_NET_16 (fragmentLength); @@ -173,20 +186,13 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) command.header.channelID = channelID; - if (packet -> flags & ENET_PACKET_FLAG_RELIABLE) - { - command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; - command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); - } - else - if (packet -> flags & ENET_PACKET_FLAG_UNSEQUENCED) + if ((packet -> flags & (ENET_PACKET_FLAG_RELIABLE | ENET_PACKET_FLAG_UNSEQUENCED)) == ENET_PACKET_FLAG_UNSEQUENCED) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED | ENET_PROTOCOL_COMMAND_FLAG_UNSEQUENCED; - command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup + 1); command.sendUnsequenced.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); } else - if (channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) + if (packet -> flags & ENET_PACKET_FLAG_RELIABLE || channel -> outgoingUnreliableSequenceNumber >= 0xFFFF) { command.header.command = ENET_PROTOCOL_COMMAND_SEND_RELIABLE | ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE; command.sendReliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); @@ -194,7 +200,6 @@ enet_peer_send (ENetPeer * peer, enet_uint8 channelID, ENetPacket * packet) else { command.header.command = ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE; - command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (channel -> outgoingUnreliableSequenceNumber + 1); command.sendUnreliable.dataLength = ENET_HOST_TO_NET_16 (packet -> dataLength); } @@ -550,7 +555,8 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin } else { - ++ channel -> outgoingUnreliableSequenceNumber; + if (outgoingCommand -> fragmentOffset == 0) + ++ channel -> outgoingUnreliableSequenceNumber; outgoingCommand -> reliableSequenceNumber = channel -> outgoingReliableSequenceNumber; outgoingCommand -> unreliableSequenceNumber = channel -> outgoingUnreliableSequenceNumber; @@ -562,6 +568,20 @@ enet_peer_setup_outgoing_command (ENetPeer * peer, ENetOutgoingCommand * outgoin outgoingCommand -> roundTripTimeoutLimit = 0; outgoingCommand -> command.header.reliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> reliableSequenceNumber); + switch (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) + { + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + outgoingCommand -> command.sendUnreliable.unreliableSequenceNumber = ENET_HOST_TO_NET_16 (outgoingCommand -> unreliableSequenceNumber); + break; + + case ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED: + outgoingCommand -> command.sendUnsequenced.unsequencedGroup = ENET_HOST_TO_NET_16 (peer -> outgoingUnsequencedGroup); + break; + + default: + break; + } + if (outgoingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_FLAG_ACKNOWLEDGE) enet_list_insert (enet_list_end (& peer -> outgoingReliableCommands), outgoingCommand); else @@ -590,29 +610,71 @@ enet_peer_queue_outgoing_command (ENetPeer * peer, const ENetProtocol * command, void enet_peer_dispatch_incoming_unreliable_commands (ENetPeer * peer, ENetChannel * channel) { - ENetListIterator currentCommand; + ENetListIterator startCommand, currentCommand; - for (currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands); + for (startCommand = currentCommand = enet_list_begin (& channel -> incomingUnreliableCommands); currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); currentCommand = enet_list_next (currentCommand)) { ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; - if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE && - incomingCommand -> reliableSequenceNumber != channel -> incomingReliableSequenceNumber) + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) + continue; + else + if (incomingCommand -> reliableSequenceNumber != channel -> incomingReliableSequenceNumber) break; + else + if (incomingCommand -> fragmentsRemaining <= 0) + channel -> incomingUnreliableSequenceNumber = incomingCommand -> unreliableSequenceNumber; + else + { + if (startCommand != currentCommand) + { + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); + + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + + } + + startCommand = enet_list_next (currentCommand); + } } - if (currentCommand == enet_list_begin (& channel -> incomingUnreliableCommands)) - return; - - enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingUnreliableCommands), enet_list_previous (currentCommand)); - - if (! peer -> needsDispatch) + if (startCommand != currentCommand) { - enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand)); - peer -> needsDispatch = 1; + if (! peer -> needsDispatch) + { + enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList); + + peer -> needsDispatch = 1; + } + } + + while (currentCommand != enet_list_begin (& channel -> incomingUnreliableCommands)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) enet_list_previous (currentCommand); + + enet_list_remove (& incomingCommand -> incomingCommandList); + + if (incomingCommand -> packet != NULL) + { + -- incomingCommand -> packet -> referenceCount; + + if (incomingCommand -> packet -> referenceCount == 0) + enet_packet_destroy (incomingCommand -> packet); + } + + if (incomingCommand -> fragments != NULL) + enet_free (incomingCommand -> fragments); + + enet_free (incomingCommand); } } @@ -640,6 +702,8 @@ enet_peer_dispatch_incoming_reliable_commands (ENetPeer * peer, ENetChannel * ch if (currentCommand == enet_list_begin (& channel -> incomingReliableCommands)) return; + channel -> incomingUnreliableSequenceNumber = 0; + enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingReliableCommands), enet_list_previous (currentCommand)); if (! peer -> needsDispatch) @@ -684,7 +748,7 @@ enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, case ENET_PROTOCOL_COMMAND_SEND_FRAGMENT: case ENET_PROTOCOL_COMMAND_SEND_RELIABLE: if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber) - goto freePacket; + goto freePacket; for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingReliableCommands)); currentCommand != enet_list_end (& channel -> incomingReliableCommands); @@ -712,15 +776,20 @@ enet_peer_queue_incoming_command (ENetPeer * peer, const ENetProtocol * command, break; case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE: + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: unreliableSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendUnreliable.unreliableSequenceNumber); + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + unreliableSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + goto freePacket; + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); currentCommand = enet_list_previous (currentCommand)) { incomingCommand = (ENetIncomingCommand *) currentCommand; - if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE) + if ((command -> header.command & ENET_PROTOCOL_COMMAND_MASK) == ENET_PROTOCOL_COMMAND_SEND_UNSEQUENCED) continue; if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) diff --git a/protocol.c b/protocol.c index 9791839..f48d271 100644 --- a/protocol.c +++ b/protocol.c @@ -23,6 +23,7 @@ static size_t commandSizes [ENET_PROTOCOL_COMMAND_COUNT] = sizeof (ENetProtocolSendUnsequenced), sizeof (ENetProtocolBandwidthLimit), sizeof (ENetProtocolThrottleConfigure), + sizeof (ENetProtocolSendFragment) }; size_t @@ -319,6 +320,7 @@ enet_protocol_handle_connect (ENetHost * host, ENetProtocolHeader * header, ENet channel -> outgoingReliableSequenceNumber = 0; channel -> outgoingUnreliableSequenceNumber = 0; channel -> incomingReliableSequenceNumber = 0; + channel -> incomingUnreliableSequenceNumber = 0; enet_list_clear (& channel -> incomingReliableCommands); enet_list_clear (& channel -> incomingUnreliableCommands); @@ -605,6 +607,132 @@ enet_protocol_handle_send_fragment (ENetHost * host, ENetPeer * peer, const ENet return 0; } +static int +enet_protocol_handle_send_unreliable_fragment (ENetHost * host, ENetPeer * peer, const ENetProtocol * command, enet_uint8 ** currentData) +{ + enet_uint32 fragmentNumber, + fragmentCount, + fragmentOffset, + fragmentLength, + reliableSequenceNumber, + startSequenceNumber, + totalLength; + enet_uint16 reliableWindow, currentWindow; + ENetChannel * channel; + ENetListIterator currentCommand; + ENetIncomingCommand * startCommand = NULL; + + if (command -> header.channelID >= peer -> channelCount || + (peer -> state != ENET_PEER_STATE_CONNECTED && peer -> state != ENET_PEER_STATE_DISCONNECT_LATER)) + return -1; + + fragmentLength = ENET_NET_TO_HOST_16 (command -> sendFragment.dataLength); + * currentData += fragmentLength; + if (* currentData > & host -> receivedData [host -> receivedDataLength]) + return -1; + + channel = & peer -> channels [command -> header.channelID]; + reliableSequenceNumber = command -> header.reliableSequenceNumber; + startSequenceNumber = ENET_NET_TO_HOST_16 (command -> sendFragment.startSequenceNumber); + + reliableWindow = reliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + currentWindow = channel -> incomingReliableSequenceNumber / ENET_PEER_RELIABLE_WINDOW_SIZE; + + if (reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + reliableWindow += ENET_PEER_RELIABLE_WINDOWS; + + if (reliableWindow < currentWindow || reliableWindow >= currentWindow + ENET_PEER_FREE_RELIABLE_WINDOWS - 1) + return 0; + + if (reliableSequenceNumber == channel -> incomingReliableSequenceNumber && + startSequenceNumber <= channel -> incomingUnreliableSequenceNumber) + return 0; + + fragmentNumber = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentNumber); + fragmentCount = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentCount); + fragmentOffset = ENET_NET_TO_HOST_32 (command -> sendFragment.fragmentOffset); + totalLength = ENET_NET_TO_HOST_32 (command -> sendFragment.totalLength); + + if (fragmentOffset >= totalLength || + fragmentOffset + fragmentLength > totalLength || + fragmentNumber >= fragmentCount) + return -1; + + for (currentCommand = enet_list_previous (enet_list_end (& channel -> incomingUnreliableCommands)); + currentCommand != enet_list_end (& channel -> incomingUnreliableCommands); + currentCommand = enet_list_previous (currentCommand)) + { + ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) currentCommand; + + if (reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + { + if (incomingCommand -> reliableSequenceNumber < channel -> incomingReliableSequenceNumber) + continue; + } + else + if (incomingCommand -> reliableSequenceNumber >= channel -> incomingReliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber < reliableSequenceNumber) + break; + + if (incomingCommand -> reliableSequenceNumber > reliableSequenceNumber) + continue; + + if (incomingCommand -> unreliableSequenceNumber <= startSequenceNumber) + { + if (incomingCommand -> unreliableSequenceNumber < startSequenceNumber) + break; + + if ((incomingCommand -> command.header.command & ENET_PROTOCOL_COMMAND_MASK) != ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT || + totalLength != incomingCommand -> packet -> dataLength || + fragmentCount != incomingCommand -> fragmentCount) + return -1; + + startCommand = incomingCommand; + break; + } + } + + if (startCommand == NULL) + { + ENetProtocol hostCommand = * command; + ENetPacket * packet = enet_packet_create (NULL, totalLength, ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT); + if (packet == NULL) + return -1; + + hostCommand.sendFragment.startSequenceNumber = startSequenceNumber; + hostCommand.sendFragment.dataLength = fragmentLength; + hostCommand.sendFragment.fragmentNumber = fragmentNumber; + hostCommand.sendFragment.fragmentCount = fragmentCount; + hostCommand.sendFragment.fragmentOffset = fragmentOffset; + hostCommand.sendFragment.totalLength = totalLength; + + startCommand = enet_peer_queue_incoming_command (peer, & hostCommand, packet, fragmentCount); + if (startCommand == NULL) + return -1; + } + + if ((startCommand -> fragments [fragmentNumber / 32] & (1 << (fragmentNumber % 32))) == 0) + { + -- startCommand -> fragmentsRemaining; + + startCommand -> fragments [fragmentNumber / 32] |= (1 << (fragmentNumber % 32)); + + if (fragmentOffset + fragmentLength > startCommand -> packet -> dataLength) + fragmentLength = startCommand -> packet -> dataLength - fragmentOffset; + + memcpy (startCommand -> packet -> data + fragmentOffset, + (enet_uint8 *) command + sizeof (ENetProtocolSendFragment), + fragmentLength); + + if (startCommand -> fragmentsRemaining <= 0) + enet_peer_dispatch_incoming_unreliable_commands (peer, channel); + } + + return 0; +} + static int enet_protocol_handle_ping (ENetHost * host, ENetPeer * peer, const ENetProtocol * command) { @@ -986,6 +1114,11 @@ enet_protocol_handle_incoming_commands (ENetHost * host, ENetEvent * event) goto commandError; break; + case ENET_PROTOCOL_COMMAND_SEND_UNRELIABLE_FRAGMENT: + if (enet_protocol_handle_send_unreliable_fragment (host, peer, command, & currentData)) + goto commandError; + break; + default: goto commandError; }