summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
authorDominik Picheta <dominikpicheta@gmail.com>2016-09-25 13:02:10 +0200
committerDominik Picheta <dominikpicheta@gmail.com>2016-09-25 13:02:10 +0200
commit8a6343b5b671abb26ab68dd940901654de31cfa2 (patch)
treeb9f4cc4739da7dd6342cee9cd83f48913b61e7bb /lib/pure
parent6e7d50310e2fdb101fa803cd99d3f041ea3b912c (diff)
downloadNim-8a6343b5b671abb26ab68dd940901654de31cfa2.tar.gz
Fixes #3847.
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/asyncdispatch.nim4
-rw-r--r--lib/pure/asynchttpserver.nim6
-rw-r--r--lib/pure/asyncnet.nim30
-rw-r--r--lib/pure/net.nim22
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 {.