diff options
author | Dominik Picheta <dominikpicheta@gmail.com> | 2016-09-25 13:02:10 +0200 |
---|---|---|
committer | Dominik Picheta <dominikpicheta@gmail.com> | 2016-09-25 13:02:10 +0200 |
commit | 8a6343b5b671abb26ab68dd940901654de31cfa2 (patch) | |
tree | b9f4cc4739da7dd6342cee9cd83f48913b61e7bb /lib/pure | |
parent | 6e7d50310e2fdb101fa803cd99d3f041ea3b912c (diff) | |
download | Nim-8a6343b5b671abb26ab68dd940901654de31cfa2.tar.gz |
Fixes #3847.
Diffstat (limited to 'lib/pure')
-rw-r--r-- | lib/pure/asyncdispatch.nim | 4 | ||||
-rw-r--r-- | lib/pure/asynchttpserver.nim | 6 | ||||
-rw-r--r-- | lib/pure/asyncnet.nim | 30 | ||||
-rw-r--r-- | lib/pure/net.nim | 22 |
4 files changed, 52 insertions, 10 deletions
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index f9085de55..b4e28d9bc 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1646,7 +1646,7 @@ proc send*(socket: AsyncFD, data: string, # -- Await Macro include asyncmacro -proc recvLine*(socket: AsyncFD): Future[string] {.async.} = +proc recvLine*(socket: AsyncFD): Future[string] {.async, deprecated.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -1664,6 +1664,8 @@ proc recvLine*(socket: AsyncFD): Future[string] {.async.} = ## ## **Note**: This procedure is mostly used for testing. You likely want to ## use ``asyncnet.recvLine`` instead. + ## + ## **Deprecated since version 0.15.0**: Use ``asyncnet.recvLine()`` instead. template addNLIfEmpty(): stmt = if result.len == 0: diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 486b0c800..a658097f9 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -9,6 +9,12 @@ ## This module implements a high performance asynchronous HTTP server. ## +## This HTTP server has not been designed to be used in production, but +## for testing applications locally. Because of this, when deploying your +## application you should use a reverse proxy (for example nginx) instead of +## allowing users to connect directly to this server. +## +## ## Examples ## -------- ## diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 334f95baa..14ebde4a2 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -388,7 +388,7 @@ proc accept*(socket: AsyncSocket, return retFut proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], - flags = {SocketFlag.SafeDisconn}) {.async.} = + flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {.async.} = ## Reads a line of data from ``socket`` into ``resString``. ## ## If a full line is read ``\r\L`` is not @@ -401,13 +401,14 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], ## is read) then line will be set to ``""``. ## The partial line **will be lost**. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: The ``Peek`` flag is not yet implemented. ## ## **Warning**: ``recvLineInto`` on unbuffered sockets assumes that the ## protocol uses ``\r\L`` to delimit a new line. - ## - ## **Warning**: ``recvLineInto`` currently uses a raw pointer to a string for - ## performance reasons. This will likely change soon to use FutureVars. assert SocketFlag.Peek notin flags ## TODO: assert(not resString.mget.isNil(), "String inside resString future needs to be initialised") @@ -454,6 +455,12 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], else: resString.mget.add socket.buffer[socket.currPos] socket.currPos.inc() + + # Verify that this isn't a DOS attack: #3847. + if resString.mget.len > maxLength: + let msg = "recvLine received more than the specified `maxLength` " & + "allowed." + raise newException(ValueError, msg) else: var c = "" while true: @@ -475,10 +482,17 @@ proc recvLineInto*(socket: AsyncSocket, resString: FutureVar[string], resString.complete() return resString.mget.add c + + # Verify that this isn't a DOS attack: #3847. + if resString.mget.len > maxLength: + let msg = "recvLine received more than the specified `maxLength` " & + "allowed." + raise newException(ValueError, msg) resString.complete() proc recvLine*(socket: AsyncSocket, - flags = {SocketFlag.SafeDisconn}): Future[string] {.async.} = + flags = {SocketFlag.SafeDisconn}, + maxLength = MaxLineLength): Future[string] {.async.} = ## Reads a line of data from ``socket``. Returned future will complete once ## a full line is read or an error occurs. ## @@ -492,6 +506,10 @@ proc recvLine*(socket: AsyncSocket, ## is read) then line will be set to ``""``. ## The partial line **will be lost**. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: The ``Peek`` flag is not yet implemented. ## ## **Warning**: ``recvLine`` on unbuffered sockets assumes that the protocol @@ -501,7 +519,7 @@ proc recvLine*(socket: AsyncSocket, # TODO: Optimise this var resString = newFutureVar[string]("asyncnet.recvLine") resString.mget() = "" - await socket.recvLineInto(resString, flags) + await socket.recvLineInto(resString, flags, maxLength) result = resString.mget() proc listen*(socket: AsyncSocket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = diff --git a/lib/pure/net.nim b/lib/pure/net.nim index a70f60a8e..d4f239c49 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -112,6 +112,7 @@ else: const BufferSize*: int = 4000 ## size of a buffered socket's buffer + MaxLineLength* = 1_000_000 type SocketImpl* = object ## socket type @@ -1006,7 +1007,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = result = recv(socket.fd, addr(c), 1, MSG_PEEK) proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, - flags = {SocketFlag.SafeDisconn}) {. + flags = {SocketFlag.SafeDisconn}, maxLength = MaxLineLength) {. tags: [ReadIOEffect, TimeEffect].} = ## Reads a line of data from ``socket``. ## @@ -1021,6 +1022,10 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. template addNLIfEmpty() = @@ -1054,8 +1059,15 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, return add(line.string, c) + # Verify that this isn't a DOS attack: #3847. + if line.string.len > maxLength: + let msg = "recvLine received more than the specified `maxLength` " & + "allowed." + raise newException(ValueError, msg) + proc recvLine*(socket: Socket, timeout = -1, - flags = {SocketFlag.SafeDisconn}): TaintedString = + flags = {SocketFlag.SafeDisconn}, + maxLength = MaxLineLength): TaintedString = ## Reads a line of data from ``socket``. ## ## If a full line is read ``\r\L`` is not @@ -1069,9 +1081,13 @@ proc recvLine*(socket: Socket, timeout = -1, ## A timeout can be specified in milliseconds, if data is not received within ## the specified time an ETimeout exception will be raised. ## + ## The ``maxLength`` parameter determines the maximum amount of characters + ## that can be read before a ``ValueError`` is raised. This prevents Denial + ## of Service (DOS) attacks. + ## ## **Warning**: Only the ``SafeDisconn`` flag is currently supported. result = "" - readLine(socket, result, timeout, flags) + readLine(socket, result, timeout, flags, maxLength) proc recvFrom*(socket: Socket, data: var string, length: int, address: var string, port: var Port, flags = 0'i32): int {. |