diff --git a/convert_client.c b/convert_client.c index a320ec0..86fb9ac 100644 --- a/convert_client.c +++ b/convert_client.c @@ -76,7 +76,8 @@ static LIST_HEAD(socket_htbl_t, socket_state) _socket_htable[NUM_BUCKETS]; static pthread_mutex_t _socket_htable_mutex = PTHREAD_MUTEX_INITIALIZER; static struct addrinfo *_converter_addr; -static const char * _convert_port = CONVERT_PORT; +static const char * _convert_port = CONVERT_PORT; +static const char * _convert_cookie = NULL; static FILE * _log; static pthread_mutex_t _log_mutex = PTHREAD_MUTEX_INITIALIZER; @@ -175,6 +176,12 @@ _redirect_connect_tlv(uint8_t *buf, size_t buf_len, struct sockaddr *addr) opts.flags = CONVERT_F_CONNECT; + if (_convert_cookie) { + opts.flags |= CONVERT_F_COOKIE; + opts.cookie_len = strlen(_convert_cookie); + opts.cookie_data = (uint8_t *)_convert_cookie; + } + switch (addr->sa_family) { case AF_INET: { struct sockaddr_in *in = (struct sockaddr_in *)addr; @@ -543,6 +550,7 @@ _validate_parameters(char *err_buf, size_t len) { const char * convert_addr = getenv("CONVERT_ADDR"); const char * convert_port = getenv("CONVERT_PORT"); + const char * convert_cookie = getenv("CONVERT_COOKIE"); if (!convert_addr) { snprintf(err_buf, len, @@ -565,6 +573,9 @@ _validate_parameters(char *err_buf, size_t len) _convert_port = convert_port; } + if (convert_cookie) + _convert_cookie = convert_cookie; + /* resolve address */ if (getaddrinfo(convert_addr, _convert_port, NULL, &_converter_addr) != 0) { @@ -572,8 +583,10 @@ _validate_parameters(char *err_buf, size_t len) return -1; } - log_info("connecting to convert service at %s:%s", - convert_addr, _convert_port); + log_info("connecting to convert service at %s:%s%s%s", + convert_addr, _convert_port, + _convert_cookie ? " with cookie: " : "", + _convert_cookie ? : ""); return 0; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9d77a2f..ca50e76 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -25,7 +25,7 @@ add_test(NAME check_convert_util set(TEST_WRAPPER "${PROJECT_SOURCE_DIR}/tests/test-wrapper") foreach(TEST_CMD "curl" "wget") - foreach(TEST_TYPE "test_ok" "test_error") + foreach(TEST_TYPE "test_ok" "test_error" "test_cookie_ok" "test_cookie_error") set (test_name "${TEST_CMD}-${TEST_TYPE}") add_test(NAME "${test_name}" diff --git a/tests/check_convert_util.c b/tests/check_convert_util.c index 0e69774..feef676 100644 --- a/tests/check_convert_util.c +++ b/tests/check_convert_util.c @@ -279,7 +279,7 @@ START_TEST (test_convert_parse_tlvs_cookie) { for (i = 0; i < cookie_len; ++i) ck_assert_msg(opts->cookie_data[i] == cookie->opaque[i], - "Should return exact copy TCP options"); + "Should return exact cookie value"); convert_free_opts(opts); free(buff); diff --git a/tests/converter_mock.py b/tests/converter_mock.py index 48fd046..0b09da3 100644 --- a/tests/converter_mock.py +++ b/tests/converter_mock.py @@ -53,7 +53,30 @@ def run(self, converter): converter.recv_end_seq += 1 payload = pkt.getlayer(Raw) if payload: - converter.recv_end_seq += len(payload.load) + payload = payload.load + + converter.recv_end_seq += len(payload) + + # For the moment, we assume the TLV connect contains the header (4 bytes) + # and the TLV Connect (4 + 16 bytes). The cookie is then at the offset 24. + cookie_flag_offset = 24 + + # The cookie will only be received in the syn pkt. + if tcp.flags.S and len(payload) > cookie_flag_offset and bytes(payload)[cookie_flag_offset] == 0x16: + + # The cookie TLV is as follows: 1 byte flag + 1 byte TLV length + # + 2 byte zeros, then Cookie data + cookie_size_offset = cookie_flag_offset + 1 + cookie_data_offset = cookie_flag_offset + 4 + + # Length is given in 32-bit words, and includes flag byte + length + # byte and the 2 bytes of zeros. + cookie_data_size = (bytes(payload)[cookie_size_offset] - 1) * 4 + + cookie_data_offset_end = cookie_data_offset + cookie_data_size + + cookie_bytes = bytes(payload)[cookie_data_offset:cookie_data_offset_end] + converter.cookie = cookie_bytes.decode('utf-8').rstrip('\x00') class RecvSyn(RecvPkt): @@ -98,6 +121,18 @@ def run(self, converter): super(SendSynAck, self).run(converter) +class SendSynAckCheckCookie(SendSynAck): + def __init__(self, payload, cookie): + SendSynAck.__init__(self, payload=payload) + self.cookie = cookie + + def run(self, converter): + # Check cookie value + if self.cookie != converter.cookie: + raise Exception("Wrong cookie '{}' instead of '{}'".format(converter.cookie, self.cookie)) + super(SendSynAckCheckCookie, self).run(converter) + + class SendHTTPResp(SendPkt): def __init__(self, data): payload = "HTTP/1.1 200 OK\r\n" diff --git a/tests/test-wrapper b/tests/test-wrapper index 49b62cf..92e737f 100755 --- a/tests/test-wrapper +++ b/tests/test-wrapper @@ -13,6 +13,7 @@ export TEST_CONVERT_LOG=convert.log export TEST_OUTPUT_LOG=output.log export TEST_CONVERT_MOCK_LOG=convert_mock.log export TEST_VALIDATE_LOG=validate.log +export TEST_CONVERT_COOKIE="cookie" SCAPY_PID= @@ -60,7 +61,11 @@ sleep 1 COMMAND=$(test_step run_cmd) -CONVERT_LOG=${TEST_CONVERT_LOG} CONVERT_ADDR=127.0.0.1 LD_PRELOAD="../libconvert_client.so" ${COMMAND} > ${TEST_OUTPUT_LOG} 2>&1 || true +CONVERT_LOG=${TEST_CONVERT_LOG} \ + CONVERT_ADDR=127.0.0.1 \ + CONVERT_COOKIE=${TEST_CONVERT_COOKIE} \ + LD_PRELOAD="../libconvert_client.so" \ + ${COMMAND} > ${TEST_OUTPUT_LOG} 2>&1 || true ret=$(test_step validate 2>&1 | tee ${TEST_VALIDATE_LOG}) [ "${ret}" == "" ] && RETURN_CODE=0 diff --git a/tests/test_cookie_error.py b/tests/test_cookie_error.py new file mode 100755 index 0000000..03a84d4 --- /dev/null +++ b/tests/test_cookie_error.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from converter_mock import * +from test_lib import * + + +class TestCookieError(TestInstance): + def run(self): + converter = Convert(tlvs=[ConvertTLV_Error(error_code=3)]) + print(converter.build()) + cookie = self.get_cookie() + + class MockConverterCookieError(MockConverter): + actions = [RecvSyn(), SendSynAckCheckCookie(converter.build(), cookie), Wait(1), SendPkt(flags='R')] + MockConverterCookieError() + + def validate(self): + self.assert_log_contains("received TLV error: 3") + + +TestCookieError() diff --git a/tests/test_cookie_ok.py b/tests/test_cookie_ok.py new file mode 100755 index 0000000..c9bfc7e --- /dev/null +++ b/tests/test_cookie_ok.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 + +from converter_mock import * +from test_lib import * + + +class TestCookieOK(TestInstance): + def run(self): + cookie = self.get_cookie() + class MockConverterCookieOK(MockConverter): + actions = [RecvSyn(), SendSynAckCheckCookie(Convert().build(), cookie), RecvHTTPGet( + ), SendHTTPResp("HELLO, WORLD!"), SendPkt(flags='RA')] + MockConverterCookieOK() + + def validate(self): + self.assert_result("HELLO, WORLD!") + + +TestCookieOK() diff --git a/tests/test_lib.py b/tests/test_lib.py index 773ea78..9134ed6 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -35,6 +35,9 @@ def _get_log(self): with open(os.environ["TEST_CONVERT_LOG"]) as f: return f.read() + def get_cookie(self): + return os.environ["TEST_CONVERT_COOKIE"] + def assert_result(self, result): assert result in self._get_result(), "Couldn't find '{}' in output".format(result)