Software /
code /
prosody
File
fallbacks/lxp.lua @ 13126:d043834f15d2
mod_http: Use RFC 7239 Forwarded header to find original client IP
Prefer over X-Forwarded-* since it has an actual specification.
Main practical difference is that Forwarded may carry more properties
than only the IP address since it is a structured header.
Since we parse it into an array, it is easier to do the logical thing
and iterate backwards trough proxies until an untrusted one is
encountered. Compare the handling of X-Forwarded-For.
The 'secure' field now accounts for the full chain of proxies, which
must be secure all the way to be considered secure.
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Sat, 03 Jun 2023 17:10:04 +0200 |
parent | 5776:bd0ff8ae98a8 |
line wrap: on
line source
local coroutine = coroutine; local tonumber = tonumber; local string = string; local setmetatable, getmetatable = setmetatable, getmetatable; local pairs = pairs; local deadroutine = coroutine.create(function() end); coroutine.resume(deadroutine); module("lxp") local entity_map = setmetatable({ ["amp"] = "&"; ["gt"] = ">"; ["lt"] = "<"; ["apos"] = "'"; ["quot"] = "\""; }, {__index = function(_, s) if s:sub(1,1) == "#" then if s:sub(2,2) == "x" then return string.char(tonumber(s:sub(3), 16)); else return string.char(tonumber(s:sub(2))); end end end }); local function xml_unescape(str) return (str:gsub("&(.-);", entity_map)); end local function parse_tag(s) local name,sattr=(s):gmatch("([^%s]+)(.*)")(); local attr = {}; for a,b in (sattr):gmatch("([^=%s]+)=['\"]([^'\"]*)['\"]") do attr[a] = xml_unescape(b); end return name, attr; end local function parser(data, handlers, ns_separator) local function read_until(str) local pos = data:find(str, nil, true); while not pos do data = data..coroutine.yield(); pos = data:find(str, nil, true); end local r = data:sub(1, pos); data = data:sub(pos+1); return r; end local function read_before(str) local pos = data:find(str, nil, true); while not pos do data = data..coroutine.yield(); pos = data:find(str, nil, true); end local r = data:sub(1, pos-1); data = data:sub(pos); return r; end local function peek() while #data == 0 do data = coroutine.yield(); end return data:sub(1,1); end local ns = { xml = "http://www.w3.org/XML/1998/namespace" }; ns.__index = ns; local function apply_ns(name, dodefault) local prefix,n = name:match("^([^:]*):(.*)$"); if prefix and ns[prefix] then return ns[prefix]..ns_separator..n; end if dodefault and ns[""] then return ns[""]..ns_separator..name; end return name; end local function push(tag, attr) ns = setmetatable({}, ns); for k,v in pairs(attr) do local xmlns = k == "xmlns" and "" or k:match("^xmlns:(.*)$"); if xmlns then ns[xmlns] = v; attr[k] = nil; end end local newattr, n = {}, 0; for k,v in pairs(attr) do n = n+1; k = apply_ns(k); newattr[n] = k; newattr[k] = v; end tag = apply_ns(tag, true); ns[0] = tag; ns.__index = ns; return tag, newattr; end local function pop() local tag = ns[0]; ns = getmetatable(ns); return tag; end while true do if peek() == "<" then local elem = read_until(">"):sub(2,-2); if elem:sub(1,1) == "!" or elem:sub(1,1) == "?" then -- neglect comments and processing-instructions elseif elem:sub(1,1) == "/" then -- end tag elem = elem:sub(2); local name = pop(); handlers:EndElement(name); -- TODO check for start-end tag name match elseif elem:sub(-1,-1) == "/" then -- empty tag elem = elem:sub(1,-2); local name,attr = parse_tag(elem); name,attr = push(name,attr); handlers:StartElement(name,attr); name = pop(); handlers:EndElement(name); else -- start tag local name,attr = parse_tag(elem); name,attr = push(name,attr); handlers:StartElement(name,attr); end else local text = read_before("<"); handlers:CharacterData(xml_unescape(text)); end end end function new(handlers, ns_separator) local co = coroutine.create(parser); return { parse = function(self, data) if not data then co = deadroutine; return true; -- eof end local success, result = coroutine.resume(co, data, handlers, ns_separator); if result then co = deadroutine; return nil, result; -- error end return true; -- success end; }; end return _M;