diff options
author | teravus | 2013-10-04 20:37:59 -0500 |
---|---|---|
committer | teravus | 2013-10-04 20:37:59 -0500 |
commit | 85593d8d25205344d512cbc976aa890a51f5f974 (patch) | |
tree | a81111b0deb8949386b62737a0211ffb0ee234d3 | |
parent | Added SimulatorFeatures/OpenSimExtras: say-range, whisper-range, shout-range,... (diff) | |
download | opensim-SC_OLD-85593d8d25205344d512cbc976aa890a51f5f974.zip opensim-SC_OLD-85593d8d25205344d512cbc976aa890a51f5f974.tar.gz opensim-SC_OLD-85593d8d25205344d512cbc976aa890a51f5f974.tar.bz2 opensim-SC_OLD-85593d8d25205344d512cbc976aa890a51f5f974.tar.xz |
* 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.
-rw-r--r-- | OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs | 67 |
1 files changed, 65 insertions, 2 deletions
diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs index de89e2e..c2925e3 100644 --- a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs | |||
@@ -28,8 +28,10 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.IO; | 30 | using System.IO; |
31 | using System.Net; | ||
31 | using System.Security.Cryptography; | 32 | using System.Security.Cryptography; |
32 | using System.Text; | 33 | using System.Text; |
34 | using System.Threading; | ||
33 | using HttpServer; | 35 | using HttpServer; |
34 | 36 | ||
35 | namespace OpenSim.Framework.Servers.HttpServer | 37 | namespace OpenSim.Framework.Servers.HttpServer |
@@ -98,6 +100,8 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
98 | /// </summary> | 100 | /// </summary> |
99 | public ValidateHandshake HandshakeValidateMethodOverride = null; | 101 | public ValidateHandshake HandshakeValidateMethodOverride = null; |
100 | 102 | ||
103 | private ManualResetEvent _receiveDone = new ManualResetEvent(false); | ||
104 | |||
101 | private OSHttpRequest _request; | 105 | private OSHttpRequest _request; |
102 | private HTTPNetworkContext _networkContext; | 106 | private HTTPNetworkContext _networkContext; |
103 | private IHttpClientContext _clientContext; | 107 | private IHttpClientContext _clientContext; |
@@ -109,6 +113,8 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
109 | private bool _closing; | 113 | private bool _closing; |
110 | private bool _upgraded; | 114 | private bool _upgraded; |
111 | private int _maxPayloadBytes = 41943040; | 115 | private int _maxPayloadBytes = 41943040; |
116 | private int _initialMsgTimeout = 0; | ||
117 | private int _defaultReadTimeout = 10000; | ||
112 | 118 | ||
113 | private const string HandshakeAcceptText = | 119 | private const string HandshakeAcceptText = |
114 | "HTTP/1.1 101 Switching Protocols\r\n" + | 120 | "HTTP/1.1 101 Switching Protocols\r\n" + |
@@ -131,6 +137,7 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
131 | { | 137 | { |
132 | _request = preq; | 138 | _request = preq; |
133 | _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing(); | 139 | _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing(); |
140 | _networkContext.Stream.ReadTimeout = _defaultReadTimeout; | ||
134 | _clientContext = pContext; | 141 | _clientContext = pContext; |
135 | _bufferLength = bufferlen; | 142 | _bufferLength = bufferlen; |
136 | _buffer = new byte[_bufferLength]; | 143 | _buffer = new byte[_bufferLength]; |
@@ -206,6 +213,16 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
206 | } | 213 | } |
207 | 214 | ||
208 | /// <summary> | 215 | /// <summary> |
216 | /// 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 | ||
217 | /// 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. | ||
218 | /// </summary> | ||
219 | public int InitialMsgTimeout | ||
220 | { | ||
221 | get { return _initialMsgTimeout; } | ||
222 | set { _initialMsgTimeout = value; } | ||
223 | } | ||
224 | |||
225 | /// <summary> | ||
209 | /// This triggers the websocket start the upgrade process | 226 | /// This triggers the websocket start the upgrade process |
210 | /// </summary> | 227 | /// </summary> |
211 | public void HandshakeAndUpgrade() | 228 | public void HandshakeAndUpgrade() |
@@ -245,6 +262,7 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
245 | string rawaccept = string.Format(HandshakeAcceptText, acceptKey); | 262 | string rawaccept = string.Format(HandshakeAcceptText, acceptKey); |
246 | SendUpgradeSuccess(rawaccept); | 263 | SendUpgradeSuccess(rawaccept); |
247 | 264 | ||
265 | |||
248 | } | 266 | } |
249 | else | 267 | else |
250 | { | 268 | { |
@@ -258,6 +276,10 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
258 | SendUpgradeSuccess(rawaccept); | 276 | SendUpgradeSuccess(rawaccept); |
259 | } | 277 | } |
260 | } | 278 | } |
279 | public IPEndPoint GetRemoteIPEndpoint() | ||
280 | { | ||
281 | return _request.RemoteIPEndPoint; | ||
282 | } | ||
261 | 283 | ||
262 | /// <summary> | 284 | /// <summary> |
263 | /// Generates a handshake response key string based on the client's | 285 | /// Generates a handshake response key string based on the client's |
@@ -290,9 +312,16 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
290 | WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true}; | 312 | WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true}; |
291 | 313 | ||
292 | byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse); | 314 | byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse); |
315 | |||
316 | |||
317 | |||
318 | |||
293 | try | 319 | try |
294 | { | 320 | { |
295 | 321 | if (_initialMsgTimeout > 0) | |
322 | { | ||
323 | _receiveDone.Reset(); | ||
324 | } | ||
296 | // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream. | 325 | // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream. |
297 | _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState); | 326 | _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState); |
298 | 327 | ||
@@ -303,6 +332,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
303 | UpgradeCompletedDelegate d = OnUpgradeCompleted; | 332 | UpgradeCompletedDelegate d = OnUpgradeCompleted; |
304 | if (d != null) | 333 | if (d != null) |
305 | d(this, new UpgradeCompletedEventArgs()); | 334 | d(this, new UpgradeCompletedEventArgs()); |
335 | if (_initialMsgTimeout > 0) | ||
336 | { | ||
337 | if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout))) | ||
338 | Close(string.Empty); | ||
339 | } | ||
306 | } | 340 | } |
307 | catch (IOException) | 341 | catch (IOException) |
308 | { | 342 | { |
@@ -489,6 +523,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
489 | /// <param name="message">the string message that is to be sent</param> | 523 | /// <param name="message">the string message that is to be sent</param> |
490 | public void SendMessage(string message) | 524 | public void SendMessage(string message) |
491 | { | 525 | { |
526 | if (_initialMsgTimeout > 0) | ||
527 | { | ||
528 | _receiveDone.Set(); | ||
529 | _initialMsgTimeout = 0; | ||
530 | } | ||
492 | byte[] messagedata = Encoding.UTF8.GetBytes(message); | 531 | byte[] messagedata = Encoding.UTF8.GetBytes(message); |
493 | WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; | 532 | WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; |
494 | textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; | 533 | textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; |
@@ -499,6 +538,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
499 | 538 | ||
500 | public void SendData(byte[] data) | 539 | public void SendData(byte[] data) |
501 | { | 540 | { |
541 | if (_initialMsgTimeout > 0) | ||
542 | { | ||
543 | _receiveDone.Set(); | ||
544 | _initialMsgTimeout = 0; | ||
545 | } | ||
502 | WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data}; | 546 | WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data}; |
503 | dataMessageFrame.Header.IsEnd = true; | 547 | dataMessageFrame.Header.IsEnd = true; |
504 | dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; | 548 | dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; |
@@ -531,6 +575,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
531 | /// </summary> | 575 | /// </summary> |
532 | public void SendPingCheck() | 576 | public void SendPingCheck() |
533 | { | 577 | { |
578 | if (_initialMsgTimeout > 0) | ||
579 | { | ||
580 | _receiveDone.Set(); | ||
581 | _initialMsgTimeout = 0; | ||
582 | } | ||
534 | WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; | 583 | WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; |
535 | pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; | 584 | pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; |
536 | pingFrame.Header.IsEnd = true; | 585 | pingFrame.Header.IsEnd = true; |
@@ -544,6 +593,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
544 | /// <param name="message"></param> | 593 | /// <param name="message"></param> |
545 | public void Close(string message) | 594 | public void Close(string message) |
546 | { | 595 | { |
596 | if (_initialMsgTimeout > 0) | ||
597 | { | ||
598 | _receiveDone.Set(); | ||
599 | _initialMsgTimeout = 0; | ||
600 | } | ||
547 | if (_networkContext == null) | 601 | if (_networkContext == null) |
548 | return; | 602 | return; |
549 | if (_networkContext.Stream != null) | 603 | if (_networkContext.Stream != null) |
@@ -583,7 +637,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
583 | WebSocketReader.Mask(psocketState.Header.Mask, unmask); | 637 | WebSocketReader.Mask(psocketState.Header.Mask, unmask); |
584 | psocketState.ReceivedBytes = new List<byte>(unmask); | 638 | psocketState.ReceivedBytes = new List<byte>(unmask); |
585 | } | 639 | } |
586 | 640 | if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0) | |
641 | { | ||
642 | _receiveDone.Set(); | ||
643 | _initialMsgTimeout = 0; | ||
644 | } | ||
587 | switch (psocketState.Header.Opcode) | 645 | switch (psocketState.Header.Opcode) |
588 | { | 646 | { |
589 | case WebSocketReader.OpCode.Ping: | 647 | case WebSocketReader.OpCode.Ping: |
@@ -696,6 +754,11 @@ namespace OpenSim.Framework.Servers.HttpServer | |||
696 | } | 754 | } |
697 | public void Dispose() | 755 | public void Dispose() |
698 | { | 756 | { |
757 | if (_initialMsgTimeout > 0) | ||
758 | { | ||
759 | _receiveDone.Set(); | ||
760 | _initialMsgTimeout = 0; | ||
761 | } | ||
699 | if (_networkContext != null && _networkContext.Stream != null) | 762 | if (_networkContext != null && _networkContext.Stream != null) |
700 | { | 763 | { |
701 | if (_networkContext.Stream.CanWrite) | 764 | if (_networkContext.Stream.CanWrite) |