diff --git a/docs/changes.txt b/docs/changes.txt index f9d2934..de374ae 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -169,3 +169,8 @@ v0.8.1 * nfqws2: ignore trailing spaces and tabs in hostlists and ipsets. "host.com " or "1.2.3.4 " are ok now * init.d: 99-lan-filter custom script * mdig: --eagain, --eagain-delay + +0.8.4 + +* winws2: fix loopback large packets processing (up to 64K) +* zapret-lib, zapret-antidpi: use numeric indexes in http dissects diff --git a/docs/manual.en.md b/docs/manual.en.md index 59c374f..f2d40c5 100644 --- a/docs/manual.en.md +++ b/docs/manual.en.md @@ -2356,7 +2356,8 @@ function http_reconstruct_req(hdis, unixeol) Parses an HTTP request or response. The `http` parameter is a multi-line string. The parsed result is a table with nested sub-tables. Headers provide the start and end positions for both the header name and the value itself. -Field names in the `headers` table correspond to lowercase header names. All positions are relative to the `http` string. + +To find a header by name use [array_field_search](#array_search) with field name "header_low" which contains header name in lower case. The HTTP request reconstructor takes a parsed table and recreates the raw string. The `unixeol` parameter replaces the standard HTTP line ending (0D0A) with 0A. This is non-standard and will break almost all servers except for Nginx. @@ -2366,9 +2367,11 @@ The HTTP request reconstructor takes a parsed table and recreates the raw string .uri string /test_uri .headers - .content-length + .1 .header string Content-Length + .header_low + string content-length .value string 330 .pos_start @@ -2379,9 +2382,11 @@ The HTTP request reconstructor takes a parsed table and recreates the raw string number 56 .pos_value_start number 59 - .host + .2 .header string Host + .header_low + string host .value string testhost.com .pos_start @@ -3370,6 +3375,7 @@ function http_methodeol(ctx, desync) - arg: [standard direction](#standard-direction) Inserts `\r\n` before the method, stripping the last two characters from the `User-Agent:` header content. This only works with Nginx; it breaks other servers. +If used with other http tampering functions should be the last ! ### http_unixeol diff --git a/docs/manual.md b/docs/manual.md index 122e5fe..22a1d96 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -2522,7 +2522,9 @@ function http_reconstruct_req(hdis, unixeol) Разборка HTTP запроса или ответа http. http представляет собой многострочный текст. Разборка представляет собой таблицу с вложенными подтаблицами. В заголовках выдаются позиции начала и конца названия заголовка и самого значения. -Названия полей в таблице headers соответствуют названию заголовков в нижнем регисте. Все позиции - внутри строки http. +Все позиции - внутри строки http. + +Для нахождения хедеров по названию используйте [array_field_search](#array_search) по полю "header_low", которое содержит название хедера в нижнем регистре. Реконструктор http запроса берет таблицу-разбор и воссоздает raw string. Параметр unixeol заменяет стандартный для http перевод сктроки 0D0A на 0A. Это нестандарт и ломает все сервера, кроме nginx. @@ -2532,9 +2534,11 @@ function http_reconstruct_req(hdis, unixeol) .uri string /test_uri .headers - .content-length + .1 .header string Content-Length + .header_low + string content-length .value string 330 .pos_start @@ -2545,9 +2549,11 @@ function http_reconstruct_req(hdis, unixeol) number 56 .pos_value_start number 59 - .host + .2 .header string Host + .header_low + string host .value string testhost.com .pos_start @@ -3552,6 +3558,7 @@ function http_methodeol(ctx, desync) - arg: [standard direction](#standard-direction) Вставляет '\r\n' перед методом, отрезая 2 последних символа из содержимого заголовка `User-Agent:`. Работает только на nginx, остальные сервера ломает. +Если используется совместно с другими функциями http тамперинга, должен идти последним. ### http_unixeol diff --git a/lua/zapret-antidpi.lua b/lua/zapret-antidpi.lua index 946ed86..dc8658f 100644 --- a/lua/zapret-antidpi.lua +++ b/lua/zapret-antidpi.lua @@ -149,12 +149,17 @@ function http_hostcase(ctx, desync) error("http_hostcase: invalid host spelling '"..spell.."'") else local hdis = http_dissect_req(desync.dis.payload) - if hdis.headers.host then - DLOG("http_hostcase: 'Host:' => '"..spell.."'") - desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers.host.pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers.host.pos_header_end+1) - return VERDICT_MODIFY + if hdis then + local idx_host = array_field_search(hdis.headers, "header_low", "host") + if idx_host then + DLOG("http_hostcase: 'Host:' => '"..spell.."'") + desync.dis.payload = string.sub(desync.dis.payload,1,hdis.headers[idx_host].pos_start-1)..spell..string.sub(desync.dis.payload,hdis.headers[idx_host].pos_header_end+1) + return VERDICT_MODIFY + else + DLOG("http_hostcase: 'Host:' header not found") + end else - DLOG("http_hostcase: 'Host:' header not found") + DLOG("http_hostcase: http dissect error") end end end @@ -162,6 +167,7 @@ end -- nfqws1 : "--methodeol" -- standard args : direction +-- NOTE : if using with other http tampering methodeol should be the last ! function http_methodeol(ctx, desync) if not desync.dis.tcp then instance_cutoff_shim(ctx, desync) @@ -170,17 +176,22 @@ function http_methodeol(ctx, desync) direction_cutoff_opposite(ctx, desync) if desync.l7payload=="http_req" and direction_check(desync) then local hdis = http_dissect_req(desync.dis.payload) - local ua = hdis.headers["user-agent"] - if ua then - if (ua.pos_end - ua.pos_value_start) < 2 then - DLOG("http_methodeol: 'User-Agent:' header is too short") + if hdis then + local idx_ua = array_field_search(hdis.headers, "header_low", "user-agent") + if idx_ua then + local ua = hdis.headers[idx_ua] + if (ua.pos_end - ua.pos_value_start) < 2 then + DLOG("http_methodeol: 'User-Agent:' header is too short") + else + DLOG("http_methodeol: applied") + desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or ""); + return VERDICT_MODIFY + end else - DLOG("http_methodeol: applied") - desync.dis.payload="\r\n"..string.sub(desync.dis.payload,1,ua.pos_end-2)..(string.sub(desync.dis.payload,ua.pos_end+1) or ""); - return VERDICT_MODIFY + DLOG("http_methodeol: 'User-Agent:' header not found") end else - DLOG("http_methodeol: 'User-Agent:' header not found") + DLOG("http_methodeol: http dissect error") end end end @@ -197,10 +208,11 @@ function http_unixeol(ctx, desync) if desync.l7payload=="http_req" and direction_check(desync) then local hdis = http_dissect_req(desync.dis.payload) if hdis then - if hdis.headers["user-agent"] then + local idx_ua = array_field_search(hdis.headers, "header_low", "user-agent") + if idx_ua then local http = http_reconstruct_req(hdis, true) if #http < #desync.dis.payload then - hdis.headers["user-agent"].value = hdis.headers["user-agent"].value .. string.rep(" ", #desync.dis.payload - #http) + hdis.headers[idx_ua].value = hdis.headers[idx_ua].value .. string.rep(" ", #desync.dis.payload - #http) end local http = http_reconstruct_req(hdis, true) if #http==#desync.dis.payload then @@ -211,7 +223,7 @@ function http_unixeol(ctx, desync) DLOG("http_unixeol: reconstruct differs in size from original: "..#http.."!="..#desync.dis.payload) end else - DLOG("http_unixeol: user-agent header absent") + DLOG("http_unixeol: 'User-Agent:' header absent") end else DLOG("http_unixeol: could not dissect http") diff --git a/lua/zapret-lib.lua b/lua/zapret-lib.lua index 5a2e381..4d6ec8a 100644 --- a/lua/zapret-lib.lua +++ b/lua/zapret-lib.lua @@ -579,7 +579,6 @@ function array_search(a, v) return k end end - return nil end -- linear search array a for a[index].f==v. return index function array_field_search(a, f, v) @@ -588,7 +587,6 @@ function array_field_search(a, f, v) return k end end - return nil end -- find pos of the next eol and pos of the next non-eol character after eol @@ -1568,7 +1566,7 @@ function http_dissect_headers(http, pos) end header,value,pos_endheader,pos_startvalue = http_dissect_header(header) if header then - headers[string.lower(header)] = { header = header, value = value, pos_start = pos, pos_end = eol, pos_header_end = pos+pos_endheader-1, pos_value_start = pos+pos_startvalue-1 } + headers[#headers+1] = { header_low = string.lower(header), header = header, value = value, pos_start = pos, pos_end = eol, pos_header_end = pos+pos_endheader-1, pos_value_start = pos+pos_startvalue-1 } end pos=pnext end @@ -1619,11 +1617,16 @@ function http_dissect_reply(http) code = tonumber(string.sub(http,10,pos-1)) if not code then return nil end pos = find_next_line(http,pos) - return { code = code, headers = http_dissect_headers(http,pos) } + local hdis = { code = code } + hdis.headers, hdis.pos_headers_end = http_dissect_headers(http,pos) + if hdis.pos_headers_end then + hdis.body = string.sub(http, hdis.pos_headers_end) + end + return hdis end function http_reconstruct_headers(headers, unixeol) local eol = unixeol and "\n" or "\r\n" - return headers and btable(headers, function(a) return a.header..": "..a.value..eol end) or "" + return headers and bitable(headers, function(a) return a.header..": "..a.value..eol end) or "" end function http_reconstruct_req(hdis, unixeol) local eol = unixeol and "\n" or "\r\n"