--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// payload_injector_translate_page.cc author Maya Dagon <mdagon@cisco.com>
+
+// Translates HTTP 1.1 block/redirect page to HTTP2.
+// 1. Headers are separated by /r/n
+// 2. Headers end with /r/n
+// 3. Must have headers and body
+// 4. Translated header length <= 2000
+// 5. Supported: HTTP/1.1 403, HTTP/1.1 307, Connection: close,
+// Content-Length: , Content-Type: , Set-Cookie: , Location:
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "payload_injector_module.h"
+
+#include "service_inspectors/http2_inspect/http2_enum.h"
+#include "utils/util.h"
+
+static const char status_403[] = "HTTP/1.1 403";
+static uint8_t status_403_h2[] = { 0, 7, ':', 's', 't', 'a', 't', 'u', 's', 3, '4', '0', '3' };
+static const char status_307[] = "HTTP/1.1 307";
+static uint8_t status_307_h2[] = { 0, 7, ':', 's', 't', 'a', 't', 'u', 's', 3, '3', '0', '7' };
+static const char connection[] = "Connection: close";
+static const uint8_t connection_h2[] = { 0, 10, 'c','o','n','n','e','c','t','i','o','n',
+ 5, 'c', 'l', 'o', 's', 'e' };
+static const char content_length[] = "Content-Length: ";
+static const char content_type[] = "Content-Type: ";
+static const char cookie[] = "Set-Cookie: ";
+static const char location[] = "Location: ";
+
+static const uint32_t max_hdr_size = 2000;
+
+// Write literal header field
+static InjectionReturnStatus write_indexed(char* hdr, uint32_t len, uint8_t*& out,
+ uint32_t& out_free_space, const uint8_t* ind, uint8_t ind_size)
+{
+ const char* sep = (char*)memchr(hdr,':',len);
+ assert(sep != nullptr);
+ const uint32_t skip_len = strlen(": ");
+ assert((sep - hdr) >= skip_len);
+ const uint32_t val_len = len - (sep - hdr) - skip_len;
+ const uint8_t max_val_len = (1<<7) - 1; // FIXIT-E bigger than this will have to be 7 bit
+ // prefix
+ // encoded - currently not supported
+ if ((val_len == 0) || (val_len > max_val_len))
+ return ERR_PAGE_TRANSLATION;
+
+ if (out_free_space < (val_len + 1 + ind_size))
+ {
+#ifndef UNIT_TEST
+ assert(false); // increase max_hdr_size
+#endif
+ return ERR_PAGE_TRANSLATION;
+ }
+
+ memcpy(out, ind, ind_size);
+ out += ind_size;
+ out[0] = val_len;
+ memcpy(out + 1, sep + skip_len, val_len);
+ out += 1 + val_len;
+ out_free_space -= val_len + 1 + ind_size;
+
+ return INJECTION_SUCCESS;
+}
+
+// Write fixed translation
+static InjectionReturnStatus write_translation(uint8_t*& out, uint32_t& out_free_space,
+ const uint8_t* translation, uint8_t size)
+{
+ if (out_free_space < size)
+ {
+#ifndef UNIT_TEST
+ assert(false); // increase max_hdr_size
+#endif
+ return ERR_PAGE_TRANSLATION;
+ }
+
+ memcpy(out, translation, size);
+ out += size;
+ out_free_space -= size;
+
+ return INJECTION_SUCCESS;
+}
+
+static InjectionReturnStatus translate_hdr_field(char* hdr, uint32_t len, uint8_t*& out,
+ uint32_t& out_free_space)
+{
+ if (len > strlen(status_403) && memcmp(hdr, status_403, strlen(status_403)) == 0)
+ {
+ return write_translation(out, out_free_space,status_403_h2, sizeof(status_403_h2));
+ }
+ else if (len > strlen(status_307) && memcmp(hdr, status_307, strlen(status_307)) == 0)
+ {
+ return write_translation(out, out_free_space,status_307_h2, sizeof(status_307_h2));
+ }
+ else if (len == strlen(connection) && memcmp(hdr, connection, strlen(connection))==0)
+ {
+ return write_translation(out, out_free_space, connection_h2, sizeof(connection_h2));
+ }
+ // The following use literal header field without indexing.
+ // The header field name index to the static table is represented using 4-bit prefix.
+ else if (len > strlen(content_length) && memcmp(hdr, content_length, strlen(content_length))==
+ 0)
+ {
+ const uint8_t ind_rep[] = { 0xf, 0xd }; // 0000 + 28 in 4 bit prefix
+ return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep));
+ }
+ else if (len > strlen(content_type) && memcmp(hdr, content_type, strlen(content_type))==0)
+ {
+ const uint8_t ind_rep[] = { 0xf, 0x10 }; // 0000 + 31 in 4 bit prefix
+ return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep));
+ }
+ else if (len > strlen(cookie) && memcmp(hdr, cookie, strlen(cookie))==0)
+ {
+ const uint8_t ind_rep[] = { 0xf, 0x28 }; // 0000 + 55 in 4 bit prefix
+ return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep));
+ }
+ else if (len > strlen(location) && memcmp(hdr, location, strlen(location))==0)
+ {
+ const uint8_t ind_rep[] = { 0xf, 0x1f }; // 0000 + 46 in 4 bit prefix
+ return write_indexed(hdr, len, out, out_free_space, ind_rep, sizeof(ind_rep));
+ }
+ else
+ return ERR_PAGE_TRANSLATION;
+}
+
+static InjectionReturnStatus get_http2_hdr(char* http_page, uint32_t len,
+ uint8_t* http2_hdr, uint32_t& hdr_len, uint32_t& body_offset)
+{
+ InjectionReturnStatus status = ERR_PAGE_TRANSLATION;
+ body_offset = 0;
+
+ uint32_t hdr_free_space = max_hdr_size;
+ char* page_cur = http_page;
+ uint8_t* hdr_cur = http2_hdr;
+ while ((page_cur - http_page) < len)
+ {
+ char* cr_newline = strstr(page_cur, "\r\n");
+ if (cr_newline != nullptr)
+ {
+ if (cr_newline == page_cur)
+ {
+ // reached end of headers
+ if ((page_cur - http_page + 2) < len)
+ body_offset = page_cur - http_page + 2;
+ break;
+ }
+ status = translate_hdr_field(page_cur, cr_newline-page_cur, hdr_cur, hdr_free_space);
+ if (status != INJECTION_SUCCESS)
+ break;
+ page_cur = cr_newline + 2;
+ }
+ else
+ break;
+ }
+
+ if (status == ERR_PAGE_TRANSLATION || body_offset == 0)
+ return ERR_PAGE_TRANSLATION;
+
+ hdr_len = hdr_cur - http2_hdr;
+
+ return INJECTION_SUCCESS;
+}
+
+static void write_3_bytes_of_int(uint8_t* out, uint32_t val)
+{
+#ifdef WORDS_BIGENDIAN
+ out[2] = (val & (0xff000000)) >> 24;
+ out[1] = (val & (0xff0000)) >> 16;
+ out[0] = (val & (0xff00)) >> 8;
+#else
+ out[2] = val & 0xff;
+ out[1] = (val & (0xff00)) >> 8;
+ out[0] = (val & (0xff0000)) >> 16;
+#endif
+}
+
+static void write_frame_hdr(uint8_t*& out, uint32_t len, uint8_t type, uint8_t flags, uint32_t
+ stream_id)
+{
+ write_3_bytes_of_int(out, len);
+ out[3] = type;
+ out[4] = flags;
+ stream_id = htonl(stream_id);
+ memcpy(out+5, &stream_id, 4);
+ out += Http2Enums::FRAME_HEADER_LENGTH;
+}
+
+InjectionReturnStatus PayloadInjectorModule::get_http2_payload(InjectionControl control,
+ uint8_t*& http2_payload, uint32_t& payload_len)
+{
+ if (control.http_page == nullptr || control.http_page_len == 0)
+ return ERR_PAGE_TRANSLATION;
+
+ // create a string version to run with strstr
+ char* page_string = (char*)snort_alloc(control.http_page_len + 1);
+ memcpy(page_string, control.http_page, control.http_page_len);
+ page_string[control.http_page_len] = '\0';
+
+ uint8_t http2_hdr[max_hdr_size];
+ uint32_t hdr_len, body_offset;
+ InjectionReturnStatus status = get_http2_hdr(page_string, control.http_page_len, http2_hdr,
+ hdr_len, body_offset);
+
+ snort_free(page_string);
+
+ if (status == ERR_PAGE_TRANSLATION)
+ return status;
+
+ const uint32_t body_len = control.http_page_len - body_offset;
+ // FIXIT-E support larger body size
+ if (body_len > 1<<14)
+ return ERR_PAGE_TRANSLATION;
+
+ payload_len = 2*Http2Enums::FRAME_HEADER_LENGTH + hdr_len + body_len;
+ http2_payload = (uint8_t*)snort_alloc(payload_len);
+
+ uint8_t* http2_payload_cur = http2_payload;
+ // FIXIT-E update flags
+ write_frame_hdr(http2_payload_cur, hdr_len, Http2Enums::FT_HEADERS, 0, control.stream_id);
+ memcpy(http2_payload_cur, http2_hdr, hdr_len);
+ http2_payload_cur += hdr_len;
+ write_frame_hdr(http2_payload_cur, body_len, Http2Enums::FT_DATA, 0, control.stream_id);
+ memcpy(http2_payload_cur, control.http_page + body_offset, body_len);
+
+ return INJECTION_SUCCESS;
+}
+
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2020-2020 Cisco and/or its affiliates. All rights reserved.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License Version 2 as published
+// by the Free Software Foundation. You may not use, modify or distribute
+// this program under any other version of the GNU General Public License.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// payload_injector_translate_test.cc author Maya Dagon <mdagon@cisco.com>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "payload_injector/payload_injector_module.h"
+
+#include "utils/util.h"
+
+#include <CppUTest/CommandLineTestRunner.h>
+#include <CppUTest/TestHarness.h>
+
+TEST_GROUP(payload_injector_translate_test)
+{
+ uint8_t* http2_payload;
+ uint32_t payload_len;
+ InjectionReturnStatus status;
+};
+
+TEST(payload_injector_translate_test, basic_hdr_translation)
+{
+ char http_page[] = "HTTP/1.1 403 Forbidden\r\nConnection: close\r\nContent-Length: "
+ "504\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n<!DOCTYPE html>\n<html><head>\n<meta"
+ "http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+ CHECK(status == INJECTION_SUCCESS);
+
+ uint8_t out[] = { 0x0, 0x0, 0x40, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x3, 0x34, 0x30, 0x33, 0x0, 0xa, 0x63, 0x6f, 0x6e,
+ 0x6e, 0x65,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5, 0x63, 0x6c, 0x6f, 0x73, 0x65, 0xf, 0xd,
+ 0x3, 0x35,
+ 0x30, 0x34, 0xf, 0x10, 0x18, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68, 0x74, 0x6d,
+ 0x6c,
+ 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55, 0x54, 0x46,
+ 0x2d,
+ 0x38, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x3c, 0x21, 0x44, 0x4f,
+ 0x43, 0x54,
+ 0x59, 0x50, 0x45, 0x20, 0x68, 0x74, 0x6d, 0x6c, 0x3e, 0xa, 0x3c, 0x68, 0x74,
+ 0x6d,
+ 0x6c, 0x3e, 0x3c, 0x68, 0x65, 0x61, 0x64, 0x3e, 0xa, 0x3c, 0x6d, 0x65, 0x74,
+ 0x61,
+ 0x68, 0x74, 0x74, 0x70, 0x2d, 0x65, 0x71, 0x75, 0x69, 0x76, 0x3d, 0x22, 0x63,
+ 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x22, 0x20, 0x63,
+ 0x6f,
+ 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x3d, 0x22, 0x74, 0x65, 0x78, 0x74, 0x2f, 0x68,
+ 0x74,
+ 0x6d, 0x6c, 0x3b, 0x20, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x3d, 0x55,
+ 0x54,
+ 0x46, 0x2d, 0x38, 0x22, 0x20, 0x2f, 0x3e, 0xa };
+ CHECK(payload_len == sizeof(out));
+ CHECK(memcmp(http2_payload, out, payload_len) == 0);
+
+ snort_free(http2_payload);
+}
+
+TEST(payload_injector_translate_test, basic_hdr_translation2)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == INJECTION_SUCCESS);
+
+ uint8_t out[] = { 0x0, 0x0, 0x36, 0x1, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x7, 0x3a, 0x73, 0x74,
+ 0x61, 0x74, 0x75, 0x73, 0x3, 0x33, 0x30, 0x37, 0xf, 0x1f, 0x8, 0x68, 0x74,
+ 0x74, 0x70,
+ 0x73, 0x3a, 0x2f, 0x2f, 0xf, 0x28, 0x1b, 0x30, 0x34, 0x66, 0x32, 0x3b, 0x20,
+ 0x4d,
+ 0x61, 0x78, 0x2d, 0x41, 0x67, 0x65, 0x3a, 0x20, 0x36, 0x30, 0x30, 0x3b, 0x20,
+ 0x70,
+ 0x61, 0x74, 0x68, 0x3d, 0x2f, 0x3b, 0x0, 0x0, 0x5, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x42,
+ 0x6f, 0x64, 0x79, 0xa };
+ CHECK(payload_len == sizeof(out));
+ CHECK(memcmp(http2_payload, out, payload_len) == 0);
+
+ snort_free(http2_payload);
+}
+
+TEST(payload_injector_translate_test, only_body)
+{
+ char http_page[] = "\r\nbody";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, no_body)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, missing_last_rn)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, missing_space_after_colon)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\r\nLocation:https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, extra_space_before_colon)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\r\nLocation :https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, unsuporrted_status)
+{
+ char http_page[] =
+ "HTTP/1.1 200 OK\r\nLocation: https://\r\nSet-Cookie: 04f2; Max-Age: 600; path=/;\r\n\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, unsupported_hdr)
+{
+ char http_page[] =
+ "HTTP/1.1 307 Proxy Redirect\r\nLocation:https://\r\nRetry-after: 120\r\n\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, hdr_ends_wo_value)
+{
+ char http_page[] = "HTTP/1.1 307 Proxy Redirect\r\nLocation: ";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, missing_value)
+{
+ char http_page[] = "HTTP/1.1 307 Proxy Redirect\r\nLocation: \r\n\r\nBody\n";
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = (uint8_t*)http_page;
+ control.http_page_len = strlen(http_page);
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+// Header value has maximum supported length + 1
+TEST(payload_injector_translate_test, val_len_too_big)
+{
+ const uint32_t size = strlen("Location: ") + 128 + strlen("\r\n\r\nbody");
+ uint8_t http_page[size];
+ memset(http_page, 'a', size);
+ memcpy(http_page, "Location: ", strlen("Location: "));
+ memcpy(http_page+128+strlen("Location: "), "\r\n\r\nbody", strlen("\r\n\r\nbody"));
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = http_page;
+ control.http_page_len = size;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+// Header value has maximum supported length 127
+TEST(payload_injector_translate_test, max_val)
+{
+ const uint32_t size = strlen("Location: ") + 127 + strlen("\r\n\r\nbody");
+ uint8_t http_page[size];
+ memset(http_page, 'a', size);
+ memcpy(http_page, "Location: ", strlen("Location: "));
+ memcpy(http_page+127+strlen("Location: "), "\r\n\r\nbody", strlen("\r\n\r\nbody"));
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = http_page;
+ control.http_page_len = size;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == INJECTION_SUCCESS);
+ snort_free(http2_payload);
+}
+
+// Translated header is exactly 2000.
+// Verify correct frame length when length is more than 1 byte.
+// Verify correct behavior for body length is 1.
+TEST(payload_injector_translate_test, http2_hdr_is_max)
+{
+ const uint32_t size = strlen("Connection: close\r\n") * 110 + strlen("Location: ") +
+ strlen("\r\n\r\nb") + 17;
+ uint8_t http_page[size];
+
+ memset(http_page, 'a', size);
+ uint8_t* cur_pos = http_page;
+ for (int i=0; i < 110; i++)
+ {
+ memcpy(cur_pos, "Connection: close\r\n", strlen("Connection: close\r\n"));
+ cur_pos += strlen("Connection: close\r\n");
+ }
+ memcpy(cur_pos, "Location: ", strlen("Location: "));
+ memcpy(http_page+size-strlen("\r\n\r\nb"), "\r\n\r\nb", strlen("\r\n\r\nb"));
+
+ InjectionControl control;
+ control.http_page = http_page;
+ control.http_page_len = size;
+ control.stream_id = 0xf000;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == INJECTION_SUCCESS);
+ CHECK(payload_len == 2019);
+ uint8_t hdr[] = { 0x0, 0x7, 0xd0, 0x1, 0x0, 0x0, 0x0, 0xf0, 0x0 };
+ CHECK(memcmp(http2_payload, hdr, sizeof(hdr))==0);
+ uint8_t body[] = { 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, 0xf0, 0x0, 'b' };
+ CHECK(memcmp(http2_payload + 2009, body, sizeof(body))==0);
+ snort_free(http2_payload);
+}
+
+// Translated header is 2001. Goes through write_indexed code path.
+TEST(payload_injector_translate_test, http2_hdr_too_big)
+{
+ const uint32_t size = strlen("Connection: close\r\n") * 110 + strlen("Location: ") +
+ strlen("\r\n\r\nbody") + 18;
+ uint8_t http_page[size];
+
+ memset(http_page, 'a', size);
+ uint8_t* cur_pos = http_page;
+ for (int i=0; i < 110; i++)
+ {
+ memcpy(cur_pos, "Connection: close\r\n", strlen("Connection: close\r\n"));
+ cur_pos += strlen("Connection: close\r\n");
+ }
+ memcpy(cur_pos, "Location: ", strlen("Location: "));
+ memcpy(http_page+size-strlen("\r\n\r\nbody"), "\r\n\r\nbody", strlen("\r\n\r\nbody"));
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = http_page;
+ control.http_page_len = size;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+// Translated header > 2000. Goes through write_translation code path.
+TEST(payload_injector_translate_test, http2_hdr_too_big2)
+{
+ const uint32_t size = strlen("Connection: close\r\n") * 112 + strlen("\r\nbody");
+ uint8_t http_page[size];
+
+ uint8_t* cur_pos = http_page;
+ for (int i=0; i < 112; i++)
+ {
+ memcpy(cur_pos, "Connection: close\r\n", strlen("Connection: close\r\n"));
+ cur_pos += strlen("Connection: close\r\n");
+ }
+ memcpy(http_page+size-strlen("\r\nbody"), "\r\nbody", strlen("\r\nbody"));
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = http_page;
+ control.http_page_len = size;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, payload_body_larger_than_max)
+{
+ static const uint32_t size = (1<<14) + 1 + strlen("HTTP/1.1 403 Forbidden\r\n\r\n");
+ uint8_t http_page[size];
+ memset(http_page,'a',size);
+ memcpy(http_page,"HTTP/1.1 403 Forbidden\r\n\r\n", strlen("HTTP/1.1 403 Forbidden\r\n\r\n"));
+
+ InjectionControl control;
+ control.stream_id = 1;
+ control.http_page = http_page;
+ control.http_page_len = size;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, http_page_is_nullptr)
+{
+ InjectionControl control;
+ control.http_page = nullptr;
+ control.http_page_len = 1;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+TEST(payload_injector_translate_test, http_page_is_0_length)
+{
+ uint8_t http_page[] = {1};
+
+ InjectionControl control;
+ control.http_page = http_page;
+ control.http_page_len = 0;
+ status = PayloadInjectorModule::get_http2_payload(control, http2_payload, payload_len);
+ CHECK(status == ERR_PAGE_TRANSLATION);
+}
+
+int main(int argc, char** argv)
+{
+ return CommandLineTestRunner::RunAllTests(argc, argv);
+}
+