mirror of
https://github.com/nmap/nmap.git
synced 2026-01-04 13:49:03 +00:00
Fix parsing of Set-Cookie headers. Closes #229
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] [GH#229] Improve parsing in http.lua for multiple Set-Cookie headers in
|
||||
a single response. [nnposter]
|
||||
|
||||
o [NSE] [GH#194] Add support for reading fragmented TLS messages to
|
||||
ssl-enum-ciphers. [Jacob Gajek]
|
||||
|
||||
|
||||
150
nselib/http.lua
150
nselib/http.lua
@@ -651,6 +651,8 @@ local function parse_status_line(status_line, response)
|
||||
return true
|
||||
end
|
||||
|
||||
local parse_set_cookie -- defined farther down
|
||||
|
||||
-- Sets response.header and response.rawheader.
|
||||
local function parse_header(header, response)
|
||||
local pos
|
||||
@@ -684,10 +686,22 @@ local function parse_header(header, response)
|
||||
|
||||
-- Set it in our table.
|
||||
name = string.lower(name)
|
||||
local value = table.concat(words, " ")
|
||||
if response.header[name] then
|
||||
response.header[name] = response.header[name] .. ", " .. table.concat(words, " ")
|
||||
-- TODO: delay concatenation until return to avoid resource exhaustion
|
||||
response.header[name] = response.header[name] .. ", " .. value
|
||||
else
|
||||
response.header[name] = table.concat(words, " ")
|
||||
response.header[name] = value
|
||||
end
|
||||
|
||||
-- Update the cookie table if this is a Set-Cookie header
|
||||
if name == "set-cookie" then
|
||||
local cookie, err = parse_set_cookie(value)
|
||||
if cookie then
|
||||
response.cookies[#response.cookies + 1] = cookie
|
||||
else
|
||||
-- Ignore any cookie parsing error
|
||||
end
|
||||
end
|
||||
|
||||
-- Next field, or end of string. (If not it's an error.)
|
||||
@@ -701,8 +715,8 @@ local function parse_header(header, response)
|
||||
return true
|
||||
end
|
||||
|
||||
-- Parse the contents of a Set-Cookie header field. The result is an array
|
||||
-- containing tables of the form
|
||||
-- Parse the contents of a Set-Cookie header field.
|
||||
-- The result is a table of the form
|
||||
--
|
||||
-- { name = "NAME", value = "VALUE", Comment = "...", Domain = "...", ... }
|
||||
--
|
||||
@@ -713,96 +727,64 @@ end
|
||||
-- "HISTORICAL". Values need not be quoted, but if they start with a quote they
|
||||
-- will be interpreted as a quoted string.
|
||||
local function parse_set_cookie(s)
|
||||
local cookies
|
||||
local name, value
|
||||
local _, pos
|
||||
local _
|
||||
|
||||
cookies = {}
|
||||
local cookie = {}
|
||||
|
||||
pos = 1
|
||||
while true do
|
||||
local cookie = {}
|
||||
-- Get the NAME=VALUE part.
|
||||
local pos = skip_space(s, 1)
|
||||
pos, cookie.name = get_token(s, pos)
|
||||
if not cookie.name then
|
||||
return nil, "Can't get cookie name."
|
||||
end
|
||||
pos = skip_space(s, pos)
|
||||
if s:sub(pos, pos) ~= "=" then
|
||||
return nil, string.format("Expected '=' after cookie name \"%s\".", cookie.name)
|
||||
end
|
||||
pos = pos + 1
|
||||
pos = skip_space(s, pos)
|
||||
if s:sub(pos, pos) == "\"" then
|
||||
pos, cookie.value = get_quoted_string(s, pos)
|
||||
else
|
||||
_, pos, cookie.value = s:find("([^; \t]*)", pos)
|
||||
pos = pos + 1
|
||||
end
|
||||
if not cookie.value then
|
||||
return nil, string.format("Can't get value of cookie named \"%s\".", cookie.name)
|
||||
end
|
||||
pos = skip_space(s, pos)
|
||||
|
||||
-- Get the NAME=VALUE part.
|
||||
pos = skip_space(s, pos)
|
||||
pos, cookie.name = get_token(s, pos)
|
||||
if not cookie.name then
|
||||
return nil, "Can't get cookie name."
|
||||
end
|
||||
pos = skip_space(s, pos)
|
||||
if pos > #s or string.sub(s, pos, pos) ~= "=" then
|
||||
return nil, string.format("Expected '=' after cookie name \"%s\".", cookie.name)
|
||||
end
|
||||
-- Loop over the attributes.
|
||||
while s:sub(pos, pos) == ";" do
|
||||
pos = pos + 1
|
||||
pos = skip_space(s, pos)
|
||||
if string.sub(s, pos, pos) == "\"" then
|
||||
pos, cookie.value = get_quoted_string(s, pos)
|
||||
else
|
||||
_, pos, cookie.value = string.find(s, "([^;]*)[ \t]*", pos)
|
||||
pos = pos + 1
|
||||
end
|
||||
if not cookie.value then
|
||||
return nil, string.format("Can't get value of cookie named \"%s\".", cookie.name)
|
||||
pos, name = get_token(s, pos)
|
||||
if not name then
|
||||
return nil, string.format("Can't get attribute name of cookie \"%s\".", cookie.name)
|
||||
end
|
||||
pos = skip_space(s, pos)
|
||||
|
||||
-- Loop over the attributes.
|
||||
while pos <= #s and string.sub(s, pos, pos) == ";" do
|
||||
if s:sub(pos, pos) == "=" then
|
||||
pos = pos + 1
|
||||
pos = skip_space(s, pos)
|
||||
pos, name = get_token(s, pos)
|
||||
if not name then
|
||||
return nil, string.format("Can't get attribute name of cookie \"%s\".", cookie.name)
|
||||
end
|
||||
pos = skip_space(s, pos)
|
||||
if pos <= #s and string.sub(s, pos, pos) == "=" then
|
||||
pos = pos + 1
|
||||
pos = skip_space(s, pos)
|
||||
if string.sub(s, pos, pos) == "\"" then
|
||||
pos, value = get_quoted_string(s, pos)
|
||||
else
|
||||
-- account for the possibility of the expires attribute being empty or improperly formatted
|
||||
local last_pos = pos
|
||||
|
||||
if string.lower(name) == "expires" then
|
||||
-- For version 0 cookies we must allow one comma for "expires".
|
||||
_, pos, value = string.find(s, "([^,]*,[^;,]*)[ \t]*", pos)
|
||||
else
|
||||
_, pos, value = string.find(s, "([^;,]*)[ \t]*", pos)
|
||||
end
|
||||
|
||||
-- account for the possibility of the expires attribute being empty or improperly formatted
|
||||
if ( not(pos) ) then
|
||||
_, pos, value = s:find("([^;]*)", last_pos)
|
||||
end
|
||||
|
||||
pos = pos + 1
|
||||
end
|
||||
if not value then
|
||||
return nil, string.format("Can't get value of cookie attribute \"%s\".", name)
|
||||
end
|
||||
if s:sub(pos, pos) == "\"" then
|
||||
pos, value = get_quoted_string(s, pos)
|
||||
else
|
||||
value = true
|
||||
_, pos, value = s:find("([^;]*)", pos)
|
||||
value = value:match("(.-)[ \t]*$")
|
||||
pos = pos + 1
|
||||
end
|
||||
cookie[name:lower()] = value
|
||||
pos = skip_space(s, pos)
|
||||
if not value then
|
||||
return nil, string.format("Can't get value of cookie attribute \"%s\".", name)
|
||||
end
|
||||
else
|
||||
value = true
|
||||
end
|
||||
|
||||
cookies[#cookies + 1] = cookie
|
||||
|
||||
if pos > #s then
|
||||
break
|
||||
end
|
||||
|
||||
if string.sub(s, pos, pos) ~= "," then
|
||||
return nil, string.format("Syntax error after cookie named \"%s\".", cookie.name)
|
||||
end
|
||||
|
||||
pos = pos + 1
|
||||
cookie[name:lower()] = value
|
||||
pos = skip_space(s, pos)
|
||||
end
|
||||
|
||||
return cookies
|
||||
return cookie
|
||||
end
|
||||
|
||||
-- Read one response from the socket <code>s</code> and return it after
|
||||
@@ -818,6 +800,7 @@ local function next_response(s, method, partial)
|
||||
["status-line"]=nil,
|
||||
header={},
|
||||
rawheader={},
|
||||
cookies={},
|
||||
body=""
|
||||
}
|
||||
|
||||
@@ -845,17 +828,6 @@ local function next_response(s, method, partial)
|
||||
end
|
||||
response.body = body
|
||||
|
||||
-- We have the Status-Line, header, and body; now do any postprocessing.
|
||||
|
||||
response.cookies = {}
|
||||
if response.header["set-cookie"] then
|
||||
response.cookies, err = parse_set_cookie(response.header["set-cookie"])
|
||||
if not response.cookies then
|
||||
-- Ignore a cookie parsing error.
|
||||
response.cookies = {}
|
||||
end
|
||||
end
|
||||
|
||||
return response, partial
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user