fix: kcp2k V1.8: fixes empty message sending/receiving undefined behaviour and fixes IPv6 errors on Nintendo Switch

This commit is contained in:
vis2k 2021-02-14 17:45:50 +08:00
parent 4cc9f2ea6a
commit 66b64f010f
2 changed files with 45 additions and 6 deletions

View File

@ -53,6 +53,15 @@ public abstract class KcpConnection
// may not be a bug in original kcp. but since it uses the define, we // may not be a bug in original kcp. but since it uses the define, we
// can use that here too. // can use that here too.
// -> we add 1 byte KcpHeader enum to each message, so -1 // -> we add 1 byte KcpHeader enum to each message, so -1
//
// IMPORTANT: max message is MTU * WND_RCV, in other words it completely
// fills the receive window! due to head of line blocking,
// all other messages have to wait while a maxed size message
// is being delivered.
// => in other words, DO NOT use max size all the time like
// for batching.
// => sending UNRELIABLE max message size most of the time is
// best for performance (use that one for batching!)
public const int ReliableMaxMessageSize = (Kcp.MTU_DEF - Kcp.OVERHEAD - CHANNEL_HEADER_SIZE) * (Kcp.WND_RCV - 1) - 1; public const int ReliableMaxMessageSize = (Kcp.MTU_DEF - Kcp.OVERHEAD - CHANNEL_HEADER_SIZE) * (Kcp.WND_RCV - 1) - 1;
// unreliable max message size is simply MTU - channel header size // unreliable max message size is simply MTU - channel header size
@ -185,7 +194,7 @@ void HandleChoked()
$"* Try to Enable NoDelay, decrease INTERVAL, disable Congestion Window (= enable NOCWND!), increase SEND/RECV WINDOW or compress data.\n" + $"* Try to Enable NoDelay, decrease INTERVAL, disable Congestion Window (= enable NOCWND!), increase SEND/RECV WINDOW or compress data.\n" +
$"* Or perhaps the network is simply too slow on our end, or on the other end.\n"); $"* Or perhaps the network is simply too slow on our end, or on the other end.\n");
// let's clear all pending sends before disconnecting with 'Bye'. // let's clear all pending sends before disconnting with 'Bye'.
// otherwise a single Flush in Disconnect() won't be enough to // otherwise a single Flush in Disconnect() won't be enough to
// flush thousands of messages to finally deliver 'Bye'. // flush thousands of messages to finally deliver 'Bye'.
// this is just faster and more robust. // this is just faster and more robust.
@ -315,8 +324,18 @@ void TickAuthenticated(uint time)
} }
case KcpHeader.Data: case KcpHeader.Data:
{ {
//Log.Warning($"Kcp recv msg: {BitConverter.ToString(message.Array, message.Offset, message.Count)}"); // call OnData IF the message contained actual data
OnData?.Invoke(message); if (message.Count > 0)
{
//Log.Warning($"Kcp recv msg: {BitConverter.ToString(message.Array, message.Offset, message.Count)}");
OnData?.Invoke(message);
}
// empty data = attacker, or something went wrong
else
{
Log.Warning("KCP: received empty Data message while Authenticated. Disconnecting the connection.");
Disconnect();
}
break; break;
} }
case KcpHeader.Ping: case KcpHeader.Ping:
@ -521,6 +540,17 @@ public void SendHandshake()
public void SendData(ArraySegment<byte> data, KcpChannel channel) public void SendData(ArraySegment<byte> data, KcpChannel channel)
{ {
// sending empty segments is not allowed.
// nobody should ever try to send empty data.
// it means that something went wrong, e.g. in Mirror/DOTSNET.
// let's make it obvious so it's easy to debug.
if (data.Count == 0)
{
Log.Warning("KcpConnection: tried sending empty message. This should never happen. Disconnecting.");
Disconnect();
return;
}
switch (channel) switch (channel)
{ {
case KcpChannel.Reliable: case KcpChannel.Reliable:
@ -539,9 +569,7 @@ public void SendData(ArraySegment<byte> data, KcpChannel channel)
// disconnect info needs to be delivered, so it goes over reliable // disconnect info needs to be delivered, so it goes over reliable
void SendDisconnect() => SendReliable(KcpHeader.Disconnect, default); void SendDisconnect() => SendReliable(KcpHeader.Disconnect, default);
protected virtual void Dispose() protected virtual void Dispose() {}
{
}
// disconnect this connection // disconnect this connection
public void Disconnect() public void Disconnect()

View File

@ -39,7 +39,12 @@ public class KcpServer
// state // state
Socket socket; Socket socket;
#if UNITY_SWITCH
// switch does not support ipv6
EndPoint newClientEP = new IPEndPoint(IPAddress.Any, 0);
#else
EndPoint newClientEP = new IPEndPoint(IPAddress.IPv6Any, 0); EndPoint newClientEP = new IPEndPoint(IPAddress.IPv6Any, 0);
#endif
// IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even // IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even
// if MaxMessageSize is larger. kcp always sends in MTU // if MaxMessageSize is larger. kcp always sends in MTU
// segments and having a buffer smaller than MTU would // segments and having a buffer smaller than MTU would
@ -82,9 +87,15 @@ public void Start(ushort port)
} }
// listen // listen
#if UNITY_SWITCH
// Switch does not support ipv6
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(IPAddress.Any, port));
#else
socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
socket.DualMode = true; socket.DualMode = true;
socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port)); socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port));
#endif
} }
public void Send(int connectionId, ArraySegment<byte> segment, KcpChannel channel) public void Send(int connectionId, ArraySegment<byte> segment, KcpChannel channel)