aboutsummaryrefslogtreecommitdiff
path: root/src/tp.lua
diff options
context:
space:
mode:
authorThijs Schreijer <thijs.schreijer@bookingexperts.com>2026-08-10 17:11:39 +0200
committerThijs Schreijer <thijs@thijsschreijer.nl>2026-08-29 17:42:13 +0200
commitc84e79f6d1ae735bfa191694ec6a62eb8231166d (patch)
tree27e8efe3a7bd838b655eb05307a2e730ff16cd8e /src/tp.lua
parent52f9f91d91aa33a1483334f69073f3e6250cd168 (diff)
downloadluasocket-c84e79f6d1ae735bfa191694ec6a62eb8231166d.tar.gz
luasocket-c84e79f6d1ae735bfa191694ec6a62eb8231166d.tar.bz2
luasocket-c84e79f6d1ae735bfa191694ec6a62eb8231166d.zip
feat(http/ftp/smtp): implement max size checks
fixes unbounded reads when using the "*l" pattern. Typically on headers and other control lines.
Diffstat (limited to 'src/tp.lua')
-rw-r--r--src/tp.lua18
1 files changed, 16 insertions, 2 deletions
diff --git a/src/tp.lua b/src/tp.lua
index bf4bc14..ff5a5cf 100644
--- a/src/tp.lua
+++ b/src/tp.lua
@@ -19,6 +19,10 @@ local _M = socket.tp
19-- Program constants 19-- Program constants
20----------------------------------------------------------------------------- 20-----------------------------------------------------------------------------
21_M.TIMEOUT = 60 21_M.TIMEOUT = 60
22-- maximum size of a single reply line
23_M.MAXLINE = 8192
24-- maximum total size of a (possibly multiline) reply
25_M.MAXREPLY = 65536
22 26
23----------------------------------------------------------------------------- 27-----------------------------------------------------------------------------
24-- Implementation 28-- Implementation
@@ -26,14 +30,24 @@ _M.TIMEOUT = 60
26-- gets server reply (works for SMTP and FTP) 30-- gets server reply (works for SMTP and FTP)
27local function get_reply(c) 31local function get_reply(c)
28 local code, current, sep 32 local code, current, sep
29 local line, err = c:receive("*l") 33 -- bounds total bytes read across a multiline reply, on top of the
34 -- per-line MAXLINE cap, so a peer can't exhaust memory by sending many
35 -- lines that each individually fit under MAXLINE
36 local budget = _M.MAXREPLY
37 local function recvline()
38 if budget <= 0 then return nil, "oversized" end
39 local line, err = c:receive("*l", nil, math.min(budget, _M.MAXLINE))
40 if line then budget = budget - #line end
41 return line, err
42 end
43 local line, err = recvline()
30 local reply = line 44 local reply = line
31 if err then return nil, err end 45 if err then return nil, err end
32 code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) 46 code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
33 if not code then return nil, "invalid server reply" end 47 if not code then return nil, "invalid server reply" end
34 if sep == "-" then -- reply is multiline 48 if sep == "-" then -- reply is multiline
35 repeat 49 repeat
36 line, err = c:receive("*l") 50 line, err = recvline()
37 if err then return nil, err end 51 if err then return nil, err end
38 current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) 52 current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)"))
39 reply = reply .. "\n" .. line 53 reply = reply .. "\n" .. line