ソースを参照

support for ENET_PACKET_FLAG_UNRELIABLE_FRAGMENT
1.3.2 release prep

eihrul 14 年 前
コミット
f38c177db0
9 ファイル変更256 行追加32 行削除
  1. 16 0
      ChangeLog
  2. 1 1
      Makefile.am
  3. 1 1
      configure.ac
  4. 2 2
      docs/mainpage.dox
  5. 1 0
      host.c
  6. 6 2
      include/enet/enet.h
  7. 2 1
      include/enet/protocol.h
  8. 94 25
      peer.c
  9. 133 0
      protocol.c

+ 16 - 0
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

+ 1 - 1
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

+ 1 - 1
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])
 

+ 2 - 2
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 <a href="http://enet.bespin.org/download/enet-1.3.1.tar.gz">here</a>. 
-The last release that is protocol compatible with the 1.2 series or earlier (1.2.3) can be downloaded <a href="http://enet.bespin.org/download/enet-1.2.3.tar.gz">here</a> 
+The most recent stable release (1.3.2) can be downloaded <a href="http://enet.bespin.org/download/enet-1.3.2.tar.gz">here</a>. 
+The last release that is protocol compatible with the 1.2 series or earlier (1.2.4) can be downloaded <a href="http://enet.bespin.org/download/enet-1.2.4.tar.gz">here</a> 
 
 To access ENet via anonymous CVS, you must use the CVSROOT
 :pserver:anonymous\@bespin.org:/var/lib/cvs/enet with an empty

+ 1 - 0
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);

+ 6 - 2
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;

+ 2 - 1
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;

+ 94 - 25
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;
+    if (startCommand != currentCommand)
+    {
+        enet_list_move (enet_list_end (& peer -> dispatchedCommands), startCommand, enet_list_previous (currentCommand));
 
-    enet_list_move (enet_list_end (& peer -> dispatchedCommands), enet_list_begin (& channel -> incomingUnreliableCommands), enet_list_previous (currentCommand));
+        if (! peer -> needsDispatch)
+        {
+            enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
 
-    if (! peer -> needsDispatch)
+            peer -> needsDispatch = 1;
+        }
+    }
+
+    while (currentCommand != enet_list_begin (& channel -> incomingUnreliableCommands))
     {
-       enet_list_insert (enet_list_end (& peer -> host -> dispatchQueue), & peer -> dispatchList);
+       ENetIncomingCommand * incomingCommand = (ENetIncomingCommand *) enet_list_previous (currentCommand);
 
-       peer -> needsDispatch = 1;
+       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)

+ 133 - 0
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;
        }