* Add an initial complete frame timeout to the WebSocket processor to make it easier to write WebSocket service code that is resistant to Denial of Service attacks.
parent
083eb7679b
commit
85593d8d25
|
@ -28,8 +28,10 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
using HttpServer;
|
using HttpServer;
|
||||||
|
|
||||||
namespace OpenSim.Framework.Servers.HttpServer
|
namespace OpenSim.Framework.Servers.HttpServer
|
||||||
|
@ -98,6 +100,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ValidateHandshake HandshakeValidateMethodOverride = null;
|
public ValidateHandshake HandshakeValidateMethodOverride = null;
|
||||||
|
|
||||||
|
private ManualResetEvent _receiveDone = new ManualResetEvent(false);
|
||||||
|
|
||||||
private OSHttpRequest _request;
|
private OSHttpRequest _request;
|
||||||
private HTTPNetworkContext _networkContext;
|
private HTTPNetworkContext _networkContext;
|
||||||
private IHttpClientContext _clientContext;
|
private IHttpClientContext _clientContext;
|
||||||
|
@ -109,6 +113,8 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
private bool _closing;
|
private bool _closing;
|
||||||
private bool _upgraded;
|
private bool _upgraded;
|
||||||
private int _maxPayloadBytes = 41943040;
|
private int _maxPayloadBytes = 41943040;
|
||||||
|
private int _initialMsgTimeout = 0;
|
||||||
|
private int _defaultReadTimeout = 10000;
|
||||||
|
|
||||||
private const string HandshakeAcceptText =
|
private const string HandshakeAcceptText =
|
||||||
"HTTP/1.1 101 Switching Protocols\r\n" +
|
"HTTP/1.1 101 Switching Protocols\r\n" +
|
||||||
|
@ -131,6 +137,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
{
|
{
|
||||||
_request = preq;
|
_request = preq;
|
||||||
_networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing();
|
_networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing();
|
||||||
|
_networkContext.Stream.ReadTimeout = _defaultReadTimeout;
|
||||||
_clientContext = pContext;
|
_clientContext = pContext;
|
||||||
_bufferLength = bufferlen;
|
_bufferLength = bufferlen;
|
||||||
_buffer = new byte[_bufferLength];
|
_buffer = new byte[_bufferLength];
|
||||||
|
@ -205,6 +212,16 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
set { _maxPayloadBytes = value; }
|
set { _maxPayloadBytes = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set this to the maximum amount of milliseconds to wait for the first complete message to be sent or received on the websocket after upgrading
|
||||||
|
/// Default, it waits forever. Use this when your Websocket consuming code opens a connection and waits for a message from the other side to avoid a Denial of Service vector.
|
||||||
|
/// </summary>
|
||||||
|
public int InitialMsgTimeout
|
||||||
|
{
|
||||||
|
get { return _initialMsgTimeout; }
|
||||||
|
set { _initialMsgTimeout = value; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This triggers the websocket start the upgrade process
|
/// This triggers the websocket start the upgrade process
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -245,6 +262,7 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
|
string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
|
||||||
SendUpgradeSuccess(rawaccept);
|
SendUpgradeSuccess(rawaccept);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -258,6 +276,10 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
SendUpgradeSuccess(rawaccept);
|
SendUpgradeSuccess(rawaccept);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public IPEndPoint GetRemoteIPEndpoint()
|
||||||
|
{
|
||||||
|
return _request.RemoteIPEndPoint;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Generates a handshake response key string based on the client's
|
/// Generates a handshake response key string based on the client's
|
||||||
|
@ -290,9 +312,16 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true};
|
WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true};
|
||||||
|
|
||||||
byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse);
|
byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Reset();
|
||||||
|
}
|
||||||
// Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream.
|
// Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream.
|
||||||
_networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState);
|
_networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState);
|
||||||
|
|
||||||
|
@ -303,6 +332,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
UpgradeCompletedDelegate d = OnUpgradeCompleted;
|
UpgradeCompletedDelegate d = OnUpgradeCompleted;
|
||||||
if (d != null)
|
if (d != null)
|
||||||
d(this, new UpgradeCompletedEventArgs());
|
d(this, new UpgradeCompletedEventArgs());
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout)))
|
||||||
|
Close(string.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
|
@ -489,6 +523,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
/// <param name="message">the string message that is to be sent</param>
|
/// <param name="message">the string message that is to be sent</param>
|
||||||
public void SendMessage(string message)
|
public void SendMessage(string message)
|
||||||
{
|
{
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Set();
|
||||||
|
_initialMsgTimeout = 0;
|
||||||
|
}
|
||||||
byte[] messagedata = Encoding.UTF8.GetBytes(message);
|
byte[] messagedata = Encoding.UTF8.GetBytes(message);
|
||||||
WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata };
|
WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata };
|
||||||
textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text;
|
textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text;
|
||||||
|
@ -499,6 +538,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
|
|
||||||
public void SendData(byte[] data)
|
public void SendData(byte[] data)
|
||||||
{
|
{
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Set();
|
||||||
|
_initialMsgTimeout = 0;
|
||||||
|
}
|
||||||
WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data};
|
WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data};
|
||||||
dataMessageFrame.Header.IsEnd = true;
|
dataMessageFrame.Header.IsEnd = true;
|
||||||
dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary;
|
dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary;
|
||||||
|
@ -531,6 +575,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void SendPingCheck()
|
public void SendPingCheck()
|
||||||
{
|
{
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Set();
|
||||||
|
_initialMsgTimeout = 0;
|
||||||
|
}
|
||||||
WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] };
|
WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] };
|
||||||
pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping;
|
pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping;
|
||||||
pingFrame.Header.IsEnd = true;
|
pingFrame.Header.IsEnd = true;
|
||||||
|
@ -544,6 +593,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
/// <param name="message"></param>
|
/// <param name="message"></param>
|
||||||
public void Close(string message)
|
public void Close(string message)
|
||||||
{
|
{
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Set();
|
||||||
|
_initialMsgTimeout = 0;
|
||||||
|
}
|
||||||
if (_networkContext == null)
|
if (_networkContext == null)
|
||||||
return;
|
return;
|
||||||
if (_networkContext.Stream != null)
|
if (_networkContext.Stream != null)
|
||||||
|
@ -583,7 +637,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
WebSocketReader.Mask(psocketState.Header.Mask, unmask);
|
WebSocketReader.Mask(psocketState.Header.Mask, unmask);
|
||||||
psocketState.ReceivedBytes = new List<byte>(unmask);
|
psocketState.ReceivedBytes = new List<byte>(unmask);
|
||||||
}
|
}
|
||||||
|
if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Set();
|
||||||
|
_initialMsgTimeout = 0;
|
||||||
|
}
|
||||||
switch (psocketState.Header.Opcode)
|
switch (psocketState.Header.Opcode)
|
||||||
{
|
{
|
||||||
case WebSocketReader.OpCode.Ping:
|
case WebSocketReader.OpCode.Ping:
|
||||||
|
@ -696,6 +754,11 @@ namespace OpenSim.Framework.Servers.HttpServer
|
||||||
}
|
}
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
if (_initialMsgTimeout > 0)
|
||||||
|
{
|
||||||
|
_receiveDone.Set();
|
||||||
|
_initialMsgTimeout = 0;
|
||||||
|
}
|
||||||
if (_networkContext != null && _networkContext.Stream != null)
|
if (_networkContext != null && _networkContext.Stream != null)
|
||||||
{
|
{
|
||||||
if (_networkContext.Stream.CanWrite)
|
if (_networkContext.Stream.CanWrite)
|
||||||
|
|
Loading…
Reference in New Issue