From fa5be4bfb351a13f8fc10bdcde16d65c5e8d2acd Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 15:51:02 +0100 Subject: [PATCH 01/16] add retest --- .gitignore | 2 - CMakeLists.txt | 7 + cmake/sanitizer.cmake | 13 + test/CMakeLists.txt | 183 +++++ test/aac.c | 34 + test/aes.c | 366 +++++++++ test/async.c | 171 ++++ test/aubuf.c | 375 +++++++++ test/aulevel.c | 89 +++ test/auresamp.c | 86 ++ test/av1.c | 811 +++++++++++++++++++ test/base64.c | 146 ++++ test/bfcp.c | 467 +++++++++++ test/combo/dtls_turn.c | 439 ++++++++++ test/conf.c | 47 ++ test/convert.c | 56 ++ test/crc32.c | 43 + test/data/client.pem | 51 ++ test/data/client_wrongkey.pem | 51 ++ test/data/fstab.json | 31 + test/data/menu.json | 27 + test/data/rfc7159.json | 14 + test/data/server-ecdsa.pem | 21 + test/data/sni/client-interm.pem | 35 + test/data/sni/root-ca.pem | 13 + test/data/sni/server-interm.pem | 37 + test/data/utf8.json | 4 + test/data/webapp.json | 88 ++ test/data/widget.json | 26 + test/dns.c | 461 +++++++++++ test/dsp.c | 63 ++ test/dtls.c | 346 ++++++++ test/dtmf.c | 59 ++ test/fir.c | 57 ++ test/fmt.c | 1090 +++++++++++++++++++++++++ test/g711.c | 174 ++++ test/h264.c | 700 ++++++++++++++++ test/h265.c | 279 +++++++ test/hash.c | 235 ++++++ test/hmac.c | 267 +++++++ test/http.c | 715 +++++++++++++++++ test/httpauth.c | 219 +++++ test/ice.c | 928 ++++++++++++++++++++++ test/jbuf.c | 242 ++++++ test/json.c | 830 +++++++++++++++++++ test/list.c | 242 ++++++ test/main.c | 275 +++++++ test/mbuf.c | 154 ++++ test/md5.c | 58 ++ test/mem.c | 133 ++++ test/mock/cert.c | 38 + test/mock/dnssrv.c | 284 +++++++ test/mock/fuzz.c | 110 +++ test/mock/nat.c | 217 +++++ test/mock/pf.c | 107 +++ test/mock/sipsrv.c | 138 ++++ test/mock/stunsrv.c | 260 ++++++ test/mock/tcpsrv.c | 73 ++ test/mock/turnsrv.c | 483 +++++++++++ test/mqueue.c | 73 ++ test/net.c | 89 +++ test/odict.c | 240 ++++++ test/pcp.c | 273 +++++++ test/remain.c | 147 ++++ test/rtcp.c | 193 +++++ test/rtmp.c | 857 ++++++++++++++++++++ test/rtp.c | 702 ++++++++++++++++ test/rtpext.c | 71 ++ test/sa.c | 391 +++++++++ test/sdp.c | 978 +++++++++++++++++++++++ test/sha.c | 70 ++ test/sip.c | 865 ++++++++++++++++++++ test/sipauth.c | 95 +++ test/sipevent.c | 439 ++++++++++ test/sipreg.c | 212 +++++ test/sipsess.c | 1321 +++++++++++++++++++++++++++++++ test/srtp.c | 1212 ++++++++++++++++++++++++++++ test/stun.c | 598 ++++++++++++++ test/sys.c | 206 +++++ test/tcp.c | 229 ++++++ test/telev.c | 77 ++ test/test.c | 1104 ++++++++++++++++++++++++++ test/test.h | 541 +++++++++++++ test/thread.c | 51 ++ test/tls.c | 648 +++++++++++++++ test/tmr.c | 50 ++ test/trace.c | 71 ++ test/trice.c | 1160 +++++++++++++++++++++++++++ test/turn.c | 535 +++++++++++++ test/udp.c | 215 +++++ test/unixsock.c | 60 ++ test/uri.c | 439 ++++++++++ test/vid.c | 301 +++++++ test/vidconv.c | 391 +++++++++ test/websock.c | 223 ++++++ 95 files changed, 28095 insertions(+), 2 deletions(-) create mode 100644 cmake/sanitizer.cmake create mode 100644 test/CMakeLists.txt create mode 100644 test/aac.c create mode 100644 test/aes.c create mode 100644 test/async.c create mode 100644 test/aubuf.c create mode 100644 test/aulevel.c create mode 100644 test/auresamp.c create mode 100644 test/av1.c create mode 100644 test/base64.c create mode 100644 test/bfcp.c create mode 100644 test/combo/dtls_turn.c create mode 100644 test/conf.c create mode 100644 test/convert.c create mode 100644 test/crc32.c create mode 100644 test/data/client.pem create mode 100644 test/data/client_wrongkey.pem create mode 100644 test/data/fstab.json create mode 100644 test/data/menu.json create mode 100644 test/data/rfc7159.json create mode 100644 test/data/server-ecdsa.pem create mode 100644 test/data/sni/client-interm.pem create mode 100644 test/data/sni/root-ca.pem create mode 100644 test/data/sni/server-interm.pem create mode 100644 test/data/utf8.json create mode 100644 test/data/webapp.json create mode 100644 test/data/widget.json create mode 100644 test/dns.c create mode 100644 test/dsp.c create mode 100644 test/dtls.c create mode 100644 test/dtmf.c create mode 100644 test/fir.c create mode 100644 test/fmt.c create mode 100644 test/g711.c create mode 100644 test/h264.c create mode 100644 test/h265.c create mode 100644 test/hash.c create mode 100644 test/hmac.c create mode 100644 test/http.c create mode 100644 test/httpauth.c create mode 100644 test/ice.c create mode 100644 test/jbuf.c create mode 100644 test/json.c create mode 100644 test/list.c create mode 100644 test/main.c create mode 100644 test/mbuf.c create mode 100644 test/md5.c create mode 100644 test/mem.c create mode 100644 test/mock/cert.c create mode 100644 test/mock/dnssrv.c create mode 100644 test/mock/fuzz.c create mode 100644 test/mock/nat.c create mode 100644 test/mock/pf.c create mode 100644 test/mock/sipsrv.c create mode 100644 test/mock/stunsrv.c create mode 100644 test/mock/tcpsrv.c create mode 100644 test/mock/turnsrv.c create mode 100644 test/mqueue.c create mode 100644 test/net.c create mode 100644 test/odict.c create mode 100644 test/pcp.c create mode 100644 test/remain.c create mode 100644 test/rtcp.c create mode 100644 test/rtmp.c create mode 100644 test/rtp.c create mode 100644 test/rtpext.c create mode 100644 test/sa.c create mode 100644 test/sdp.c create mode 100644 test/sha.c create mode 100644 test/sip.c create mode 100644 test/sipauth.c create mode 100644 test/sipevent.c create mode 100644 test/sipreg.c create mode 100644 test/sipsess.c create mode 100644 test/srtp.c create mode 100644 test/stun.c create mode 100644 test/sys.c create mode 100644 test/tcp.c create mode 100644 test/telev.c create mode 100644 test/test.c create mode 100644 test/test.h create mode 100644 test/thread.c create mode 100644 test/tls.c create mode 100644 test/tmr.c create mode 100644 test/trace.c create mode 100644 test/trice.c create mode 100644 test/turn.c create mode 100644 test/udp.c create mode 100644 test/unixsock.c create mode 100644 test/uri.c create mode 100644 test/vid.c create mode 100644 test/vidconv.c create mode 100644 test/websock.c diff --git a/.gitignore b/.gitignore index c1d56c287..93efff050 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,4 @@ /build* -test -test.c test.d test.o *stamp diff --git a/CMakeLists.txt b/CMakeLists.txt index ad847996f..eb4b06e19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -725,3 +725,10 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libre.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig COMPONENT Development ) + + +############################################################################## +# Test +# + +add_subdirectory(test) diff --git a/cmake/sanitizer.cmake b/cmake/sanitizer.cmake new file mode 100644 index 000000000..29079dc47 --- /dev/null +++ b/cmake/sanitizer.cmake @@ -0,0 +1,13 @@ +if(USE_SANITIZER STREQUAL "address") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") +elseif(USE_SANITIZER STREQUAL "thread") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") +elseif(USE_SANITIZER STREQUAL "undefined") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") +elseif(USE_SANITIZER STREQUAL "memory") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=memory") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=memory") +endif() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 000000000..5fdfe32c3 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,183 @@ +# +# CMakeLists.txt +# +# Copyright (C) 2010 - 2022 Alfred E. Heggestad +# + +############################################################################## +# +# Versioning +# + +project(retest C) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) + + +############################################################################## +# +# Module/Package Includes +# + +find_package(RE REQUIRED) + +############################################################################## +# +# Compile options/definitions +# + +option(USE_SANITIZER "Sanitizers like: address, thread, undefined, memory") +include(sanitizer) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_BUILD_TYPE Debug) + +if(MSVC) + add_compile_options("/W3") +else() + add_compile_options( + -Wall + -Wbad-function-cast + -Wcast-align + -Wextra + -Wmissing-declarations + -Wmissing-prototypes + -Wnested-externs + -Wold-style-definition + -Wshadow -Waggregate-return + -Wstrict-prototypes + -Wvla + ) +endif() + +if(CMAKE_C_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wshorten-64-to-32) +endif() + +include_directories( + ../include + . + ${RE_INCLUDE_DIRS} +) + +find_package(re CONFIG REQUIRED HINTS ../re/cmake) + +############################################################################## +# +# Source/Header section +# + +set(SRCS + aac.c + aes.c + aubuf.c + aulevel.c + auresamp.c + async.c + av1.c + base64.c + bfcp.c + conf.c + convert.c + crc32.c + dns.c + dsp.c + dtmf.c + fir.c + fmt.c + g711.c + h264.c + h265.c + hash.c + hmac.c + http.c + httpauth.c + ice.c + jbuf.c + json.c + list.c + main.c + mbuf.c + md5.c + mem.c + mock/dnssrv.c + mock/fuzz.c + mock/nat.c + mock/pf.c + mock/sipsrv.c + mock/stunsrv.c + mock/tcpsrv.c + mock/turnsrv.c + mqueue.c + net.c + odict.c + pcp.c + remain.c + rtcp.c + rtmp.c + rtp.c + rtpext.c + sa.c + sdp.c + sha.c + sip.c + sipauth.c + sipevent.c + sipreg.c + sipsess.c + srtp.c + stun.c + sys.c + tcp.c + telev.c + test.c + thread.c + tmr.c + trace.c + trice.c + turn.c + udp.c + unixsock.c + uri.c + vid.c + vidconv.c + websock.c +) + +if(USE_OPENSSL) + list(APPEND SRCS + tls.c + dtls.c + combo/dtls_turn.c + mock/cert.c + ) +endif() + + +############################################################################## +# +# Main target object +# + +set(LINKLIBS re ${RE_LIBRARIES} ${OPENSSL_LIBRARIES}) +if(WIN32) + list(APPEND LINKLIBS qwave iphlpapi wsock32 ws2_32) +else() + list(APPEND LINKLIBS -lpthread -lm ${RESOLV_LIBRARY}) +endif() + +if(ZLIB_FOUND) + list(APPEND LINKLIBS ZLIB::ZLIB) +endif() + +add_executable(${PROJECT_NAME} ${SRCS}) +set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS 1) + +target_link_libraries(${PROJECT_NAME} PRIVATE ${LINKLIBS}) +target_compile_definitions(${PROJECT_NAME} PRIVATE ${RE_DEFINITIONS}) + +if(USE_OPENSSL) + target_include_directories(${PROJECT_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}) +endif() diff --git a/test/aac.c b/test/aac.c new file mode 100644 index 000000000..76f93832a --- /dev/null +++ b/test/aac.c @@ -0,0 +1,34 @@ +/** + * @file aac.c AAC (Advanced Audio Coding) Testcode + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "aactest" +#define DEBUG_LEVEL 5 +#include + + +int test_aac(void) +{ + static const uint8_t buf[2] = {0x12, 0x10}; + struct aac_header hdr; + int err; + + err = aac_header_decode(&hdr, buf, sizeof(buf)); + if (err) + return err; + + TEST_EQUALS(44100, hdr.sample_rate); + TEST_EQUALS(2, hdr.channels); + TEST_EQUALS(1024, hdr.frame_size); + + out: + return err; +} diff --git a/test/aes.c b/test/aes.c new file mode 100644 index 000000000..e2b7238ce --- /dev/null +++ b/test/aes.c @@ -0,0 +1,366 @@ +/** + * @file aes.c AES (Advanced Encryption Standard) Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "aestest" +#define DEBUG_LEVEL 5 +#include + + +/* + * http://www.inconteam.com/software-development/41-encryption/ + * 55-aes-test-vectors#aes-crt + * + * AES CTR 128-bit encryption mode + */ +static int test_aes_ctr_loop(void) +{ + const char *init_vec_str = "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"; + uint8_t encr_key[16]; + uint8_t iv_enc[AES_BLOCK_SIZE]; + uint8_t iv_dec[AES_BLOCK_SIZE]; + size_t i; + int err = 0; + struct aes *enc = NULL, *dec = NULL; + + static const struct { + char test_str[33]; + char *ciph_str; + } testv[] = { + + {"6bc1bee22e409f96e93d7e117393172a", + "874d6191b620e3261bef6864990db6ce"}, + + {"ae2d8a571e03ac9c9eb76fac45af8e51", + "9806f66b7970fdff8617187bb9fffdff"}, + + {"30c81c46a35ce411e5fbc1191a0a52ef", + "5ae4df3edbd5d35e5b4f09020db03eab"}, + + {"f69f2445df4f9b17ad2b417be66c3710", + "1e031dda2fbe03d1792170a0f3009cee"}, + }; + + err |= str_hex(encr_key, sizeof(encr_key), + "2b7e151628aed2a6abf7158809cf4f3c"); + err |= str_hex(iv_enc, sizeof(iv_enc), init_vec_str); + err |= str_hex(iv_dec, sizeof(iv_dec), init_vec_str); + if (err) + return err; + + err = aes_alloc(&enc, AES_MODE_CTR, encr_key, 128, iv_enc); + err |= aes_alloc(&dec, AES_MODE_CTR, encr_key, 128, iv_dec); + if (err) + goto out; + + for (i=0; iencr_key_str); + if (err) { + DEBUG_WARNING("could not set key\n"); + break; + } + + err |= str_hex(iv, sizeof(iv), test->iv_str); + if (err) { + DEBUG_WARNING("could not set IV\n"); + return err; + } + + err = aes_alloc(&enc, AES_MODE_GCM, encr_key, key_bits, iv); + if (err) + goto out; + + if (str_isset(test->aad_str)) { + + err = str_hex(aad, sizeof(aad), test->aad_str); + if (err) { + DEBUG_WARNING("could not set aad\n"); + break; + } + } + + if (str_isset(test->plain_str)) { + err |= str_hex(test_vector, sizeof(test_vector), + test->plain_str); + clen = sizeof(test_vector); + } + else { + clen = 0; + } + + if (str_isset(test->ciph_str)) { + err |= str_hex(cipher_text, sizeof(cipher_text), + test->ciph_str); + if (err) { + DEBUG_WARNING("str_hex error\n"); + break; + } + } + + err |= str_hex(tag_ref, sizeof(tag_ref), testv[i].tag_str); + if (err) { + DEBUG_WARNING("tag size mismatch\n"); + break; + } + + /* Encrypt */ + if (str_isset(test->aad_str)) { + err = aes_encr(enc, NULL, aad, sizeof(aad)); + TEST_ERR(err); + } + if (clen) { + err = aes_encr(enc, out, test_vector, clen); + TEST_ERR(err); + + TEST_MEMCMP(cipher_text, sizeof(cipher_text), + out, sizeof(out)); + } + + err = aes_get_authtag(enc, tag, tagsz); + TEST_ERR(err); + + if (test->success) { + TEST_MEMCMP(tag_ref, sizeof(tag_ref), tag, tagsz); + } + enc = mem_deref(enc); + + /* Decrypt */ + err = aes_alloc(&dec, AES_MODE_GCM, encr_key, key_bits, iv); + if (err) + goto out; + + if (str_isset(test->aad_str)) { + err = aes_decr(dec, NULL, aad, sizeof(aad)); + TEST_ERR(err); + } + + err = aes_decr(dec, clear, out, clen); + TEST_ERR(err); + + e = aes_authenticate(dec, tag_ref, tagsz); + if (test->success) { + if (e) { + err = e; + DEBUG_WARNING("aes_authenticate error\n"); + break; + } + + if (clen) { + TEST_MEMCMP(test_vector, sizeof(test_vector), + clear, sizeof(clear)); + } + } + else { + TEST_EQUALS(EAUTH, e); + } + + dec = mem_deref(dec); + } + + out: + mem_deref(enc); + mem_deref(dec); + + return err; +} diff --git a/test/async.c b/test/async.c new file mode 100644 index 000000000..011920b72 --- /dev/null +++ b/test/async.c @@ -0,0 +1,171 @@ +/** + * @file async.c Testcode for re async + * + * Copyright (C) 2022 Sebastian Reimers + */ +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 + +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#include "test.h" + +#define DEBUG_MODULE "async" +#define DEBUG_LEVEL 5 +#include + +struct test_cnt { + int tests; + int done; +}; + +struct test { + char domain[128]; + struct sa sa; + int err; + int err_expected; + struct test_cnt *cnt; +}; + +static int blocking_getaddr(void *arg) +{ + int err; + struct test *test = arg; + struct addrinfo *res = NULL; + struct addrinfo hints; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_flags = AI_V4MAPPED | AI_ADDRCONFIG; + + + /* Blocking */ + err = getaddrinfo(test->domain, NULL, &hints, &res); + if (err) + return EADDRNOTAVAIL; + + sa_set_sa(&test->sa, res->ai_addr); + freeaddrinfo(res); + + return 0; +} + + +static void completed(int err, void *arg) +{ + struct test *test = arg; + struct sa sa; + + if (err) + goto out; + + err = re_thread_check(); + TEST_ERR(err); + + sa_set_str(&sa, "127.0.0.1", 0); + if (!sa_cmp(&sa, &test->sa, SA_ADDR)) + err = EINVAL; + + TEST_ERR(err); + +out: + test->err = err; + if (++test->cnt->done >= test->cnt->tests) + re_cancel(); +} + + +static int test_re_thread_async(void) +{ + int err; + + struct test_cnt cnt = {0, 0}; + + struct test testv[] = { + {"localhost", {.len = 0}, -1, 0, &cnt}, + {"test.notfound", {.len = 0}, -1, EADDRNOTAVAIL, &cnt}}; + + cnt.tests = RE_ARRAY_SIZE(testv); + + err = re_thread_async_init(2); + TEST_ERR(err); + + for (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) { + err = re_thread_async(blocking_getaddr, completed, &testv[i]); + TEST_ERR(err); + } + + err = re_main_timeout(200); + TEST_ERR(err); + + for (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) { + TEST_EQUALS(testv[i].err_expected, testv[i].err); + } + +out: + re_thread_async_close(); + return err; +} + + +static void never_callback(int err, void *arg) +{ + (void)err; + (void)arg; + + DEBUG_WARNING("async: never_callback called!\n"); + abort(); +} + + +static void timer_cancel(void *arg) +{ + (void)arg; + + re_cancel(); +} + + +static int test_re_thread_async_cancel(void) +{ + int err; + struct tmr tmr; + + err = re_thread_async_init(2); + TEST_ERR(err); + + err = re_thread_async_id(1, NULL, never_callback, NULL); + TEST_ERR(err); + + re_thread_async_cancel(1); + + tmr_init(&tmr); + tmr_start(&tmr, 0, timer_cancel, NULL); + + err = re_main_timeout(200); + TEST_ERR(err); + +out: + re_thread_async_close(); + return err; +} + + +int test_async(void) +{ + int err; + + err = test_re_thread_async(); + TEST_ERR(err); + + err = test_re_thread_async_cancel(); + TEST_ERR(err); + +out: + return err; +} diff --git a/test/aubuf.c b/test/aubuf.c new file mode 100644 index 000000000..d80bea16b --- /dev/null +++ b/test/aubuf.c @@ -0,0 +1,375 @@ +/** + * @file aubuf.c Audio-buffer Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_aubuf" +#define DEBUG_LEVEL 5 +#include + +#define AUDIO_TIMEBASE 1000000U + +enum { + FRAMES = 80, +}; + + +static int test_aubuf_raw(void) +{ + struct aubuf *ab = NULL; + int16_t sampv_in[2 * FRAMES]; + int16_t sampv_out[2 * FRAMES]; + struct mbuf *mb; + unsigned i; + int err; + + mb = mbuf_alloc(FRAMES * sizeof(int16_t)); + if (!mb) + return ENOMEM; + + for (i=0; ipos = 0; + + err = aubuf_append(ab, mb); + TEST_ERR(err); + TEST_EQUALS(4 * FRAMES, aubuf_cur_size(ab)); + + memset(sampv_out, 0, sizeof(sampv_out)); + aubuf_read(ab, (uint8_t *)sampv_out, 2 * FRAMES * sizeof(int16_t)); + TEST_MEMCMP(sampv_in, sizeof(sampv_in), sampv_out, sizeof(sampv_out)); + TEST_EQUALS(0, aubuf_cur_size(ab)); + + out: + mem_deref(ab); + mem_deref(mb); + return err; +} + + +static int test_aubuf_samp(void) +{ + struct aubuf *ab = NULL; + int16_t sampv_in[2 * FRAMES]; + int16_t sampv_out[2 * FRAMES]; + unsigned i; + int err; + + for (i=0; i +#include +#include "test.h" + + +#define DEBUG_MODULE "aulevel" +#define DEBUG_LEVEL 5 +#include + + +#define PREC .6 + + +int test_aulevel(void) +{ + double level; + struct auframe af; + int err = 0; + + static struct { + int16_t sampv[2]; + double level; + } testv[] = { + + { { 0, -0}, -96.0 }, + { { 0, 1}, -93.0 }, + { { 1, -1}, -90.0 }, + { { 2, -2}, -84.0 }, + { { 4, -4}, -78.0 }, + { { 8, -8}, -72.0 }, + { { 16, -16}, -66.0 }, + { { 32, -32}, -60.0 }, + { { 64, -64}, -54.0 }, + { { 128, -128}, -48.0 }, + { { 256, -256}, -42.0 }, + { { 512, -512}, -36.0 }, + { { 1024, -1024}, -30.0 }, + { { 2048, -2048}, -24.0 }, + { { 4096, -4096}, -18.0 }, + { { 8192, -8192}, -12.0 }, + { {16384, -16384}, -6.0 }, + { {32767, -32768}, 0.0 }, + }; + + static struct { + int16_t sampv[4]; + double level; + } testv4[] = { + { {32767, -32768, 16384, -16384}, -2.0 }, + }; + + auframe_init(&af, AUFMT_RAW, testv[0].sampv, + RE_ARRAY_SIZE(testv[0].sampv), 48000, 2); + TEST_EQUALS(AULEVEL_UNDEF, af.level); + + level = auframe_level(&af); + TEST_EQUALS(AULEVEL_UNDEF, level); + + auframe_init(&af, AUFMT_S16LE, NULL, 0, 48000, 2); + level = auframe_level(&af); + TEST_EQUALS(AULEVEL_UNDEF, level); + + for (size_t i = 0; i < RE_ARRAY_SIZE(testv); i++) { + auframe_init(&af, AUFMT_S16LE, testv[i].sampv, + RE_ARRAY_SIZE(testv[i].sampv), 48000, 2); + + level = auframe_level(&af); + + ASSERT_DOUBLE_EQ(testv[i].level, level, PREC); + } + + for (size_t i = 0; i < RE_ARRAY_SIZE(testv4); i++) { + auframe_init(&af, AUFMT_S16LE, testv4[i].sampv, + RE_ARRAY_SIZE(testv4[i].sampv), 48000, 2); + + level = auframe_level(&af); + + ASSERT_DOUBLE_EQ(testv4[i].level, level, PREC); + } + +out: + return err; +} diff --git a/test/auresamp.c b/test/auresamp.c new file mode 100644 index 000000000..4d47cbe2f --- /dev/null +++ b/test/auresamp.c @@ -0,0 +1,86 @@ +/** + * @file auresamp.c Audio-resampler Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_auresamp" +#define DEBUG_LEVEL 5 +#include + + +#define SRATE 44100 +#define CHANNELS_IN 1 +#define CHANNELS_OUT 2 + + +#define SAMPLES 8 + + +/* samples from random.org with atmospheric noise */ +static const int16_t inv[CHANNELS_IN * SAMPLES] = { + 0x513a, + 0x3f11, + 0x4224, + 0x601d, + 0x1dc6, + 0x2fb1, + 0x66ee, + 0x7d53 +}; + +static const int16_t ref_outv[CHANNELS_OUT * SAMPLES] = { + 0x513a, + 0x513a, + 0x3f11, + 0x3f11, + 0x4224, + 0x4224, + 0x601d, + 0x601d, + 0x1dc6, + 0x1dc6, + 0x2fb1, + 0x2fb1, + 0x66ee, + 0x66ee, + 0x7d53, + 0x7d53 +}; + + +int test_auresamp(void) +{ + struct auresamp rs; + int16_t outv[CHANNELS_OUT * SAMPLES]; + size_t outc = RE_ARRAY_SIZE(outv); + int err; + + auresamp_init(&rs); + + err = auresamp_setup(&rs, SRATE, CHANNELS_IN, SRATE, CHANNELS_OUT); + TEST_ERR(err); + + /* resample from mono to stereo */ + err = auresamp(&rs, outv, &outc, inv, RE_ARRAY_SIZE(inv)); + TEST_ERR(err); + + TEST_EQUALS(RE_ARRAY_SIZE(outv), outc); + +#if 0 + re_printf("\nInput samples:\n"); + hexdump(stdout, inv, sizeof(inv)); + re_printf("Output samples:\n"); + hexdump(stdout, outv, sizeof(outv)); +#endif + + TEST_MEMCMP(ref_outv, sizeof(ref_outv), outv, sizeof(outv)); + + out: + return err; +} diff --git a/test/av1.c b/test/av1.c new file mode 100644 index 000000000..bbd90b3c7 --- /dev/null +++ b/test/av1.c @@ -0,0 +1,811 @@ +/** + * @file src/av1.c AV1 testcode + * + * Copyright (C) 2010 - 2022 Alfred E. Heggestad + */ + +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "av1test" +#define DEBUG_LEVEL 5 +#include + + +static int test_leb128(void) +{ + struct mbuf *mb = NULL; + int err = 0; + + static const uint64_t valuev[] = { + + 0, + + /* from random.org */ + 449787982, + 435590144, + 64565769, + 698509268, + 524090268, + + 0x000000ff, /* max 8-bit */ + 0x0000ffff, /* max 16-bit */ + 0xffffffff /* max 32-bit */ + }; + + for (size_t i=0; ipos = 0; + + err = av1_leb128_decode(mb, &val_dec); + ASSERT_EQ(0, err); + + ASSERT_EQ(val, val_dec); + + mb = mem_deref(mb); + } + + out: + mem_deref(mb); + + return err; +} + + +static int test_av1_aggr(void) +{ + static const struct test { + uint8_t byte; + unsigned z; + unsigned y; + unsigned w; + unsigned n; + } testv[] = { + + /* Sample aggregation headers from Chrome 102 */ + {0x28, 0, 0, 2, 1}, + {0x50, 0, 1, 1, 0}, + }; + int err = 0; + + for (size_t i=0; ibyte, + .size = 1, + .pos = 0, + .end = 1 + }; + + err = av1_aggr_hdr_decode(&hdr, &mb); + if (err) + break; + + ASSERT_EQ(test->z, hdr.z); + ASSERT_EQ(test->y, hdr.y); + ASSERT_EQ(test->w, hdr.w); + ASSERT_EQ(test->n, hdr.n); + } + + out: + return err; +} + + +static int test_av1_obu(void) +{ + struct av1_obu_hdr hdr; + static const uint8_t buf[] = { + + /* libaom OBU_TEMPORAL_DELIMITER [type=2 x=0 s=1 size=0] */ + 0x12, 0x00, + + /* libaom OBU_SEQUENCE_HEADER [type=1 x=0 s=1 size=12] */ + 0x0a, 0x0c, 0x00, 0x00, + 0x00, 0x04, 0x3c, 0xff, + 0xbf, 0x81, 0xb5, 0x32, + 0x00, 0x80 + }; + struct mbuf mb = { + .buf = (uint8_t *)buf, + .size = sizeof(buf), + .pos = 0, + .end = sizeof(buf) + }; + int err; + + err = av1_obu_decode(&hdr, &mb); + if (err) + goto out; + + ASSERT_EQ(2, hdr.type); + ASSERT_EQ(0, hdr.x); + ASSERT_EQ(1, hdr.s); + ASSERT_EQ(0, hdr.size); + + err = av1_obu_decode(&hdr, &mb); + if (err) + goto out; + + ASSERT_EQ(1, hdr.type); + ASSERT_EQ(0, hdr.x); + ASSERT_EQ(1, hdr.s); + ASSERT_EQ(12, hdr.size); + + ASSERT_EQ(2, av1_obu_count(buf, sizeof(buf))); + + out: + return err; +} + + +static const uint64_t dummy_ts = 0x0102030405060708ULL; + +struct test { + /* input: */ + size_t pktsize; + + /* output: */ + struct mbuf *mb; + unsigned marker_count; + unsigned new_count; + uint8_t w_saved; +}; + + +static int av1_packet_handler(bool marker, uint64_t rtp_ts, + const uint8_t *hdr, size_t hdr_len, + const uint8_t *pld, size_t pld_len, + void *arg) +{ + struct test *test = arg; + struct mbuf *mb = mbuf_alloc(hdr_len + pld_len); + struct av1_aggr_hdr aggr_hdr; + int err = 0; + + ASSERT_EQ(dummy_ts, rtp_ts); + ASSERT_TRUE((hdr_len + pld_len) <= test->pktsize); + + err = mbuf_write_mem(mb, hdr, hdr_len); + err |= mbuf_write_mem(mb, pld, pld_len); + if (err) + goto out; + + mb->pos = 0; + + err = av1_aggr_hdr_decode(&aggr_hdr, mb); + if (err) + goto out; + + /* XXX: check Z and Y flags */ + + /* Save the first W field */ + if (test->w_saved == 255) + test->w_saved = aggr_hdr.w; + + if (aggr_hdr.n) + ++test->new_count; + + err = mbuf_write_mem(test->mb, mbuf_buf(mb), mbuf_get_left(mb)); + if (err) + goto out; + + if (marker) { + ++test->marker_count; + test->mb->pos = 0; + } + + out: + mem_deref(mb); + return err; +} + + +static int copy_obu(struct mbuf *mb_bs, const uint8_t *buf, size_t size) +{ + struct av1_obu_hdr hdr; + struct mbuf wrap = { + .buf = (uint8_t *)buf, + .size = size, + .pos = 0, + .end = size + }; + bool has_size = true; + + int err = av1_obu_decode(&hdr, &wrap); + if (err) { + DEBUG_WARNING("av1: decode: could not decode OBU" + " [%zu bytes]: %m\n", size, err); + return err; + } + + switch (hdr.type) { + + case AV1_OBU_SEQUENCE_HEADER: + case AV1_OBU_FRAME_HEADER: + case AV1_OBU_METADATA: + case AV1_OBU_FRAME: + case AV1_OBU_REDUNDANT_FRAME_HEADER: + case AV1_OBU_TILE_GROUP: + + err = av1_obu_encode(mb_bs, hdr.type, has_size, + hdr.size, mbuf_buf(&wrap)); + if (err) + return err; + break; + + case AV1_OBU_TEMPORAL_DELIMITER: + case AV1_OBU_TILE_LIST: + case AV1_OBU_PADDING: + /* MUST be ignored by receivers. */ + DEBUG_WARNING("av1: decode: copy: unexpected obu type %u (%s)" + " [x=%d, s=%d, size=%zu]\n", + hdr.type, av1_obu_name(hdr.type), + hdr.x, hdr.s, hdr.size); + return EPROTO; + + default: + DEBUG_WARNING("av1: decode: copy: unknown obu type %u (%s)" + " [x=%d, s=%d, size=%zu]\n", + hdr.type, av1_obu_name(hdr.type), + hdr.x, hdr.s, hdr.size); + return EPROTO; + } + + return 0; +} + + +/* Convert RTP OBUs to AV1 bitstream */ +static int convert_rtp_to_bs(struct mbuf *mb_bs, const uint8_t *buf, + size_t buf_size, uint8_t w) +{ + struct mbuf mb_rtp = { + .buf = (uint8_t *)buf, + .size = buf_size, + .pos = 0, + .end = buf_size + }; + size_t size; + int err; + + /* prepend Temporal Delimiter */ + err = av1_obu_encode(mb_bs, AV1_OBU_TEMPORAL_DELIMITER, true, 0, NULL); + if (err) + return err; + + if (w) { + for (unsigned i=0; i mbuf_get_left(&mb_rtp)) + return EBADMSG; + + size = (size_t)val; + } + + err = copy_obu(mb_bs, mbuf_buf(&mb_rtp), size); + if (err) + return err; + + mbuf_advance(&mb_rtp, size); + } + } + else { + while (mbuf_get_left(&mb_rtp) >= 2) { + + uint64_t val; + + /* each OBU element MUST be preceded by length field */ + err = av1_leb128_decode(&mb_rtp, &val); + if (err) + return err; + + if (val > mbuf_get_left(&mb_rtp)) + return EBADMSG; + + size = (size_t)val; + + err = copy_obu(mb_bs, mbuf_buf(&mb_rtp), size); + if (err) + return err; + + mbuf_advance(&mb_rtp, size); + } + } + + return 0; +} + + +static int test_av1_packetize_base(unsigned count_bs, unsigned count_rtp, + unsigned exp_w_first, size_t pktsize, + const uint8_t *buf, size_t size) +{ + struct test test; + struct mbuf *mb_bs = mbuf_alloc(1024); + bool new_flag = true; + int err; + + if (!mb_bs) + return ENOMEM; + + memset(&test, 0, sizeof(test)); + + ASSERT_EQ(count_bs, av1_obu_count(buf, size)); + ASSERT_EQ(count_rtp, av1_obu_count_rtp(buf, size)); + + test.pktsize = pktsize; + test.w_saved = 255; + + test.mb = mbuf_alloc(1024); + if (!test.mb) { + err = ENOMEM; + goto out; + } + + err = av1_packetize_high(&new_flag, true, dummy_ts, + buf, size, test.pktsize, + av1_packet_handler, &test); + if (err) + goto out; + + ASSERT_EQ(1, test.marker_count); + ASSERT_EQ(1, test.new_count); + ASSERT_EQ(exp_w_first, test.w_saved); + + err = convert_rtp_to_bs(mb_bs, test.mb->buf, test.mb->end, + test.w_saved); + TEST_ERR(err); + + /* compare bitstream with test-vector */ + TEST_MEMCMP(buf, size, mb_bs->buf, mb_bs->end); + + out: + mem_deref(test.mb); + mem_deref(mb_bs); + + return err; +} + + +static const uint8_t pkt_aom[] = { + + /* Temporal Delimiter */ + 0x12, 0x00, + + /* Sequence header */ + 0x0a, 0x0a, + 0x00, 0x00, 0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80, +}; + +static const uint8_t pkt_aom5[] = { + + /* Temporal Delimiter */ + 0x12, 0x00, + + /* Sequence header */ + 0x0a, 0x0a, + 0x00, 0x00, 0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80, + + /* Frame */ + 0x32, 0x17, + 0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb, + 0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36, + + /* Frame */ + 0x32, 0x17, + 0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb, + 0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36, + + /* Frame */ + 0x32, 0x17, + 0x10, 0x01, 0x92, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x57, 0xb6, 0xd3, 0xfb, + 0x3b, 0xe3, 0xe1, 0x31, 0xeb, 0x4f, 0x36, +}; + + +/* + * https://dl8.webmfiles.org/BeachDrone-AV1.webm + * + * frame 3: size=320 pts=134 (0.134000 sec) + * obu: type=2,OBU_TEMPORAL_DELIMITER x=0 s=1 size=0 + * obu: type=3,OBU_FRAME_HEADER x=0 s=1 size=23 + * obu: type=4,OBU_TILE_GROUP x=0 s=1 size=290 + * + */ +static const char pkt_beach[] = + "12001a17301a2049648406a21a47fbdf" + "cbb4180c4002041157404022a202001c" + "64b538c87ccb8807fc1658bcd98ada85" + "6a35745f32824a2ee8d5e11d80476188" + "917a6662c19f0ca9eace86b8ac3ae880" + "0561949ecbbc26f800d904d1714219a1" + "0d1d0410370c6e0b8dead1bf1e8a291b" + "fd0a1254a6e038998e091c7d5233b138" + "68acf6225840618dcbfd948ed99943dd" + "93df6037f6fda997cd2f8467b601d94e" + "09169d57f8fa9c8d6abfcab091366231" + "48c89c7d5a8b86544140a827f48a2b0b" + "15d6836f4ceab733dd2f2ebbb20cb69a" + "684dafb9403610e0560bad66b728c8fd" + "38c315a1f63ac3d2fca0da95fdbfb9f8" + "e61b4f18b90a455dad2fc91a32401007" + "2942753e34c95c6d3693a555e660e6ca" + "628a22fed94f3618d912b84a272e00da" + "44b8cf62a7abfd5d0396e8848d8bd56d" + "195bb21814c15700e825a4d9fe2a64f8" + ; + + +static int test_av1_packetize(void) +{ + uint8_t buf[320]; + int err; + + err = test_av1_packetize_base(2, 1, 1, 1200, pkt_aom, sizeof(pkt_aom)); + if (err) + return err; + + err = test_av1_packetize_base(5, 4, 0, 10, pkt_aom5, sizeof(pkt_aom5)); + if (err) + return err; + + err = str_hex(buf, sizeof(buf), pkt_beach); + TEST_ERR(err); + + err = test_av1_packetize_base(3, 2, 2, 100, buf, sizeof(buf)); + if (err) + return err; + + out: + return err; +} + + +#define AV1_PACKET1_SIZE 1188 +#define AV1_PACKET2_SIZE 231 + + +struct state { + uint8_t buf_packet1[AV1_PACKET1_SIZE]; + uint8_t buf_packet2[AV1_PACKET2_SIZE]; + unsigned count; +}; + + +static int interop_packet_handler(bool marker, uint64_t rtp_ts, + const uint8_t *hdr, size_t hdr_len, + const uint8_t *pld, size_t pld_len, + void *arg) +{ + struct state *state = arg; + struct mbuf *mb = mbuf_alloc(hdr_len + pld_len); + int err = 0; + (void)marker; + (void)rtp_ts; + + if (!mb) + return ENOMEM; + + err = mbuf_write_mem(mb, hdr, hdr_len); + err |= mbuf_write_mem(mb, pld, pld_len); + if (err) + goto out; + + switch (state->count) { + + case 0: + TEST_MEMCMP(state->buf_packet1, sizeof(state->buf_packet1), + mb->buf, mb->end); + break; + + case 1: + TEST_MEMCMP(state->buf_packet2, sizeof(state->buf_packet2), + mb->buf, mb->end); + break; + + default: + err = EPROTO; + break; + } + + out: + state->count = state->count + 1; + + mem_deref(mb); + return err; +} + + +/* + * Test AV1 interop with Chrome. + */ +static int test_av1_interop(void) +{ +#define AV1_FRAME_SIZE 1421 + + static const char frame[] = + + "12000a0a00000024cf7f0d80340132fc" + "0a10717800ffffff16e6180000000b01" + "bbc1318ad86995cba97034ff8767d6fd" + "ade65542dbc40b9e44cc8e479f68b4b9" + "5c7e78cabd7344021a5d99d51918f3e9" + "b6a0afe14686c45b6dc9ff25d4dddd91" + "8a4dcc3998be6af61811000b0d886601" + "53036febf3c7fef1defb75b3ba396cb6" + "f6a8bb84def6603617a995f270102f87" + "a1eccaadac954247c9a116a1343ce905" + "2aa5b90dba23bc7299b85dd829523aa0" + "0a15a2db9e24dc34622ff7c772f4b0dd" + "dff7afdbc95748c68d3ab706ffc4b772" + "f44fd1bcbe20309a908a0dbdba8ce5db" + "1dc9de2d75f48c5976e6fb941b5da795" + "e96c5a8a70b5e55d0d8d8d3b084bb09d" + "d32e83d121087052ee4597b17f1ac46d" + "8b284742c095534146fd6dd6161e67cd" + "58ef8f092a75b32585e7efecd001b4ad" + "292804ce0aa4318fd7b5824497f39f19" + "82174ed1ff800416f565393cfadc7c9e" + "0140a4140ab96ac5d7e4b7891e2ff6d7" + "6c789d81e28645f3873d1ddbb9e3152c" + "4137cc1f13c743fa6454c849e7fe703e" + "1e7ee19e5ea3b728b460a67b009fa952" + "0608ed4dea672a6df720a892f42203c1" + "13cc56903148e249e3b5f5f7266b0cf0" + "539ffdac040ef551b589ee92bd4b081a" + "652af89d56e7546b2b8ea35300324e2a" + "3a74af972642454c5d3ba8caef3fe19c" + "a2a31729113858d8f13fbde793ba7834" + "b6e855f60b4302e42f8c7d32ed48e50d" + "b9a87aa57ec0384293cd7fa4c02f2909" + "b68d4f07afbe22059f52efaaab98d170" + "ba612e8c05a68e048c3f66b7452269f6" + "704346897559ab38dcc4f138e3796217" + "02c0661a8f09ab7d57c1e2bbb3d58899" + "28d2d189f7d33900c7fe606579a77709" + "551254e1d2301f5445857e1d132edc01" + "605128705cb22ff1184e70dc8985169e" + "aafc996f81116ce8007f141f1908eb9c" + "707c415ada0923e42f6e822453b1e330" + "385b377e7f19f1d36a93a404affef91b" + "6587849ef244940c636f3c458986f104" + "174cb6af58160c28c0929aee986da31c" + "1a0596ccecedf2dad9202ade93c4010e" + "b39462aaf111aa53444fdf654e82a454" + "909f97e361026a265c37a0616407589d" + "01bb068ece454ba616612a29d67f61a7" + "2aac84871f0503752525137a3b189c5e" + "34cffb6d600c868eb54125f8861c9bac" + "a580ef457eacd68b8dc30f32aa4cb7cb" + "d3e20ced165b71c0617024f5423ee017" + "3aad3af71a30f33609fcef771c3810b9" + "fb61a350cfa97d6e5f219d593d28f4e4" + "66590f89ad0851149852225eb07a042f" + "9d8fb97f0f2437fb37e3102f6010794b" + "e0ad882519f913c8db117aa093e663dd" + "2183ac731449e62f803ba24086ea28f3" + "814c33bdf9863927b544e1a74ebf6b20" + "64dcb92efd8e8b71aab354601f0e75d7" + "5686fe86984e6735c4ed2eef2b919236" + "4c46a963e88661c5ea8f278fc1efa306" + "67046926a2a75c23a5d63af373478cb8" + "c55e11f9de4a61d77c5b11080fe258e4" + "8509d86aa93249012678d1c40056e9f3" + "44261079a1729a7b7853322b016847f4" + "6ca4cdd0b107c7aa6024889ccd4b4002" + "e2f69b53ed0d0063bf80936fb970bc12" + "0fcabeb82b41b2c75bcb5211b6b5d404" + "cbdcc175adeaad1ebac4e026989e3365" + "d676ff62e674595509f48a43ee2ba010" + "f12f8799e4c357fd369a108aa2f1a073" + "e7a25e0cdb92be13e5267fe9d8d5e6b5" + "31b8cb9f0549ad56e586670133ab39ed" + "7124d942c2742f5e78c52f10c009bb48" + "13b26fb55217f369c33400976663b912" + "c1bd389762be20a040cee498411c47a0" + "4c1e53d7b36c958dbdb56b58ebfc5a88" + "faca07c3739c9bf28bfb8d7cd50f1fc5" + "82d54aee4a17073b0552d989e51d6501" + "35bcca12fc5f4c92924912d7a5a91b82" + "edb8c0fda7e43526658c4ddd15a0d3e4" + "d24a996aa902f9e51b43e67974fd59ed" + "3ea2a6ede7ea3033d8d6f2d2dc624204" + "558433c6a0a7315e970bba563c0dcb15" + "879b64ff57418984b998bd4c70f33c95" + "29d1184ad74cbcf14927771f562ae036" + "fac2e439966307e5d9ae4d5984" + ; + + + static const char packet1[] = + + /* NOTE: W=2 */ + "68" + + "0b0800000024cf7f0d80340130107178" + "00ffffff16e6180000000b01bbc1318a" + "d86995cba97034ff8767d6fdade65542" + "dbc40b9e44cc8e479f68b4b95c7e78ca" + "bd7344021a5d99d51918f3e9b6a0afe1" + "4686c45b6dc9ff25d4dddd918a4dcc39" + "98be6af61811000b0d88660153036feb" + "f3c7fef1defb75b3ba396cb6f6a8bb84" + "def6603617a995f270102f87a1eccaad" + "ac954247c9a116a1343ce9052aa5b90d" + "ba23bc7299b85dd829523aa00a15a2db" + "9e24dc34622ff7c772f4b0dddff7afdb" + "c95748c68d3ab706ffc4b772f44fd1bc" + "be20309a908a0dbdba8ce5db1dc9de2d" + "75f48c5976e6fb941b5da795e96c5a8a" + "70b5e55d0d8d8d3b084bb09dd32e83d1" + "21087052ee4597b17f1ac46d8b284742" + "c095534146fd6dd6161e67cd58ef8f09" + "2a75b32585e7efecd001b4ad292804ce" + "0aa4318fd7b5824497f39f1982174ed1" + "ff800416f565393cfadc7c9e0140a414" + "0ab96ac5d7e4b7891e2ff6d76c789d81" + "e28645f3873d1ddbb9e3152c4137cc1f" + "13c743fa6454c849e7fe703e1e7ee19e" + "5ea3b728b460a67b009fa9520608ed4d" + "ea672a6df720a892f42203c113cc5690" + "3148e249e3b5f5f7266b0cf0539ffdac" + "040ef551b589ee92bd4b081a652af89d" + "56e7546b2b8ea35300324e2a3a74af97" + "2642454c5d3ba8caef3fe19ca2a31729" + "113858d8f13fbde793ba7834b6e855f6" + "0b4302e42f8c7d32ed48e50db9a87aa5" + "7ec0384293cd7fa4c02f2909b68d4f07" + "afbe22059f52efaaab98d170ba612e8c" + "05a68e048c3f66b7452269f670434689" + "7559ab38dcc4f138e379621702c0661a" + "8f09ab7d57c1e2bbb3d5889928d2d189" + "f7d33900c7fe606579a77709551254e1" + "d2301f5445857e1d132edc0160512870" + "5cb22ff1184e70dc8985169eaafc996f" + "81116ce8007f141f1908eb9c707c415a" + "da0923e42f6e822453b1e330385b377e" + "7f19f1d36a93a404affef91b6587849e" + "f244940c636f3c458986f104174cb6af" + "58160c28c0929aee986da31c1a0596cc" + "ecedf2dad9202ade93c4010eb39462aa" + "f111aa53444fdf654e82a454909f97e3" + "61026a265c37a0616407589d01bb068e" + "ce454ba616612a29d67f61a72aac8487" + "1f0503752525137a3b189c5e34cffb6d" + "600c868eb54125f8861c9baca580ef45" + "7eacd68b8dc30f32aa4cb7cbd3e20ced" + "165b71c0617024f5423ee0173aad3af7" + "1a30f33609fcef771c3810b9fb61a350" + "cfa97d6e5f219d593d28f4e466590f89" + "ad0851149852225eb07a042f9d8fb97f" + "0f2437fb37e3102f6010794be0ad8825" + "19f913c8db117aa093e663dd2183ac73" + "1449e62f803ba24086ea28f3814c33bd" + "f9863927b544e1a74ebf6b2064dcb92e" + "fd8e8b71aab354601f0e75d75686fe86" + "984e6735c4ed2eef2b9192364c46a963" + "e88661c5ea8f278fc1efa30667046926" + "a2a75c23a5d63af373478cb8c55e11f9" + "de4a61d77c5b11080fe258e48509d86a" + "a93249012678d1c40056e9f344261079" + "a1729a7b7853322b016847f46ca4cdd0" + "b107c7aa6024889ccd4b4002e2f69b53" + "ed0d0063bf80936fb970bc120fcabeb8" + "2b41b2c75bcb5211b6b5d404cbdcc175" + "adeaad1ebac4e026989e3365d676ff62" + "e674595509f48a43ee2ba010f12f8799" + "e4c357fd369a108aa2f1a073e7a25e0c" + "db92be13e5267fe9d8d5e6b531b8cb9f" + "0549ad" + ; + + + static const char packet2[] = + + /* NOTE: W=1 */ + + "90" + + "56e586670133ab39ed7124d942c2742f" + "5e78c52f10c009bb4813b26fb55217f3" + "69c33400976663b912c1bd389762be20" + "a040cee498411c47a04c1e53d7b36c95" + "8dbdb56b58ebfc5a88faca07c3739c9b" + "f28bfb8d7cd50f1fc582d54aee4a1707" + "3b0552d989e51d650135bcca12fc5f4c" + "92924912d7a5a91b82edb8c0fda7e435" + "26658c4ddd15a0d3e4d24a996aa902f9" + "e51b43e67974fd59ed3ea2a6ede7ea30" + "33d8d6f2d2dc624204558433c6a0a731" + "5e970bba563c0dcb15879b64ff574189" + "84b998bd4c70f33c9529d1184ad74cbc" + "f14927771f562ae036fac2e439966307" + "e5d9ae4d5984" + ; + + struct state state; + uint8_t buf[AV1_FRAME_SIZE]; + bool new_flag = true; + int err; + + state.count = 0; + + err = str_hex(buf, sizeof(buf), frame); + TEST_ERR(err); + + err = str_hex(state.buf_packet1, sizeof(state.buf_packet1), packet1); + TEST_ERR(err); + + err = str_hex(state.buf_packet2, sizeof(state.buf_packet2), packet2); + TEST_ERR(err); + + err = av1_packetize_high(&new_flag, true, dummy_ts, + buf, sizeof(buf), 1188, + interop_packet_handler, &state); + if (err) + goto out; + + out: + return err; +} + + +int test_av1(void) +{ + int err; + + err = test_leb128(); + if (err) + return err; + + err = test_av1_aggr(); + if (err) + return err; + + err = test_av1_obu(); + if (err) + return err; + + err = test_av1_packetize(); + if (err) + return err; + + err = test_av1_interop(); + if (err) + return err; + + return err; +} diff --git a/test/base64.c b/test/base64.c new file mode 100644 index 000000000..8bf023f5d --- /dev/null +++ b/test/base64.c @@ -0,0 +1,146 @@ +/** + * @file base64.c Base64 Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_base64" +#define DEBUG_LEVEL 5 +#include + + +int test_base64(void) +{ + const struct { + struct pl pl; + struct pl b64; + struct pl b64url; + } testv[] = { + {PL(""), PL(""), PL("")}, + {PL("f"), PL("Zg=="), PL("Zg")}, + {PL("fo"), PL("Zm8="), PL("Zm8")}, + {PL("foo"), PL("Zm9v"), PL("Zm9v")}, + {PL("foob"), PL("Zm9vYg=="), PL("Zm9vYg")}, + {PL("fooba"), PL("Zm9vYmE="), PL("Zm9vYmE")}, + {PL("foobar"), PL("Zm9vYmFy"), PL("Zm9vYmFy")}, + {PL("\xff\x01\xfe\x02"), PL("/wH+Ag=="), PL("_wH-Ag")}, + + {PL("asdlkjqopinzidfj84r77fsgljsdf9823r"), + PL("YXNkbGtqcW9waW56aWRmajg0cjc3ZnNnbGpzZGY5ODIzcg=="), + PL("YXNkbGtqcW9waW56aWRmajg0cjc3ZnNnbGpzZGY5ODIzcg")}, + {PL("918nvbakishdl8317237dlakskdkaldj"), + PL("OTE4bnZiYWtpc2hkbDgzMTcyMzdkbGFrc2tka2FsZGo="), + PL("OTE4bnZiYWtpc2hkbDgzMTcyMzdkbGFrc2tka2FsZGo")}, + {PL("very10long..testxyzstring/.,-=-3029===7823#'];'#';]#'"), + PL("dmVyeTEwbG9uZy4udGVzdHh5enN0cmluZy8uLC0" + "9LTMwMjk9PT03ODIzIyddOycjJztdIyc="), + PL("dmVyeTEwbG9uZy4udGVzdHh5enN0cmluZy8uLC0" + "9LTMwMjk9PT03ODIzIyddOycjJztdIyc")}, + }; + uint32_t i; + int err = 0; + uint8_t b64_buf[128]; + size_t olen; + + for (i=0; ip, pl->l, buf, &olen); + TEST_ERR(err); + + if (olen != testv[i].b64.l) { + DEBUG_WARNING("b64_encode %u failed: l=%u olen=%u\n", + i, testv[i].b64.l, olen); + err = EINVAL; + TEST_ERR(err); + } + if (0 != memcmp(testv[i].b64.p, buf, olen)) { + DEBUG_WARNING("b64_encode %u failed: ref=%r, enc=%b\n", + i, &testv[i].b64, buf, olen); + err = EINVAL; + TEST_ERR(err); + } + + /* Encode URL */ + olen = sizeof(buf); + err = base64url_encode((uint8_t *)pl->p, pl->l, buf, &olen); + TEST_ERR(err); + + if (olen != testv[i].b64url.l) { + DEBUG_WARNING("b64_encode %u failed: l=%u olen=%u\n", + i, testv[i].b64url.l, olen); + err = EINVAL; + TEST_ERR(err); + } + if (0 != memcmp(testv[i].b64url.p, buf, olen)) { + DEBUG_WARNING("b64_encode %u failed: ref=%r, enc=%b\n", + i, &testv[i].b64url, buf, olen); + err = EINVAL; + TEST_ERR(err); + } + + /* Decode */ + b = &testv[i].b64; + olen = sizeof(b64_buf); + err = base64_decode(b->p, b->l, b64_buf, &olen); + TEST_ERR(err); + + if (olen != testv[i].pl.l) { + DEBUG_WARNING("b64_decode %u failed: l=%u olen=%u\n", + i, testv[i].pl.l, olen); + err = EINVAL; + TEST_ERR(err); + } + if (0 != memcmp(testv[i].pl.p, b64_buf, olen)) { + DEBUG_WARNING("b64_decode %u failed: ref=%r, enc=%b\n", + i, &testv[i].pl, b64_buf, olen); + err = EINVAL; + TEST_ERR(err); + } + + /* Decode Url */ + b = &testv[i].b64url; + olen = sizeof(b64_buf); + err = base64_decode(b->p, b->l, b64_buf, &olen); + TEST_ERR(err); + + if (olen != testv[i].pl.l) { + DEBUG_WARNING( + "b64_decode url %u failed: l=%u olen=%u\n", i, + testv[i].pl.l, olen); + err = EINVAL; + TEST_ERR(err); + } + if (0 != memcmp(testv[i].pl.p, b64_buf, olen)) { + DEBUG_WARNING( + "b64_decode url %u failed: ref=%r, enc=%b\n", + i, &testv[i].pl, b64_buf, olen); + err = EINVAL; + TEST_ERR(err); + } + } + + /* Invalid checks */ + char c = 'A'; + olen = sizeof(b64_buf); + err = base64_decode(&c, sizeof(c), b64_buf, &olen); + TEST_ERR(err); + + struct pl inv; + pl_set_str(&inv, "Zm8="); + olen = 1; + err = base64_decode(inv.p, inv.l, b64_buf, &olen); + TEST_EQUALS(EOVERFLOW, err); + + err = 0; +out: + return err; +} diff --git a/test/bfcp.c b/test/bfcp.c new file mode 100644 index 000000000..90103df90 --- /dev/null +++ b/test/bfcp.c @@ -0,0 +1,467 @@ +/** + * @file bfcp.c BFCP Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "bfcptest" +#define DEBUG_LEVEL 5 +#include + + +static const uint8_t bfcp_msg[] = + + /* FloorRequest */ + "\x20\x01\x00\x04" /* | ver | primitive | length | */ + "\x01\x02\x03\x04" /* | conference id | */ + "\xfe\xdc\xba\x98" /* | transaction id | user id | */ + "" + "\x04\x04\x00\x01" /* FLOOR-ID */ + "\x02\x04\x00\x02" /* BENEFICIARY-ID */ + "\x10\x03\x58\x00" /* PARTICIPANT-PROVIDED-INFO */ + "\x08\x04\x40\x00" /* PRIORITY */ + + /* FloorRelease */ + "\x20\x02\x00\x01" /* | ver | primitive | length | */ + "\x01\x02\x03\x04" /* | conference id | */ + "\xfe\xdc\xba\x98" /* | transaction id | user id | */ + "" + "\x06\x04\x00\x03" /* FLOOR-REQUEST-ID */ + + /* UserStatus w/FLOOR-REQUEST-INFORMATION */ + "\x20\x06\x00\x12" /* | ver | primitive | length | */ + "\x01\x02\x03\x04" /* | conference id | */ + "\xfe\xdc\xba\x98" /* | transaction id | user id | */ + "" + "\x1e\x48\x88\x99" /* FLOOR-ID */ + "\x24\x0c\x74\xad" /* OVERALL-REQUEST-STATUS */ + "\x0a\x04\x04\x02" + "\x12\x04\x4f\x4b" + "\x22\x0c\x00\x02" /* FLOOR-REQUEST-STATUS #1 */ + "\x0a\x04\x02\x02" + "\x12\x04\x6f\x6b" + "\x22\x0c\x00\x04" /* FLOOR-REQUEST-STATUS #2 */ + "\x0a\x04\x07\x03" + "\x12\x04\x6a\x61" + "\x1c\x0c\x00\x01" /* BENEFICIARY-INFORMATION */ + "\x18\x03\x61\x00" + "\x1a\x03\x62\x00" + "\x20\x0c\x00\x02" /* REQUESTED-BY-INFORMATION */ + "\x18\x03\x63\x00" + "\x1a\x03\x64\x00" + "\x08\x04\x40\x00" /* PRIORITY */ + "\x10\x03\x78\x00" /* PARTICIPANT-PROVIDED-INFO */ + + /* Hello */ + "\x20\x0b\x00\x00" /* | ver | primitive | length | */ + "\x01\x02\x03\x04" /* | conference id | */ + "\xfe\xdc\xba\x98" /* | transaction id | user id | */ + "" + + ""; + + +static int parse_msg(const uint8_t *p, size_t n) +{ + struct mbuf *mb = mbuf_alloc(512); + int err; + if (!mb) + return ENOMEM; + + err = mbuf_write_mem(mb, p, n); + if (err) + return err; + + mb->pos = 0; + + while (mbuf_get_left(mb) >= 4) { + struct bfcp_msg *msg; + + err = bfcp_msg_decode(&msg, mb); + if (err) + break; + + mem_deref(msg); + } + + mem_deref(mb); + return err; +} + + +int test_bfcp(void) +{ + const size_t sz = sizeof(bfcp_msg) - 1; + struct mbuf *mb; + struct bfcp_reqstatus oreqstatus, reqstatus1, reqstatus2; + uint16_t floorid = 1, bfid = 2, frid = 3, freqid; + uint16_t ofreqid, floorid1, floorid2, rbid; + enum bfcp_priority prio = BFCP_PRIO_NORMAL; + int n, err = 0; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = bfcp_msg_encode(mb, 1, false, BFCP_FLOOR_REQUEST, + 0x01020304, 0xfedc, 0xba98, 4, + BFCP_FLOOR_ID, 0, &floorid, + BFCP_BENEFICIARY_ID, 0, &bfid, + BFCP_PART_PROV_INFO, 0, "X", + BFCP_PRIORITY, 0, &prio); + if (err) + goto out; + + err = bfcp_msg_encode(mb, 1, false, BFCP_FLOOR_RELEASE, + 0x01020304, 0xfedc, 0xba98, 1, + BFCP_FLOOR_REQUEST_ID, 0, &frid); + if (err) + goto out; + + freqid = 0x8899; + + ofreqid = 0x74ad; + oreqstatus.status = BFCP_DENIED; + oreqstatus.qpos = 2; + + floorid1 = 2; + reqstatus1.status = BFCP_ACCEPTED; + reqstatus1.qpos = 2; + + floorid2 = 4; + reqstatus2.status = BFCP_REVOKED; + reqstatus2.qpos = 3; + + bfid = 1; + rbid = 2; + prio = BFCP_PRIO_NORMAL; + + err = bfcp_msg_encode(mb, 1, false, BFCP_USER_STATUS, + 0x01020304, 0xfedc, 0xba98, 1, + + BFCP_FLOOR_REQ_INFO, 7, &freqid, + + BFCP_OVERALL_REQ_STATUS, 2, &ofreqid, + BFCP_REQUEST_STATUS, 0, &oreqstatus, + BFCP_STATUS_INFO, 0, "OK", + + BFCP_FLOOR_REQ_STATUS, 2, &floorid1, + BFCP_REQUEST_STATUS, 0, &reqstatus1, + BFCP_STATUS_INFO, 0, "ok", + + BFCP_FLOOR_REQ_STATUS, 2, &floorid2, + BFCP_REQUEST_STATUS, 0, &reqstatus2, + BFCP_STATUS_INFO, 0, "ja", + + BFCP_BENEFICIARY_INFO, 2, &bfid, + BFCP_USER_DISP_NAME, 0, "a", + BFCP_USER_URI, 0, "b", + + BFCP_REQUESTED_BY_INFO, 2, &rbid, + BFCP_USER_DISP_NAME, 0, "c", + BFCP_USER_URI, 0, "d", + + BFCP_PRIORITY, 0, &prio, + + BFCP_PART_PROV_INFO, 0, "x"); + if (err) + goto out; + + err = bfcp_msg_encode(mb, 1, false, BFCP_HELLO, + 0x01020304, 0xfedc, 0xba98, 0); + if (err) + goto out; + + if (mb->end != sz) { + DEBUG_WARNING("expected %u bytes, got %u bytes\n", + sz, mb->end); + + (void)re_printf("\nEncoded message:\n"); + hexdump(stderr, mb->buf, mb->end); + + err = EPROTO; + goto out; + } + if (!err) { + n = memcmp(mb->buf, bfcp_msg, mb->end); + if (0 != n) { + err = EBADMSG; + DEBUG_WARNING("error offset: %d\n", n); + } + } + + if (err) { + DEBUG_WARNING("BFCP encode error: %m\n", err); + + (void)re_printf("\nReference message:\n"); + hexdump(stderr, bfcp_msg, sz); + + (void)re_printf("\nEncoded message:\n"); + hexdump(stderr, mb->buf, mb->end); + goto out; + } + + out: + mem_deref(mb); + return err; +} + + +int test_bfcp_bin(void) +{ + static const uint8_t msg[] = + + "\x20\x04\x00\x04" + "\x00\x00\x00\x01" + "\x00\x01\x00\x01" + + "\x1e\x10\x00\x01" + "\x24\x08\x00\x01" + "\x0a\x04\x03\x00" + "\x22\x04\x00\x02" + + ""; + int err = 0; + + err |= parse_msg(msg, sizeof(msg) - 1); + err |= parse_msg(bfcp_msg, sizeof(bfcp_msg) - 1); + + return err; +} + + +enum handler_flags { + conn_handler_called = 1u, + estab_handler_called = 1u << 1u, + recv_handler_called = 1u << 2u, + resp_handler_called = 1u << 3u, + close_handler_called = 1u << 4u +}; + + +struct test_bfcp_peer { + struct bfcp_conn *bfcp; + enum bfcp_transp transp; + unsigned int flags; + struct sa addr, peer; + int handler_err; + bool client; +}; + + +static void test_bfcp_peer_destructor(void *arg) +{ + struct test_bfcp_peer *p = (struct test_bfcp_peer *)arg; + mem_deref(p->bfcp); +} + + +static void receive_handler(const struct bfcp_msg *msg, void *arg) +{ + struct test_bfcp_peer *p = (struct test_bfcp_peer *)arg; + p->flags |= recv_handler_called; + + DEBUG_INFO("Receive handler called, client: %d\n", (int)p->client); + + p->handler_err = bfcp_reply(p->bfcp, msg, BFCP_HELLO_ACK, 0u); +} + + +static void response_handler(int err, const struct bfcp_msg *msg, void *arg) +{ + struct test_bfcp_peer *p = (struct test_bfcp_peer *)arg; + (void)err; + (void)msg; + + p->flags |= resp_handler_called; + + DEBUG_INFO("Response handler called, client: %d\n", (int)p->client); + + re_cancel(); +} + + +static void close_handler(int err, void *arg) +{ + struct test_bfcp_peer *p = (struct test_bfcp_peer *)arg; + (void)err; + + p->flags |= close_handler_called; + + DEBUG_INFO("Close handler called, client: %d\n", (int)p->client); +} + + +static void established_handler(void *arg) +{ + struct test_bfcp_peer *p = (struct test_bfcp_peer *)arg; + p->flags |= estab_handler_called; + + DEBUG_INFO("Established handler called, client: %d\n", (int)p->client); + + if (p->transp == BFCP_TCP && p->client) { + p->handler_err = bfcp_request(p->bfcp, &p->peer, BFCP_VER2, + BFCP_HELLO, 0u, 0u, &response_handler, p, 0u); + } +} + + +static void connection_handler(const struct sa *peer, void *arg) +{ + struct test_bfcp_peer *p = (struct test_bfcp_peer *)arg; + (void)peer; + + p->flags |= conn_handler_called; + + DEBUG_INFO("New connection handler called, client: %d\n", + (int)p->client); + + if (p->transp == BFCP_TCP && !p->client) { + if (!bfcp_sock(p->bfcp)) { + p->handler_err = bfcp_accept(p->bfcp); + } + else { + bfcp_reject(p->bfcp); + p->handler_err = EALREADY; + } + } + else { + p->handler_err = ENOSYS; + } +} + + +int test_bfcp_udp(void) +{ + struct test_bfcp_peer *cli = NULL, *srv = NULL; + int err = 0; + + if (test_mode == TEST_MEMORY) { + /* OOM testing fails because some mem_alloc fails on + * the receiving side, and no packet gets received. + * For UDP, this is not registered as a connection timeout. */ + err = ESKIPPED; + goto out; + } + + cli = (struct test_bfcp_peer *)mem_zalloc(sizeof(*cli), + &test_bfcp_peer_destructor); + if (!cli) { + err = ENOMEM; + goto out; + } + cli->transp = BFCP_UDP; + cli->client = true; + + srv = (struct test_bfcp_peer *)mem_zalloc(sizeof(*srv), + &test_bfcp_peer_destructor); + if (!srv) { + err = ENOMEM; + goto out; + } + srv->transp = BFCP_UDP; + + err = sa_set_str(&cli->addr, "127.0.0.1", 0); + TEST_ERR(err); + srv->addr = cli->addr; + + err = bfcp_listen(&srv->bfcp, BFCP_UDP, &srv->addr, NULL, NULL, + NULL, &receive_handler, NULL, srv); + TEST_ERR(err); + cli->peer = srv->addr; + + err = bfcp_connect(&cli->bfcp, BFCP_UDP, &cli->addr, &cli->peer, + NULL, &receive_handler, NULL, cli); + TEST_ERR(err); + srv->peer = cli->addr; + + err = bfcp_request(cli->bfcp, &cli->peer, BFCP_VER1, BFCP_HELLO, 0u, + 0u, &response_handler, cli, 0u); + TEST_ERR(err); + + err = re_main_timeout(100); + TEST_ERR(err); + + TEST_EQUALS(resp_handler_called, cli->flags); + TEST_EQUALS(recv_handler_called, srv->flags); + + err = srv->handler_err; + +out: + mem_deref(cli); + mem_deref(srv); + + return err; +} + + +int test_bfcp_tcp(void) +{ + struct test_bfcp_peer *cli = NULL, *srv = NULL; + int err = 0; + + cli = (struct test_bfcp_peer *)mem_zalloc(sizeof(*cli), + &test_bfcp_peer_destructor); + if (!cli) { + err = ENOMEM; + goto out; + } + cli->transp = BFCP_TCP; + cli->client = true; + + srv = (struct test_bfcp_peer *)mem_zalloc(sizeof(*srv), + &test_bfcp_peer_destructor); + if (!srv) { + err = ENOMEM; + goto out; + } + srv->transp = BFCP_TCP; + + err = sa_set_str(&cli->addr, "127.0.0.1", 0); + TEST_ERR(err); + srv->addr = cli->addr; + + err = bfcp_listen(&srv->bfcp, BFCP_TCP, &srv->addr, NULL, + &connection_handler, &established_handler, &receive_handler, + &close_handler, srv); + TEST_ERR(err); + cli->peer = srv->addr; + + err = bfcp_connect(&cli->bfcp, BFCP_TCP, &cli->addr, &cli->peer, + &established_handler, &receive_handler, &close_handler, cli); + TEST_ERR(err); + srv->peer = cli->addr; + + err = re_main_timeout(100); + TEST_ERR(err); + + if (cli->handler_err) { + DEBUG_WARNING("client error: %m\n", cli->handler_err); + err = cli->handler_err; + goto out; + } + + if (srv->handler_err) { + DEBUG_WARNING("server error: %m\n", srv->handler_err); + err = srv->handler_err; + goto out; + } + + TEST_EQUALS((estab_handler_called | resp_handler_called), cli->flags); + TEST_EQUALS((conn_handler_called | estab_handler_called | + recv_handler_called), srv->flags); + + err = srv->handler_err; + +out: + mem_deref(cli); + mem_deref(srv); + + return err; +} diff --git a/test/combo/dtls_turn.c b/test/combo/dtls_turn.c new file mode 100644 index 000000000..385c082d3 --- /dev/null +++ b/test/combo/dtls_turn.c @@ -0,0 +1,439 @@ +/** + * @file combo/dtls_turn.c DTLS over TURN combination test + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "dtls_turn" +#define DEBUG_LEVEL 5 +#include + + +/* + * Combined test of DTLS over TURN, involving two agents. + * + * Agent A: Agent B: + * ------- ------- + * + * + * DTLS DTLS + * Client Server + * | | + * | | + * | .................UDP + * TURN TURN + * Client Server + * | | + * | | + * UDP............UDP + */ + + +enum { + LAYER_DTLS = 100, + LAYER_TURN = -100 +}; + + +struct agent { + + /* DTLS layer: */ + struct tls *tls; + struct dtls_sock *dtls_sock; + struct tls_conn *dtls_conn; + bool dtls_active; + unsigned dtls_n_conn; + unsigned dtls_n_estab; + unsigned dtls_n_recv; + + /* TURN layer: */ + bool use_turn; + bool turn_channels; + struct turnc *turnc; + struct turnserver *turnsrv; + unsigned turn_n_alloc_resp; + unsigned turn_n_perm_resp; + unsigned turn_n_chan_resp; + + /* common stuff: */ + struct agent *peer; + struct udp_sock *us; + struct sa addr; + unsigned udp_n_recv; + int err; +}; + + +static int agent_start(struct agent *ag); + + +static void complete_test(struct agent *ag, int err) +{ + ag->err = err; + re_cancel(); +} + + +static bool are_established(struct agent *ag) +{ + return ag->dtls_n_estab && ag->peer->dtls_n_estab; +} + + +static void dtls_estab_handler(void *arg) +{ + struct agent *ag = arg; + + ++ag->dtls_n_estab; + + if (are_established(ag)) { + re_cancel(); + } +} + + +static void dtls_recv_handler(struct mbuf *mb, void *arg) +{ + struct agent *ag = arg; + int err; + + ++ag->dtls_n_recv; + + if (!ag->dtls_active) { + + /* ECHO SERVER */ + err = dtls_send(ag->dtls_conn, mb); + if (err) { + complete_test(ag, err); + } + } +} + + +static void dtls_close_handler(int err, void *arg) +{ + struct agent *ag = arg; + (void)err; + + ag->dtls_conn = mem_deref(ag->dtls_conn); +} + + +static void dtls_conn_handler(const struct sa *src, void *arg) +{ + struct agent *ag = arg; + int err; + (void)src; + + TEST_ASSERT(!ag->dtls_active); + + ++ag->dtls_n_conn; + + TEST_ASSERT(ag->dtls_conn == NULL); + + err = dtls_accept(&ag->dtls_conn, ag->tls, ag->dtls_sock, + dtls_estab_handler, dtls_recv_handler, + dtls_close_handler, ag); + if (err) + goto out; + + out: + if (err) + complete_test(ag, err); +} + + +static void turnc_perm_handler(void *arg) +{ + struct agent *ag = arg; + + ++ag->turn_n_perm_resp; + + /* Permission has been granted, we can start DTLS */ + agent_start(ag); +} + + +static void turnc_chan_handler(void *arg) +{ + struct agent *ag = arg; + + ++ag->turn_n_chan_resp; + + /* Channel has been created, we can start DTLS */ + agent_start(ag); +} + + +static bool is_turn_ready(struct agent *ag) +{ + if (ag->use_turn) + return ag->turn_n_alloc_resp; + else + return true; +} + + +static bool are_turn_ready(struct agent *ag) +{ + return is_turn_ready(ag) && is_turn_ready(ag->peer); +} + + +static int agent_permchan(struct agent *ag) +{ + int err; + + /* Channels or Permission is needed for sending data */ + if (ag->turn_channels) { + err = turnc_add_chan(ag->turnc, &ag->peer->addr, + turnc_chan_handler, ag); + } + else { + err = turnc_add_perm(ag->turnc, &ag->peer->addr, + turnc_perm_handler, ag); + } + + return err; +} + + +static void turnc_handler(int err, uint16_t scode, const char *reason, + const struct sa *relay_addr, + const struct sa *mapped_addr, + const struct stun_msg *msg, + void *arg) +{ + struct agent *ag = arg; + + (void)reason; + (void)mapped_addr; + (void)msg; + + ++ag->turn_n_alloc_resp; + + if (err || scode) { + complete_test(ag, err ? err : EPROTO); + return; + } + + /* Public address must be updated */ + ag->addr = *relay_addr; + + if (are_turn_ready(ag)) { + + agent_permchan(ag); + agent_permchan(ag->peer); + } +} + + +/* in this test we expect no UDP packets */ +static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct agent *ag = arg; + (void)src; + (void)mb; + + ++ag->udp_n_recv; +} + + +static void destructor(void *arg) +{ + struct agent *ag = arg; + + mem_deref(ag->dtls_conn); + mem_deref(ag->dtls_sock); + mem_deref(ag->tls); + + mem_deref(ag->turnc); + mem_deref(ag->turnsrv); + + mem_deref(ag->us); +} + + +static int agent_alloc(struct agent **agp, uint16_t lport, + bool use_turn, bool turn_channels, bool dtls_active) +{ + struct agent *ag; + int err; + + ag = mem_zalloc(sizeof(*ag), destructor); + if (!ag) + return ENOMEM; + + /* allocate common */ + err = sa_set_str(&ag->addr, "127.0.0.1", lport); + if (err) + goto out; + + err = udp_listen(&ag->us, &ag->addr, udp_recv, ag); + if (err) + goto out; + + err = udp_local_get(ag->us, &ag->addr); + if (err) + goto out; + + /* allocate TURN */ + ag->use_turn = use_turn; + if (use_turn) { + ag->turn_channels = turn_channels; + + err = turnserver_alloc(&ag->turnsrv); + if (err) + goto out; + + err = turnc_alloc(&ag->turnc, NULL, IPPROTO_UDP, ag->us, + LAYER_TURN, &ag->turnsrv->laddr, + "username", "password", 600, + turnc_handler, ag); + if (err) + goto out; + } + + /* allocate DTLS */ + ag->dtls_active = dtls_active; + + err = tls_alloc(&ag->tls, TLS_METHOD_DTLSV1, NULL, NULL); + if (err) + goto out; + + err = tls_set_certificate(ag->tls, test_certificate_ecdsa, + strlen(test_certificate_ecdsa)); + if (err) + goto out; + + err = dtls_listen(&ag->dtls_sock, NULL, ag->us, 4, LAYER_DTLS, + dtls_conn_handler, ag); + if (err) + goto out; + + out: + if (err) + mem_deref(ag); + else if (agp) + *agp = ag; + + return err; +} + + +static int agent_start(struct agent *ag) +{ + int err = 0; + + if (ag->dtls_active) { + + TEST_ASSERT(ag->dtls_conn == NULL); + + err = dtls_connect(&ag->dtls_conn, ag->tls, ag->dtls_sock, + &ag->peer->addr, dtls_estab_handler, + dtls_recv_handler, + dtls_close_handler, ag); + if (err) + return err; + } + + out: + return err; +} + + +static int agent_verify(struct agent *ag) +{ + int err = 0; + + /* common stuff */ + TEST_EQUALS(0, ag->err); + TEST_EQUALS(0, ag->udp_n_recv); + + /* TURN */ + if (ag->use_turn) { + + TEST_EQUALS(1, ag->turn_n_alloc_resp); + TEST_EQUALS(ag->turn_channels ? 0 : 1, ag->turn_n_perm_resp); + TEST_EQUALS(ag->turn_channels ? 1u : 0, ag->turn_n_chan_resp); + + TEST_ASSERT(ag->turnsrv->n_allocate >= 1); + if (ag->turn_channels) { + TEST_ASSERT(ag->turnsrv->n_chanbind >= 1); + TEST_ASSERT(ag->turnsrv->n_createperm == 0); + TEST_EQUALS(0, ag->turnsrv->n_send); + TEST_ASSERT(ag->turnsrv->n_raw >= 2); + } + else { + TEST_ASSERT(ag->turnsrv->n_chanbind == 0); + TEST_ASSERT(ag->turnsrv->n_createperm >= 1); + TEST_EQUALS(2, ag->turnsrv->n_send); + TEST_EQUALS(0, ag->turnsrv->n_raw); + } + } + + /* DTLS */ + TEST_ASSERT(ag->dtls_conn != NULL); + TEST_EQUALS(ag->dtls_active ? 0 : 1, ag->dtls_n_conn); + TEST_EQUALS(1, ag->dtls_n_estab); + TEST_EQUALS(0, ag->dtls_n_recv); + + out: + return err; +} + + +static bool have_dtls_support(enum tls_method method) +{ + struct tls *tls = NULL; + int err; + + err = tls_alloc(&tls, method, NULL, NULL); + + mem_deref(tls); + + return err != ENOSYS; +} + + +int test_dtls_turn(void) +{ + struct agent *a=0, *b=0; + int err = 0; + + if (!have_dtls_support(TLS_METHOD_DTLSV1)) { + re_fprintf(stderr, "skip DTLS/TURN test\n"); + return ESKIPPED; + } + + err |= agent_alloc(&a, 0, true, true, true); + err |= agent_alloc(&b, 0, false, false, false); + if (err) + goto out; + + /* connect the 2 agents */ + a->peer = b; b->peer = a; + + /* start it! */ + err = re_main_timeout(1000); + if (err) + goto out; + + TEST_EQUALS(0, a->err); + TEST_EQUALS(0, b->err); + + /* verify results after test is complete */ + err |= agent_verify(a); + err |= agent_verify(b); + if (err) + goto out; + + out: + mem_deref(b); + mem_deref(a); + return err; +} diff --git a/test/conf.c b/test/conf.c new file mode 100644 index 000000000..6ab431116 --- /dev/null +++ b/test/conf.c @@ -0,0 +1,47 @@ +/** + * @file conf.c Testcode for Configuration module + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +int test_conf(void) +{ + static const char *cfg = + "string_val\trattarei\n" + "u32_val 42\n"; + char str[256]; + struct conf *conf; + struct pl pl; + uint32_t u32; + int err; + + err = conf_alloc_buf(&conf, (uint8_t *)cfg, strlen(cfg)); + if (err) + return err; + + err = conf_get_str(conf, "string_val", str, sizeof(str)); + if (err) + goto out; + if (strcmp(str, "rattarei")) + goto badmsg; + + err = conf_get_u32(conf, "u32_val", &u32); + if (u32 != 42) + goto badmsg; + + /* Non-existing parameters */ + if (0 == conf_get(conf, "rattarei", &pl)) + goto badmsg; + + out: + mem_deref(conf); + return err; + + badmsg: + mem_deref(conf); + return EBADMSG; +} diff --git a/test/convert.c b/test/convert.c new file mode 100644 index 000000000..0f22ea8eb --- /dev/null +++ b/test/convert.c @@ -0,0 +1,56 @@ +/** + * @file convert.c Conversion Testcode + * + * Copyright (C) 2022 Sebastian Reimers + */ +#include +#include "test.h" + +#define DEBUG_MODULE "testconvert" +#define DEBUG_LEVEL 5 +#include + + +int test_try_into(void) +{ + int err = 0; + size_t size; + uint16_t u16 = 0; + int i; + + /* Testing size_t -> uint16_t */ + size = SIZE_MAX; + err = try_into(u16, size); + TEST_EQUALS(ERANGE, err); + + size = 5000; + err = try_into(u16, size); + TEST_ERR(err); + TEST_EQUALS(size, u16); + + size = SIZE_MAX; + err = try_into(u16, size); + TEST_EQUALS(ERANGE, err); + + /* Testing int -> uint16_t */ + i = INT_MAX; + err = try_into(u16, i); + TEST_EQUALS(ERANGE, err); + + i = -50; + err = try_into(u16, i); + TEST_EQUALS(ERANGE, err); + + /* Testing size_t -> int */ + size = SIZE_MAX; + err = try_into(i, size); + TEST_EQUALS(ERANGE, err); + + size = INT_MAX; + err = try_into(i, size); + TEST_ERR(err); + TEST_EQUALS(INT_MAX, i); + +out: + return err; +} diff --git a/test/crc32.c b/test/crc32.c new file mode 100644 index 000000000..6d213c774 --- /dev/null +++ b/test/crc32.c @@ -0,0 +1,43 @@ +/** + * @file crc32.c CRC32 Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testcrc32" +#define DEBUG_LEVEL 4 +#include + + +int test_crc32(void) +{ + const struct { + const char *str; + uint32_t crc; + } testv[] = { + {"string", 0x9ebeb2a9 }, + {"hei", 0x95610594 }, + {"0ndka98d198aloidks9zaz1oqs5jilk", 0x92a398f6 }, + }; + size_t i; + + for (i=0; i +#include +#include "test.h" + + +#define DEBUG_MODULE "dns" +#define DEBUG_LEVEL 5 +#include + + +enum { + NUM_TESTS = 32, + IP_127_0_0_1 = 0x7f000001, + IP_127_0_0_2 = 0x7f000002, + IP_127_0_0_3 = 0x7f000003, + IP_127_0_0_4 = 0x7f000004, + IP_127_0_0_5 = 0x7f000005, +}; + + +static int mkstr(char **strp) +{ + size_t sz = 8; + char *str; + + str = mem_alloc(sz, NULL); + if (!str) + return ENOMEM; + + rand_str(str, sz); + + *strp = str; + + return 0; +} + + +static int mkrr(struct dnsrr *rr, uint16_t type) +{ + int err; + + err = mkstr(&rr->name); + if (err) + return err; + + rr->type = type; + rr->dnsclass = DNS_CLASS_IN; + rr->ttl = 3600; + rr->rdlen = 2; + + switch (type) { + + case DNS_TYPE_A: + rr->rdata.a.addr = rand_u32(); + break; + + case DNS_TYPE_NS: + err |= mkstr(&rr->rdata.ns.nsdname); + break; + + case DNS_TYPE_CNAME: + err |= mkstr(&rr->rdata.cname.cname); + break; + + case DNS_TYPE_SOA: + err |= mkstr(&rr->rdata.soa.mname); + err |= mkstr(&rr->rdata.soa.rname); + rr->rdata.soa.serial = rand_u32(); + rr->rdata.soa.refresh = rand_u32(); + rr->rdata.soa.retry = rand_u32(); + rr->rdata.soa.expire = rand_u32(); + rr->rdata.soa.ttlmin = rand_u32(); + break; + + case DNS_TYPE_PTR: + err |= mkstr(&rr->rdata.ptr.ptrdname); + break; + + case DNS_TYPE_MX: + rr->rdata.mx.pref = rand_u16(); + err |= mkstr(&rr->rdata.mx.exchange); + break; + + case DNS_TYPE_TXT: + err |= mkstr(&rr->rdata.txt.data); + break; + + case DNS_TYPE_AAAA: + rand_bytes(rr->rdata.aaaa.addr, 16); + break; + + case DNS_TYPE_SRV: + rr->rdata.srv.pri = rand_u16(); + rr->rdata.srv.weight = rand_u16(); + rr->rdata.srv.port = rand_u16(); + err |= mkstr(&rr->rdata.srv.target); + break; + + case DNS_TYPE_NAPTR: + rr->rdata.naptr.order = rand_u16(); + rr->rdata.naptr.pref = rand_u16(); + err |= mkstr(&rr->rdata.naptr.flags); + err |= mkstr(&rr->rdata.naptr.services); + err |= mkstr(&rr->rdata.naptr.regexp); + err |= mkstr(&rr->rdata.naptr.replace); + break; + } + + return err; +} + + +int test_dns_hdr(void) +{ + struct mbuf *mb; + uint16_t u16 = 9753; /* pseudo-random (predictable) */ + size_t i; + int err = 0; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + for (i=0; ipos = mb->end = 0; + err = dns_hdr_encode(mb, &hdr); + if (err) + break; + + mb->pos = 0; + err = dns_hdr_decode(mb, &hdr2); + if (err) + break; + + if (0 != memcmp(&hdr, &hdr2, sizeof(hdr))) { + (void)re_fprintf(stderr, + "dnshdr mismatch:\n%02w\n%02w\n", + &hdr, sizeof(hdr), + &hdr2, sizeof(hdr2)); + err = EBADMSG; + break; + } + + u16 *= 17; + } + + mem_deref(mb); + + return err; +} + + +int test_dns_rr(void) +{ + struct hash *ht = NULL; + struct dnsrr *rr = NULL, *rr2 = NULL; + struct mbuf *mb; + size_t i; + int err = ENOMEM; + + static const uint16_t typev[] = { + DNS_TYPE_A, DNS_TYPE_NS, DNS_TYPE_CNAME, + DNS_TYPE_SOA, DNS_TYPE_PTR, DNS_TYPE_MX, + DNS_TYPE_AAAA, DNS_TYPE_SRV, DNS_TYPE_NAPTR, + DNS_TYPE_TXT + }; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = hash_alloc(&ht, 32); + if (err) + goto out; + + for (i=0; ipos = mb->end = 0; + err = dns_rr_encode(mb, rr, 0, ht, 0); + if (err) + break; + + mb->pos = 0; + err = dns_rr_decode(mb, &rr2, 0); + if (err) + break; + + if (!dns_rr_cmp(rr, rr2, false)) { + (void)re_fprintf(stderr, + "dns_rr:\nrr: %02w\n\nrr2: %02w\n", + rr, sizeof(*rr), rr2, sizeof(*rr2)); + hexdump(stderr, mb->buf, mb->end); + err = EBADMSG; + break; + } + + rr = mem_deref(rr); + rr2 = mem_deref(rr2); + } + + out: + hash_flush(ht); + mem_deref(ht); + mem_deref(rr2); + mem_deref(rr); + mem_deref(mb); + + return err; +} + + +/* Testcase to reproduce dname_decode looping error */ +int test_dns_dname(void) +{ + static struct test { + const char *str; + } testv[] = { + { + "c000000c000100000e10002725324a57" + "4d6e3837745836435541597754705361" + "4c4c626743726e3475424e3642365957" + "524e00" + }, + { + "31203700a22c9f17ea75de16785277fa" + "db1094a7782b65a177715e45ffc59f9a" + "73143748aaaf99aede63325c1f48e7fa" + "56f9da" + }, + }; + struct mbuf *mb; + char *name = NULL; + size_t i; + int err = 0; + + mb = mbuf_alloc(4096); + if (!mb) + return ENOMEM; + + for (i=0; istr) / 2; + int e; + + err = str_hex(mb->buf, size, test->str); + if (err) + goto out; + + mb->pos = 0; + mb->end = size; + mb->size = size; + + /* Expect EINVAL */ + e = dns_dname_decode(mb, &name, 0); + TEST_EQUALS(EINVAL, e); + + name = mem_deref(name); + } + + out: + mem_deref(mb); + + return err; +} + + +struct test_dns { + int err; + uint32_t addr; + struct dnsc *dnsc; +}; + + +static void query_handler(int err, const struct dnshdr *hdr, struct list *ansl, + struct list *authl, struct list *addl, void *arg) +{ + struct dnsrr *rr = list_ledata(list_head(ansl)); + struct test_dns *data = arg; + struct sa sa; + (void)hdr; + (void)authl; + (void)addl; + (void)arg; + + if (!data || !rr) { + re_cancel(); + return; + } + + TEST_ERR(err); + + TEST_EQUALS(DNS_TYPE_A, rr->type); + TEST_EQUALS(data->addr, rr->rdata.a.addr); + + sa_set_in(&sa, rr->rdata.a.addr, 0); + + DEBUG_INFO("%s. IN A %j\n", rr->name, &sa); + +out: + data->err = err; + re_cancel(); +} + + +static int check_dns(struct test_dns *data, const char *name, uint32_t addr, + bool main) +{ + struct dns_query *q = NULL; + int err; + + data->addr = addr; + data->err = ENODATA; + + err = dnsc_query(&q, data->dnsc, name, DNS_TYPE_A, DNS_CLASS_IN, true, + query_handler, data); + TEST_ERR(err); + + if (main) { + err = re_main_timeout(100); + TEST_ERR(err); + } + + /* check query handler result */ + err = data->err; + +out: + mem_deref(q); + return err; +} + + +int test_dns_integration(void) +{ + struct dns_server *srv = NULL; + struct test_dns data = {0}; + struct dns_query *q; + int err; + + /* Setup Mocking DNS Server */ + err = dns_server_alloc(&srv, false); + TEST_ERR(err); + + err = dns_server_add_a(srv, "test1.example.net", IP_127_0_0_1, 1); + TEST_ERR(err); + + err = dnsc_alloc(&data.dnsc, NULL, &srv->addr, 1); + TEST_ERR(err); + + /* Test system getaddrinfo */ + dnsc_getaddrinfo(data.dnsc, true); + err = check_dns(&data, "localhost", IP_127_0_0_1, true); + TEST_EQUALS(dnsc_getaddrinfo_enabled(data.dnsc), true); + TEST_ERR(err); + dnsc_getaddrinfo(data.dnsc, false); + TEST_EQUALS(dnsc_getaddrinfo_enabled(data.dnsc), false); + + err = check_dns(&data, "test1.example.net", IP_127_0_0_1, true); + TEST_ERR(err); + + /* Test does not exist */ + err = check_dns(&data, "test2.example.net", IP_127_0_0_1, true); + TEST_EQUALS(ENODATA, err); + + dns_server_flush(srv); + + err = dns_server_add_a(srv, "test1.example.net", IP_127_0_0_2, 1); + TEST_ERR(err); + + err = dns_server_add_a(srv, "test2.example.net", IP_127_0_0_3, 1); + TEST_ERR(err); + + err = dns_server_add_a(srv, "test3.example.net", IP_127_0_0_4, 1); + TEST_ERR(err); + + /* --- Test DNS Cache --- */ + err = check_dns(&data, "test1.example.net", IP_127_0_0_1, true); + TEST_ERR(err); + + err = check_dns(&data, "test2.example.net", IP_127_0_0_3, true); + TEST_ERR(err); + + err = check_dns(&data, "test2.example.net", IP_127_0_0_3, true); + TEST_ERR(err); + + /* Check another resource record afterwards */ + err = check_dns(&data, "test3.example.net", IP_127_0_0_4, true); + TEST_ERR(err); + + sys_msleep(100); /* wait until TTL timer expires */ + re_main_timeout(1); /* execute tmr callbacks */ + + /* --- Check expired TTL --- */ + err = check_dns(&data, "test1.example.net", IP_127_0_0_2, true); + TEST_ERR(err); + + /* --- Test explicit DNS cache flush --- */ + dns_server_flush(srv); + err = dns_server_add_a(srv, "test1.example.net", IP_127_0_0_5, 1); + TEST_ERR(err); + dnsc_cache_flush(data.dnsc); + err = check_dns(&data, "test1.example.net", IP_127_0_0_5, true); + TEST_ERR(err); + + /* --- Test early query cancellation --- */ + err = dnsc_query(&q, data.dnsc, "test1.example.net", DNS_TYPE_A, + DNS_CLASS_IN, true, query_handler, &data); + TEST_ERR(err); + mem_deref(q); + + err = check_dns(&data, "test1.example.net", IP_127_0_0_5, true); + TEST_ERR(err); + + /* --- Leave query open for cleanup test --- */ + err = dnsc_query(&q, data.dnsc, "test1.example.net", DNS_TYPE_A, + DNS_CLASS_IN, true, query_handler, &data); + TEST_ERR(err); + +out: + mem_deref(data.dnsc); + mem_deref(srv); + + return err; +} diff --git a/test/dsp.c b/test/dsp.c new file mode 100644 index 000000000..26c17cd82 --- /dev/null +++ b/test/dsp.c @@ -0,0 +1,63 @@ +/** + * @file dsp.c Testcode for librem's DSP module + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test/dsp" +#define DEBUG_LEVEL 5 +#include + + +static int test_saturate(void) +{ + int err = 0; + + /* saturate_u8 */ + TEST_EQUALS( 0, saturate_u8(-100)); + TEST_EQUALS( 0, saturate_u8( 0)); + TEST_EQUALS( 42, saturate_u8( 42)); + TEST_EQUALS(255, saturate_u8( 255)); + TEST_EQUALS(255, saturate_u8( 355)); + TEST_EQUALS(255, saturate_u8(9692)); + + /* saturate_s16 */ + TEST_EQUALS(-32768, saturate_s16(-65535)); + TEST_EQUALS(-32768, saturate_s16(-32768)); + TEST_EQUALS( 0, saturate_s16( 0)); + TEST_EQUALS( 32767, saturate_s16( 32767)); + TEST_EQUALS( 32767, saturate_s16( 65535)); + + /* saturate_add16 */ + TEST_EQUALS(-32768, saturate_add16(-30000, -30000)); + TEST_EQUALS( -2000, saturate_add16( -1000, -1000)); + TEST_EQUALS( 2, saturate_add16( 1, 1)); + TEST_EQUALS( 32767, saturate_add16( 32766, 1)); + TEST_EQUALS( 32767, saturate_add16( 30000, 30000)); + + /* saturate_sub16 */ + TEST_EQUALS(-32768, saturate_sub16(-50000, -10000)); + TEST_EQUALS( -2000, saturate_sub16( -1000, 1000)); + TEST_EQUALS( 0, saturate_sub16( 1, 1)); + TEST_EQUALS( 32765, saturate_sub16( 32766, 1)); + TEST_EQUALS( 32767, saturate_sub16( 50000, 10000)); + + out: + return err; +} + + +int test_dsp(void) +{ + int err; + + err = test_saturate(); + if (err) + return err; + + return err; +} diff --git a/test/dtls.c b/test/dtls.c new file mode 100644 index 000000000..ac19c02f6 --- /dev/null +++ b/test/dtls.c @@ -0,0 +1,346 @@ +/** + * @file dtls.c DTLS Testcode + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "dtls_test" +#define DEBUG_LEVEL 5 +#include + + +struct dtls_test { + bool dtls_srtp; + struct dtls_sock *sock_cli, *sock_srv; + struct tls_conn *conn_cli, *conn_srv; + struct tls *tls; + int err; + + struct { + enum srtp_suite suite; + uint8_t cli_key[30]; + uint8_t srv_key[30]; + } cli, srv; + + uint8_t fp[32]; + char cn[64]; + unsigned n_srv_estab; + unsigned n_srv_recv; + unsigned n_cli_estab; + unsigned n_cli_recv; + unsigned n_conn; +}; + +static const char *common_name = "127.0.0.1"; +static const char *payload_str = "hello from a cute DTLS client"; + + +static void abort_test(struct dtls_test *t, int err) +{ + t->err = err; + re_cancel(); +} + + +static int send_data(struct dtls_test *t, const char *data) +{ + struct mbuf mb; + int err; + + TEST_ASSERT(t->conn_cli != NULL); + + mb.buf = (void *)data; + mb.pos = 0; + mb.end = str_len(data); + mb.size = str_len(data); + + err = dtls_send(t->conn_cli, &mb); + TEST_ERR(err); + + out: + return err; +} + + +static void srv_estab_handler(void *arg) +{ + struct dtls_test *t = arg; + int err = 0; + + ++t->n_srv_estab; + + if (t->dtls_srtp) { + err = tls_srtp_keyinfo(t->conn_srv, &t->srv.suite, + t->srv.cli_key, sizeof(t->srv.cli_key), + t->srv.srv_key, sizeof(t->srv.srv_key)); + TEST_ERR(err); + } + + out: + if (err) + abort_test(t, err); +} + + +static void srv_recv_handler(struct mbuf *mb, void *arg) +{ + struct dtls_test *t = arg; + int err; + + ++t->n_srv_recv; + + /* echo */ + err = dtls_send(t->conn_srv, mb); + TEST_ERR(err); + + out: + if (err) + abort_test(t, err); +} + + +static void srv_close_handler(int err, void *arg) +{ + struct dtls_test *t = arg; + (void)err; + + t->conn_srv = mem_deref(t->conn_srv); +} + + +static void cli_estab_handler(void *arg) +{ + struct dtls_test *t = arg; + int err; + + ++t->n_cli_estab; + + err = tls_peer_fingerprint(t->conn_cli, TLS_FINGERPRINT_SHA256, + t->fp, sizeof(t->fp)); + TEST_ERR(err); + + err = tls_peer_common_name(t->conn_cli, t->cn, sizeof(t->cn)); + TEST_ERR(err); + + if (t->dtls_srtp) { + + err = tls_srtp_keyinfo(t->conn_cli, &t->cli.suite, + t->cli.cli_key, sizeof(t->cli.cli_key), + t->cli.srv_key, sizeof(t->cli.srv_key)); + TEST_ERR(err); + } + + err = send_data(t, payload_str); + TEST_ERR(err); + + out: + if (err) + abort_test(t, err); +} + + +static void cli_recv_handler(struct mbuf *mb, void *arg) +{ + struct dtls_test *t = arg; + int err = 0; + + ++t->n_cli_recv; + + TEST_STRCMP(payload_str, strlen(payload_str), + mbuf_buf(mb), mbuf_get_left(mb)); + + out: + abort_test(t, err); +} + + +static void cli_close_handler(int err, void *arg) +{ + struct dtls_test *t = arg; + (void)err; + + t->conn_cli = mem_deref(t->conn_cli); +} + + +static void conn_handler(const struct sa *src, void *arg) +{ + struct dtls_test *t = arg; + int err; + (void)src; + + ++t->n_conn; + + TEST_ASSERT(t->conn_srv == NULL); + + err = dtls_accept(&t->conn_srv, t->tls, t->sock_srv, + srv_estab_handler, srv_recv_handler, + srv_close_handler, t); + if (err) { + if (err == EPROTO) + err = ENOMEM; + TEST_ERR(err); + } + + out: + if (err) + abort_test(t, err); +} + + +static int test_dtls_srtp_base(enum tls_method method, bool dtls_srtp) +{ + static const char *srtp_suites = + "SRTP_AES128_CM_SHA1_80:" + "SRTP_AES128_CM_SHA1_32"; + struct dtls_test test; + struct udp_sock *us = NULL; + struct sa cli, srv; + uint8_t fp[32]; + int err; + + memset(&test, 0, sizeof(test)); + + test.dtls_srtp = dtls_srtp; + + err = tls_alloc(&test.tls, method, NULL, NULL); + TEST_ERR(err); + + err = tls_set_certificate(test.tls, test_certificate_ecdsa, + strlen(test_certificate_ecdsa)); + TEST_ERR(err); + + if (dtls_srtp) { + err = tls_set_srtp(test.tls, srtp_suites); + + /* SRTP not supported */ + if (err == ENOSYS) { + err = 0; + goto out; + } + + TEST_ERR(err); + } + + err = tls_fingerprint(test.tls, TLS_FINGERPRINT_SHA256, + fp, sizeof(fp)); + TEST_EQUALS(0, err); + + (void)sa_set_str(&cli, "127.0.0.1", 0); + (void)sa_set_str(&srv, "127.0.0.1", 0); + + err = udp_listen(&us, &srv, NULL, NULL); + TEST_ERR(err); + + err = udp_local_get(us, &srv); + TEST_ERR(err); + + err = dtls_listen(&test.sock_srv, NULL, us, 4, 0, conn_handler, &test); + TEST_ERR(err); + + err = dtls_listen(&test.sock_cli, &cli, NULL, 4, 0, NULL, NULL); + TEST_ERR(err); + + err = dtls_connect(&test.conn_cli, test.tls, test.sock_cli, + &srv, cli_estab_handler, + cli_recv_handler, cli_close_handler, &test); + if (err) { + if (err == EPROTO) + err = ENOMEM; + TEST_ERR(err); + } + + err = re_main_timeout(800); + TEST_ERR(err); + + if (test.err) { + err = test.err; + goto out; + } + + /* verify result after test is complete */ + TEST_EQUALS(1, test.n_srv_estab); + TEST_EQUALS(1, test.n_srv_recv); + TEST_EQUALS(1, test.n_cli_estab); + TEST_EQUALS(1, test.n_cli_recv); + TEST_EQUALS(1, test.n_conn); + + TEST_MEMCMP(fp, sizeof(fp), test.fp, sizeof(test.fp)); + TEST_STRCMP(common_name, strlen(common_name), + test.cn, strlen(test.cn)); + + if (dtls_srtp) { + + TEST_EQUALS(test.cli.suite, test.srv.suite); + TEST_MEMCMP(test.cli.cli_key, sizeof(test.cli.cli_key), + test.srv.cli_key, sizeof(test.srv.cli_key)); + TEST_MEMCMP(test.cli.srv_key, sizeof(test.cli.srv_key), + test.srv.srv_key, sizeof(test.srv.srv_key)); + } + + out: + test.conn_cli = mem_deref(test.conn_cli); + test.conn_srv = mem_deref(test.conn_srv); + test.sock_cli = mem_deref(test.sock_cli); + test.sock_srv = mem_deref(test.sock_srv); + test.tls = mem_deref(test.tls); + mem_deref(us); + + return err; +} + + +static bool have_dtls_support(enum tls_method method) +{ + struct tls *tls = NULL; + int err; + + err = tls_alloc(&tls, method, NULL, NULL); + + mem_deref(tls); + + return err != ENOSYS; +} + + +int test_dtls(void) +{ + int err = 0; + + /* NOTE: DTLS v1.0 should be available on all + * supported platforms. + */ + if (!have_dtls_support(TLS_METHOD_DTLSV1)) { + (void)re_printf("skip DTLS 1.0 tests\n"); + return ESKIPPED; + } + else { + err = test_dtls_srtp_base(TLS_METHOD_DTLSV1, false); + if (err) + return err; + } + + return 0; +} + + +int test_dtls_srtp(void) +{ + int err = 0; + + if (!have_dtls_support(TLS_METHOD_DTLSV1)) { + (void)re_printf("skip DTLS tests\n"); + return ESKIPPED; + } + + err = test_dtls_srtp_base(TLS_METHOD_DTLSV1, true); + if (err) + return err; + + return 0; +} diff --git a/test/dtmf.c b/test/dtmf.c new file mode 100644 index 000000000..b66b11add --- /dev/null +++ b/test/dtmf.c @@ -0,0 +1,59 @@ +/** + * @file dtmf.c Testcode for librem's DTMF module + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test/dtmf" +#define DEBUG_LEVEL 5 +#include + + +static void dtmf_dec_handler(char digit, void *arg) +{ + char *buf = arg; + + buf[str_len(buf)] = digit; +} + + +int test_dtmf(void) +{ +#define SRATE 8000 + static const char digits[] = "2*A#7"; + char dbuf[256] = ""; + struct dtmf_dec *dec = NULL; + struct mbuf *mb = NULL; + size_t i; + int err = 0; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + err = dtmf_dec_alloc(&dec, SRATE, 1, dtmf_dec_handler, dbuf); + if (err) + goto out; + + /* generate audio samples with test digits */ + for (i=0; ibuf, mb->end / 2); + + TEST_STRCMP(digits, str_len(digits), dbuf, str_len(dbuf)); + + out: + mem_deref(dec); + mem_deref(mb); + return err; +} diff --git a/test/fir.c b/test/fir.c new file mode 100644 index 000000000..cd4067f14 --- /dev/null +++ b/test/fir.c @@ -0,0 +1,57 @@ +/** + * @file fir.c FIR-filter Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "fir" +#define DEBUG_LEVEL 5 +#include + + +/* 48kHz sample-rate, 8kHz cutoff (pass 0-7kHz, stop 9-24kHz) */ +static const int16_t fir_48_8[] = { + 238, 198, -123, -738, -1268, -1204, -380, 714, + 1164, 376, -1220, -2206, -1105, 2395, 6909, 10069, + 10069, 6909, 2395, -1105, -2206, -1220, 376, 1164, + 714, -380, -1204, -1268, -738, -123, 198, 238 +}; + + +int test_fir(void) +{ +#define NUM_SAMPLES 8 + struct fir fir; + static const int16_t samp_in[NUM_SAMPLES] = + {-8000, -4000, -2000, 0, 2000, 4000, 8000, 4000}; + static const int16_t samp_out_exp[NUM_SAMPLES] = + { -59, -78, -9, 183, 421, 534, 391, -38}; + int16_t samp_out[NUM_SAMPLES]; + int err = 0; + + fir_reset(&fir); + + /* verify FIR-filter state */ + TEST_EQUALS(0, fir.index); + + /* process the FIR filter */ + fir_filter(&fir, samp_out, samp_in, RE_ARRAY_SIZE(samp_in), + 1, fir_48_8, RE_ARRAY_SIZE(fir_48_8)); + + /* verify FIR-filter state */ + TEST_EQUALS(NUM_SAMPLES, fir.index); + TEST_ASSERT(NUM_SAMPLES <= RE_ARRAY_SIZE(fir.history)); + TEST_MEMCMP(samp_in, sizeof(samp_in), fir.history, sizeof(samp_in)); + + /* verify output samples */ + TEST_MEMCMP(samp_out_exp, sizeof(samp_out_exp), + samp_out, sizeof(samp_out)); + + out: + return err; +} diff --git a/test/fmt.c b/test/fmt.c new file mode 100644 index 000000000..5e688992d --- /dev/null +++ b/test/fmt.c @@ -0,0 +1,1090 @@ +/** + * @file fmt.c Formatting Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testfmt" +#define DEBUG_LEVEL 5 +#include + + +int test_fmt_pl(void) +{ + const struct pl pl = PL("rattarei"); + const struct pl pl0 = PL("rattarei"); + const struct pl pl0_ = PL("rAtTaReI"); + const struct pl pl1 = PL("foobar"); + const struct pl pl2 = PL("rarei"); + const struct pl pl3 = PL("8zmoijdalij32li34jkljsldsjalkfj9jshruhga" + "laksjdliasjf98uasehr98wehrdisflsdifholis" + "djlweijrdisfjslkdfjslfjowiejrodflaijsdla" + "ksjdlaskdfjslkdfjsldfkjsdlfkjsdlkrtjqwli" + "ejsldfjsldfkjsdlfkjdiajsduhiafhurjiejidi"); + const struct pl pl4 = PL("rattarei4"); + const char str0[] = "rattarei"; + const char str1[] = "rattaray"; + struct pl pl5, pl6; + const struct pl pl7 = PL("hei"); + const struct pl pl7_ = PL("Hei"); + const struct pl pl7__ = PL("Duz"); + const struct pl pl_empty = PL(""); + + /* pl_cmp() */ + if (EINVAL != pl_cmp(NULL, NULL)) + goto out; + if (0 != pl_cmp(&pl, &pl0)) + goto out; + if (0 == pl_cmp(&pl, &pl1)) + goto out; + if (0 != pl_cmp(&pl, &pl0)) + goto out; + if (0 == pl_cmp(&pl, &pl2)) + goto out; + if (0 != pl_cmp(&pl3, &pl3)) + goto out; + if (0 != pl_cmp(&pl_null, &pl_null)) + goto out; + + /* pl_casecmp() */ + if (EINVAL != pl_casecmp(NULL, NULL)) + goto out; + if (0 != pl_casecmp(&pl, &pl0)) + goto out; + if (0 != pl_casecmp(&pl, &pl0_)) + goto out; + if (0 == pl_casecmp(&pl, &pl1)) + goto out; + if (0 == pl_casecmp(&pl, &pl4)) + goto out; + pl5.p = str0; + pl5.l = 6; + pl6.p = str1; + pl6.l = 6; + if (0 != pl_casecmp(&pl5, &pl6)) + goto out; + if (0 != pl_casecmp(&pl, &pl0)) + goto out; + if (0 != pl_casecmp(&pl, &pl0_)) + goto out; + if (0 == pl_casecmp(&pl, &pl2)) + goto out; + if (0 != pl_casecmp(&pl7, &pl7_)) + goto out; + if (0 == pl_casecmp(&pl7, &pl7__)) + goto out; + if (0 != pl_casecmp(&pl_null, &pl_null)) + goto out; + + /* pl_strcmp() */ + if (EINVAL != pl_strcmp(NULL, NULL)) + goto out; + if (0 != pl_strcmp(&pl0, str0)) + goto out; + if (0 == pl_strcmp(&pl0, str1)) + goto out; + if (0 == pl_strcmp(&pl3, str0)) + goto out; + + /* pl_strcasecmp() */ + if (EINVAL != pl_strcasecmp(NULL, NULL)) + goto out; + if (0 != pl_strcasecmp(&pl0_, str0)) + goto out; + if (0 == pl_strcasecmp(&pl0_, str1)) + goto out; + if (0 == pl_strcasecmp(&pl3, str0)) + goto out; + + /* pl_strchr() */ + if (pl0.p != pl_strchr(&pl0, 'r')) + goto out; + if (NULL != pl_strchr(&pl0, 'B')) + goto out; + + /* pl_strrchr() */ + if (pl0.p + 5 != pl_strrchr(&pl0, 'r')) + goto out; + if (NULL != pl_strrchr(&pl0, 'B')) + goto out; + if (NULL != pl_strrchr(&pl_empty, 'r')) + goto out; + + return 0; + out: + return EINVAL; +} + + +int test_fmt_pl_i32(void) +{ + const struct { + const struct pl pl; + int32_t v; + } testv[] = { + /* Error cases */ + {PL("hei"), 0}, + {PL("abc"), 0}, + {PL(""), 0}, + {{NULL, 2}, 0}, + {{"fo", 0}, 0}, + {PL("2147483648"), -2147483647 - 1}, + {PL("9223372036854775808"), 0}, + + /* Working cases */ + {PL("0"), 0}, + {PL("1"), 1}, + {PL("-1"), -1}, + {PL("123"), 123}, + {PL("-123"), -123}, + {PL("5467"), 5467}, + {PL("-123"), -123}, + {PL("-5467"), -5467}, + {PL("2147483647"), 2147483647}, + {PL("+2147483647"), 2147483647}, + {PL("-2147483648"), -2147483647 - 1}, + }; + uint32_t i; + int err = 0; + + for (i=0; i .0); + + v = pl_float(&neg); + TEST_ASSERT(v < .0); + + out: + return err; +} + + +int test_fmt_regex(void) +{ + const struct pl pl = PL("hei42sann!"); + const struct pl pl1 = PL("42"); + const struct pl pl2 = PL("sann"); + const struct pl pl3 = PL(";foo=\"bla;bla\""); + const struct pl pl4 = PL("bla;bla"); + const struct pl pl5 = PL("a \"b \"1123\"\" c"); + const struct pl pl6 = PL("-42"); + const struct pl pla = PL("a"); + const struct pl plb = PL("b \"1123\""); + const struct pl plc = PL("c"); + struct pl pln, pls, foo, a, b, c, d, e; + int err = 0; + + /* Successful case */ + err = re_regex(pl.p, pl.l, "Hei[0-9]+[^!]+", &pln, &pls); + if (err) + goto out; + err = pl_cmp(&pln, &pl1); + if (err) + goto out; + err = pl_cmp(&pls, &pl2); + if (err) + goto out; + + /* Quoted strings */ + err = re_regex(pl3.p, pl3.l, ";foo=\"[^\"]+\"", &foo); + if (err) + goto out; + err = pl_cmp(&foo, &pl4); + if (err) { + DEBUG_WARNING("regex quoted string failed (%r)\n", &foo); + goto out; + } + + err = re_regex(pl3.p, pl3.l, ";foo=[~]+", &foo); + if (err) + goto out; + err = pl_cmp(&foo, &pl4); + if (err) + goto out; + + /* re_regex('a "b \"1123\"" c', "[^ ]+ [~ ]+ [^ ]+", &a, &b, &c); + result: a='a', b='b \"1123\"', c='c' */ + err = re_regex(pl5.p, pl5.l, "[^ ]+ [~ ]+ [^ ]+", &a, &b, &c); + if (err) + goto out; + err = pl_cmp(&a, &pla); + if (err) + goto out; + err = pl_cmp(&b, &plb); + if (err) + goto out; + err = pl_cmp(&c, &plc); + if (err) + goto out; + + /* Failing case */ + if (0 == re_regex(pl.p, pl.l, "tull")) { + err = EINVAL; + goto out; + } + + if (0 == re_regex(pl.p, pl.l, "[^\r\n]+\r\n", &pln)) { + err = EINVAL; + goto out; + } + + /* Test escaping */ + if (0 == re_regex(pl.p, pl.l, "[\\^0-9]*\\]", NULL)) { + err = EINVAL; + goto out; + } + err = re_regex(pl6.p, pl6.l, "[\\-0-9\\^]+", &pln); + if (err) + goto out; + err = pl_strcmp(&pln, "-42"); + if (err) + goto out; + + /* verify that optional matching sets the PL to zero + if there is no match */ + e.p = "x"; + e.l = 42; + err = re_regex(pl4.p, pl4.l, "[a-z]+;[0-9]*", &d, &e); + if (err) + goto out; + TEST_ASSERT(!pl_isset(&e)); + + return 0; + + out: + DEBUG_WARNING("regex failed (%d)\n", err); + + return err; +} + + +static int fooprint(struct re_printf *pf, void *arg) +{ + int a = *(int *)arg; + + return re_hprintf(pf, "[a=%d]", a); +} + + +static int va_printf(struct mbuf *mb, const char *fmt, ...) +{ + va_list ap; + int err; + + va_start(ap, fmt); + err = mbuf_printf(mb, "[%v]", fmt, &ap); + va_end(ap); + + return err; +} + + +int test_fmt_print(void) +{ + const struct pl ref1 = PL("-12345 -1234567890 -1234567890123456789"); + const struct pl ref2 = PL("12345 1234567890 1234567890123456789"); + const struct pl ref3 = PL("65535 4294967295 18446744073709551615"); + struct pl pl; + struct mbuf mb; + const int a = 42; + char *s = NULL; + int err; + + mbuf_init(&mb); + err = mbuf_printf(&mb, "%d %ld %lld", -12345, -1234567890L, + -1234567890123456789LL); + if (err) + goto out; + pl.p = (char *)mb.buf; + pl.l = mb.end; + err = pl_cmp(&pl, &ref1); + if (err) { + DEBUG_WARNING("print 1: ref=(%r) buf=(%r)\n", &ref1, &pl); + goto out; + } + + mbuf_reset(&mb); + err = mbuf_printf(&mb, "%u %lu %llu", 12345, 1234567890UL, + 1234567890123456789ULL); + if (err) + goto out; + pl.p = (char *)mb.buf; + pl.l = mb.end; + err = pl_cmp(&pl, &ref2); + if (err) { + DEBUG_WARNING("print 2: buf: %r\n", &pl); + goto out; + } + + mbuf_reset(&mb); + err = mbuf_printf(&mb, "%u %lu %llu", 65535, 4294967295UL, + 18446744073709551615ULL); + if (err) + goto out; + pl.p = (char *)mb.buf; + pl.l = mb.end; + err = pl_cmp(&pl, &ref3); + if (err) { + DEBUG_WARNING("print 3: buf: %r\n", &pl); + goto out; + } + + mbuf_reset(&mb); + err = mbuf_printf(&mb, "fookokaoskdokoskdookokokq%Hbar", fooprint, &a); + if (err) + goto out; + pl.p = (char *)mb.buf; + pl.l = mb.end; + err = pl_strcmp(&pl, "fookokaoskdokoskdookokokq[a=42]bar"); + if (err) { + DEBUG_WARNING("print 4: buf: %r\n", &pl); + goto out; + } + + mbuf_reset(&mb); + err = va_printf(&mb, "foo%d%s", 42, "barrompabarplaplsdpalspdlplplp"); + if (err) + goto out; + pl.p = (char *)mb.buf; + pl.l = mb.end; + err = pl_strcmp(&pl, "[foo42barrompabarplaplsdpalspdlplplp]"); + if (err) { + DEBUG_WARNING("print 5: buf: %r\n", &pl); + goto out; + } + + /* dynamic print */ + err = re_sdprintf(&s, "okaspdokaspodkjalsj%fkmzl12kpdokasdlkj", 3.14); + if (err) + goto out; + + pl_set_str(&pl, s); + err = pl_strcmp(&pl, "okaspdokaspodkjalsj3.140000kmzl12kpdokasdlkj"); + if (err) { + DEBUG_WARNING("sdprintf: %r\n", &pl); + goto out; + } + + out: + mbuf_reset(&mb); + mem_deref(s); + + return err; +} + + +int test_fmt_snprintf(void) +{ + const struct pl ref3 = PL("65535 4294967295 18446744073709551615"); + const uint8_t v[] = {0xfa, 0xce, 0xb0, 0x0c}; + struct sa sa4; + const char addr4[] = "1.2.3.4"; +#ifdef HAVE_INET6 + struct sa sa6; + const char addr6[] = "2001:5c0:8fff:ffff::d"; +#endif + char buf[128], sbuf[8]; + int n, err; + + /* Test binary vector printing */ + n = re_snprintf(buf, sizeof(buf), "%w", v, sizeof(v)); + if (2*sizeof(v) != n) { + err = EINVAL; + goto out; + } + if (0 != strcmp(buf, "faceb00c")) { + err = EINVAL; + goto out; + } + + /* Test sockaddr printing */ + err = sa_set_str(&sa4, addr4, 0); + if (err) { + DEBUG_WARNING("sa_set_str4: %m\n", err); + goto out; + } +#ifdef HAVE_INET6 + err = sa_set_str(&sa6, addr6, 0); + if (err) { + DEBUG_WARNING("sa_set_str6: %m\n", err); + goto out; + } +#endif + + (void)re_snprintf(buf, sizeof(buf), "%j", &sa4); + if (0 != strcmp(buf, addr4)) { + err = EINVAL; + goto out; + } +#ifdef HAVE_INET6 + (void)re_snprintf(buf, sizeof(buf), "%j", &sa6); + if (0 != strcmp(buf, addr6)) { + err = EINVAL; + goto out; + } +#endif + + /* Overflow */ + n = re_snprintf(buf, 3, "12"); + if (2 != n) { + err = EINVAL; + goto out; + } + n = re_snprintf(buf, 3, "123"); + if (-1 != n) { + err = EINVAL; + goto out; + } + n = re_snprintf(buf, 4, "%u", 12345); + if (-1 != n) { + err = EINVAL; + goto out; + } + n = re_snprintf(buf, 4, "%s", "asdasd"); + if (-1 != n) { + err = EINVAL; + goto out; + } + n = re_snprintf(buf, 37, "%r", &ref3); + if (-1 != n) { + err = EINVAL; + goto out; + } + + /* Double */ + (void)re_snprintf(buf, sizeof(buf), "%f", 123.456); + if (0 != strcmp(buf, "123.456000")) + goto perr; + (void)re_snprintf(buf, sizeof(buf), "%f", -123.456); + if (0 != strcmp(buf, "-123.456000")) + goto perr; + (void)re_snprintf(buf, sizeof(buf), "%.3f", 123.456); + if (0 != strcmp(buf, "123.456")) + goto perr; + (void)re_snprintf(buf, sizeof(buf), "%6.3f", 3.14); + if (0 != strcmp(buf, " 3.140")) + goto perr; + (void)re_snprintf(buf, sizeof(buf), "%06.3f", 3.14); + if (0 != strcmp(buf, "03.140")) + goto perr; + (void)re_snprintf(buf, sizeof(buf), "%6.3f", -3.14); + if (0 != strcmp(buf, "-3.140")) + goto perr; + +#if 0 + (void)re_snprintf(buf, sizeof(buf), "%05f", strtod("inf", NULL)); + if (0 != strcmp(buf, " inf")) + goto perr; +#endif + + (void)re_snprintf(buf, sizeof(buf), "%.2f", 123123123123.00); + if (0 != strcmp(buf, "123123123123.00")) + goto perr; + + memset(sbuf, 0xff, sizeof(sbuf)); + n = re_snprintf(sbuf, sizeof(sbuf), "ab %d cd", 42); + if (n != -1 || strcmp(sbuf, "ab 42")) { + DEBUG_WARNING("n=%d sbuf='%s'\n", n, sbuf); + goto perr; + } + + out: + return err; + perr: + DEBUG_WARNING("bad msg: '%s'\n", buf); + return EBADMSG; +} + + +int test_fmt_str(void) +{ + const struct { + const char *dst; + const char *src; + uint32_t n; + } testv[] = { + {"foo", "foo", 64}, + {"foo", "foo", 4}, + {"fo", "foo", 3}, + {"123456789", "1234567890", 10} + }; + char buf[64]; + size_t i; + int err = 0; + + for (i=0; i= RE_ARRAY_SIZE(testv)) { + DEBUG_WARNING("param: too many parameters (%u > %u)\n", + *i, RE_ARRAY_SIZE(testv)); + *err = EOVERFLOW; + return; + } + + if (!testv[*i].present) { + DEBUG_WARNING("param: %u: unexpected param '%r'\n", + *i, name); + *err = EBADMSG; + } + + if (0 != pl_strcmp(name, testv[*i].pname)) { + DEBUG_WARNING("param: %u: name mismatch: '%r' != '%s'\n", + *i, name, testv[*i].pname); + *err = EBADMSG; + } + + if (testv[*i].pval && 0 != pl_strcmp(val, testv[*i].pval)) { + DEBUG_WARNING("param: %u: value mismatch: '%r' != '%s'\n", + *i, val, testv[*i].pval); + *err = EBADMSG; + } + + ++(*i); +} + + +int test_fmt_param(void) +{ + size_t i; + int err = 0; + void *argv[2]; + + argv[0] = &i; + argv[1] = &err; + + for (i=0; i= 0); + + pl_set_str(&pl, buf); + TEST_EQUALS(pl.l, (size_t)n); + TEST_EQUALS(pl.l, 12); + + err = re_regex(pl.p, pl.l, + "[0-2]1[0-9]1:[0-5]1[0-9]1:[0-5]1[0-9]1\\.[0-9]3", + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + TEST_ERR(err); + + out: + return err; +} + + +int test_fmt_str_error(void) +{ + char buf[256]; + int err = 0; + + TEST_ASSERT(str_isset(str_error(EINVAL, buf, sizeof(buf)))); + out: + return err; +} + + +int test_fmt_unicode(void) +{ + const char input[] = "abc\\b\\f\\n\\r\\t\\u0001"; + char buf[1024], buf2[1024]; + struct pl pl; + int err = 0; + + pl_set_str(&pl, input); + + re_snprintf(buf, sizeof(buf), "%H", utf8_decode, &pl); + + TEST_STRCMP("abc\b\f\n\r\t\x01", 9U, buf, str_len(buf)); + + re_snprintf(buf2, sizeof(buf2), "%H", utf8_encode, buf); + + TEST_STRCMP(input, str_len(input), buf2, str_len(buf2)); + + out: + return err; +} + + +int test_fmt_unicode_decode(void) +{ + static const struct test { + const char *utf8_ref; + const char *str; + } unitestv[] = { + + /* UTF8 Binary: Unicode: */ + { "\x40", "\\u0040" }, /* The '@' symbol */ + { "\xc3\x85", "\\u00C5" }, + { "\xE2\x82\xAC", "\\u20AC" }, /* Euro symbol */ + { "\xF0\x9D\x84\x9E", "\\uD834\\uDD1E" }, /* G-key */ + { "\xF0\x9F\x98\x82", "\\uD83D\\uDE02" }, /* Face with tears of joy */ + + }; + size_t i; + char buf[256]; + int err = 0; + + for (i=0; iutf8_ref); + + pl_set_str(&pl, test->str); + + n = re_snprintf(buf, sizeof(buf), "%H", utf8_decode, &pl); + +#if 0 + re_printf("printed: '%s'\n", buf); +#endif + + TEST_MEMCMP(test->utf8_ref, n_exp, buf, n); + } + + out: + return err; +} + + +int test_fmt_str_bool(void) +{ + bool en; + int err = 0; + size_t i; + + enum curstate { + STATE_TRUE, + STATE_FALSE, + STATE_UNSUP + }; + + + enum curstate state = STATE_TRUE; + + static const char *truestr[] = { + "1", + "true", + "enable", + "on", + "yes", + "Yes", + "YEs", + }; + + static const char *falsestr[] = { + "0", + "false", + "disable", + "off", + "no", + "NO", + }; + + static const char *notsup[] = { + "xxx", + "not", + "sure", + "notsure", + "YESS", + }; + + for (i = 0; i < RE_ARRAY_SIZE(truestr); i++) { + err = str_bool(&en, truestr[i]); + TEST_ERR(err); + TEST_EQUALS(true, en); + } + + state = STATE_FALSE; + for (i = 0; i < RE_ARRAY_SIZE(falsestr); i++) { + err = str_bool(&en, falsestr[i]); + TEST_ERR(err); + TEST_EQUALS(false, en); + } + + state = STATE_UNSUP; + for (i = 0; i < RE_ARRAY_SIZE(notsup); i++) { + err = str_bool(&en, notsup[i]); + TEST_EQUALS(err, EINVAL); + } + + err = 0; + out: + if (err && state == STATE_UNSUP) { + DEBUG_WARNING("processed unsupported string number %d: %s\n", + i, notsup[i]); + } + else if (err) { + DEBUG_WARNING("could not successfully convert %s\n", + state == STATE_TRUE ? truestr[i] : + falsestr[i]); + } + + return err; +} + + +int test_fmt_str_itoa(void) +{ + char buf[ITOA_BUFSZ]; + char *s; + int err = 0; + + s = str_itoa(0, buf, 10); + TEST_ASSERT(!str_casecmp(s, "0")); + + s = str_itoa(42, buf, 10); + TEST_ASSERT(!str_casecmp(s, "42")); + + s = str_itoa(UINT32_MAX, buf, 10); + TEST_ASSERT(!str_casecmp(s, "4294967295")); + + s = str_itoa(UINT32_MAX, buf, 16); + TEST_ASSERT(!str_casecmp(s, "FFFFFFFF")); + + s = str_itoa(23, buf, 2); + TEST_ASSERT(!str_casecmp(s, "10111")); + + s = str_itoa(UINT32_MAX, buf, 2); + TEST_ASSERT(!str_casecmp(s, "11111111111111111111111111111111")); + +out: + if (err) + DEBUG_WARNING("err itoa string: %s\n", s); + + return err; +} diff --git a/test/g711.c b/test/g711.c new file mode 100644 index 000000000..e364a7624 --- /dev/null +++ b/test/g711.c @@ -0,0 +1,174 @@ +/** + * @file g711.c G.711 test vectors + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_g711" +#define DEBUG_LEVEL 5 +#include + + +/* G.711 reference tables - copied from re/src/g711/g711.c */ + + +static const signed short _st_alaw2linear16[256] = { + -5504, -5248, -6016, -5760, -4480, -4224, -4992, + -4736, -7552, -7296, -8064, -7808, -6528, -6272, + -7040, -6784, -2752, -2624, -3008, -2880, -2240, + -2112, -2496, -2368, -3776, -3648, -4032, -3904, + -3264, -3136, -3520, -3392, -22016, -20992, -24064, + -23040, -17920, -16896, -19968, -18944, -30208, -29184, + -32256, -31232, -26112, -25088, -28160, -27136, -11008, + -10496, -12032, -11520, -8960, -8448, -9984, -9472, + -15104, -14592, -16128, -15616, -13056, -12544, -14080, + -13568, -344, -328, -376, -360, -280, -264, + -312, -296, -472, -456, -504, -488, -408, + -392, -440, -424, -88, -72, -120, -104, + -24, -8, -56, -40, -216, -200, -248, + -232, -152, -136, -184, -168, -1376, -1312, + -1504, -1440, -1120, -1056, -1248, -1184, -1888, + -1824, -2016, -1952, -1632, -1568, -1760, -1696, + -688, -656, -752, -720, -560, -528, -624, + -592, -944, -912, -1008, -976, -816, -784, + -880, -848, 5504, 5248, 6016, 5760, 4480, + 4224, 4992, 4736, 7552, 7296, 8064, 7808, + 6528, 6272, 7040, 6784, 2752, 2624, 3008, + 2880, 2240, 2112, 2496, 2368, 3776, 3648, + 4032, 3904, 3264, 3136, 3520, 3392, 22016, + 20992, 24064, 23040, 17920, 16896, 19968, 18944, + 30208, 29184, 32256, 31232, 26112, 25088, 28160, + 27136, 11008, 10496, 12032, 11520, 8960, 8448, + 9984, 9472, 15104, 14592, 16128, 15616, 13056, + 12544, 14080, 13568, 344, 328, 376, 360, + 280, 264, 312, 296, 472, 456, 504, + 488, 408, 392, 440, 424, 88, 72, + 120, 104, 24, 8, 56, 40, 216, + 200, 248, 232, 152, 136, 184, 168, + 1376, 1312, 1504, 1440, 1120, 1056, 1248, + 1184, 1888, 1824, 2016, 1952, 1632, 1568, + 1760, 1696, 688, 656, 752, 720, 560, + 528, 624, 592, 944, 912, 1008, 976, + 816, 784, 880, 848 +}; + + +static const signed short _st_ulaw2linear16[256] = { + -32124, -31100, -30076, -29052, -28028, -27004, -25980, + -24956, -23932, -22908, -21884, -20860, -19836, -18812, + -17788, -16764, -15996, -15484, -14972, -14460, -13948, + -13436, -12924, -12412, -11900, -11388, -10876, -10364, + -9852, -9340, -8828, -8316, -7932, -7676, -7420, + -7164, -6908, -6652, -6396, -6140, -5884, -5628, + -5372, -5116, -4860, -4604, -4348, -4092, -3900, + -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, + -1980, -1884, -1820, -1756, -1692, -1628, -1564, + -1500, -1436, -1372, -1308, -1244, -1180, -1116, + -1052, -988, -924, -876, -844, -812, -780, + -748, -716, -684, -652, -620, -588, -556, + -524, -492, -460, -428, -396, -372, -356, + -340, -324, -308, -292, -276, -260, -244, + -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, + -64, -56, -48, -40, -32, -24, -16, + -8, -2, 32124, 31100, 30076, 29052, 28028, + 27004, 25980, 24956, 23932, 22908, 21884, 20860, + 19836, 18812, 17788, 16764, 15996, 15484, 14972, + 14460, 13948, 13436, 12924, 12412, 11900, 11388, + 10876, 10364, 9852, 9340, 8828, 8316, 7932, + 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, + 4092, 3900, 3772, 3644, 3516, 3388, 3260, + 3132, 3004, 2876, 2748, 2620, 2492, 2364, + 2236, 2108, 1980, 1884, 1820, 1756, 1692, + 1628, 1564, 1500, 1436, 1372, 1308, 1244, + 1180, 1116, 1052, 988, 924, 876, 844, + 812, 780, 748, 716, 684, 652, 620, + 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, + 260, 244, 228, 212, 196, 180, 164, + 148, 132, 120, 112, 104, 96, 88, + 80, 72, 64, 56, 48, 40, 32, + 24, 16, 8, 2 +}; + + +int test_g711_alaw(void) +{ + uint32_t i; + uint8_t val; + int n = 0; + int err = 0; + + val = g711_pcm2alaw(-32768); + TEST_EQUALS(42, val); + + for (i=0; i<256; i++) { + uint8_t alaw = i, alaw2; + int16_t pcm_ref = _st_alaw2linear16[alaw]; + int16_t pcm = g711_alaw2pcm(alaw); + + if (pcm_ref != pcm) { + DEBUG_INFO("alaw: %u: bogus sample: ref=%d gen=%d\n", + i, pcm_ref, pcm); + ++n; + } + + alaw2 = g711_pcm2alaw(pcm); + if (alaw2 != alaw) { + DEBUG_INFO("alaw: %u: bogus alaw %d != %d\n", + i, alaw, alaw2); + ++n; + } + } + + if (n) { + DEBUG_WARNING("alaw: error samples: %d\n", n); + } + +out: + return n ? EINVAL : err; +} + + +int test_g711_ulaw(void) +{ + uint32_t i; + uint8_t val; + int n = 0; + int err = 0; + + val = g711_pcm2ulaw(-32768); + TEST_EQUALS(0, val); + + for (i=0; i<256; i++) { + uint8_t ulaw = i, ulaw2; + int16_t pcm_ref = _st_ulaw2linear16[ulaw]; + int16_t pcm = g711_ulaw2pcm(ulaw); + + if (pcm != pcm_ref) { + DEBUG_INFO("ulaw: %u: bogus sample ref=%d gen=%d\n", + i, pcm_ref, pcm); + ++n; + } + + ulaw2 = g711_pcm2ulaw(pcm_ref); + if (ulaw2 != ulaw) { + DEBUG_INFO("ulaw: %u: bogus ulaw %d != %d\n", + i, ulaw, ulaw2); + ++n; + } + } + + if (n) { + DEBUG_WARNING("ulaw: error samples: %d\n", n); + } +out: + return n ? EINVAL : err; +} diff --git a/test/h264.c b/test/h264.c new file mode 100644 index 000000000..e23fc48ec --- /dev/null +++ b/test/h264.c @@ -0,0 +1,700 @@ +/** + * @file h264.c H.264 Testcode + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "h264test" +#define DEBUG_LEVEL 5 +#include + + +#if 0 +static void dump_annexb(const uint8_t *start, size_t size) +{ + const uint8_t *end = start + size; + unsigned count = 0; + + re_printf("---- H.264 Annex-B: ----\n"); + + const uint8_t *r = h264_find_startcode(start, end); + + while (r < end) { + + struct h264_nal_header hdr; + + /* skip zeros */ + while (!*(r++)) + ; + + const uint8_t *r1 = h264_find_startcode(r, end); + size_t nal_len = r1 - r; + + h264_nal_header_decode_buf(&hdr, r); + + re_printf(".... nal: len=%2zu nri=%u type=%s\n", + nal_len, + hdr.nri, h264_nal_unit_name(hdr.type)); + + r = r1; + + ++count; + } + + re_printf("Total NAL units: %u\n", count); + re_printf("\n"); +} + + +static void dump_rtp(const uint8_t *p, size_t size) +{ + struct h264_nal_header hdr; + + h264_nal_header_decode_buf(&hdr, p); + + re_printf("RTP NAL: size=%zu nri=%u type=%u(%s)\n", + size, + hdr.nri, hdr.type, h264_nal_unit_name(hdr.type)); + re_printf("\n"); +} +#endif + + +static int test_h264_stap_a_encode(void) +{ + static const uint8_t frame[] = { + + /* AUD */ + 0x00, 0x00, 0x01, + 0x09, 0x10, + + /* SPS */ + 0x00, 0x00, 0x01, + 0x67, 0x42, 0xc0, 0x1f, 0x8c, 0x8d, 0x40, + + /* PPS */ + 0x00, 0x00, 0x01, + 0x68, 0xce, 0x3c, 0x80, + + /* IDR_SLICE */ + 0x00, 0x00, 0x01, + 0x65, 0xb8, 0x00, 0x04, 0x00, 0x00, 0x05, 0x39, + }; +#define MAX_NRI 3 + struct mbuf *mb_pkt = mbuf_alloc(256); + struct mbuf *mb_frame = mbuf_alloc(256); + struct h264_nal_header hdr; + int err; + + if (!mb_pkt || !mb_frame) { + err = ENOMEM; + goto out; + } + + err = h264_stap_encode(mb_pkt, frame, sizeof(frame)); + if (err) + goto out; + + mb_pkt->pos = 0; + + err = h264_nal_header_decode(&hdr, mb_pkt); + ASSERT_EQ(0, err); + + ASSERT_EQ(MAX_NRI, hdr.nri); /* NOTE: max NRI */ + ASSERT_EQ(H264_NALU_STAP_A, hdr.type); + + err = h264_stap_decode_annexb(mb_frame, mb_pkt); + ASSERT_EQ(0, err); + + TEST_MEMCMP(frame, sizeof(frame), mb_frame->buf, mb_frame->end); + + out: + mem_deref(mb_frame); + mem_deref(mb_pkt); + + return err; +} + + +static int test_h264_stap_a_decode(void) +{ + static const uint8_t pkt[] = { + + /* SPS */ + 0x00, 0x0e, + 0x67, 0x42, 0xc0, 0x1f, 0x8c, 0x8d, + 0x40, 0x50, 0x1e, 0xd0, 0x0f, 0x08, + 0x84, 0x6a, + + /* PPS */ + 0x00, 0x04, + 0x68, 0xce, 0x3c, 0x80, + + /* AUD */ + 0x00, 0x02, + 0x09, 0x10, + }; + struct mbuf *mb_pkt = mbuf_alloc(256); + struct mbuf *mb_frame = mbuf_alloc(256); + struct mbuf *mb_pkt2 = mbuf_alloc(256); + int err; + + if (!mb_pkt || !mb_frame || !mb_pkt2) { + err = ENOMEM; + goto out; + } + + err = mbuf_write_mem(mb_pkt, pkt, sizeof(pkt)); + ASSERT_EQ(0, err); + + mb_pkt->pos = 0; + + err = h264_stap_decode_annexb(mb_frame, mb_pkt); + TEST_ERR(err); + + err = h264_stap_encode(mb_pkt2, mb_frame->buf, mb_frame->end); + ASSERT_EQ(0, err); + + TEST_MEMCMP(pkt, sizeof(pkt), mb_pkt2->buf+1, mb_pkt2->end-1); + + out: + mem_deref(mb_frame); + mem_deref(mb_pkt2); + mem_deref(mb_pkt); + + return err; +} + + +int test_h264(void) +{ + struct h264_nal_header hdr, hdr2; + struct mbuf *mb; + static const uint8_t nal = 0x25; + int err; + + mb = mbuf_alloc(1); + if (!mb) + return ENOMEM; + + hdr.f = 0; + hdr.nri = 1; + hdr.type = H264_NALU_IDR_SLICE; + + err = h264_nal_header_encode(mb, &hdr); + if (err) + goto out; + + TEST_EQUALS(1, mb->pos); + TEST_EQUALS(1, mb->end); + TEST_EQUALS(nal, mb->buf[0]); + + mb->pos = 0; + + err = h264_nal_header_decode(&hdr2, mb); + if (err) + goto out; + + TEST_EQUALS(1, mb->pos); + TEST_EQUALS(1, mb->end); + + TEST_EQUALS(0, hdr2.f); + TEST_EQUALS(1, hdr2.nri); + TEST_EQUALS(5, hdr2.type); + + err = test_h264_stap_a_encode(); + if (err) + goto out; + + err = test_h264_stap_a_decode(); + if (err) + goto out; + + out: + mem_deref(mb); + return err; +} + + +int test_h264_sps(void) +{ + static const struct test { + const char *buf; + struct h264_sps sps; + struct vidsz size; + } testv[] = { + + /* sony 1920 x 1080 (scaling list) + * + * sps:0 profile:122/41 poc:0 ref:0 120x68 MB-AFF 8B8 + * crop:0/0/0/8 VUI 422 1/50 b10 reo:-1 + * + */ + { + .buf = + "7a1029b6d420223319c6632321011198ce33191821033a46" + "656a6524ade91232141a2634ada441822301502b1a246948" + "30402e111208c68c0441284c34f01e0113f2e03c60202028" + "0000030008000003019420", + .sps = { + 122,41,0,2, + 4,0,0,120,68, + 0,0,0,8 + }, + .size = {1920, 1080} + }, + + /* rv + * + * sps:0 profile:66/52 poc:2 ref:1 120x68 FRM 8B8 + * crop:0/0/0/8 VUI 420 1/360 b8 reo:0 + */ + { + .buf = "42c034da01e0089f961000000300", + .sps = { + 66,52,0,1, + 4,2,1,120,68, + 0,0,0,8 + }, + .size = {1920, 1080} + }, + + /* confcall + * + * sps:0 profile:100/40 poc:0 ref:3 120x68 FRM + * 8B8 crop:0/0/0/8 VUI 420 1/60 b8 reo:1 + */ + + { + .buf = + "640028acd100780227e5c05a808080" + "a0000003002000000781e3062240", + .sps = { + 100,40,0,1, + 4,0,3,120,68, + 0,0,0,8 + }, + .size = {1920, 1080} + }, + + /* expert + * + * sps:0 profile:100/31 poc:0 ref:4 80x45 FRM + */ + { + .buf = + "64001facd9405005bb011000000300100000030320f1831960", + .sps = { + 100,31,0,1, + 4,0,4,80,45, + 0,0,0,0 + }, + .size = {1280, 720} + }, + + /* px + * + * sps:0 profile:66/31 poc:2 ref:1 80x45 FRM + * crop:0/0/0/0 VUI 420 2000/120000 b8 reo:0 + */ + { + .buf = + "42c01f95a014016c8400001f40000753023c2211a8", + .sps = { + 66,31,0,1, + 8,2,1,80,45, + 0,0,0,0 + }, + .size = {1280, 720} + }, + + /* allonsy 854x480 + * + * sps:0 profile:77/30 poc:0 ref:3 54x30 FRM 8B8 + * crop:0/10/0/0 VUI 420 1/50 b8 reo:1 + * + */ + { + .buf = + "4d401ee8806c1ef37808800000030080000019078b1689", + .sps = { + 77,30,0,1, + 4,0,3,54,30, + 0,10,0,0 + }, + .size = {854, 480} + }, + + /* sony 1920x1080 + * + * sps:0 profile:122/40 poc:0 ref:4 120x68 FRM 8B8 + * crop:0/0/0/8 VUI 422 1/50 b10 reo:2 + * + */ + { + .buf = + "7a0028b6cd940780227e2701100" + "0000300100000030320f1831960", + .sps = { + 122,40,0,2, + 4,0,4,120,68, + 0,0,0,8 + }, + .size = {1920, 1080} + }, + + /* testsrc2 yuv444 400x200 + * + * sps:0 profile:244/13 poc:0 ref:4 25x13 FRM 8B8 + * crop:0/0/0/8 VUI 444 1/50 b8 reo:2 + * + */ + { + .buf = + "f4000d919b283237f13808800000030080000019078a14cb", + .sps = { + 244,13,0,3, + 4,0,4,25,13, + 0,0,0,8 + }, + .size = {400, 200} + }, + + /* jellyfish 4K 3840 x 2160 + * + * sps:0 profile:100/51 poc:0 ref:3 240x135 FRM 8B8 + * crop:0/0/0/0 VUI 420 1001/60000 b8 reo:1 + * + */ + { + .buf = + "640033ac2ca400f0010fbff0001000152020202800001f" + "4800075307510001cd9400000568bc37e31c1da162d120", + .sps = { + 100,51,0,1, + 8,0,3,240,135, + 0,0,0,0 + }, + .size = {3840, 2160} + }, + }; + const struct test *test_short; + struct h264_sps sps; + uint8_t buf[256]; + size_t i; + size_t max_len; + int e, err; + + for (i=0; isps; + size_t len = str_len(test->buf)/2; + struct vidsz size; + + err = str_hex(buf, len, test->buf); + if (err) + return err; + + err = h264_sps_decode(&sps, buf, len); + if (err) + return err; + + h264_sps_resolution(&sps, &size.w, &size.h); + + TEST_EQUALS(ref.profile_idc, sps.profile_idc); + + TEST_EQUALS(ref.level_idc, sps.level_idc); + + TEST_EQUALS(ref.seq_parameter_set_id, + sps.seq_parameter_set_id); + + TEST_EQUALS(ref.chroma_format_idc, + sps.chroma_format_idc); + + TEST_EQUALS(ref.log2_max_frame_num, + sps.log2_max_frame_num); + + TEST_EQUALS(ref.pic_order_cnt_type, + sps.pic_order_cnt_type); + + TEST_EQUALS(ref.max_num_ref_frames, + sps.max_num_ref_frames); + + TEST_EQUALS(ref.pic_width_in_mbs, + sps.pic_width_in_mbs); + + TEST_EQUALS(ref.pic_height_in_map_units, + sps.pic_height_in_map_units); + + TEST_EQUALS(ref.frame_crop_left_offset, + sps.frame_crop_left_offset); + TEST_EQUALS(ref.frame_crop_right_offset, + sps.frame_crop_right_offset); + TEST_EQUALS(ref.frame_crop_top_offset, + sps.frame_crop_top_offset); + TEST_EQUALS(ref.frame_crop_bottom_offset, + sps.frame_crop_bottom_offset); + + /* verify correct resolution */ + TEST_EQUALS(test->size.w, size.w); + TEST_EQUALS(test->size.h, size.h); + } + + test_short = &testv[0]; + max_len = str_len(test_short->buf) / 2; + + err = str_hex(buf, max_len, test_short->buf); + if (err) + return err; + + for (i = 1; i <= max_len; i++) { + + size_t len = i; + + e = h264_sps_decode(&sps, buf, len); + + switch (e) { + + case EBADMSG: + case EINVAL: + case 0: + break; + + default: + DEBUG_WARNING("unexpected error code %d (%m)\n", + e, e); + return EINVAL; + } + } + + out: + return err; +} + + +struct state { + + /* depacketizer */ + struct mbuf *mb; + size_t frag_start; + bool frag; + + /* test */ + uint8_t buf[256]; + size_t len; + unsigned count; + bool complete; +}; + + +static void fragment_rewind(struct state *vds) +{ + vds->mb->pos = vds->frag_start; + vds->mb->end = vds->frag_start; +} + + +static int depack_handle_h264(struct state *st, bool marker, + struct mbuf *src) +{ + static const uint8_t nal_seq[3] = {0, 0, 1}; + struct h264_nal_header h264_hdr; + int err; + + err = h264_nal_header_decode(&h264_hdr, src); + if (err) + return err; + +#if 0 + re_printf("decode: %s %s type=%2d %s \n", + marker ? "[M]" : " ", + h264_is_keyframe(h264_hdr.type) ? "" : " ", + h264_hdr.type, + h264_nal_unit_name(h264_hdr.type)); +#endif + + if (h264_hdr.f) { + DEBUG_WARNING("H264 forbidden bit set!\n"); + return EBADMSG; + } + + /* handle NAL types */ + if (1 <= h264_hdr.type && h264_hdr.type <= 23) { + + --src->pos; + + /* prepend H.264 NAL start sequence */ + err = mbuf_write_mem(st->mb, nal_seq, sizeof(nal_seq)); + + err |= mbuf_write_mem(st->mb, mbuf_buf(src), + mbuf_get_left(src)); + if (err) + goto out; + } + else if (H264_NALU_FU_A == h264_hdr.type) { + + struct h264_fu fu; + + err = h264_fu_hdr_decode(&fu, src); + if (err) + return err; + + h264_hdr.type = fu.type; + + if (fu.s) { + if (st->frag) { + DEBUG_WARNING("start: lost fragments;" + " ignoring previous NAL\n"); + fragment_rewind(st); + } + + st->frag_start = st->mb->pos; + st->frag = true; + + /* prepend H.264 NAL start sequence */ + mbuf_write_mem(st->mb, nal_seq, sizeof(nal_seq)); + + /* encode NAL header back to buffer */ + err = h264_nal_header_encode(st->mb, &h264_hdr); + if (err) + goto out; + } + else { + if (!st->frag) { + re_printf("ignoring fragment" + " (nal=%u)\n", fu.type); + return 0; + } + } + + err = mbuf_write_mem(st->mb, mbuf_buf(src), + mbuf_get_left(src)); + if (err) + goto out; + + if (fu.e) + st->frag = false; + } + else if (H264_NALU_STAP_A == h264_hdr.type) { + + while (mbuf_get_left(src) >= 2) { + + uint16_t len = ntohs(mbuf_read_u16(src)); + struct h264_nal_header lhdr; + + if (mbuf_get_left(src) < len) + return EBADMSG; + + err = h264_nal_header_decode(&lhdr, src); + if (err) + return err; + + --src->pos; + + err = mbuf_write_mem(st->mb, + nal_seq, sizeof(nal_seq)); + err |= mbuf_write_mem(st->mb, mbuf_buf(src), len); + if (err) + goto out; + + src->pos += len; + } + } + else { + DEBUG_WARNING("decode: unknown NAL type %u\n", + h264_hdr.type); + return EBADMSG; + } + + if (!marker) + return 0; + + /* verify complete packet */ + st->complete = true; + TEST_MEMCMP(st->buf, st->len, st->mb->buf, st->mb->end); + + out: + mbuf_rewind(st->mb); + st->frag = false; + + return err; +} + + +enum { DUMMY_TS = 36000 }; + + +static int packet_handler(bool marker, uint64_t rtp_ts, + const uint8_t *hdr, size_t hdr_len, + const uint8_t *pld, size_t pld_len, + void *arg) +{ + struct state *state = arg; + struct mbuf *mb_pkt = mbuf_alloc(hdr_len + pld_len); + int err; + + if (!mb_pkt) + return ENOMEM; + + ASSERT_EQ(DUMMY_TS, rtp_ts); + + ++state->count; + + err = mbuf_write_mem(mb_pkt, hdr, hdr_len); + err |= mbuf_write_mem(mb_pkt, pld, pld_len); + if (err) + goto out; + + mb_pkt->pos = 0; + + err = depack_handle_h264(state, marker, mb_pkt); + + out: + mem_deref(mb_pkt); + return err; +} + + +/* bitstream in Annex-B format (with startcode 00 00 01) */ +static const char *bitstream = "000001650010e2238712983719283719823798"; + + +int test_h264_packet(void) +{ + struct state state; + const size_t pktsize = 8; + int err; + + memset(&state, 0, sizeof(state)); + + state.len = strlen(bitstream)/2; + + err = str_hex(state.buf, state.len, bitstream); + if (err) + return err; + + state.mb = mbuf_alloc(1024); + if (!state.mb) + return ENOMEM; + + err = h264_packetize(DUMMY_TS, state.buf, state.len, pktsize, + packet_handler, &state); + if (err) + goto out; + + ASSERT_TRUE(state.count >= 1); + ASSERT_TRUE(state.complete); + + out: + mem_deref(state.mb); + + return err; +} diff --git a/test/h265.c b/test/h265.c new file mode 100644 index 000000000..b934eac43 --- /dev/null +++ b/test/h265.c @@ -0,0 +1,279 @@ +/** + * @file h265.c H.265 Testcode + * + * Copyright (C) 2010 - 2022 Alfred E. Heggestad + */ + +#include +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "h265test" +#define DEBUG_LEVEL 5 +#include + + +int test_h265(void) +{ + uint8_t buf[H265_HDR_SIZE]; + struct h265_nal hdr; + enum {TID = 1}; + int err; + + h265_nal_encode(buf, H265_NAL_VPS_NUT, TID); + + err = h265_nal_decode(&hdr, buf); + if (err) + goto out; + + ASSERT_EQ(32, hdr.nal_unit_type); + ASSERT_EQ(TID, hdr.nuh_temporal_id_plus1); + + out: + return err; +} + + +enum { + H265_FU_HDR_SIZE = 1 +}; + +struct h265_fu { + unsigned s:1; + unsigned e:1; + unsigned type:6; +}; + + +static inline int h265_fu_decode(struct h265_fu *fu, struct mbuf *mb) +{ + uint8_t v; + + if (mbuf_get_left(mb) < 1) + return EBADMSG; + + v = mbuf_read_u8(mb); + + fu->s = v>>7 & 0x1; + fu->e = v>>6 & 0x1; + fu->type = v>>0 & 0x3f; + + return 0; +} + + +struct state { + + /* depacketizer */ + struct mbuf *mb; + size_t frag_start; + bool frag; + + /* test */ + uint8_t buf[256]; + size_t len; + unsigned count; + bool complete; +}; + + +static void fragment_rewind(struct state *vds) +{ + vds->mb->pos = vds->frag_start; + vds->mb->end = vds->frag_start; +} + + +static int depack_handle_h265(struct state *st, bool marker, + struct mbuf *mb) +{ + static const uint8_t nal_seq[3] = {0, 0, 1}; + struct h265_nal hdr; + int err; + + if (mbuf_get_left(mb) < H265_HDR_SIZE) + return EBADMSG; + + err = h265_nal_decode(&hdr, mbuf_buf(mb)); + if (err) + return err; + + mbuf_advance(mb, H265_HDR_SIZE); + +#if 0 + re_printf("h265: decode: [%s] %s type=%2d %s\n", + marker ? "M" : " ", + h265_is_keyframe(hdr.nal_unit_type) ? "" : " ", + hdr.nal_unit_type, + h265_nalunit_name(hdr.nal_unit_type)); +#endif + + if (st->frag && hdr.nal_unit_type != H265_NAL_FU) { + re_printf("h265: lost fragments; discarding previous NAL\n"); + fragment_rewind(st); + st->frag = false; + } + + /* handle NAL types */ + if (hdr.nal_unit_type <= 40) { + + mb->pos -= H265_HDR_SIZE; + + err = mbuf_write_mem(st->mb, nal_seq, 3); + err |= mbuf_write_mem(st->mb, mbuf_buf(mb),mbuf_get_left(mb)); + if (err) + goto out; + } + else if (H265_NAL_FU == hdr.nal_unit_type) { + + struct h265_fu fu; + + err = h265_fu_decode(&fu, mb); + if (err) + return err; + + if (fu.s) { + if (st->frag) { + DEBUG_WARNING("h265: lost fragments;" + " ignoring NAL\n"); + fragment_rewind(st); + } + + st->frag_start = st->mb->pos; + st->frag = true; + + hdr.nal_unit_type = fu.type; + + err = mbuf_write_mem(st->mb, nal_seq, 3); + err |= h265_nal_encode_mbuf(st->mb, &hdr); + if (err) + goto out; + } + else { + if (!st->frag) { + re_printf("h265: ignoring fragment\n"); + return 0; + } + } + + err = mbuf_write_mem(st->mb, mbuf_buf(mb), mbuf_get_left(mb)); + if (err) + goto out; + + if (fu.e) + st->frag = false; + } + else if (hdr.nal_unit_type == H265_NAL_AP) { + + while (mbuf_get_left(mb) >= 2) { + + const uint16_t len = ntohs(mbuf_read_u16(mb)); + + if (mbuf_get_left(mb) < len) + return EBADMSG; + + err = mbuf_write_mem(st->mb, nal_seq, 3); + err |= mbuf_write_mem(st->mb, mbuf_buf(mb), len); + if (err) + goto out; + + mb->pos += len; + } + } + else { + DEBUG_WARNING("unknown H265 NAL type %u (%s) [%zu bytes]\n", + hdr.nal_unit_type, + h265_nalunit_name(hdr.nal_unit_type), + mbuf_get_left(mb)); + return EPROTO; + } + + if (!marker) + return 0; + + /* verify complete packet */ + st->complete = true; + TEST_MEMCMP(st->buf, st->len, st->mb->buf, st->mb->end); + + out: + mbuf_rewind(st->mb); + st->frag = false; + + return err; +} + + +enum { DUMMY_TS = 36000 }; + + +static int packet_handler(bool marker, uint64_t rtp_ts, + const uint8_t *hdr, size_t hdr_len, + const uint8_t *pld, size_t pld_len, + void *arg) +{ + struct state *state = arg; + struct mbuf *mb_pkt = mbuf_alloc(hdr_len + pld_len); + int err; + + if (!mb_pkt) + return ENOMEM; + + ASSERT_EQ(DUMMY_TS, rtp_ts); + + ++state->count; + + err = mbuf_write_mem(mb_pkt, hdr, hdr_len); + err |= mbuf_write_mem(mb_pkt, pld, pld_len); + if (err) + goto out; + + mb_pkt->pos = 0; + + err = depack_handle_h265(state, marker, mb_pkt); + + out: + mem_deref(mb_pkt); + return err; +} + + +/* bitstream in Annex-B format (with startcode 00 00 01) */ +/* H265_NAL_VPS_NUT */ +static const char *bitstream = + "00000140010c01ffff01600000030090000003000003003cba024000000001"; + + +int test_h265_packet(void) +{ + struct state state; + const size_t pktsize = 8; + int err; + + memset(&state, 0, sizeof(state)); + + state.len = strlen(bitstream)/2; + + err = str_hex(state.buf, state.len, bitstream); + if (err) + return err; + + state.mb = mbuf_alloc(1024); + if (!state.mb) + return ENOMEM; + + err = h265_packetize(DUMMY_TS, state.buf, state.len, pktsize, + packet_handler, &state); + if (err) + goto out; + + ASSERT_TRUE(state.count >= 1); + ASSERT_TRUE(state.complete); + + out: + mem_deref(state.mb); + + return err; +} diff --git a/test/hash.c b/test/hash.c new file mode 100644 index 000000000..661c2f81a --- /dev/null +++ b/test/hash.c @@ -0,0 +1,235 @@ +/** + * @file hash.c Hash Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testhash" +#define DEBUG_LEVEL 4 +#include + + +struct my_elem { + struct le he; + const struct pl *name; /* hash key */ +}; + +const struct pl martin = PL("Martin"), + alfred = PL("Alfred"), + atle = PL("Atle"); + + +static bool hash_cmp_handler(struct le *le, void *arg) +{ + const struct my_elem *me = le->data; + const struct pl *name = arg; + + return 0==pl_cmp(me->name, name); +} + + +static int test_hash_basic(void) +{ +#define BUCKET_SIZE 4 + struct my_elem elems[3]; + struct hash *h; + struct my_elem *elem; + int err; + + /* Clear hash elements */ + memset(elems, 0, sizeof(elems)); + + elems[0].name = &martin; + elems[1].name = &alfred; + elems[2].name = &atle; + + err = hash_alloc(&h, BUCKET_SIZE); + if (err) + return err; + + TEST_EQUALS(BUCKET_SIZE, hash_bsize(h)); + + /* API test */ + if (hash_lookup(NULL, hash_joaat_pl(elems[0].name), + hash_cmp_handler, (void *)elems[0].name)) { + err = EINVAL; + goto out; + } + if (hash_lookup(h, hash_joaat_pl(elems[0].name), + NULL, (void *)elems[0].name)) { + err = EINVAL; + goto out; + } + + /* Hashtable is empty */ + hash_unlink(&elems[0].he); + hash_unlink(&elems[1].he); + + /* Hashtable with 1 element */ + hash_append(h, hash_joaat_pl(elems[0].name), &elems[0].he, &elems[0]); + + elem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[0].name), + hash_cmp_handler, + (void *)elems[0].name)); + if (elem != &elems[0]) { + err = EINVAL; + goto out; + } + + elem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[1].name), + hash_cmp_handler, + (void *)elems[1].name)); + if (elem) { + err = EINVAL; + goto out; + } + + /* Hashtable with 2 elements */ + hash_append(h, hash_joaat_pl(elems[1].name), &elems[1].he, &elems[1]); + + elem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[0].name), + hash_cmp_handler, + (void *)elems[0].name)); + if (elem != &elems[0]) { + err = EINVAL; + goto out; + } + + elem = list_ledata(hash_lookup(h, hash_joaat_pl(elems[1].name), + hash_cmp_handler, + (void *)elems[1].name)); + if (elem != &elems[1]) { + err = EINVAL; + goto out; + } + + hash_unlink(&elems[0].he); + hash_unlink(&elems[1].he); + + err = 0; + out: + h = mem_deref(h); + return err; +} + + +static int test_hash_robustapi(void) +{ + struct hash *h = NULL; + struct le he; + int err = 0; + + TEST_EQUALS(EINVAL, hash_alloc(NULL, 4)); + TEST_EQUALS(EINVAL, hash_alloc(&h, 0)); + + hash_append(h, 0, NULL, NULL); + hash_append(NULL, 0, &he, NULL); + + hash_unlink(NULL); + + out: + return err; +} + + +#define MAGIC1 0x7fbb0001 +#define MAGIC2 0x7fbb0002 +struct object { + uint32_t magic1; + struct le he; + uint32_t magic2; + char buffer[32]; + uint32_t key; +}; + + +static void obj_destructor(void *arg) +{ + struct object *obj = arg; + + list_unlink(&obj->he); +} + + +static bool cmp_handler(struct le *le, void *arg) +{ + struct object *obj = le->data; + + return obj->key == *(uint32_t *)arg; +} + + +static int test_hash_large(void) +{ +#define SZ 8 +#define NUM_ENTRIES SZ*SZ + struct hash *ht = NULL; + unsigned i; + int err = 0; + + err = hash_alloc(&ht, SZ); + if (err) + goto out; + + /* add a lot of objects to hash-table */ + for (i=0; imagic1 = MAGIC1; + obj->magic2 = MAGIC2; + obj->key = key; + + /* ownership of 'obj' transferred to the hash-table */ + hash_append(ht, key, &obj->he, obj); + } + + /* verify that all objects can be found */ + for (i=0; imagic1); + TEST_EQUALS(MAGIC2, obj->magic2); + TEST_EQUALS(i, obj->key); + } + + out: + hash_flush(ht); /* destroys all the objects */ + mem_deref(ht); + + return err; +} + + +int test_hash(void) +{ + int err; + + err = test_hash_basic(); + if (err) + return err; + + err = test_hash_robustapi(); + if (err) + return err; + + err = test_hash_large(); + if (err) + return err; + + return 0; +} diff --git a/test/hmac.c b/test/hmac.c new file mode 100644 index 000000000..6e4576d19 --- /dev/null +++ b/test/hmac.c @@ -0,0 +1,267 @@ +/** + * @file hmac.c HMAC Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testhmac" +#define DEBUG_LEVEL 4 +#include + + +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif + + +int test_hmac_sha1(void) +{ + /* RFC 2202 */ + const struct { + const void *key; + uint32_t key_len; + const void *data; + uint32_t data_len; + char digest[40 + 1]; + } testv[] = { + {"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b" + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", 20, + "Hi There", 8, + "b617318655057264e28bc0b6fb378c8ef146be00"}, + + {"Jefe", 4, + "what do ya want for nothing?", 28, + "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79"}, + + {"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", 20, + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", 50, + "125d7342b9ac11cd91a39af48aa17b4f63f175d3"}, + + {"01234567890123456789", 20, + "dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfgh66h" + "91928ha8shdoalijelwjeoriwjeorijwe98fj98j98j384jo" + "dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfgh66h" + "91928ha8shdoalijelwjeoriwjeorijwe98fj98j98j38s4f" + "dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfghsda" + "91928ha8shdoalijelwjeoriwjeorijwe98fj98j98j384jo" + "dalskdmlkasndoiqwjeoi3hjoijqweolk6y52fsdfsfgh66h" + "91928ha8shdoalijelwjeoriwjeorijwe98fj98jqwe98j38", 384, + "4b00628735c763b3c0dc398deb4370e99f822490"} + }; + struct hmac *hmac = NULL; + uint32_t i; + int err = 0; + + for (i=0; ikey) / 2; + uint8_t data[MAX_DATA_LEN]; + size_t data_len = str_len(test->data) / 2; + uint8_t digest[SHA256_DIGEST_LENGTH]; + uint8_t md[SHA256_DIGEST_LENGTH]; + + TEST_ASSERT(key_len <= sizeof(key)); + TEST_ASSERT(data_len <= sizeof(data)); + + err |= str_hex(key, key_len, test->key); + err |= str_hex(data, data_len, test->data); + err |= str_hex(digest, sizeof(digest), test->digest); + TEST_ERR(err); + + err = hmac_create(&hmac, HMAC_HASH_SHA256, key, key_len); + if (err) + break; + + err = hmac_digest(hmac, md, sizeof(md), data, data_len); + if (err) + break; + + TEST_MEMCMP(digest, sizeof(digest), md, sizeof(md)); + + hmac = mem_deref(hmac); + } + + out: + mem_deref(hmac); + + if (err == ENOTSUP) + err = ESKIPPED; + + return err; +} diff --git a/test/http.c b/test/http.c new file mode 100644 index 000000000..682bc46bc --- /dev/null +++ b/test/http.c @@ -0,0 +1,715 @@ +/** + * @file http.c HTTP Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_http" +#define DEBUG_LEVEL 5 +#include + +enum large_body_test { + REQ_BODY_CHUNK_SIZE = 26 * 42, + REQ_BODY_SIZE = REQ_BODY_CHUNK_SIZE * 480 - 26, + REQ_HTTP_REQUESTS = 2 +}; + +enum { + IP_127_0_0_1 = 0x7f000001, +}; + +static int test_http_response_no_reasonphrase(void) +{ + struct http_msg *msg = NULL; + struct mbuf *mb; + int err; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = mbuf_write_str(mb, /* _---- no space here! */ + "HTTP/1.1 429\r\n" + "Server: nginx\r\n" + "Content-Length: 0\r\n" + "\r\n"); + if (err) + goto out; + + mb->pos = 0; + + err = http_msg_decode(&msg, mb, false); + if (err) + goto out; + + TEST_STRCMP("1.1", 3, msg->ver.p, msg->ver.l); + TEST_EQUALS(429, msg->scode); + TEST_STRCMP("", 0, msg->reason.p, msg->reason.l); + + out: + mem_deref(msg); + mem_deref(mb); + return err; +} + + +int test_http(void) +{ + static const char req[] = + "GET /path/file.html HTTP/1.1\r\n" + "From: plopp@klukk.no\r\n" + "User-Agent: libre HTTP client/1.0\r\n" + "Allow: GET, HEAD, PUT\r\n" + "\r\n" + ""; + struct mbuf *mb; + struct http_msg *msg = NULL; + const struct http_hdr *hdr; + int err; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = mbuf_write_str(mb, req); + if (err) + goto out; + + mb->pos = 0; + err = http_msg_decode(&msg, mb, true); + if (err) + goto out; + + if (0 != pl_strcmp(&msg->met, "GET")) goto badmsg; + if (0 != pl_strcmp(&msg->path, "/path/file.html")) goto badmsg; + if (0 != pl_strcmp(&msg->ver, "1.1")) goto badmsg; + if (pl_isset(&msg->prm)) goto badmsg; + + hdr = http_msg_hdr(msg, HTTP_HDR_FROM); + if (!hdr || 0 != pl_strcmp(&hdr->val, "plopp@klukk.no")) + goto badmsg; + hdr = http_msg_hdr(msg, HTTP_HDR_USER_AGENT); + if (!hdr || 0 != pl_strcmp(&hdr->val, "libre HTTP client/1.0")) + goto badmsg; + hdr = http_msg_hdr(msg, HTTP_HDR_CONTENT_TYPE); + if (hdr) + goto badmsg; + if (msg->clen != 0) + goto badmsg; + + if (!http_msg_hdr_has_value(msg, HTTP_HDR_ALLOW, "GET") || + !http_msg_hdr_has_value(msg, HTTP_HDR_ALLOW, "HEAD") || + !http_msg_hdr_has_value(msg, HTTP_HDR_ALLOW, "PUT")) + goto badmsg; + if (3 != http_msg_hdr_count(msg, HTTP_HDR_ALLOW)) + goto badmsg; + + err = test_http_response_no_reasonphrase(); + if (err) + goto out; + + goto out; + + badmsg: + (void)re_fprintf(stderr, "%H\n", http_msg_print, msg); + err = EBADMSG; + + out: + mem_deref(msg); + mem_deref(mb); + + return err; +} + + +struct test { + struct mbuf *mb_body; + size_t clen; + uint32_t n_request; + uint32_t n_response; + size_t i_req_body; + bool secure; + int err; +}; + + +static void abort_test(struct test *t, int err) +{ + t->err = err; + re_cancel(); +} + + +static void http_req_handler(struct http_conn *conn, + const struct http_msg *msg, void *arg) +{ + struct test *t = arg; + struct mbuf *mb_body = mbuf_alloc(1024); + int err = 0; + + if (!mb_body) { + err = ENOMEM; + goto out; + } + + ++t->n_request; + + if (t->secure) { + TEST_ASSERT(http_conn_tls(conn) != NULL); + } + else { + TEST_ASSERT(http_conn_tls(conn) == NULL); + } + + /* verify HTTP request */ + TEST_STRCMP("1.1", 3, msg->ver.p, msg->ver.l); + TEST_STRCMP("GET", 3, msg->met.p, msg->met.l); + TEST_STRCMP("/index.html", 11, msg->path.p, msg->path.l); + TEST_STRCMP("", 0, msg->prm.p, msg->prm.l); + TEST_EQUALS(t->clen, msg->clen); + TEST_STRCMP("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", 52, + mbuf_buf(msg->mb), mbuf_get_left(msg->mb)); + + /* Create a chunked response body */ + err = mbuf_write_str(mb_body, + "2\r\n" + "ab\r\n" + + "4\r\n" + "cdef\r\n" + + "8\r\n" + "ghijklmn\r\n" + + "c\r\n" + "opqrstuvwxyz\r\n" + + "0\r\n" + "\r\n" + ); + if (err) + goto out; + + t->clen = mb_body->end; + + err = http_reply(conn, 200, "OK", + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + mb_body->end, + mb_body->buf, mb_body->end + ); + + out: + mem_deref(mb_body); + if (err) + abort_test(t, err); +} + + +static void http_put_req_handler(struct http_conn *conn, + const struct http_msg *msg, void *arg) +{ + struct test *t = arg; + struct mbuf *mb_body = mbuf_alloc(1024); + int err = 0; + size_t l = 0; + size_t cmp_len; + + if (!mb_body) { + err = ENOMEM; + goto out; + } + + ++t->n_request; + + if (t->secure) { + TEST_ASSERT(http_conn_tls(conn) != NULL); + } + else { + TEST_ASSERT(http_conn_tls(conn) == NULL); + } + + /* verify HTTP request */ + TEST_STRCMP("1.1", 3, msg->ver.p, msg->ver.l); + TEST_STRCMP("PUT", 3, msg->met.p, msg->met.l); + TEST_STRCMP("/index.html", 11, msg->path.p, msg->path.l); + TEST_STRCMP("", 0, msg->prm.p, msg->prm.l); + TEST_EQUALS(t->clen, msg->clen); + + l = mbuf_get_left(msg->mb); + + while (l > 0) { + cmp_len = min(l, 26); + TEST_STRCMP("abcdefghijklmnopqrstuvwxyz", cmp_len, + mbuf_buf(msg->mb), cmp_len); + mbuf_advance(msg->mb, cmp_len); + l -= cmp_len; + } + + /* Create a chunked response body */ + err = mbuf_write_str(mb_body, + "2\r\n" + "ab\r\n" + + "4\r\n" + "cdef\r\n" + + "8\r\n" + "ghijklmn\r\n" + + "c\r\n" + "opqrstuvwxyz\r\n" + + "0\r\n" + "\r\n" + ); + if (err) + goto out; + + t->clen = mb_body->end; + + err = http_reply(conn, 200, "OK", + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: %zu\r\n" + "\r\n" + "%b", + mb_body->end, + mb_body->buf, mb_body->end + ); + + out: + mem_deref(mb_body); + if (err) + abort_test(t, err); +} + + +static void http_resp_handler(int err, const struct http_msg *msg, void *arg) +{ + struct test *t = arg; + bool chunked; + + if (err) { + /* translate error code */ + err = ENOMEM; + goto out; + } + +#if 0 + re_printf("%H\n", http_msg_print, msg); + re_printf("BODY: %b\n", msg->mb->buf, msg->mb->end); +#endif + + ++t->n_response; + + /* verify HTTP response */ + TEST_STRCMP("1.1", 3, msg->ver.p, msg->ver.l); + TEST_STRCMP("", 0, msg->met.p, msg->met.l); + TEST_STRCMP("", 0, msg->path.p, msg->path.l); + TEST_STRCMP("", 0, msg->prm.p, msg->prm.l); + TEST_EQUALS(200, msg->scode); + TEST_STRCMP("OK", 2, msg->reason.p, msg->reason.l); + TEST_EQUALS(t->clen, msg->clen); + + chunked = http_msg_hdr_has_value(msg, HTTP_HDR_TRANSFER_ENCODING, + "chunked"); + TEST_ASSERT(chunked); + + TEST_STRCMP("text", 4, msg->ctyp.type.p, msg->ctyp.type.l); + TEST_STRCMP("plain", 5, msg->ctyp.subtype.p, msg->ctyp.subtype.l); + + re_cancel(); + + out: + if (err) + abort_test(t, err); +} + + +static int http_data_handler(const uint8_t *buf, size_t size, + const struct http_msg *msg, void *arg) +{ + struct test *t = arg; + (void)msg; + + if (!t->mb_body) + t->mb_body = mbuf_alloc(256); + if (!t->mb_body) + return 0; + + return mbuf_write_mem(t->mb_body, buf, size); +} + + +static size_t http_req_body_handler(struct mbuf *mb, void *arg) +{ + struct test *t = arg; + + if (t->i_req_body >= t->clen) + return 0; + + if (mbuf_write_mem(mb, + (const uint8_t*) "abcdefghijklmnopqrstuvwxyz", + strlen("abcdefghijklmnopqrstuvwxyz"))) { + mbuf_reset(mb); + return 0; + } + + t->i_req_body += strlen("abcdefghijklmnopqrstuvwxyz"); + return strlen("abcdefghijklmnopqrstuvwxyz"); +} + + +static size_t http_req_long_body_handler(struct mbuf *mb, void *arg) +{ + struct test *t = arg; + size_t l = 0; + size_t wlen; + + /* Create a chunked response body */ + while ( l < REQ_BODY_CHUNK_SIZE && t->i_req_body < t->clen) { + wlen = min(min(26, REQ_BODY_CHUNK_SIZE - l), + t->clen - t->i_req_body); + if (wlen <= 0) + return l; + + if (mbuf_write_mem(mb, + (const uint8_t*) "abcdefghijklmnopqrstuvwxyz", + wlen)) { + mbuf_reset(mb); + return 0; + } + l += wlen; + t->i_req_body += (uint32_t)wlen; + } + + return l; +} + + +static int test_http_loop_base(bool secure, const char *met, bool http_conn, + bool dns_srv_query, bool dns_set_conf_test) +{ + struct http_sock *sock = NULL; + struct http_cli *cli = NULL; + struct http_req *req = NULL; + struct http_reqconn *conn = NULL; + struct dnsc *dnsc = NULL; + struct dns_server *dns_srv = NULL; + struct sa srv, dns; + struct test t; + char url[256]; + char path[256]; + int err = 0; + unsigned int i; + bool put = false; + struct mbuf *mb_body = NULL; + struct pl pl; + struct dnsc_conf dconf; + struct http_conf hconf = { + 30000, + 30000, + 900000, + }; + + + if (!strcmp(met, "PUT")) + put = true; + + memset(&t, 0, sizeof(t)); + + t.secure = secure; + + if (dns_srv_query) { + /* Setup Mocking DNS Server */ + err = dns_server_alloc(&dns_srv, false); + TEST_ERR(err); + + err = dns_server_add_a(dns_srv, "test1.example.net", + IP_127_0_0_1, 1); + TEST_ERR(err); + } + + err |= sa_set_str(&srv, "127.0.0.1", 0); + err |= sa_set_str(&dns, "127.0.0.1", 53); /* note: unused */ + + if (secure) { + + re_snprintf(path, sizeof(path), "%s/server-ecdsa.pem", + test_datapath()); + + err = https_listen(&sock, &srv, path, + put ? http_put_req_handler : http_req_handler, &t); + } + else { + err = http_listen(&sock, &srv, + put ? http_put_req_handler : http_req_handler, &t); + } + if (err) + goto out; + + err = tcp_sock_local_get(http_sock_tcp(sock), &srv); + if (err) + goto out; + + err = dnsc_alloc(&dnsc, NULL, dns_srv ? &dns_srv->addr : &dns, 1); + if (err) + goto out; + + dconf.query_hash_size = 16; + dconf.tcp_hash_size = 2; + dconf.conn_timeout = hconf.conn_timeout; + dconf.idle_timeout = hconf.idle_timeout; + dconf.cache_ttl_max = 1800; + dconf.getaddrinfo = dnsc_getaddrinfo_enabled(dnsc); + + err = http_client_alloc(&cli, dnsc); + if (err) + goto out; + + if (put) + http_client_set_bufsize_max(cli, REQ_BODY_CHUNK_SIZE + 128); + +#ifdef USE_TLS + if (secure) { + err = http_client_add_ca(cli, path); + if (err) + goto out; + } +#endif + + (void)re_snprintf(url, sizeof(url), + "http%s://%s:%u/index.html", + secure ? "s" : "", + dns_srv_query ? "test1.example.net" : "127.0.0.1", + sa_port(&srv)); + + for (i = 1; i <= 10*REQ_HTTP_REQUESTS; i++) { + t.i_req_body = 0; + + err = http_client_set_config(cli, &hconf); + if (err) + goto out; + + if (dns_set_conf_test) { + err = dnsc_conf_set(dnsc, &dconf); + if (err) + goto out; + } + + t.clen = put ? REQ_BODY_SIZE : + 2 * strlen("abcdefghijklmnopqrstuvwxyz"); + + + if (http_conn) { + err = http_reqconn_alloc(&conn, cli, + http_resp_handler, http_data_handler, &t); + if (err) + goto out; + + if (put) { + err = http_reqconn_set_req_bodyh(conn, + put ? http_req_long_body_handler : + http_req_body_handler, + t.clen); + if (err) + goto out; + + pl_set_str(&pl, "PUT"); + err = http_reqconn_set_method(conn, &pl); + if (err) + goto out; + } + else { + mb_body = mbuf_alloc(t.clen); + if (!mb_body) + goto out; + + if (mbuf_write_str(mb_body, + "abcdefghijklmnopqrstuvwxyz"\ + "abcdefghijklmnopqrstuvwxyz")) + goto out; + + err = http_reqconn_set_body(conn, mb_body); + mb_body = mem_deref(mb_body); + if (err) + goto out; + } + + pl_set_str(&pl, url); + err = http_reqconn_send(conn, &pl); + } + else { + err = http_request(&req, cli, met, url, + http_resp_handler, http_data_handler, + put ? http_req_long_body_handler : + http_req_body_handler, + &t, + "Content-Length: %zu\r\n%s\r\n%s", + t.clen, + t.clen > REQ_BODY_CHUNK_SIZE ? + "Expect: 100-continue\r\n" : "", + "abcdefghijklmnopqrstuvwxyz"); + } + + if (err) + goto out; + + err = re_main_timeout(secure ? 1800 : 900); + if (err) + goto out; + + if (t.err) { + err = t.err; + goto out; + } + + /* verify results after HTTP traffic */ + TEST_EQUALS(i, t.n_request); + TEST_EQUALS(i, t.n_response); + + if (t.mb_body) + TEST_STRCMP("abcdefghijklmnopqrstuvwxyz", 26, + t.mb_body->buf, t.mb_body->end); + + t.mb_body = mem_deref(t.mb_body); + req = mem_deref(req); + conn = mem_deref(conn); + } + + out: + mem_deref(t.mb_body); + mem_deref(req); + mem_deref(conn); + mem_deref(cli); + mem_deref(dnsc); + mem_deref(sock); + mem_deref(dns_srv); + + return err; +} + + +#ifdef USE_TLS +int test_http_client_set_tls(void) +{ + struct sa dns; + struct dnsc *dnsc = NULL; + struct http_cli *cli = NULL; + struct tls *tls = NULL, *tls_test = NULL, *tls_cli = NULL; + int err; + + TEST_EINVAL(http_client_get_tls, NULL, NULL); + TEST_EINVAL(http_client_set_tls, NULL, NULL); + + /* Setup */ + err = sa_set_str(&dns, "127.0.0.1", 53); /* note: unused */ + TEST_ERR(err); + + err = dnsc_alloc(&dnsc, NULL, &dns, 1); + TEST_ERR(err); + + err = http_client_alloc(&cli, dnsc); + TEST_ERR(err); + + /* Test original Http Client TLS Context */ + TEST_EINVAL(http_client_get_tls, cli, NULL); + err = http_client_get_tls(cli, &tls_cli); + TEST_ERR(err); + tls_cli = mem_ref(tls_cli); + TEST_EQUALS(2, mem_nrefs(tls_cli)); + + /* Allocate new TLS Context */ + err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL); + TEST_ERR(err); + TEST_NOT_EQUALS(tls, tls_cli); + + /* Set and verify new TLS Context */ + TEST_EINVAL(http_client_set_tls, cli, NULL); + err = http_client_set_tls(cli, tls); + TEST_ERR(err); + TEST_EQUALS(2, mem_nrefs(tls)); + TEST_EQUALS(1, mem_nrefs(tls_cli)); + + err = http_client_get_tls(cli, &tls_test); + TEST_ERR(err); + + TEST_EQUALS(tls, tls_test); + +out: + if (cli) { + mem_deref(cli); + mem_deref(tls_cli); + } + + if (dnsc) + mem_deref(dnsc); + + if (tls) { + TEST_EQUALS(1, mem_nrefs(tls)); + mem_deref(tls); + } + + return err; +} +#endif + + +int test_http_loop(void) +{ + return test_http_loop_base(false, "GET", false, false, false); +} + + +#ifdef USE_TLS +int test_https_loop(void) +{ + return test_http_loop_base(true, "GET", false, false, false); +} +#endif + + +int test_http_large_body(void) +{ + return test_http_loop_base(false, "PUT", false, false, false); +} + + +#ifdef USE_TLS +int test_https_large_body(void) +{ + return test_http_loop_base(true, "PUT", false, false, false); +} +#endif + + +int test_http_conn(void) +{ + return test_http_loop_base(false, "GET", true, false, false); +} + + +int test_http_conn_large_body(void) +{ + return test_http_loop_base(false, "PUT", true, false, false); +} + + +int test_dns_http_integration(void) +{ + return test_http_loop_base(false, "GET", true, true, false); +} + + +int test_dns_cache_http_integration(void) +{ + return test_http_loop_base(false, "GET", true, true, true); +} diff --git a/test/httpauth.c b/test/httpauth.c new file mode 100644 index 000000000..a0afb53dc --- /dev/null +++ b/test/httpauth.c @@ -0,0 +1,219 @@ +/** + * @file httpauth.c HTTP Authentication Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "httpauth" +#define DEBUG_LEVEL 5 +#include + + +static int pl_equal(const char *name, const struct pl *a, const struct pl *b) +{ + static const struct pl plnil = PL("nil"); + int err; + + if ((a->p && !b->p) || (!a->p && b->p)) + err = EINVAL; + else + err = pl_cmp(a, b); + + if (err) { + DEBUG_WARNING("%s mismatch: '%r' vs '%r'\n", + name, + a->p ? a : &plnil, + b->p ? b : &plnil); + } + + return err; +} + + +static bool chall_equal(const struct httpauth_digest_chall *a, + const struct httpauth_digest_chall *b) +{ + int err = 0; + + err |= pl_equal("realm", &a->realm, &b->realm); + err |= pl_equal("nonce", &a->nonce, &b->nonce); + err |= pl_equal("opaque", &a->opaque, &b->opaque); + err |= pl_equal("stale", &a->stale, &b->stale); + err |= pl_equal("algorithm", &a->algorithm, &b->algorithm); + err |= pl_equal("qop", &a->qop, &b->qop); + + return err == 0; +} + + +static bool resp_equal(const struct httpauth_digest_resp *a, + const struct httpauth_digest_resp *b) +{ + int err = 0; + + err |= pl_equal("realm", &a->realm, &b->realm); + err |= pl_equal("nonce", &a->nonce, &b->nonce); + err |= pl_equal("response", &a->response, &b->response); + err |= pl_equal("username", &a->username, &b->username); + err |= pl_equal("uri", &a->uri, &b->uri); + err |= pl_equal("nc", &a->nc, &b->nc); + err |= pl_equal("cnonce", &a->cnonce, &b->cnonce); + err |= pl_equal("qop", &a->qop, &b->qop); + + return err == 0; +} + + +int test_httpauth_chall(void) +{ + static const struct { + const char *hval; + struct httpauth_digest_chall chall; + int err; + } testv[] = { + { + "Digest realm=\"realm\"," + " nonce=\"4ee102da2fb730e04a26e8da913249b264f391c3\"," + " opaque=\"123\", stale=\"true\"" + " algorithm=\"MD5\"" + , + {PL("realm"), + PL("4ee102da2fb730e04a26e8da913249b264f391c3"), + PL("123"), + PL("true"), + PL("MD5"), + PL_INIT}, + 0 + }, + + { + "Digest realm=\"creytiv.com\"," + " nonce=\"9c916919cbc6ad7f54a4f64e5b5115074ee109fa\"" + ", qop=\"auth\"", + {PL("creytiv.com"), + PL("9c916919cbc6ad7f54a4f64e5b5115074ee109fa"), + PL_INIT, PL_INIT, PL_INIT, + PL("auth"), + }, + 0 + }, + + { + "Basic bogus", + {PL_INIT, PL_INIT, PL_INIT, PL_INIT, PL_INIT, PL_INIT}, + EBADMSG + }, + }; + size_t i; + int err = 0; + + for (i=0; i +#include +#include "test.h" + + +#define DEBUG_MODULE "test_ice" +#define DEBUG_LEVEL 5 +#include + + +/* + * Protocol testcode for 2 ICE agents. We setup 2 ICE agents A and B, + * with only a basic host candidate. Gathering is done using a small + * STUN Server running on localhost, SDP is exchanged with Offer/Answer. + * Finally the connectivity checks are run and the result is verified. + * + * + * .-------------. + * | STUN Server | + * '-------------' + * STUN / \ STUN + * / \ + * / \ + * .-------------. .-------------. + * | ICE-Agent A |---------------------| ICE-Agent B | + * '-------------' Connectivity '-------------' + * checks + * + */ + + +struct attrs { + struct attr { + char name[16]; + char value[128]; + } attrv[16]; + unsigned attrc; +}; + +struct agent { + struct icem *icem; + struct udp_sock *us; + struct sa laddr; + struct attrs attr_s; + struct attrs attr_m; + struct ice_test *it; /* parent */ + struct stunserver *stun; + struct turnserver *turn; + char name[16]; + uint8_t compid; + bool offerer; + bool use_turn; + size_t n_cand; + + char lufrag[8]; + char lpwd[32]; + + /* results: */ + bool gathering_ok; + bool conncheck_ok; + + struct stun_ctrans *ct_gath; +}; + +struct ice_test { + struct agent *a; + struct agent *b; + struct tmr tmr; + int err; +}; + + +static void icetest_check_gatherings(struct ice_test *it); +static void icetest_check_connchecks(struct ice_test *it); + + +/* + * Test tools + */ + + +static void complete_test(struct ice_test *it, int err) +{ + it->err = err; + +#if 0 + re_printf("\n\x1b[32m%H\x1b[;m\n", icem_debug, it->a->icem); + re_printf("\n\x1b[36m%H\x1b[;m\n", icem_debug, it->b->icem); +#endif + + re_cancel(); +} + + +static bool find_debug_string(struct icem *icem, const char *str) +{ + char buf[1024]; + + if (re_snprintf(buf, sizeof(buf), "%H", icem_debug, icem) < 0) + return false; + + return 0 == re_regex(buf, strlen(buf), str); +} + + +static int attr_add(struct attrs *attrs, const char *name, + const char *value, ...) +{ + struct attr *attr = &attrs->attrv[attrs->attrc]; + va_list ap; + int r, err = 0; + + TEST_ASSERT(attrs->attrc <= RE_ARRAY_SIZE(attrs->attrv)); + + TEST_ASSERT(strlen(name) < sizeof(attr->name)); + str_ncpy(attr->name, name, sizeof(attr->name)); + + if (value) { + va_start(ap, value); + r = re_vsnprintf(attr->value, sizeof(attr->value), value, ap); + va_end(ap); + TEST_ASSERT(r > 0); + } + + attrs->attrc++; + + out: + return err; +} + + +static const char *attr_find(const struct attrs *attrs, const char *name) +{ + unsigned i; + + if (!attrs || !name) + return NULL; + + for (i=0; iattrc; i++) { + const struct attr *attr = &attrs->attrv[i]; + + if (0 == str_casecmp(attr->name, name)) + return attr->value; + } + + return NULL; +} + + +/* + * ICE Agent + */ + + +static void agent_destructor(void *arg) +{ + struct agent *agent = arg; + + mem_deref(agent->icem); + mem_deref(agent->us); + mem_deref(agent->stun); + mem_deref(agent->turn); +} + + +static struct agent *agent_other(struct agent *agent) +{ + if (agent->it->a == agent) + return agent->it->b; + else + return agent->it->a; +} + + +static int agent_encode_sdp(struct agent *ag) +{ + struct le *le; + int err = 0; + + for (le = icem_lcandl(ag->icem)->head; le; le = le->next) { + + struct cand *cand = le->data; + + err = attr_add(&ag->attr_m, "candidate", "%H", + ice_cand_encode, cand); + if (err) + break; + } + + err |= attr_add(&ag->attr_m, "ice-ufrag", ag->lufrag); + err |= attr_add(&ag->attr_m, "ice-pwd", ag->lpwd); + + return err; +} + + +static int agent_verify_outgoing_sdp(const struct agent *agent) +{ + const char *cand, *ufrag, *pwd; + char buf[1024]; + int err = 0; + + if (re_snprintf(buf, sizeof(buf), + "7f000001 %u UDP 2113929465 127.0.0.1 %u typ host", + agent->compid, sa_port(&agent->laddr)) < 0) { + return ENOMEM; + } + cand = attr_find(&agent->attr_m, "candidate"); + TEST_STRCMP(buf, str_len(buf), cand, str_len(cand)); + + ufrag = attr_find(&agent->attr_m, "ice-ufrag"); + pwd = attr_find(&agent->attr_m, "ice-pwd"); + TEST_STRCMP(agent->lufrag, str_len(agent->lufrag), + ufrag, str_len(ufrag)); + TEST_STRCMP(agent->lpwd, str_len(agent->lpwd), + pwd, str_len(pwd)); + + TEST_ASSERT(NULL == attr_find(&agent->attr_s, "ice-lite")); + + out: + return err; +} + + +static int agent_decode_sdp(struct agent *agent, struct agent *other) +{ + unsigned i; + int err = 0; + + for (i=0; iattr_s.attrc; i++) { + struct attr *attr = &other->attr_s.attrv[i]; + err = ice_sdp_decode(agent->icem, attr->name, attr->value); + if (err) + return err; + } + + for (i=0; iattr_m.attrc; i++) { + struct attr *attr = &other->attr_m.attrv[i]; + err = icem_sdp_decode(agent->icem, attr->name, attr->value); + if (err) + return err; + } + + return err; +} + + +static int send_sdp(struct agent *agent) +{ + int err; + + /* verify ICE states */ + TEST_ASSERT(!icem_mismatch(agent->icem)); + + /* after gathering is complete we expect: + * 1 local candidate + * 0 remote candidates + * checklist and validlist is empty + */ + TEST_EQUALS(agent->n_cand, list_count(icem_lcandl(agent->icem))); + TEST_EQUALS(0, list_count(icem_rcandl(agent->icem))); + TEST_EQUALS(0, list_count(icem_checkl(agent->icem))); + TEST_EQUALS(0, list_count(icem_validl(agent->icem))); + + if (agent->use_turn) { + /* verify that default candidate is the relayed address */ + TEST_SACMP(&agent->turn->relay, + icem_cand_default(agent->icem, agent->compid), + SA_ALL); + } + else { + /* verify that default candidate is our local address */ + TEST_SACMP(&agent->laddr, + icem_cand_default(agent->icem, agent->compid), + SA_ALL); + } + + /* we should not have selected candidate-pairs yet */ + TEST_ASSERT(!icem_selected_laddr(agent->icem, agent->compid)); + + err = agent_encode_sdp(agent); + if (err) + return err; + + err = agent_verify_outgoing_sdp(agent); + if (err) + return err; + + out: + return err; +} + + +static void agent_gather_handler(int err, uint16_t scode, const char *reason, + void *arg) +{ + struct agent *agent = arg; + + if (err) + goto out; + if (scode) { + DEBUG_WARNING("gathering failed: %u %s\n", scode, reason); + complete_test(agent->it, EPROTO); + return; + } + + /* Eliminate redundant local candidates */ + icem_cand_redund_elim(agent->icem); + + err = icem_comps_set_default_cand(agent->icem); + if (err) { + DEBUG_WARNING("ice: set default cands failed (%m)\n", err); + goto out; + } + + agent->gathering_ok = true; + + err = send_sdp(agent); + if (err) + goto out; + + icetest_check_gatherings(agent->it); + + return; + + out: + complete_test(agent->it, err); +} + + +static void stun_resp_handler(int err, uint16_t scode, const char *reason, + const struct stun_msg *msg, void *arg) +{ + struct agent *ag = arg; + struct stun_attr *attr; + struct ice_cand *lcand; + + if (err || scode > 0) { + DEBUG_WARNING("STUN Request failed: %m\n", err); + goto out; + } + + /* base candidate */ + lcand = icem_cand_find(icem_lcandl(ag->icem), ag->compid, NULL); + if (!lcand) + goto out; + + attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); + if (!attr) + attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); + if (!attr) { + DEBUG_WARNING("no Mapped Address in Response\n"); + err = EPROTO; + goto out; + } + + err = icem_lcand_add(ag->icem, icem_lcand_base(lcand), + ICE_CAND_TYPE_SRFLX, + &attr->v.sa); + + out: + agent_gather_handler(err, scode, reason, ag); +} + + +static int icem_gather_srflx(struct agent *ag, const struct sa *srv) +{ + int err; + + err = stun_request(&ag->ct_gath, icem_stun(ag->icem), IPPROTO_UDP, + ag->us, srv, 0, + STUN_METHOD_BINDING, + NULL, false, 0, + stun_resp_handler, ag, 1, + STUN_ATTR_SOFTWARE, stun_software); + if (err) + return err; + + return 0; +} + + +static void agent_connchk_handler(int err, bool update, void *arg) +{ + struct agent *agent = arg; + struct agent *other = agent_other(agent); + const struct sa *laddr, *raddr; + + if (err) { + if (err != ENOMEM) { + DEBUG_WARNING("%s: connectivity checks failed: %m\n", + agent->name, err); + } + + complete_test(agent->it, err); + return; + } + + if (agent->offerer ^ update) { + DEBUG_WARNING("error in update flag\n"); + complete_test(agent->it, EPROTO); + return; + } + + agent->conncheck_ok = true; + + /* verify ICE states */ + TEST_ASSERT(!icem_mismatch(agent->icem)); + + /* after connectivity checks are complete we expect: + * 1 local candidate + * 1 remote candidates + */ + TEST_EQUALS(agent->n_cand, list_count(icem_lcandl(agent->icem))); + TEST_EQUALS(other->n_cand, list_count(icem_rcandl(agent->icem))); + TEST_EQUALS(0, list_count(icem_checkl(agent->icem))); + TEST_EQUALS(agent->n_cand * other->n_cand, + list_count(icem_validl(agent->icem))); + + laddr = icem_selected_laddr(agent->icem, agent->compid); + raddr = &agent_other(agent)->laddr; + + if (!sa_cmp(&agent->laddr, laddr, SA_ALL)) { + DEBUG_WARNING("unexpected selected address: %J\n", laddr); + complete_test(agent->it, EPROTO); + return; + } + + if (!icem_verify_support(agent->icem, agent->compid, raddr)) { + complete_test(agent->it, EPROTO); + return; + } + +#if 0 + (void)re_printf("Agent %s -- Selected address: local=%J remote=%J\n", + agent->name, laddr, raddr); +#endif + + icetest_check_connchecks(agent->it); + + out: + if (err) + complete_test(agent->it, err); +} + + +static int agent_alloc(struct agent **agentp, struct ice_test *it, + bool use_turn, + const char *name, uint8_t compid, bool offerer) +{ + struct agent *agent; + enum ice_role lrole; + uint64_t tiebrk; + int err; + + agent = mem_zalloc(sizeof(*agent), agent_destructor); + if (!agent) + return ENOMEM; + + re_snprintf(agent->lufrag, sizeof(agent->lufrag), + "ufrag-%s", name); + re_snprintf(agent->lpwd, sizeof(agent->lpwd), + "password-0123456789abcdef-%s", name); + + agent->use_turn = use_turn; + agent->it = it; + str_ncpy(agent->name, name, sizeof(agent->name)); + agent->compid = compid; + agent->offerer = offerer; + + if (agent->use_turn) { + err = turnserver_alloc(&agent->turn); + if (err) + goto out; + } + else { + err = stunserver_alloc(&agent->stun); + if (err) + goto out; + } + + err = sa_set_str(&agent->laddr, "127.0.0.1", 0); + if (err) + goto out; + + err = udp_listen(&agent->us, &agent->laddr, 0, 0); + if (err) + goto out; + + err = udp_local_get(agent->us, &agent->laddr); + if (err) + goto out; + + lrole = offerer ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED; + tiebrk = offerer ? 2 : 1; + + err = icem_alloc(&agent->icem, lrole, IPPROTO_UDP, 0, + tiebrk, agent->lufrag, agent->lpwd, + agent_connchk_handler, agent); + if (err) + goto out; + +#if 0 + icem_conf(agent->icem)->debug = true; +#endif + + if (offerer) { + TEST_EQUALS(ICE_ROLE_CONTROLLING, + icem_local_role(agent->icem)); + } + else { + TEST_EQUALS(ICE_ROLE_CONTROLLED, + icem_local_role(agent->icem)); + } + + icem_set_name(agent->icem, name); + + err = icem_comp_add(agent->icem, compid, agent->us); + if (err) + goto out; + + err = icem_lcand_add_base(agent->icem, ICE_CAND_TYPE_HOST, compid, 0, + "eth0", ICE_TRANSP_UDP, &agent->laddr); + TEST_ERR(err); + + ++agent->n_cand; + + /* Start gathering now -- full mode only + * + * A lite implementation doesn't gather candidates; + * it includes only host candidates for any media stream. + */ + + if (agent->use_turn) { + + re_printf("turn disabled\n"); + } + else { + err = icem_gather_srflx(agent, + &agent->stun->laddr); + } + + if (err) + goto out; + + out: + if (err) + mem_deref(agent); + else + *agentp = agent; + + return err; +} + + +/* + * ICE Test + */ + + +static int verify_after_sdp_exchange(struct agent *agent) +{ + struct agent *other = agent_other(agent); + int err = 0; + + /* verify remote mode (after SDP exchange) */ + TEST_ASSERT(find_debug_string(agent->icem, + "remote_mode=Full")); + + /* verify ICE states */ + TEST_ASSERT(!icem_mismatch(agent->icem)); + + /* after SDP was exchanged, we expect: + * 1 local candidate + * 1 remote candidates + * checklist and validlist is empty + */ + TEST_EQUALS(agent->n_cand, list_count(icem_lcandl(agent->icem))); + TEST_EQUALS(other->n_cand, list_count(icem_rcandl(agent->icem))); + TEST_EQUALS(0, list_count(icem_checkl(agent->icem))); + TEST_EQUALS(0, list_count(icem_validl(agent->icem))); + + if (agent->use_turn) { + /* verify that default candidate is the relayed address */ + TEST_SACMP(&agent->turn->relay, + icem_cand_default(agent->icem, agent->compid), + SA_ALL); + } + else { + /* verify that default candidate is our local address */ + TEST_SACMP(&agent->laddr, + icem_cand_default(agent->icem, agent->compid), + SA_ALL); + } + + /* we should not have selected candidate-pairs yet */ + TEST_ASSERT(!icem_selected_laddr(agent->icem, agent->compid)); + + out: + if (err) { + DEBUG_WARNING("agent %s failed\n", agent->name); + } + return err; +} + + +static int agent_start(struct agent *agent) +{ + struct agent *other = agent_other(agent); + int err = 0; + + /* verify that check-list is empty before we start */ + TEST_EQUALS(0, list_count(icem_checkl(agent->icem))); + TEST_EQUALS(0, list_count(icem_validl(agent->icem))); + + err = icem_conncheck_start(agent->icem); + if (err) + return err; + + TEST_EQUALS(agent->n_cand * other->n_cand, + list_count(icem_checkl(agent->icem))); + + TEST_EQUALS(0, list_count(icem_validl(agent->icem))); + + out: + return err; +} + + +static int agent_verify_completed(struct agent *agent) +{ + struct agent *other = agent_other(agent); + uint32_t validc; + int err = 0; + + TEST_ASSERT(agent->gathering_ok); + TEST_ASSERT(agent->conncheck_ok); + + TEST_EQUALS(0, list_count(icem_checkl(agent->icem))); + validc = list_count(icem_validl(agent->icem)); + + TEST_EQUALS(agent->n_cand * other->n_cand, validc); + + /* verify state of STUN/TURN server */ + if (agent->use_turn) { + TEST_ASSERT(agent->turn->n_allocate >= 1); + TEST_ASSERT(agent->turn->n_chanbind >= 1); + } + else { + TEST_ASSERT(agent->stun->nrecv >= 1); + } + + out: + return err; +} + + +static void icetest_check_gatherings(struct ice_test *it) +{ + int err; + + if (!it->a->gathering_ok) + return; + if (!it->b->gathering_ok) + return; + + /* both gatherings are complete + * exchange SDP and start conncheck + */ + + err = agent_decode_sdp(it->a, it->b); + if (err) + goto out; + err = agent_decode_sdp(it->b, it->a); + if (err) + goto out; + + err = verify_after_sdp_exchange(it->a); + if (err) + goto error; + err = verify_after_sdp_exchange(it->b); + if (err) + goto error; + + err = agent_start(it->a); + if (err) + goto out; + err = agent_start(it->b); + if (err) + goto out; + + return; + + out: + error: + complete_test(it, err); +} + + +static void tmr_handler(void *arg) +{ + struct ice_test *it = arg; + +#if 0 + re_printf("\n\x1b[32m%H\x1b[;m\n", icem_debug, it->a->icem); + re_printf("\n\x1b[36m%H\x1b[;m\n", icem_debug, it->b->icem); +#endif + + complete_test(it, 0); +} + + +static void icetest_check_connchecks(struct ice_test *it) +{ + if (!it->a->conncheck_ok) + return; + if (!it->b->conncheck_ok) + return; + + /* start an async timer to let the socket traffic complete */ + tmr_start(&it->tmr, 1, tmr_handler, it); +} + + +static void icetest_destructor(void *arg) +{ + struct ice_test *it = arg; + + tmr_cancel(&it->tmr); + mem_deref(it->b); + mem_deref(it->a); +} + + +static int icetest_alloc(struct ice_test **itp, + bool turn_a, + bool turn_b) +{ + struct ice_test *it; + int err; + + it = mem_zalloc(sizeof(*it), icetest_destructor); + if (!it) + return ENOMEM; + + err = agent_alloc(&it->a, it, turn_a, "A", 7, true); + if (err) + goto out; + + err = agent_alloc(&it->b, it, turn_b, "B", 7, false); + if (err) + goto out; + + out: + if (err) + mem_deref(it); + else + *itp = it; + + return err; +} + + +static int _test_ice_loop(bool turn_a, + bool turn_b) +{ + struct ice_test *it = NULL; + int err; + + err = icetest_alloc(&it, turn_a, turn_b); + if (err) + goto out; + + err = re_main_timeout(300); + if (err) + goto out; + + /* read back global errorcode */ + if (it->err) { + err = it->err; + goto out; + } + + /* now verify all results after test was finished */ + err = agent_verify_completed(it->a); + err |= agent_verify_completed(it->b); + if (err) + goto out; + + out: + mem_deref(it); + + return err; +} + + +/* also verify that these symbols are exported */ +static int test_ice_basic_candidate(void) +{ + static const enum ice_cand_type typev[4] = { + ICE_CAND_TYPE_HOST, + ICE_CAND_TYPE_SRFLX, + ICE_CAND_TYPE_PRFLX, + ICE_CAND_TYPE_RELAY + }; + unsigned i; + int err = 0; + + for (i=0; i 0); + TEST_ASSERT(sa_isset(&cand.addr, SA_ALL)); + + n = re_snprintf(buf, sizeof(buf), "%H", + ice_cand_attr_encode, &cand); + if (n < 0) + return ENOMEM; + + TEST_STRCMP(testv[i], strlen(testv[i]), buf, (unsigned)n); + } + + out: + return err; +} + + +int test_ice_cand(void) +{ + int err = 0; + + err = test_ice_basic_candidate(); + if (err) + return err; + + err = test_ice_cand_prio(); + if (err) + return err; + + err = test_ice_cand_attribute(); + if (err) + return err; + + return err; +} + + +int test_ice_loop(void) +{ + return _test_ice_loop(false, false); +} diff --git a/test/jbuf.c b/test/jbuf.c new file mode 100644 index 000000000..800eb66bd --- /dev/null +++ b/test/jbuf.c @@ -0,0 +1,242 @@ +/** + * @file jbuf.c Jitterbuffer Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test jbuf" +#define DEBUG_LEVEL 5 +#include + +int test_jbuf(void) +{ + struct rtp_header hdr, hdr2; + struct jbuf *jb; + char *frv[3]; + uint32_t i; + void *mem = NULL; + int err; + + memset(frv, 0, sizeof(frv)); + + err = jbuf_alloc(&jb, 0, 10); + if (err) + return err; + + for (i=0; i min reached, read first frame\n"); + err = jbuf_get(jb, &hdr2, &mem); + TEST_ERR(err); + TEST_EQUALS(160, hdr2.seq); + TEST_EQUALS(mem, frv[0]); + mem = mem_deref(mem); + + DEBUG_INFO("n <= min, leads to ENOENT\n"); + TEST_EQUALS(ENOENT, jbuf_get(jb, &hdr2, &mem)); + + /* Four frames */ + DEBUG_INFO("test frame: Four frames\n"); + jbuf_flush(jb); + hdr.seq = 320; + err = jbuf_put(jb, &hdr, frv[0]); + TEST_ERR(err); + + hdr.seq = 480; + err = jbuf_put(jb, &hdr, frv[1]); + TEST_ERR(err); + + hdr.seq = 490; + err = jbuf_put(jb, &hdr, frv[2]); + TEST_ERR(err); + + hdr.seq = 491; + err = jbuf_put(jb, &hdr, frv[3]); + TEST_ERR(err); + + TEST_EQUALS(EAGAIN, jbuf_get(jb, &hdr2, &mem)); + TEST_EQUALS(320, hdr2.seq); + TEST_EQUALS(mem, frv[0]); + mem = mem_deref(mem); + + TEST_EQUALS(EAGAIN, jbuf_get(jb, &hdr2, &mem)); + TEST_EQUALS(480, hdr2.seq); + TEST_EQUALS(mem, frv[1]); + mem = mem_deref(mem); + + TEST_EQUALS(0, jbuf_get(jb, &hdr2, &mem)); + TEST_EQUALS(490, hdr2.seq); + TEST_EQUALS(mem, frv[2]); + mem = mem_deref(mem); + + TEST_EQUALS(ENOENT, jbuf_get(jb, &hdr2, &mem)); + err = 0; + + out: + mem_deref(jb); + mem_deref(mem); + for (i=0; i +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "json" +#define DEBUG_LEVEL 5 +#include + + +enum { + DICT_BSIZE = 32, + MAX_LEVELS = 8, +}; + + +static int test_json_basic_parser(void) +{ + static const char *str = + "{" + " \"name\" : \"Herr Alfred\"," + " \"height\" : 1.86," + " \"weight\" : 90," + " \"has_depth\" : false," + " \"has_money\" : true," + " \"array\" : [1, 2, 3, \"x\", \"y\"]," + " \"negative\" : -42," + " \"negativef\" : -0.0042," + " \"expo_pos\" : 2.0E3," + " \"expo_neg\" : 2.0E-3," + " \"foo\x1d\" : \"foo\x1d\"," + " \"object\" : {" + " \"one\" : 1," + " \"two\" : 2" + " }" + "}"; + + struct odict *dict = NULL, *sub; + const struct odict_entry *o, *e; + int err; + + err = json_decode_odict(&dict, DICT_BSIZE, + str, strlen(str), MAX_LEVELS); + if (err) + goto out; + + TEST_EQUALS(12U, odict_count(dict, false)); + TEST_EQUALS(17U, odict_count(dict, true)); + + o = odict_lookup(dict, "name"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_STRING, odict_entry_type(o)); + TEST_STRCMP("Herr Alfred", 11, odict_entry_str(o), + str_len(odict_entry_str(o))); + + o = odict_lookup(dict, "height"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o)); + TEST_ASSERT(odict_entry_dbl(o) > .0); + + o = odict_lookup(dict, "weight"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_INT, odict_entry_type(o)); + TEST_EQUALS(90, odict_entry_int(o)); + + o = odict_lookup(dict, "has_depth"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_BOOL, odict_entry_type(o)); + TEST_ASSERT(!odict_entry_boolean(o)); + + o = odict_lookup(dict, "has_money"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_BOOL, odict_entry_type(o)); + TEST_ASSERT(odict_entry_boolean(o)); + + o = odict_lookup(dict, "array"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_ARRAY, odict_entry_type(o)); + TEST_EQUALS(5U, odict_count(odict_entry_array(o), false)); + e = odict_get_type(odict_entry_array(o), ODICT_INT, "0"); + TEST_EQUALS(1, odict_entry_int(e)); + e = odict_get_type(odict_entry_array(o), ODICT_INT, "1"); + TEST_EQUALS(2, odict_entry_int(e)); + e = odict_get_type(odict_entry_array(o), ODICT_INT, "2"); + TEST_EQUALS(3, odict_entry_int(e)); + + o = odict_lookup(dict, "negative"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_INT, odict_entry_type(o)); + TEST_EQUALS(-42, odict_entry_int(o)); + + o = odict_lookup(dict, "negativef"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o)); + TEST_ASSERT(odict_entry_dbl(o) < .0); + + o = odict_lookup(dict, "expo_pos"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o)); + TEST_ASSERT(odict_entry_dbl(o) > .0); + + o = odict_lookup(dict, "expo_neg"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_DOUBLE, odict_entry_type(o)); + TEST_ASSERT(odict_entry_dbl(o) > .0); + + o = odict_lookup(dict, "foo\x1d"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_STRING, odict_entry_type(o)); + TEST_STRCMP("foo\x1d", 4, odict_entry_str(o), + str_len(odict_entry_str(o))); + + /* object */ + o = odict_lookup(dict, "object"); + TEST_ASSERT(o != NULL); + TEST_EQUALS(ODICT_OBJECT, odict_entry_type(o)); + sub = odict_entry_object(o); + e = odict_lookup(sub, "one"); + TEST_ASSERT(e != NULL); + TEST_EQUALS(ODICT_INT, odict_entry_type(e)); + TEST_EQUALS(1, odict_entry_int(e)); + e = odict_lookup(sub, "two"); + TEST_ASSERT(e != NULL); + TEST_EQUALS(ODICT_INT, odict_entry_type(e)); + TEST_EQUALS(2, odict_entry_int(e)); + + /* non-existing entry */ + o = odict_lookup(dict, "not-found"); + TEST_ASSERT(o == NULL); + + out: + mem_deref(dict); + return err; +} + + +/* verify a bunch of JSON messages */ +static int test_json_verify_decode(void) +{ + static const struct test { + unsigned num; + unsigned num_total; + char *str; + } testv[] = { + { + 0, + 0, + "{}" + }, + { + 1, + 1, + "\"yyyyyyyyyy\"" + }, + { + 1, + 1, + "42" + }, + { + 1, + 1, + "1.30142114406914976E17" + }, + { + 1, + 1, + "true" + }, + { + 1, + 1, + "{\"a\":1}" + }, + { + 2, + 2, + "{\"a\":1,\"b\":2}" + }, + { + 5, + 5, + "{" + " \"aaaaa\" : \"yyyyyyyyyy\"," + " \"bbbbb\" : \"yyyyyyyyyy\"," + " \"ccccc\" : \"yyyyyyyyyy\"," + " \"ddddd\" : \"yyyyyyyyyy\"," + " \"eeeee\" : \"yyyyyyyyyy\"" + "}" + }, + { + 2, + 2, + "{\"num\":42,\"str\":\"hei du\"}" + }, + { + 6, + 6, + "{" + " \"zero\" : 0," + " \"one\" : 1," + " \"false\" : 0," + " \"true\" : 1," + " \"0\" : false," + " \"1\" : true" + "}" + }, + + /* arrays */ + { + 2, + 8, + "{" + " \"array\" : [1,2,3,4,5]," + " \"arraz\" : [\"ole\", \"dole\", \"doffen\"]" + "}" + }, + + { + 1, + 0, + "{" + " \"empty_array\" : []" + "}" + }, + + { + 1, + 1, + "{" + " \"array_with_object\" : [ { \"key\" : 42 } ]" + "}" + }, + + { + 1, + 3, + "{" + " \"array_with_bool_and_null\" : [" + " true, false, null" + " ]" + "}" + }, + + { + 1, + 30, + "{" + " \"array\" : [" + " 0, 1, 2, 3, 4, 5, 6, 7, 8, 9," + " 10,11,12,13,14,15,16,17,18,19," + " 20,21,22,23,24,25,26,27,28,29" + " ]" + "}" + }, + + /* nested arrays */ + { + 1, + 4, + "{" + " \"array\": [" + " 1," + " 2," + " [" + " \"[][][][\"," + " \"][][][\"" + " ]" + " ]" + "}" + }, + + /* null */ + { + 1, + 1, + "{" + " \"empty\": null" + "}" + }, + + /* escaped string */ + { + 2, + 2, + "{" + " \"string1\": \"\\\"\\/\\b\\f\\n\\r\\t\", " + " \"string2\": \"\\\"/\\b\\f\\n\\r\\t\"" + " }" + }, + + { + 2, + 2, + "{" + " \"string\" : \"\\r\\n\" , " + " \"boolean\" : true" + "}" + }, + + { + 2, + 2, + "{" + " \"string\" : \"a\\r\\n\" , " + " \"null\" : null" + "}" + }, + + /* key with escaped string */ + { + 1, + 1, + "{ \"\\\"\\b\\f\\n\\r\\t\":\"value\"}" + }, + + { + 2, + 3, + "{" + " \"type\": \"object\"," + " \"properties\": {" + " \"id\": {" + " \"description\": \"The unique identifier\"," + " \"type\": \"integer\"" + " }" + " }" + "}" + }, + + { + 1, + 2, + "{" + " \"a\": {" + " \"b\": {" + " \"c\": {" + " \"d\": {" + " \"e\": {" + " \"f\": {" + " \"string\": \"hei hei\"," + " \"number\": 4242" + " }" + " }" + " }" + " }" + " }" + " }" + "}" + }, + + /* unicode */ + { + 1, + 1, + "{ \"\\u0001key\": \"val\\u0002\" }" + }, + + /* numbers */ + + { + 2, + 2, + "{ \"start\": 1372701600000, " + " \"stop\": -1372701600000 }" + }, + + { + 4, + 4, + "{" + " \"a\": 1.30142114406914976E17, " + " \"b\": 1.7555215491128452E-19, " + " \"c\": -4.57371918053102129E18, " + " \"d\": -1.3014211440691497E-17 " + "}" + }, + + /* array with objects (legal JSON) */ + { + 2, + 4, + "[" + " {" + " \"foo\" : 111," + " \"bar\" : 111" + " }," + " {" + " \"foo\" : 222," + " \"bar\" : 222" + " }" + "]" + } + }; + struct odict *dict = NULL, *dict2 = NULL; + struct mbuf *mb_enc = NULL; + unsigned i; + int err = 0; + + for (i=0; istr, str_len(t->str), MAX_LEVELS); + if (err) + goto out; + + TEST_EQUALS(t->num, odict_count(dict, false)); + TEST_EQUALS(t->num_total, odict_count(dict, true)); + + mb_enc = mbuf_alloc(1024); + if (!mb_enc) { + err = ENOMEM; + goto out; + } + + /* verify that the JSON object can be encoded */ + err = mbuf_printf(mb_enc, "%H", json_encode_odict, dict); + TEST_ERR(err); + + /* decode it again */ + err = json_decode_odict(&dict2, DICT_BSIZE, + (void *)mb_enc->buf, mb_enc->end, + MAX_LEVELS); + if (err) { + goto out; + } + + TEST_ASSERT(odict_compare(dict, dict2, false)); + + dict = mem_deref(dict); + dict2 = mem_deref(dict2); + mb_enc = mem_deref(mb_enc); + } + + out: + mem_deref(dict2); + mem_deref(dict); + mem_deref(mb_enc); + + return err; +} + + +static int test_json_exponent(void) +{ + static const char *str = + "{" + " \"exponents\" : [1e2, 1e-2, 9E18, -9E18]" + "}"; + struct odict *dict = NULL; + const struct odict_entry *arr, *e; + static const double values[] = { + 100.0, + 0.01, + 9000000000000000000.0, + -9000000000000000000.0, + }; + struct le *le; + unsigned i; + int err; + + err = json_decode_odict(&dict, DICT_BSIZE, + str, strlen(str), MAX_LEVELS); + if (err) + goto out; + + arr = odict_lookup(dict, "exponents"); + + TEST_EQUALS(RE_ARRAY_SIZE(values), + odict_count(odict_entry_array(arr), false)); + + for (le = list_head(&odict_entry_array(arr)->lst), i = 0; le; + le = le->next, ++i) { + + e = le->data; + + TEST_ASSERT(e != NULL); + TEST_EQUALS(ODICT_DOUBLE, odict_entry_type(e)); + TEST_EQUALS( values[i], odict_entry_dbl(e)); + } + + out: + mem_deref(dict); + return err; +} + + +int test_json(void) +{ + int err = 0; + + err = test_json_exponent(); + if (err) + return err; + + err = test_json_basic_parser(); + if (err) + return err; + + err = test_json_verify_decode(); + if (err) + return err; + + return err; +} + + +/* check a bunch of bad JSON messages, unparsable */ +int test_json_bad(void) +{ + static const struct test { + int err; + char *str; + } testv[] = { + { + EBADMSG, + "}" + }, + { + EBADMSG, + "{]" + }, + { + EBADMSG, + "{[}" + }, + { + EBADMSG, + "]" + }, + + /* boolean values */ + { + EBADMSG, + "{ \"short_true\" : t }" + }, + { + EBADMSG, + "{ \"short_false\" : f }" + }, + { + EBADMSG, + "{ \"short_null\" : n }" + }, + { + EBADMSG, + "{ \"a\" : frue }" + }, + { + EBADMSG, + "{ \"a\" : talse }" + }, + + /* string values */ + { + EBADMSG, + "{ \"invalid_unicode\" : \"\\u000g\" }" + }, + + /* corrupt data */ + { + EBADMSG, + "10t[3e9e66\"49\"[[72677:[f58{.fn}0{59\":8\"e}[" + }, + { + EBADMSG, + "1t34:{{:f{1.n{\"\"n8[0f7e}:53e6{7:28:{n{00:7" + }, + { + EBADMSG, + "}3][ne5}.5n41ef96f99\":n47{9[n[1:0f5\"}985}{" + }, + { + EBADMSG, + "}3][ne5}.5n41ef96f99\":n47{9[n[1:0f5\"}985}{" + }, + { + EBADMSG, + "8n0}3:28e27}8]75:[:e47968e96n[:2f]n1:]n2[t" + }, + + { + EBADMSG, + "{" + }, + { + EBADMSG, + "[" + }, + { + EBADMSG, + "{ \"broken_key }" + }, + { + EBADMSG, + "{ \"key\" : \"broken_value }" + }, + { + 0, + "\"hei\"" + }, + { + 0, + "123" + }, + }; + struct odict *dict = NULL; + unsigned i; + int err = 0; + + for (i=0; istr, str_len(t->str), MAX_LEVELS); + if (e == ENOMEM) + break; + TEST_EQUALS(t->err, e); + + if (e) { + TEST_ASSERT(dict == NULL); + } + else { + TEST_ASSERT(dict != NULL); + } + + dict = mem_deref(dict); + } + + out: + mem_deref(dict); + return err; +} + + +static int test_json_file_parse(const char *filename) +{ + struct mbuf *mb_ref = NULL, *mb_enc = NULL; + struct odict *dict = NULL, *dict2 = NULL; + char path[256]; + unsigned max_levels = 480; + int err; + + mb_ref = mbuf_alloc(1024); + mb_enc = mbuf_alloc(1024); + if (!mb_ref || !mb_enc) { + err = ENOMEM; + goto out; + } + + re_snprintf(path, sizeof(path), "%s/%s", test_datapath(), filename); + + err = test_load_file(mb_ref, path); + if (err) + goto out; + + err = json_decode_odict(&dict, DICT_BSIZE, + (void *)mb_ref->buf, mb_ref->end, + max_levels); + if (err) { + goto out; + } + + TEST_ASSERT(dict != NULL); + TEST_ASSERT(odict_count(dict, true) > 0); + +#if 0 + re_printf("%s: JSON parsed OK (%zu elements)\n", + filename, odict_count(dict, true)); +#endif + + /* verify that JSON object can be encoded */ + err = mbuf_printf(mb_enc, "%H", json_encode_odict, dict); + TEST_ERR(err); + + /* decode it again */ + err = json_decode_odict(&dict2, DICT_BSIZE, + (void *)mb_enc->buf, mb_enc->end, max_levels); + if (err) { + goto out; + } + + TEST_ASSERT(odict_compare(dict, dict2, false)); + + out: + mem_deref(dict2); + mem_deref(dict); + mem_deref(mb_enc); + mem_deref(mb_ref); + return err; +} + + +int test_json_file(void) +{ + const char *files[] = { + "fstab.json", + "menu.json", + "rfc7159.json", + "webapp.json", + "widget.json", + }; + unsigned i; + int err = 0; + + for (i=0; ilst.head, i=0; le; le = le->next, ++i) { + + struct odict_entry *ae = le->data; + unsigned key; + + key = atoi(odict_entry_key(ae)); + + TEST_EQUALS(i, key); + } + + /* should not exist */ + re_snprintf(buf, sizeof(buf), "%u", num); + e = odict_lookup(arr, buf); + TEST_ASSERT(e == NULL); + + out: + return err; +} + + +int test_json_array(void) +{ + static const char *str = + "{" + " \"array1\" : [0,1,2,3,4,5,6,7]," + " \"array2\" : [\"ole\",\"dole\",\"doffen\"]," + " \"array3\" : [ {\"x\":0}, {\"x\":0}, {\"x\":0} ]," + " \"object\" : {" + " \"array4\" : [0,1,2,3]" + " }" + "}"; + + struct odict *dict = NULL, *obj; + int err; + + err = json_decode_odict(&dict, DICT_BSIZE, + str, strlen(str), MAX_LEVELS); + if (err) + goto out; + + err |= verify_array(odict_get_array(dict, "array1"), 8); + err |= verify_array(odict_get_array(dict, "array2"), 3); + err |= verify_array(odict_get_array(dict, "array3"), 3); + if (err) + goto out; + + obj = odict_get_object(dict, "object"); + TEST_ASSERT(obj != NULL); + err |= verify_array(odict_get_array(obj, "array4"), 4); + if (err) + goto out; + + out: + mem_deref(dict); + return err; +} diff --git a/test/list.c b/test/list.c new file mode 100644 index 000000000..f5d030af7 --- /dev/null +++ b/test/list.c @@ -0,0 +1,242 @@ +/** + * @file list.c Linked-lists Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testlist" +#define DEBUG_LEVEL 5 +#include + + +struct node { + struct le le; + int value; +}; + +int test_list(void) +{ + struct node node1, node2; + struct list list; + int err = EINVAL; + + list_init(&list); + + memset(&node1, 0, sizeof(node1)); + memset(&node2, 0, sizeof(node2)); + + /* Test empty list */ + TEST_EQUALS(0, list_count(&list)); + + /* Test with one node */ + list_append(&list, &node1.le, &node1); + TEST_EQUALS(1, list_count(&list)); + + list_unlink(&node1.le); + + TEST_EQUALS(0, list_count(&list)); + + /* Test with two nodes */ + list_append(&list, &node1.le, &node1); + list_append(&list, &node2.le, &node2); + TEST_EQUALS(2, list_count(&list)); + + list_unlink(&node1.le); + + TEST_EQUALS(1, list_count(&list)); + + list_unlink(&node2.le); + + /* Test empty list */ + TEST_EQUALS(0, list_count(&list)); + + err = 0; + + out: + return err; +} + + +static void node_destructor(void *arg) +{ + struct node *node = arg; + + if (node->le.prev || node->le.next || node->le.list || node->le.data) { + DEBUG_WARNING("le: prev=%p next=%p data=%p\n", + node->le.prev, node->le.next, node->le.data); + } + + list_unlink(&node->le); +} + + +/** + * Test linked list with external reference to objects + */ +int test_list_ref(void) +{ + struct list list; + struct node *node, *node2; + int err = 0; + + list_init(&list); + + node = mem_zalloc(sizeof(*node), node_destructor); + node2 = mem_zalloc(sizeof(*node2), node_destructor); + if (!node || !node2) { + err = ENOMEM; + goto out; + } + + mem_ref(node); + + list_append(&list, &node->le, node); + list_append(&list, &node2->le, node2); + + out: + list_flush(&list); + memset(&list, 0xa5, sizeof(list)); /* mark as deleted */ + + /* note: done after list_flush() */ + mem_deref(node); + + return err; +} + + +static bool sort_handler(struct le *le1, struct le *le2, void *arg) +{ + struct node *node1 = le1->data; + struct node *node2 = le2->data; + (void)arg; + + /* NOTE: important to use less than OR equal to, otherwise + the list_sort function may be stuck in a loop */ + return node1->value <= node2->value; +} + + +#define NUM_ELEMENTS 100 +static int test_sort(bool sorted) +{ + struct list lst; + struct le *le; + int prev_value = 0; + bool prev_value_set = false; + unsigned i; + unsigned value_counter = 7; + int err = 0; + + list_init(&lst); + + /* add many elements with a random value */ + for (i=0; ivalue = -50 + (value_counter % 100); + value_counter *= 3; + + if (sorted) + list_insert_sorted(&lst, sort_handler, NULL, &node->le, + node); + else + list_append(&lst, &node->le, node); + } + + /* sort the list in ascending order */ + if (!sorted) + list_sort(&lst, sort_handler, NULL); + + /* verify that the list is sorted */ + for (le = lst.head; le; le = le->next) { + + struct node *node = le->data; + + if (prev_value_set) { + TEST_ASSERT(node->value >= prev_value); + } + + prev_value = node->value; + prev_value_set = true; + } + + out: + list_flush(&lst); + + return err; +} + + +int test_list_sort(void) +{ + int err; + + err = test_sort(false); + TEST_ERR(err); + + err = test_sort(true); + TEST_ERR(err); + out: + return err; +} + + +struct flush_data { + struct le le; + struct list *flushl; +}; + + +static void data_destroy(void *arg) +{ + struct flush_data *data = arg; + struct le *le; + + LIST_FOREACH(data->flushl, le) + { + assert(list_count(data->flushl)); + } +} + + +int test_list_flush(void) +{ + struct flush_data *data[2]; + struct list flushl = LIST_INIT; + int err = 0; + + data[0] = mem_zalloc(sizeof(struct flush_data), data_destroy); + if (!data[0]) + return ENOMEM; + + data[1] = mem_zalloc(sizeof(struct flush_data), data_destroy); + if (!data[1]) { + mem_deref(data[0]); + return ENOMEM; + } + + data[0]->flushl = &flushl; + data[1]->flushl = &flushl; + + list_append(&flushl, &data[0]->le, data[0]); + list_append(&flushl, &data[1]->le, data[1]); + + list_flush(&flushl); + + TEST_EQUALS(0, list_count(&flushl)); + +out: + return err; +} diff --git a/test/main.c b/test/main.c new file mode 100644 index 000000000..f8ffe8dc8 --- /dev/null +++ b/test/main.c @@ -0,0 +1,275 @@ +/** + * @file main.c Main regression testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include +#ifdef HAVE_GETOPT +#include +#endif +#include +#include "test.h" + + +#define DEBUG_MODULE "retest" +#define DEBUG_LEVEL 5 +#include + + +#ifdef HAVE_SIGNAL +static void signal_handler(int num) +{ + re_fprintf(stderr, "forced exit by signal %d -- test aborted\n", num); + + exit(0); +} +#endif + + +#ifdef HAVE_GETOPT +static void usage(void) +{ + (void)re_fprintf(stderr, "Usage: retest [-rotalp] [-hmv]" + " \n"); + + (void)re_fprintf(stderr, "\ntest group options:\n"); + (void)re_fprintf(stderr, "\t-r Run regular tests\n"); + (void)re_fprintf(stderr, "\t-o Run OOM memory tests\n"); + (void)re_fprintf(stderr, "\t-i Run integration tests\n"); + (void)re_fprintf(stderr, "\t-p Run performance tests\n"); + (void)re_fprintf(stderr, "\t-t Run tests in multi-threads\n"); + (void)re_fprintf(stderr, "\t-a Run all tests (default)\n"); + (void)re_fprintf(stderr, "\t-l List all testcases and exit\n"); + + (void)re_fprintf(stderr, "\ncommon options:\n"); + (void)re_fprintf(stderr, "\t-d Path to data files\n"); + (void)re_fprintf(stderr, "\t-h Help\n"); + (void)re_fprintf(stderr, "\t-m Async polling method to use\n"); + (void)re_fprintf(stderr, "\t-v Verbose output\n"); +} +#endif + + +static void dbg_handler(int level, const char *p, size_t len, void *arg) +{ + (void)level; + (void)arg; + + printf("%.*s", (int)len, p); +} + + +int main(int argc, char *argv[]) +{ + struct memstat mstat; + bool do_reg = false; + bool do_oom = false; + bool do_int = false; + bool do_perf = false; + bool do_all = true; /* run all tests is default */ + bool do_list = false; + bool do_thread = false; + enum dbg_flags flags; + bool verbose = false; + const char *name = NULL; + enum poll_method method = poll_method_best(); + int err = 0; + +#ifdef HAVE_SIGNAL + signal(SIGINT, signal_handler); + signal(SIGTERM, signal_handler); +#endif + + (void)sys_coredump_set(true); + +#ifdef HAVE_GETOPT + for (;;) { + const int c = getopt(argc, argv, "hroipaltvm:d:"); + if (0 > c) + break; + + switch (c) { + + case '?': + case 'h': + usage(); + return -2; + + case 'r': + do_reg = true; + do_all = false; + break; + + case 'o': + do_oom = true; + do_all = false; + break; + + case 'i': + do_int = true; + do_all = false; + break; + + case 'p': + do_perf = true; + do_all = false; + break; + + case 'a': + do_all = true; + break; + + case 'l': + do_list = true; + do_all = false; + break; + + case 't': + do_thread = true; + do_all = false; + break; + + case 'v': + verbose = true; + break; + + case 'm': { + struct pl pollname; + pl_set_str(&pollname, optarg); + err = poll_method_type(&method, &pollname); + if (err) { + re_fprintf(stderr, + "could not resolve async polling" + " method '%r'\n", &pollname); + return err; + } + } + break; + + case 'd': + test_set_datapath(optarg); + break; + } + } + + argc -= optind; + + if (argc < 0 || argc > 1) { + usage(); + return -2; + } + + if (argc >= 1) { + name = argv[optind]; + printf("single testcase: %s\n", name); + } + +#else + (void)argc; + (void)argv; + do_reg = true; + do_oom = false; + do_int = false; + do_perf = false; + do_all = false; + verbose = true; +#endif + + /* Initialise debugging */ +#if defined(WIN32) + flags = 0; +#else + flags = DBG_ANSI; +#endif + + dbg_init(DBG_INFO, flags); + + /* Initialise library */ + err = libre_init(); + if (err) + goto out; + + err = poll_method_set(method); + if (err) { + DEBUG_WARNING("could not set polling method '%s' (%m)\n", + poll_method_name(method), err); + goto out; + } + + dbg_handler_set(dbg_handler, 0); + + DEBUG_NOTICE("libre version %s (%s/%s)\n", sys_libre_version_get(), + sys_arch_get(), sys_os_get()); + + dbg_handler_set(NULL, 0); + + if (do_all) { + do_reg = true; + do_oom = true; + do_int = true; + do_thread = true; + } + + if (do_list) { + test_listcases(); + goto out; + } + + /* + * Different test-groups specified below: + */ + + re_printf("using async polling method '%s'\n", + poll_method_name(method)); + + if (verbose) { + re_printf("using datapath '%s'\n", test_datapath()); + } + + if (do_reg) { + err = test_reg(name, verbose); + TEST_ERR(err); + } + + if (do_oom) { + err = test_oom(name, verbose); + TEST_ERR(err); + } + + if (do_int) { + err = test_integration(name, verbose); + TEST_ERR(err); + } + + if (do_perf) { + err = test_perf(name, verbose); + TEST_ERR(err); + } + + if (do_thread) { + err = test_multithread(); + TEST_ERR(err); + } + + out: + re_thread_async_close(); + + /* Check for open timers */ + tmr_debug(); + + libre_close(); + + /* Check for memory leaks */ + mem_debug(); + + if (0 == mem_get_stat(&mstat)) { + + if (mstat.bytes_cur || mstat.blocks_cur) + return 2; + } + + return err; +} diff --git a/test/mbuf.c b/test/mbuf.c new file mode 100644 index 000000000..9f94cdf90 --- /dev/null +++ b/test/mbuf.c @@ -0,0 +1,154 @@ +/** + * @file mbuf.c Mbuffer Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_mbuf" +#define DEBUG_LEVEL 5 +#include + + +static int test_mbuf_basic(void) +{ + struct mbuf mb; + struct pl pl, hei = PL("hei"), foo = PL("foo"); + static const char *pattern = "mmmmmmmmm"; + char *str = NULL; + int err; + + mbuf_init(&mb); + + /* write */ + err = mbuf_write_u8(&mb, 0x5a); + if (err) + goto out; + err = mbuf_write_u16(&mb, 0x5a5a); + if (err) + goto out; + err = mbuf_write_u32(&mb, 0x5a5a5a5a); + if (err) + goto out; + err = mbuf_write_str(&mb, "hei foo"); + if (err) + goto out; + + /* read */ + mb.pos = 0; + if (0x5a != mbuf_read_u8(&mb)) { + err = EINVAL; + goto out; + } + if (0x5a5a != mbuf_read_u16(&mb)) { + err = EINVAL; + goto out; + } + if (0x5a5a5a5a != mbuf_read_u32(&mb)) { + err = EINVAL; + goto out; + } + pl.p = (char *)mbuf_buf(&mb); + pl.l = 3; + err = pl_cmp(&hei, &pl); + if (err) + goto out; + + mb.pos += 4; + pl.p = (char *)mbuf_buf(&mb); + pl.l = mbuf_get_left(&mb); + err = pl_cmp(&foo, &pl); + if (err) + goto out; + + /* Test mbuf_strdup() */ + err = mbuf_strdup(&mb, &str, 3); + if (err) + goto out; + err = pl_strcmp(&foo, str); + TEST_ERR(err); + + mb.pos = mb.end = 0; + err = mbuf_fill(&mb, 'm', 9); + if (err) + goto out; + if (mb.pos != strlen(pattern) || + mb.end != strlen(pattern) || + 0 != memcmp(mb.buf, pattern, 9)) { + err = EBADMSG; + goto out; + } + + /* Test position and end */ + mbuf_set_posend(&mb, 2, 4); + + ASSERT_EQ(2, mbuf_pos(&mb)); + ASSERT_EQ(4, mbuf_end(&mb)); + + out: + mbuf_reset(&mb); + mem_deref(str); + + return err; +} + + +static int test_mbuf_shift(void) +{ + static const uint8_t payload[10] = {0,1,2,3,4,5,6,7,8,9}; + struct mbuf *mb; + int err; + + mb = mbuf_alloc(sizeof(payload)); + if (!mb) + return ENOMEM; + + err = mbuf_write_mem(mb, payload, sizeof(payload)); + if (err) + goto out; + mb->pos = 0; + + /* inject a header in the front */ + err = mbuf_shift(mb, 64); + if (err) + goto out; + + TEST_EQUALS(64, mb->pos); + TEST_EQUALS(64+10, mb->end); + TEST_MEMCMP(payload, sizeof(payload), + mbuf_buf(mb), mbuf_get_left(mb)); + + /* remove a header in the front */ + err = mbuf_shift(mb, -1); + if (err) + goto out; + + TEST_EQUALS(63, mb->pos); + TEST_EQUALS(63+10, mb->end); + TEST_MEMCMP(payload, sizeof(payload), + mbuf_buf(mb), mbuf_get_left(mb)); + + out: + mem_deref(mb); + + return err; +} + + +int test_mbuf(void) +{ + int err; + + err = test_mbuf_basic(); + if (err) + return err; + + err = test_mbuf_shift(); + if (err) + return err; + + return 0; +} diff --git a/test/md5.c b/test/md5.c new file mode 100644 index 000000000..7580e1937 --- /dev/null +++ b/test/md5.c @@ -0,0 +1,58 @@ +/** + * @file md5.c MD5 Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testmd5" +#define DEBUG_LEVEL 4 +#include + + +int test_md5(void) +{ + const struct pl str = PL("a93akjshdla81mx.kjda09sdkjl12jdlksaldkjas"); + const uint8_t ref[16] = { + 0x9d, 0x97, 0xa5, 0xf8, 0x8d, 0x1b, 0x09, 0x7c, + 0x9f, 0xf9, 0xe2, 0x9d, 0xd5, 0x43, 0xb1, 0x1d + }; + uint8_t digest[16]; + int err; + + /* Test constants */ + if (16 != MD5_SIZE) { + DEBUG_WARNING("MD5_SIZE is %u (should be 16)\n", MD5_SIZE); + return EINVAL; + } + if (33 != MD5_STR_SIZE) { + DEBUG_WARNING("MD5_STR_SIZE is %u (should be 33)\n", + MD5_STR_SIZE); + return EINVAL; + } + + /* Test md5() */ + md5((const uint8_t *)str.p, str.l, digest); + + if (0 != memcmp(digest, ref, sizeof(digest))) { + DEBUG_WARNING("md5 b0Rken: %02w\n", digest, sizeof(digest)); + return EINVAL; + } + + /* Test md5_printf() */ + err = md5_printf(digest, "%r", &str); + if (err) + goto out; + + if (0 != memcmp(digest, ref, sizeof(digest))) { + DEBUG_WARNING("md5_printf() is b0Rken: %02w\n", digest, + sizeof(digest)); + return EINVAL; + } + + out: + return err; +} diff --git a/test/mem.c b/test/mem.c new file mode 100644 index 000000000..71d7adda3 --- /dev/null +++ b/test/mem.c @@ -0,0 +1,133 @@ +/** + * @file mem.c Memory Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include "test.h" + + +#define DEBUG_MODULE "test_mem" +#define DEBUG_LEVEL 5 +#include + + +#define PATTERN 0xfcfcfcfc + +enum { +#if defined(__x86_64__) + /* Use 16-byte alignment on x86-x32 as well */ + mem_alignment = 16u, +#else + mem_alignment = sizeof(void*) >= 8u ? 16u : 8u, +#endif +}; + +struct obj { + uint32_t pattern; +}; + +static void destructor(void *arg) +{ + struct obj *obj = arg; + + if (PATTERN != obj->pattern) { + DEBUG_WARNING("destroy error: %08x\n", obj->pattern); + } +} + + +int test_mem(void) +{ + struct obj *obj, *old; + int err = EINVAL; + + obj = mem_alloc(sizeof(*obj), destructor); + if (!obj) + return ENOMEM; + + obj->pattern = PATTERN; + + TEST_EQUALS(1, mem_nrefs(obj)); + TEST_ASSERT(is_aligned(obj, mem_alignment)); + + obj = mem_ref(obj); + TEST_EQUALS(2, mem_nrefs(obj)); + + mem_deref(obj); + + TEST_EQUALS(1, mem_nrefs(obj)); + + old = obj; + obj = mem_realloc(old, sizeof(*obj) + 16); + + if (!obj) { + mem_deref(old); + err = ENOMEM; + TEST_ERR(err); + } + + TEST_ASSERT(is_aligned(obj, mem_alignment)); + + old = mem_ref(obj); + TEST_EQUALS(2, mem_nrefs(obj)); + + obj = mem_realloc(obj, sizeof(*obj) + 64); + TEST_EQUALS(1, mem_nrefs(old)); + mem_deref(old); + TEST_EQUALS(1, mem_nrefs(obj)); + + err = 0; + + out: + mem_deref(obj); + return err; +} + + +#ifndef SIZE_MAX +#define SIZE_MAX (~((size_t)0)) +#endif + + +int test_mem_reallocarray(void) +{ + void *a, *b; + int err = 0; + + /* expect success */ + a = mem_reallocarray(NULL, 10, 10, NULL); + if (!a) + return ENOMEM; + + /* expect failure */ + b = mem_reallocarray(NULL, SIZE_MAX, SIZE_MAX, NULL); + TEST_ASSERT(b == NULL); + + out: + mem_deref(a); + + return err; +} + + +int test_mem_secure(void) +{ + int r, err = 0; + + /* compare */ + r = mem_seccmp(NULL, NULL, 42); + TEST_ASSERT(r < 0); + + r = mem_seccmp((uint8_t *)"abc", (uint8_t *)"abc", 3); + TEST_EQUALS(0, r); + + r = mem_seccmp((uint8_t *)"aaa", (uint8_t *)"bbb", 3); + TEST_ASSERT(r > 0); + + r = mem_seccmp((uint8_t *)"ccc", (uint8_t *)"aaa", 3); + TEST_ASSERT(r > 0); + + out: + return err; +} diff --git a/test/mock/cert.c b/test/mock/cert.c new file mode 100644 index 000000000..61522062a --- /dev/null +++ b/test/mock/cert.c @@ -0,0 +1,38 @@ +/** + * @file cert.c TLS Certificate + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include "test.h" + + +/** + * X509/PEM certificate with ECDSA keypair + * + * $ openssl ecparam -out ec_key.pem -name prime256v1 -genkey + * $ openssl req -new -key ec_key.pem -x509 -nodes -days 3650 -out cert.pem + */ +const char test_certificate_ecdsa[] = +"-----BEGIN CERTIFICATE-----\r\n" +"MIICBzCCAa2gAwIBAgIUZy0UqzsDq7fGUsZh6QxkXgCa030wCgYIKoZIzj0EAwIw\r\n" +"WTELMAkGA1UEBhMCTk8xEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu\r\n" +"dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJMTI3LjAuMC4xMB4XDTE5\r\n" +"MDUyNDE5NTM0OFoXDTI5MDUyMTE5NTM0OFowWTELMAkGA1UEBhMCTk8xEzARBgNV\r\n" +"BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0\r\n" +"ZDESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE\r\n" +"inP/oBEqBbXRxDzyk7sbh8rRJbfbXBRG2uJl2g6YhSkYZkifGyEueJ7+A9D9LfBh\r\n" +"b5+lKXuJc02XQW5IwUmToqNTMFEwHQYDVR0OBBYEFH1vSH2IBZvKYNDPfPOk41Dw\r\n" +"hyTWMB8GA1UdIwQYMBaAFH1vSH2IBZvKYNDPfPOk41DwhyTWMA8GA1UdEwEB/wQF\r\n" +"MAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAOm79QetPxioy/S0Rk9lhPgfBslgM6f4\r\n" +"tihVBSpe0FdJAiAC6Usj7p3H8dvu9Oa1gtOXSJkh1MT6pkfW21YseRWP4A==\r\n" +"-----END CERTIFICATE-----\r\n" +"-----BEGIN EC PARAMETERS-----\r\n" +"BggqhkjOPQMBBw==\r\n" +"-----END EC PARAMETERS-----\r\n" +"-----BEGIN EC PRIVATE KEY-----\r\n" +"MHcCAQEEIMWTO9/z24fiq13MM5UF1CVD3yJjVXRe0qpTCmmZU5ppoAoGCCqGSM49\r\n" +"AwEHoUQDQgAEinP/oBEqBbXRxDzyk7sbh8rRJbfbXBRG2uJl2g6YhSkYZkifGyEu\r\n" +"eJ7+A9D9LfBhb5+lKXuJc02XQW5IwUmTog==\r\n" +"-----END EC PRIVATE KEY-----\r\n" + ; diff --git a/test/mock/dnssrv.c b/test/mock/dnssrv.c new file mode 100644 index 000000000..ddf13347c --- /dev/null +++ b/test/mock/dnssrv.c @@ -0,0 +1,284 @@ +/** + * @file mock/dnssrv.c Mock DNS server + * + * Copyright (C) 2010 - 2016 Alfred E. Heggestad + */ +#include +#include +#include "../test.h" + + +#define DEBUG_MODULE "mock/dnssrv" +#define DEBUG_LEVEL 5 +#include + + +#define LOCAL_PORT 0 + + +static void dns_server_match(struct dns_server *srv, struct list *rrl, + const char *name, uint16_t type) +{ + struct dnsrr *rr0 = NULL; + struct le *le; + + le = srv->rrl.head; + while (le) { + + struct dnsrr *rr = le->data; + le = le->next; + + if (type == rr->type && 0 == str_casecmp(name, rr->name)) { + + if (!rr0) + rr0 = rr; + list_append(rrl, &rr->le_priv, rr); + } + } + + /* If rotation is enabled, then rotate multiple entries + in a deterministic way (no randomness please) */ + if (srv->rotate && rr0) { + list_unlink(&rr0->le); + list_append(&srv->rrl, &rr0->le, rr0); + } +} + + +static void decode_dns_query(struct dns_server *srv, const struct sa *src, + struct mbuf *mb) +{ + struct list rrl = LIST_INIT; + struct dnshdr hdr; + struct le *le; + char *qname = NULL; + size_t start, end; + uint16_t type, dnsclass; + int err = 0; + + start = mb->pos; + end = mb->end; + + if (dns_hdr_decode(mb, &hdr) || hdr.qr || hdr.nq != 1) { + DEBUG_WARNING("unable to decode query header\n"); + return; + } + + err = dns_dname_decode(mb, &qname, start); + if (err) { + DEBUG_WARNING("unable to decode query name\n"); + goto out; + } + + if (mbuf_get_left(mb) < 4) { + DEBUG_WARNING("unable to decode query type/class\n"); + goto out; + } + + type = ntohs(mbuf_read_u16(mb)); + dnsclass = ntohs(mbuf_read_u16(mb)); + + DEBUG_INFO("dnssrv: type=%s query-name='%s'\n", dns_rr_typename(type), + qname); + + if (dnsclass == DNS_CLASS_IN) { + dns_server_match(srv, &rrl, qname, type); + } + + hdr.qr = true; + hdr.tc = false; + hdr.rcode = DNS_RCODE_OK; + hdr.nq = 1; + hdr.nans = list_count(&rrl); + + mb->pos = start; + + err = dns_hdr_encode(mb, &hdr); + if (err) + goto out; + + mb->pos = end; + + DEBUG_INFO("dnssrv: @@ found %u answers for %s\n", list_count(&rrl), + qname); + + for (le = rrl.head; le; le = le->next) { + struct dnsrr *rr = le->data; + + err = dns_rr_encode(mb, rr, 0, NULL, start); + if (err) + goto out; + } + + mb->pos = start; + + (void)udp_send(srv->us, src, mb); + +out: + list_clear(&rrl); + mem_deref(qname); +} + + +static void udp_recv(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct dns_server *srv = arg; + + decode_dns_query(srv, src, mb); +} + + +static void destructor(void *arg) +{ + struct dns_server *srv = arg; + + list_flush(&srv->rrl); + mem_deref(srv->us); +} + + +void dns_server_flush(struct dns_server *srv) +{ + list_flush(&srv->rrl); +} + + +int dns_server_alloc(struct dns_server **srvp, bool rotate) +{ + struct dns_server *srv; + int err; + + if (!srvp) + return EINVAL; + + srv = mem_zalloc(sizeof(*srv), destructor); + if (!srv) + return ENOMEM; + + err = sa_set_str(&srv->addr, "127.0.0.1", LOCAL_PORT); + if (err) + goto out; + + err = udp_listen(&srv->us, &srv->addr, udp_recv, srv); + if (err) + goto out; + + err = udp_local_get(srv->us, &srv->addr); + if (err) + goto out; + + srv->rotate = rotate; + +out: + if (err) + mem_deref(srv); + else + *srvp = srv; + + return err; +} + + +int dns_server_add_a(struct dns_server *srv, const char *name, uint32_t addr, + int64_t ttl) +{ + struct dnsrr *rr; + int err; + + if (!srv || !name) + return EINVAL; + + rr = dns_rr_alloc(); + if (!rr) + return ENOMEM; + + err = str_dup(&rr->name, name); + if (err) + goto out; + + rr->type = DNS_TYPE_A; + rr->dnsclass = DNS_CLASS_IN; + rr->ttl = ttl; + rr->rdlen = 0; + + rr->rdata.a.addr = addr; + + list_append(&srv->rrl, &rr->le, rr); + +out: + if (err) + mem_deref(rr); + + return err; +} + + +int dns_server_add_aaaa(struct dns_server *srv, const char *name, + const uint8_t *addr) +{ + struct dnsrr *rr; + int err; + + if (!srv || !name) + return EINVAL; + + rr = dns_rr_alloc(); + if (!rr) + return ENOMEM; + + err = str_dup(&rr->name, name); + if (err) + goto out; + + rr->type = DNS_TYPE_AAAA; + rr->dnsclass = DNS_CLASS_IN; + rr->ttl = 3600; + rr->rdlen = 0; + + memcpy(rr->rdata.aaaa.addr, addr, 16); + + list_append(&srv->rrl, &rr->le, rr); + +out: + if (err) + mem_deref(rr); + + return err; +} + + +int dns_server_add_srv(struct dns_server *srv, const char *name, uint16_t pri, + uint16_t weight, uint16_t port, const char *target) +{ + struct dnsrr *rr; + int err; + + if (!srv || !name || !port || !target) + return EINVAL; + + rr = dns_rr_alloc(); + if (!rr) + return ENOMEM; + + err = str_dup(&rr->name, name); + if (err) + goto out; + + rr->type = DNS_TYPE_SRV; + rr->dnsclass = DNS_CLASS_IN; + rr->ttl = 3600; + rr->rdlen = 0; + + rr->rdata.srv.pri = pri; + rr->rdata.srv.weight = weight; + rr->rdata.srv.port = port; + str_dup(&rr->rdata.srv.target, target); + + list_append(&srv->rrl, &rr->le, rr); + +out: + if (err) + mem_deref(rr); + + return err; +} diff --git a/test/mock/fuzz.c b/test/mock/fuzz.c new file mode 100644 index 000000000..dd94a9e93 --- /dev/null +++ b/test/mock/fuzz.c @@ -0,0 +1,110 @@ +/** + * @file mock/fuzz.c Packet fuzzing + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/fuzz" +#define DEBUG_LEVEL 5 +#include + + +struct fuzz { + struct tcp_helper *th; + struct tcp_conn *tc; + size_t packet_count; +}; + + +static void destructor(void *data) +{ + struct fuzz *fuzz = data; + + mem_deref(fuzz->th); + mem_deref(fuzz->tc); +} + + +static void apply_fuzzing(struct fuzz *fuzz, struct mbuf *mb) +{ + const size_t len = mbuf_get_left(mb); + size_t pos; + bool flip; + unsigned bit; + + if (len == 0) + return; + + ++fuzz->packet_count; + + pos = rand_u16() % len; + bit = rand_u16() % 8; + + /* percent change of corrupt packet */ + flip = ((rand_u16() % 100) < 33); + + if (flip) { + re_printf("### flipped bit on pos %zu\n", pos); + + /* flip a random bit */ + mbuf_buf(mb)[pos] ^= 1<tc = mem_ref(tc); + + err = tcp_register_helper(&fuzz->th, tc, -1000, NULL, + helper_send_handler, + helper_recv_handler, fuzz); + if (err) + goto out; + + out: + if (err) + mem_deref(fuzz); + else + *fuzzp = fuzz; + + return err; +} diff --git a/test/mock/nat.c b/test/mock/nat.c new file mode 100644 index 000000000..9dbafa106 --- /dev/null +++ b/test/mock/nat.c @@ -0,0 +1,217 @@ +/** + * @file mock/nat.c Mock NAT-box + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/nat" +#define DEBUG_LEVEL 5 +#include + + +enum { + LAYER_NAT = -1000 +}; + + +static void nat_binding_add(struct nat *nat, const struct sa *addr) +{ + if (!nat || !addr) + return; + + if (nat->bindingc >= RE_ARRAY_SIZE(nat->bindingv)) { + DEBUG_WARNING("NAT-box at max capacity\n"); + return; + } + + nat->bindingv[nat->bindingc++] = *addr; +} + + +static struct sa *nat_binding_find_addr(struct nat *nat, const struct sa *addr) +{ + unsigned i; + + if (!nat || !addr) + return NULL; + + for (i=0; ibindingc; i++) { + + if (sa_cmp(addr, &nat->bindingv[i], SA_ALL)) + return &nat->bindingv[i]; + } + + return NULL; +} + + +static struct sa *nat_binding_find(struct nat *nat, uint16_t port) +{ + unsigned i; + + if (!nat || !port) + return NULL; + + for (i=0; ibindingc; i++) { + + if (port == sa_port(&nat->bindingv[i])) + return &nat->bindingv[i]; + } + + return NULL; +} + + +static bool nat_helper_send(int *err, struct sa *dst, + struct mbuf *mb, void *arg) +{ + struct nat *nat = arg; + struct sa *cli; + (void)mb; + + cli = nat_binding_find(nat, sa_port(dst)); + +#if 0 + re_printf("nat: send INGRESS %J -> %J\n", dst, cli); +#endif + + if (cli) { + *dst = *cli; + return false; + } + else { + *err = ENOTCONN; + DEBUG_WARNING("nat: binding to %J not found\n", dst); + return true; + } +} + + +static bool nat_helper_recv(struct sa *src, struct mbuf *mb, void *arg) +{ + struct nat *nat = arg; + struct sa map; + (void)mb; + + if (!nat_binding_find(nat, sa_port(src))) { + nat_binding_add(nat, src); + } + + map = nat->public_addr; + sa_set_port(&map, sa_port(src)); + +#if 0 + re_printf("nat: recv EGRESS %J -> %J\n", src, &map); +#endif + + *src = map; + + return false; +} + + +static bool firewall_egress(int *err, struct sa *dst, + struct mbuf *mb, void *arg) +{ + struct nat *nat = arg; + (void)err; + (void)mb; + + /* add egress mapping to external addr */ + if (!nat_binding_find_addr(nat, dst)) { + nat_binding_add(nat, dst); + } + + return false; +} + + +static bool firewall_ingress(struct sa *src, + struct mbuf *mb, void *arg) +{ + struct nat *nat = arg; + (void)mb; + + /* check if external address has a mapping */ + if (!nat_binding_find_addr(nat, src)) { + + DEBUG_NOTICE("firewall: drop 1 packet from %J\n", src); + return true; + } + + return false; +} + + +static void nat_destructor(void *arg) +{ + struct nat *nat = arg; + + mem_deref(nat->uh); + mem_deref(nat->us); +} + + +/* inbound NAT */ +int nat_alloc(struct nat **natp, enum natbox_type type, + struct udp_sock *us, const struct sa *public_addr) +{ + struct nat *nat; + int err = 0; + + if (!natp || !us) + return EINVAL; + + if (type == NAT_INBOUND_SNAT && !public_addr) + return EINVAL; + + if (udp_helper_find(us, LAYER_NAT)) { + DEBUG_WARNING("udp helper already exist on layer %d\n", + LAYER_NAT); + return EPROTO; + } + + nat = mem_zalloc(sizeof(*nat), nat_destructor); + if (!nat) + return ENOMEM; + + nat->type = type; + if (public_addr) + nat->public_addr = *public_addr; + nat->us = mem_ref(us); + + switch (type) { + + case NAT_INBOUND_SNAT: + err = udp_register_helper(&nat->uh, us, LAYER_NAT, + nat_helper_send, + nat_helper_recv, nat); + break; + + case NAT_FIREWALL: + err = udp_register_helper(&nat->uh, us, LAYER_NAT, + firewall_egress, + firewall_ingress, nat); + break; + + default: + DEBUG_WARNING("invalid NAT type %d\n", type); + err = ENOTSUP; + break; + } + if (err) + goto out; + + out: + if (err) + mem_deref(nat); + else + *natp = nat; + + return err; +} diff --git a/test/mock/pf.c b/test/mock/pf.c new file mode 100644 index 000000000..8d2ccd500 --- /dev/null +++ b/test/mock/pf.c @@ -0,0 +1,107 @@ +/** + * @file mock/pf.c Mock packet filter + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/pf" +#define DEBUG_LEVEL 5 +#include + + +static bool stun_attr_handler(const struct stun_attr *attr, void *arg) +{ + (void)arg; + (void)re_printf(" %s", stun_attr_name(attr->type)); + return false; +} + + +static void sniff_stun(struct mbuf *mb) +{ + struct stun_msg *msg; + size_t pos; + int err; + + pos = mb->pos; + err = stun_msg_decode(&msg, mb, NULL); + if (err) { + DEBUG_WARNING("could not decode STUN packet (%m)\n", err); + return; + } + + stun_msg_attr_apply(msg, stun_attr_handler, 0); + (void)re_printf("\n"); + + mem_deref(msg); + + mb->pos = pos; +} + + +/* egress */ +static bool pf_send_handler(int *err, struct sa *dst, + struct mbuf *mb, void *arg) +{ + struct pf *pf = arg; + (void)err; + (void)dst; + + (void)re_printf("[%s] send -- ", pf->name); + sniff_stun(mb); + + return false; +} + + +/* ingress */ +static bool pf_recv_handler(struct sa *src, struct mbuf *mb, void *arg) +{ + struct pf *pf = arg; + (void)src; + + (void)re_printf("[%s] recv --", pf->name); + sniff_stun(mb); + + return false; +} + + +static void pf_destructor(void *arg) +{ + struct pf *pf = arg; + + mem_deref(pf->uh); + mem_deref(pf->us); +} + + +int pf_create(struct pf **pfp, struct udp_sock *us, const char *name) +{ + struct pf *pf; + int err; + + if (!pfp || !us) + return EINVAL; + + pf = mem_zalloc(sizeof(*pf), pf_destructor); + if (!pf) + return ENOMEM; + + pf->us = mem_ref(us); + str_ncpy(pf->name, name, sizeof(pf->name)); + + err = udp_register_helper(&pf->uh, us, + -1000, /* very low layer */ + pf_send_handler, pf_recv_handler, pf); + + if (err) + mem_deref(pf); + else + *pfp = pf; + + return err; +} diff --git a/test/mock/sipsrv.c b/test/mock/sipsrv.c new file mode 100644 index 000000000..8f93df2a0 --- /dev/null +++ b/test/mock/sipsrv.c @@ -0,0 +1,138 @@ +/** + * @file mock/sipsrv.c Mock SIP server + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/sipsrv" +#define DEBUG_LEVEL 5 +#include + + +#define LOCAL_PORT 0 +#define LOCAL_SECURE_PORT 0 + + +static bool sip_msg_handler(const struct sip_msg *msg, void *arg) +{ + struct sip_server *srv = arg; + int err; + + if (0 == pl_strcmp(&msg->met, "REGISTER")) { + ++srv->n_register_req; + } + else { + DEBUG_NOTICE("method not handled (%r)\n", &msg->met); + return false; + } + + if (srv->terminate) + err = sip_reply(srv->sip, msg, 503, "Server Error"); + else + err = sip_reply(srv->sip, msg, 200, "OK"); + + if (err) { + DEBUG_WARNING("could not reply: %m\n", err); + } + +#if 0 + if (srv->terminate) + re_cancel(); +#endif + + return true; +} + + +static void destructor(void *arg) +{ + struct sip_server *srv = arg; + + srv->terminate = true; + + sip_close(srv->sip, false); + mem_deref(srv->sip); +} + + +int sip_server_alloc(struct sip_server **srvp) +{ + struct sip_server *srv; + struct sa laddr, laddrs; + struct tls *tls = NULL; + int err; + + if (!srvp) + return EINVAL; + + srv = mem_zalloc(sizeof *srv, destructor); + if (!srv) + return ENOMEM; + + err = sa_set_str(&laddr, "127.0.0.1", LOCAL_PORT); + err |= sa_set_str(&laddrs, "127.0.0.1", LOCAL_SECURE_PORT); + if (err) + goto out; + + err = sip_alloc(&srv->sip, NULL, 16, 16, 16, + "mock SIP server", NULL, NULL); + if (err) + goto out; + + err |= sip_transp_add(srv->sip, SIP_TRANSP_UDP, &laddr); + err |= sip_transp_add(srv->sip, SIP_TRANSP_TCP, &laddr); + if (err) + goto out; + +#ifdef USE_TLS + err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + err = tls_set_certificate(tls, test_certificate_ecdsa, + strlen(test_certificate_ecdsa)); + if (err) + goto out; + + err |= sip_transp_add(srv->sip, SIP_TRANSP_TLS, &laddrs, tls); +#endif + if (err) + goto out; + + err = sip_listen(&srv->lsnr, srv->sip, true, sip_msg_handler, srv); + if (err) + goto out; + + out: + mem_deref(tls); + if (err) + mem_deref(srv); + else + *srvp = srv; + + return err; +} + + +int sip_server_uri(struct sip_server *srv, char *uri, size_t sz, + enum sip_transp tp) +{ + struct sa laddr; + int err; + + if (!srv || !uri || !sz) + return EINVAL; + + err = sip_transp_laddr(srv->sip, &laddr, tp, NULL); + if (err) + return err; + + if (re_snprintf(uri, sz, "sip:%J%s", &laddr, sip_transp_param(tp)) < 0) + return ENOMEM; + + return 0; +} diff --git a/test/mock/stunsrv.c b/test/mock/stunsrv.c new file mode 100644 index 000000000..59ecd60c6 --- /dev/null +++ b/test/mock/stunsrv.c @@ -0,0 +1,260 @@ +/** + * @file mock/stunsrv.c Mock STUN server + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/stunsrv" +#define DEBUG_LEVEL 5 +#include + + +enum { + TCP_MAX_LENGTH = 2048, +}; + + +static void process_msg(struct stunserver *stun, + int proto, void *sock, + const struct sa *src, const struct sa *dst, + struct mbuf *mb) +{ + struct stun_msg *msg; + bool fp = false; + int err; + (void)dst; + + stun->nrecv++; + + err = stun_msg_decode(&msg, mb, NULL); + if (err) + return; + +#if 0 + stun_msg_dump(msg); +#endif + + TEST_EQUALS(0x0001, stun_msg_type(msg)); + TEST_EQUALS(STUN_CLASS_REQUEST, stun_msg_class(msg)); + TEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg)); + + /* mirror FINGERPRINT attribute back in response */ + fp = NULL != stun_msg_attr(msg, STUN_ATTR_FINGERPRINT); + if (fp) { + TEST_EQUALS(0, stun_msg_chk_fingerprint(msg)); + } + + err = stun_reply(proto, sock, src, + 0, msg, NULL, 0, fp, 2, + STUN_ATTR_MAPPED_ADDR, src, + STUN_ATTR_XOR_MAPPED_ADDR, src); + + out: + if (err) { + (void)stun_ereply(proto, sock, src, 0, msg, 400, + "Bad Request", NULL, 0, fp, 0); + } + + mem_deref(msg); +} + + +static void stunserver_udp_recv(const struct sa *src, struct mbuf *mb, + void *arg) +{ + struct stunserver *stun = arg; + + process_msg(stun, IPPROTO_UDP, stun->us, src, &stun->laddr, mb); +} + + +static void tcp_recv(struct mbuf *mb, void *arg) +{ + struct stunserver *stun = arg; + int err = 0; + + if (stun->mb) { + size_t pos; + + pos = stun->mb->pos; + + stun->mb->pos = stun->mb->end; + + err = mbuf_write_mem(stun->mb, mbuf_buf(mb),mbuf_get_left(mb)); + if (err) { + goto out; + } + + stun->mb->pos = pos; + } + else { + stun->mb = mem_ref(mb); + } + + for (;;) { + + size_t len, pos, end; + uint16_t typ; + + if (mbuf_get_left(stun->mb) < 4) + break; + + typ = ntohs(mbuf_read_u16(stun->mb)); + len = ntohs(mbuf_read_u16(stun->mb)); + + if (len > TCP_MAX_LENGTH) { + DEBUG_WARNING("tcp: bad length: %zu\n", len); + err = EBADMSG; + goto out; + } + + if (typ < 0x4000) + len += STUN_HEADER_SIZE; + else if (typ < 0x8000) + len += 4; + else { + DEBUG_WARNING("tcp: bad type: 0x%04x\n", typ); + err = EBADMSG; + goto out; + } + + stun->mb->pos -= 4; + + if (mbuf_get_left(stun->mb) < len) + break; + + pos = stun->mb->pos; + end = stun->mb->end; + + stun->mb->end = pos + len; + + process_msg(stun, IPPROTO_TCP, stun->tc, &stun->paddr, + &stun->laddr_tcp, stun->mb); + + /* 4 byte alignment */ + while (len & 0x03) + ++len; + + stun->mb->pos = pos + len; + stun->mb->end = end; + + if (stun->mb->pos >= stun->mb->end) { + stun->mb = mem_deref(stun->mb); + break; + } + } + + out: + if (err) { + stun->mb = mem_deref(stun->mb); + } +} + + +static void tcp_close(int err, void *arg) +{ + struct stunserver *stun = arg; + (void)err; + + stun->tc = mem_deref(stun->tc); +} + + +static void tcp_conn_handler(const struct sa *peer, void *arg) +{ + struct stunserver *stun = arg; + int err; + + /* max 1 TCP connection */ + TEST_ASSERT(stun->tc == NULL); + err = tcp_accept(&stun->tc, stun->ts, NULL, tcp_recv, tcp_close, stun); + if (err) + goto out; + + stun->paddr = *peer; + + out: + if (err) { + /* save the error code */ + stun->err = err; + + tcp_reject(stun->ts); + } +} + + +static void stunserver_destructor(void *arg) +{ + struct stunserver *stun = arg; + + mem_deref(stun->us); + mem_deref(stun->mb); + mem_deref(stun->tc); + mem_deref(stun->ts); +} + + +/* Both UDP- and TCP-transport enabled by default */ +int stunserver_alloc(struct stunserver **stunp) +{ + struct stunserver *stun; + struct sa laddr; + int err; + + if (!stunp) + return EINVAL; + + stun = mem_zalloc(sizeof(*stun), stunserver_destructor); + if (!stun) + return ENOMEM; + + sa_set_str(&laddr, "127.0.0.1", 0); + + err = udp_listen(&stun->us, &laddr, stunserver_udp_recv, stun); + if (err) + goto out; + + err = udp_local_get(stun->us, &stun->laddr); + if (err) + goto out; + + err = tcp_listen(&stun->ts, &laddr, tcp_conn_handler, stun); + if (err) + goto out; + + err = tcp_local_get(stun->ts, &stun->laddr_tcp); + if (err) + goto out; + +#if 0 + DEBUG_NOTICE("stunserver: udp=%J, tcp=%J\n", + &stun->laddr, &stun->laddr_tcp); +#endif + + out: + if (err) + mem_deref(stun); + else + *stunp = stun; + + return err; +} + + +const struct sa *stunserver_addr(const struct stunserver *stun, int proto) +{ + if (!stun) + return NULL; + + switch (proto) { + + case IPPROTO_UDP: return &stun->laddr; + case IPPROTO_TCP: return &stun->laddr_tcp; + default: return NULL; + } + + return NULL; +} diff --git a/test/mock/tcpsrv.c b/test/mock/tcpsrv.c new file mode 100644 index 000000000..b41a58623 --- /dev/null +++ b/test/mock/tcpsrv.c @@ -0,0 +1,73 @@ +/** + * @file mock/tcpsrv.c Mock TCP server + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/tcpsrv" +#define DEBUG_LEVEL 5 +#include + + +static void destructor(void *arg) +{ + struct tcp_server *srv = arg; + + mem_deref(srv->ts); +} + + +static void tcp_conn_handler(const struct sa *peer, void *arg) +{ + struct tcp_server *srv = arg; + (void)peer; + + switch (srv->behavior) { + + case BEHAVIOR_REJECT: + tcp_reject(srv->ts); + break; + + default: + DEBUG_WARNING("behavior not implemented\n"); + break; + } +} + + +int tcp_server_alloc(struct tcp_server **srvp, enum behavior behavior) +{ + struct tcp_server *srv; + struct sa laddr; + int err; + + if (!srvp) + return EINVAL; + + srv = mem_zalloc(sizeof(*srv), destructor); + if (!srv) + return ENOMEM; + + sa_set_str(&laddr, "127.0.0.1", 0); + + srv->behavior = behavior; + + err = tcp_listen(&srv->ts, &laddr, tcp_conn_handler, srv); + if (err) + goto out; + + err = tcp_local_get(srv->ts, &srv->laddr); + if (err) + goto out; + + out: + if (err) + mem_deref(srv); + else + *srvp = srv; + + return err; +} diff --git a/test/mock/turnsrv.c b/test/mock/turnsrv.c new file mode 100644 index 000000000..fa5cf6b74 --- /dev/null +++ b/test/mock/turnsrv.c @@ -0,0 +1,483 @@ +/** + * @file mock/turnsrv.c Mock TURN server + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "mock/turnsrv" +#define DEBUG_LEVEL 5 +#include + + +enum { + TCP_MAX_LENGTH = 2048, +}; + + +static struct channel *find_channel_numb(struct turnserver *tt, uint16_t nr) +{ + size_t i; + + if (!tt) + return NULL; + + for (i=0; ichanc; i++) { + + if (tt->chanv[i].nr == nr) + return &tt->chanv[i]; + } + + return NULL; +} + + +static struct channel *find_channel_peer(struct turnserver *tt, + const struct sa *peer) +{ + size_t i; + + if (!tt) + return NULL; + + for (i=0; ichanc; i++) { + + if (sa_cmp(&tt->chanv[i].peer, peer, SA_ALL)) + return &tt->chanv[i]; + } + + return NULL; +} + + +static int add_permission(struct turnserver *tt, const struct sa *peer) +{ + int err = 0; + + TEST_ASSERT(tt->permc < RE_ARRAY_SIZE(tt->permv)); + tt->permv[tt->permc] = *peer; + ++tt->permc; + out: + return err; +} + + +static struct sa *find_permission(struct turnserver *tt, + const struct sa *peer) +{ + size_t i; + + if (!tt) + return NULL; + + for (i=0; ipermc; i++) { + + if (sa_cmp(&tt->permv[i], peer, SA_ADDR)) + return &tt->permv[i]; + } + + return NULL; +} + + +/* Receive packet on the "relayed" address -- relay to the client */ +static void relay_udp_recv(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct turnserver *turn = arg; + struct channel *chan; + int err = 0; + + ++turn->n_recv; + + chan = find_channel_peer(turn, src); + if (chan) { + uint16_t len = (uint16_t)mbuf_get_left(mb); + size_t start; + + if (mb->pos < 4) { + DEBUG_WARNING("relay_udp_recv: mb pos < 4\n"); + return; + } + + mb->pos -= 4; + start = mb->pos; + + (void)mbuf_write_u16(mb, htons(chan->nr)); + (void)mbuf_write_u16(mb, htons(len)); + + mb->pos = start; + + err = udp_send(turn->us, &turn->cli, mb); + } + else { + err = stun_indication(IPPROTO_UDP, turn->us, + &turn->cli, 0, STUN_METHOD_DATA, + NULL, 0, false, 2, + STUN_ATTR_XOR_PEER_ADDR, src, + STUN_ATTR_DATA, mb); + } + + if (err) { + DEBUG_WARNING("relay_udp_recv: error %m\n", err); + } +} + + +static void process_msg(struct turnserver *turn, int proto, void *sock, + const struct sa *src, struct mbuf *mb) +{ + struct stun_msg *msg = NULL; + struct sa laddr; + int err = 0; + + if (stun_msg_decode(&msg, mb, NULL)) { + + uint16_t numb, len; + struct channel *chan; + + if (!turn->us_relay) + return; + + ++turn->n_raw; + + numb = ntohs(mbuf_read_u16(mb)); + len = ntohs(mbuf_read_u16(mb)); + + if (mbuf_get_left(mb) < len) { + DEBUG_WARNING("short length: %zu < %u\n", + mbuf_get_left(mb), len); + } + + chan = find_channel_numb(turn, numb); + if (!chan) { + DEBUG_WARNING("channel not found: numb=%u\n", numb); + return; + } + + /* relay data from channel to peer */ + (void)udp_send(turn->us_relay, &chan->peer, mb); + return; + } + +#if 0 + re_printf("process: %s:%p:%J %s\n", + net_proto2name(proto), sock, src, + stun_method_name(stun_msg_method(msg))); +#endif + + switch (stun_msg_method(msg)) { + + case STUN_METHOD_ALLOCATE: + /* Max 1 allocation for now */ + ++turn->n_allocate; + + if (turn->us_relay) { + err = EALREADY; + goto out; + } + + turn->cli = *src; + + err = sa_set_str(&laddr, "127.0.0.1", 0); + if (err) + goto out; + + err = udp_listen(&turn->us_relay, &laddr, + relay_udp_recv, turn); + if (err) + goto out; + + err = udp_local_get(turn->us_relay, &turn->relay); + if (err) + goto out; + + udp_rxbuf_presz_set(turn->us_relay, 4); + + err = stun_reply(proto, sock, src, 0, + msg, NULL, 0, false, + 2, + STUN_ATTR_XOR_MAPPED_ADDR, src, + STUN_ATTR_XOR_RELAY_ADDR, &turn->relay); + break; + + case STUN_METHOD_CREATEPERM: { + struct stun_attr *peer; + + ++turn->n_createperm; + + peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); + TEST_ASSERT(peer != NULL); + + add_permission(turn, &peer->v.xor_peer_addr); + + /* todo: install permissions and check them */ + err = stun_reply(proto, sock, src, 0, + msg, NULL, 0, false, + 0); + } + break; + + case STUN_METHOD_CHANBIND: { + struct stun_attr *chnr, *peer; + + ++turn->n_chanbind; + + TEST_ASSERT(turn->us_relay != NULL); + + chnr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); + peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); + if (!chnr || !peer) { + DEBUG_WARNING("CHANBIND: missing chnr/peer attrib\n"); + goto out; + } + + TEST_ASSERT(turn->chanc < RE_ARRAY_SIZE(turn->chanv)); + turn->chanv[turn->chanc].nr = chnr->v.channel_number; + turn->chanv[turn->chanc].peer = peer->v.xor_peer_addr; + ++turn->chanc; + + err = stun_reply(proto, sock, src, 0, + msg, NULL, 0, false, + 0); + } + break; + + case STUN_METHOD_SEND: { + struct stun_attr *peer, *data; + + ++turn->n_send; + + TEST_ASSERT(turn->us_relay != NULL); + + peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); + data = stun_msg_attr(msg, STUN_ATTR_DATA); + + if (!peer || !data) { + DEBUG_WARNING("SEND: missing peer/data attrib\n"); + goto out; + } + + /* check for valid Permission */ + if (!find_permission(turn, &peer->v.xor_peer_addr)) { + DEBUG_NOTICE("no permission to peer %j\n", + &peer->v.xor_peer_addr); + goto out; + } + + err = udp_send(turn->us_relay, &peer->v.xor_peer_addr, + &data->v.data); + } + break; + + case STUN_METHOD_REFRESH: { + uint32_t lifetime = 1; /* short test lifetime */ + err = stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 1, + STUN_ATTR_LIFETIME, &lifetime); + } + break; + + default: + DEBUG_WARNING("unknown STUN method: %s\n", + stun_method_name(stun_msg_method(msg))); + err = EPROTO; + break; + } + + if (err) + goto out; + + out: + if (err && stun_msg_class(msg) == STUN_CLASS_REQUEST) { + (void)stun_ereply(proto, sock, src, 0, msg, + 500, "Server Error", + NULL, 0, false, 0); + } + + mem_deref(msg); +} + + +/* Simulated TURN server */ +static void srv_udp_recv(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct turnserver *turn = arg; + + process_msg(turn, IPPROTO_UDP, turn->us, src, mb); +} + + +static void tcp_estab_handler(void *arg) +{ + struct turnserver *turn = arg; + (void)turn; +} + + +static void tcp_recv_handler(struct mbuf *mb, void *arg) +{ + struct turnserver *conn = arg; + int err = 0; + + if (conn->mb) { + size_t pos; + + pos = conn->mb->pos; + + conn->mb->pos = conn->mb->end; + + err = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb)); + if (err) { + DEBUG_WARNING("tcp: buffer write error: %m\n", err); + goto out; + } + + conn->mb->pos = pos; + } + else { + conn->mb = mem_ref(mb); + } + + for (;;) { + + size_t len, pos, end; + uint16_t typ; + + if (mbuf_get_left(conn->mb) < 4) + break; + + typ = ntohs(mbuf_read_u16(conn->mb)); + len = ntohs(mbuf_read_u16(conn->mb)); + + if (len > TCP_MAX_LENGTH) { + re_printf("tcp: bad length: %zu\n", len); + err = EBADMSG; + goto out; + } + + if (typ < 0x4000) + len += STUN_HEADER_SIZE; + else if (typ < 0x8000) + len += 4; + else { + re_printf("tcp: bad type: 0x%04x\n", typ); + err = EBADMSG; + goto out; + } + + conn->mb->pos -= 4; + + if (mbuf_get_left(conn->mb) < len) + break; + + pos = conn->mb->pos; + end = conn->mb->end; + + conn->mb->end = pos + len; + + process_msg(conn, IPPROTO_TCP, conn->tc, &conn->paddr, + conn->mb); + + /* 4 byte alignment */ + while (len & 0x03) + ++len; + + conn->mb->pos = pos + len; + conn->mb->end = end; + + if (conn->mb->pos >= conn->mb->end) { + conn->mb = mem_deref(conn->mb); + break; + } + } + + out: + if (err) { + conn->mb = mem_deref(conn->mb); + } +} + + +static void tcp_close_handler(int err, void *arg) +{ + struct turnserver *turn = arg; + (void)err; + + turn->tc = mem_deref(turn->tc); +} + + +static void tcp_conn_handler(const struct sa *peer, void *arg) +{ + struct turnserver *turn = arg; + int err = 0; + + if (turn->tc) { + tcp_reject(turn->ts); + } + else { + err = tcp_accept(&turn->tc, turn->ts, tcp_estab_handler, + tcp_recv_handler, tcp_close_handler, turn); + if (err) + tcp_reject(turn->ts); + + turn->paddr = *peer; + } +} + + +static void destructor(void *arg) +{ + struct turnserver *turn = arg; + + mem_deref(turn->us); + mem_deref(turn->us_relay); + mem_deref(turn->tc); + mem_deref(turn->ts); + mem_deref(turn->mb); +} + + +int turnserver_alloc(struct turnserver **turnp) +{ + struct turnserver *turn; + struct sa laddr; + int err = 0; + + if (!turnp) + return EINVAL; + + turn = mem_zalloc(sizeof(*turn), destructor); + if (!turn) + return ENOMEM; + + err = sa_set_str(&laddr, "127.0.0.1", 0); + if (err) + goto out; + + err = udp_listen(&turn->us, &laddr, srv_udp_recv, turn); + if (err) + goto out; + + err = udp_local_get(turn->us, &turn->laddr); + if (err) + goto out; + + err = tcp_listen(&turn->ts, &laddr, tcp_conn_handler, turn); + if (err) + goto out; + + err = tcp_sock_local_get(turn->ts, &turn->laddr_tcp); + if (err) + goto out; + + out: + if (err) + mem_deref(turn); + else + *turnp = turn; + + return err; +} diff --git a/test/mqueue.c b/test/mqueue.c new file mode 100644 index 000000000..983c0a708 --- /dev/null +++ b/test/mqueue.c @@ -0,0 +1,73 @@ +/** + * @file mqueue.c Message queue testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "mqueue_test" +#define DEBUG_LEVEL 5 +#include + + +#define NUM_EVENTS 3 + + +struct test { + int idv[NUM_EVENTS]; + void *datav[NUM_EVENTS]; + unsigned idc; +}; + + +static void mqueue_handler(int id, void *data, void *arg) +{ + struct test *test = arg; + + test->idv [test->idc] = id; + test->datav[test->idc] = data; + + test->idc++; + + if (test->idc >= NUM_EVENTS) + re_cancel(); +} + + +int test_mqueue(void) +{ + struct mqueue *mq; + struct test test; + int i; + int err; + + memset(&test, 0, sizeof(test)); + + err = mqueue_alloc(&mq, mqueue_handler, &test); + if (err) + return err; + + for (i=0; i +#include "test.h" + + +#define DEBUG_MODULE "test_net" +#define DEBUG_LEVEL 5 +#include + + +static bool ipv6_handler(const char *ifname, const struct sa *sa, void *arg) +{ + bool *supp = arg; + (void)ifname; + + if (AF_INET6 == sa_af(sa)) { + *supp = true; + return true; + } + + return false; +} + + +static bool ipv6_supported(void) +{ + bool supp = false; + + net_if_apply(ipv6_handler, &supp); + + return supp; +} + + +int test_net_dst_source_addr_get(void) +{ + struct sa dst; + struct sa ip; + int err; + + sa_init(&dst, AF_INET); + sa_init(&ip, AF_UNSPEC); + + sa_set_str(&dst, "127.0.0.1", 53); + + err = net_dst_source_addr_get(&dst, &ip); + if (err) + return err; + + TEST_ASSERT(sa_is_loopback(&ip)); + + if (ipv6_supported()) { + + sa_init(&dst, AF_INET6); + sa_init(&ip, AF_UNSPEC); + sa_set_str(&dst, "::1", 53); + + err = net_dst_source_addr_get(&dst, &ip); + if (err) + return err; + + TEST_ASSERT(sa_is_loopback(&ip)); + } + else { + DEBUG_NOTICE("ipv6 disabled\n"); + } + +out: + return err; +} + + +int test_net_if(void) +{ + struct sa ip; + int err; + char ifname[255]; + + sa_set_str(&ip, "127.0.0.1", 0); + + err = net_if_getname(ifname, sizeof(ifname), AF_INET, &ip); + TEST_ERR(err); + TEST_ASSERT(str_isset(ifname)); + +out: + return err; +} diff --git a/test/odict.c b/test/odict.c new file mode 100644 index 000000000..ae1e2694d --- /dev/null +++ b/test/odict.c @@ -0,0 +1,240 @@ +/** + * @file odict.c Testcode for Ordered Dictionary + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "odict" +#define DEBUG_LEVEL 5 +#include + + +struct dtest { + const char *key; + enum odict_type type; + union { + int64_t i; + double d; + char *s; + bool b; + } u; +}; + + +static int compare(const struct dtest *test, const struct odict_entry *entry) +{ + int err = 0; + + TEST_ASSERT(entry != NULL); + + TEST_STRCMP(test->key, str_len(test->key), + odict_entry_key(entry), strlen(odict_entry_key(entry))); + + TEST_EQUALS(test->type, odict_entry_type(entry)); + + switch (test->type) { + + case ODICT_INT: + TEST_EQUALS(test->u.i, odict_entry_int(entry)); + break; + + case ODICT_BOOL: + TEST_EQUALS(test->u.b, odict_entry_boolean(entry)); + break; + + case ODICT_DOUBLE: + TEST_EQUALS(test->u.d, odict_entry_dbl(entry)); + break; + + case ODICT_STRING: + TEST_STRCMP(test->u.s, str_len(test->u.s), + odict_entry_str(entry), + str_len(odict_entry_str(entry))); + break; + + default: + DEBUG_WARNING("cannot compare type %d\n", test->type); + err = EINVAL; + goto out; + } + + out: + return err; +} + + +int test_odict(void) +{ + static const struct dtest testv[] = { + {"a", ODICT_INT, {.i = 42 } }, + {"b", ODICT_DOUBLE, {.d = 3.14} }, + {"c", ODICT_STRING, {.s = "rattarei"} }, + {"d", ODICT_BOOL, {.b = true } }, + {"e", ODICT_BOOL, {.b = false } }, + + {"ha73ofhjausdhkjquhriasjdhkaksjdkjqkjhwkjhqweaaaaaa", + ODICT_INT, {.i = 617284773 } }, + {"mkkpnaidhs747fusyudyaisdiayiakakakaakajdjiwi123456", + ODICT_INT, {.i = 12345678 } }, + {"lllalsidjjlalksjdlaksjdlkjasd", + ODICT_DOUBLE, {.d = 123456.123456} }, + {"8adhw6fjshdkhjaskjdhakdhaskhdkjh", + ODICT_DOUBLE, {.d = -0.007} }, + + {"hafgsdudue6dhcgfjgieiwuehsdkjhsdjkhasdkjasdkjhasd", + ODICT_STRING, + {.s = "kasjdsuher7yw783897njxdvkhjskhdkhsdkhjsdkhjasdasd"} + }, + {"liajsdoiausdoaudoaisudaoisdjalsijdalsidjalidjaslidj", + ODICT_STRING, + {.s = "bdjkhdiuasd7y78yw4y78ryuseyugfasygdasygdh"} + }, + }; + struct odict *dict = NULL; + size_t i; + int err; + + TEST_ASSERT(odict_type_iscontainer(ODICT_OBJECT)); + TEST_ASSERT(odict_type_iscontainer(ODICT_ARRAY)); + TEST_ASSERT(odict_type_isreal(ODICT_INT)); + TEST_ASSERT(odict_type_isreal(ODICT_DOUBLE)); + TEST_ASSERT(odict_type_isreal(ODICT_STRING)); + TEST_ASSERT(odict_type_isreal(ODICT_BOOL)); + TEST_ASSERT(odict_type_isreal(ODICT_NULL)); + + err = odict_alloc(&dict, 64); + if (err) + goto out; + + for (i=0; ikey)); + + switch (test->type) { + + case ODICT_INT: + err = odict_entry_add(dict, test->key, + test->type, test->u.i); + break; + + case ODICT_BOOL: + err = odict_entry_add(dict, test->key, + test->type, (int)test->u.b); + break; + + case ODICT_DOUBLE: + err = odict_entry_add(dict, test->key, + test->type, test->u.d); + break; + + case ODICT_STRING: + err = odict_entry_add(dict, test->key, + test->type, test->u.s); + break; + + default: + err = EINVAL; + goto out; + break; + } + if (err) + goto out; + + /* verify that entry exist after adding */ + entry = odict_lookup(dict, test->key); + TEST_ASSERT(entry != NULL); + + err = compare(test, entry); + TEST_ERR(err); + } + + /* verify size of dictionary, after adding all entries */ + TEST_EQUALS(RE_ARRAY_SIZE(testv), odict_count(dict, false)); + + /* compare dictionary with itself */ + TEST_ASSERT(odict_compare(dict, dict, false)); + + /* remove all entries */ + for (i=0; ikey); + TEST_ASSERT(e != NULL); + + /* delete entry from dictionary */ + mem_deref(e); + + /* entry should not exist anymore */ + e = (struct odict_entry *)odict_lookup(dict, test->key); + TEST_ASSERT(e == NULL); + } + + /* verify size of dictionary, after removing all entries */ + TEST_EQUALS(0, odict_count(dict, false)); + + out: + mem_deref(dict); + return err; +} + + +int test_odict_array(void) +{ + struct odict *arr = NULL; + const struct odict_entry *e; + int err; + + err = odict_alloc(&arr, 64); + if (err) + goto out; + + /* test an empty array */ + TEST_EQUALS(0, odict_count(arr, false)); + TEST_ASSERT(NULL == odict_lookup(arr, "0")); + TEST_ASSERT(NULL == odict_lookup(arr, "255")); + + /* add some elements of variying types */ + err = odict_entry_add(arr, "0", ODICT_STRING, "hei"); + if (err) + goto out; + + TEST_EQUALS(1, odict_count(arr, false)); + + err = odict_entry_add(arr, "1", ODICT_INT, 1LL); + err |= odict_entry_add(arr, "2", ODICT_INT, 2LL); + err |= odict_entry_add(arr, "3", ODICT_INT, 3LL); + if (err) + goto out; + + TEST_EQUALS(4, odict_count(arr, false)); + + /* verify that elements are correct */ + e = odict_lookup(arr, "0"); + TEST_ASSERT(e != NULL); + TEST_EQUALS(ODICT_STRING, odict_entry_type(e)); + TEST_STRCMP("hei", (size_t)3, odict_entry_str(e), + str_len(odict_entry_str(e))); + + e = odict_lookup(arr, "3"); + TEST_ASSERT(e != NULL); + TEST_EQUALS(ODICT_INT, odict_entry_type(e)); + TEST_EQUALS(3LL, odict_entry_int(e)); + +#if 0 + re_printf("%H\n", odict_debug, arr); +#endif + + out: + mem_deref(arr); + return err; +} diff --git a/test/pcp.c b/test/pcp.c new file mode 100644 index 000000000..3a6e992cd --- /dev/null +++ b/test/pcp.c @@ -0,0 +1,273 @@ +/** + * @file pcp.c Port Control Protocol (PCP) Testcode + * + * Copyright (C) 2010 - 2022 Alfred E. Heggestad + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "pcptest" +#define DEBUG_LEVEL 5 +#include + + +static int test_pcp_request_loop(size_t offset) +{ + struct mbuf *mb; + struct pcp_msg *msg = NULL; + static const uint8_t nonce[12] = { + 0xc0, 0xff, 0xee, 0x00, + 0xc0, 0xff, 0xee, 0x00, + 0xc0, 0xff, 0xee, 0x00, + }; + size_t i; + int err = 0; + const uint16_t int_port = 2000; + static const struct { + enum pcp_opcode opcode; + uint32_t lifetime; + const char *ipaddr; + } testreqv[] = { + {PCP_MAP, 600, "10.0.0.20" }, + {PCP_MAP, 3600, "2a02:fe0:cf12:91:226:8ff:fee1:cdf3" }, + {PCP_PEER, 600, "46.123.65.9" }, + {PCP_PEER, 999999, "2a02:fe0:cf12:91:226:8ff:fee1:cdf3" }, + }; + + mb = mbuf_alloc(offset + 512); + if (!mb) + return ENOMEM; + + for (i=0; ipos = mb->end = offset; + err = pcp_msg_req_encode(mb, testreqv[i].opcode, + testreqv[i].lifetime, + &sa, &peer, 0); + if (err) + break; + + TEST_ASSERT(mb->pos != offset); + TEST_ASSERT(mb->end != offset); + + mb->pos = offset; + err = pcp_msg_decode(&msg, mb); + if (err) + break; + + TEST_EQUALS(PCP_VERSION, msg->hdr.version); + TEST_EQUALS(false, msg->hdr.resp); + TEST_EQUALS(testreqv[i].opcode, msg->hdr.opcode); + TEST_EQUALS(testreqv[i].lifetime, msg->hdr.lifetime); + TEST_SACMP(&sa, &msg->hdr.cli_addr, SA_ADDR); + + switch (testreqv[i].opcode) { + + case PCP_MAP: + case PCP_PEER: + TEST_MEMCMP(nonce, sizeof(nonce), + msg->pld.map.nonce, + sizeof(msg->pld.map.nonce)); + TEST_EQUALS(IPPROTO_UDP, msg->pld.map.proto); + TEST_EQUALS(int_port, msg->pld.map.int_port); + break; + + default: + break; + } + + TEST_EQUALS(0, list_count(&msg->optionl)); + + msg = mem_deref(msg); + } + + out: + mem_deref(msg); + mem_deref(mb); + + return err; +} + + +static const uint8_t peer_request[] = { + + 0x02, 0x02, 0x00, 0x00, /* version | opcode */ + 0x00, 0x00, 0x02, 0x58, /* lifetime */ + 0x2a, 0x00, 0x14, 0x50, /* . */ + 0x40, 0x0f, 0x08, 0x03, /* |client IP-address */ + 0x00, 0x00, 0x00, 0x00, /* | */ + 0x00, 0x00, 0x10, 0x00, /* ' */ + + 0xa9, 0x5f, 0xc9, 0xb7, /* */ + 0x12, 0x3b, 0xa9, 0x66, /* nonce */ + 0x33, 0xcd, 0xe2, 0xb9, /* */ + + 0x11, 0x00, 0x00, 0x00, /* protocol */ + 0x13, 0x8c, 0x13, 0x8d, /* internal port | external port */ + 0x2a, 0x00, 0x14, 0x50, /* . */ + 0x40, 0x0f, 0x08, 0x03, /* |external IP Address */ + 0x00, 0x00, 0x00, 0x00, /* | */ + 0x00, 0x00, 0x20, 0x00, /* ' */ + + 0x13, 0x8e, 0x00, 0x00, /* remote peer port */ + + 0x2a, 0x00, 0x14, 0x50, /* */ + 0x40, 0x0f, 0x08, 0x03, /* remote peer IP-address */ + 0x00, 0x00, 0x00, 0x00, /* */ + 0x00, 0x00, 0x30, 0x00, /* */ + + 0x01, 0x00, 0x00, 0x10, /* opcode THIRD_PARTY header */ + 0x2a, 0x00, 0x14, 0x50, /* . */ + 0x40, 0x0f, 0x08, 0x03, /* |internal IP address */ + 0x00, 0x00, 0x00, 0x00, /* | */ + 0x00, 0x00, 0x40, 0x00, /* ' */ + + }; + + +static int test_pcp_message(void) +{ + enum pcp_opcode opcode = PCP_PEER; + uint32_t lifetime = 600; + static const uint8_t nonce[] = { + 0xa9, 0x5f, 0xc9, 0xb7, + 0x12, 0x3b, 0xa9, 0x66, + 0x33, 0xcd, 0xe2, 0xb9, + }; + int proto = IPPROTO_UDP; + struct pcp_peer peer; + struct sa int_addr, thi_addr; + struct mbuf *mb; + struct pcp_msg *msg = NULL; + enum {DUMMY_OFFSET = 64}; + int err = 0; + + memcpy(peer.map.nonce, nonce, sizeof(peer.map.nonce)); + peer.map.proto = IPPROTO_UDP; + peer.map.int_port = 5004; + + err |= sa_set_str(&int_addr, "2a00:1450:400f:803::1000", 0); + err |= sa_set_str(&peer.map.ext_addr, "2a00:1450:400f:803::2000", + 5005); + err |= sa_set_str(&peer.remote_addr, "2a00:1450:400f:803::3000", 5006); + err |= sa_set_str(&thi_addr, "2a00:1450:400f:803::4000", 0); + if (err) + return err; + + mb = mbuf_alloc(DUMMY_OFFSET + 512); + if (!mb) + return ENOMEM; + + mb->pos = DUMMY_OFFSET; + err = pcp_msg_req_encode(mb, opcode, lifetime, &int_addr, &peer, + 1, PCP_OPTION_THIRD_PARTY, &thi_addr); + if (err) + goto out; + + TEST_ASSERT(mb->pos != DUMMY_OFFSET); + TEST_ASSERT(mb->pos == mb->end); + + mb->pos = DUMMY_OFFSET; + + TEST_MEMCMP(peer_request, sizeof(peer_request), + mbuf_buf(mb), mbuf_get_left(mb)); + + err = pcp_msg_decode(&msg, mb); + if (err) + goto out; + + TEST_EQUALS(PCP_VERSION, msg->hdr.version); + TEST_EQUALS(false, msg->hdr.resp); + TEST_EQUALS(opcode, msg->hdr.opcode); + TEST_EQUALS(lifetime, msg->hdr.lifetime); + TEST_SACMP(&int_addr, &msg->hdr.cli_addr, SA_ALL); + + TEST_MEMCMP(nonce, sizeof(nonce), + msg->pld.peer.map.nonce, sizeof(msg->pld.peer.map.nonce)); + TEST_EQUALS(proto, msg->pld.peer.map.proto); + TEST_EQUALS(5004, msg->pld.peer.map.int_port); + TEST_SACMP(&peer.map.ext_addr, &msg->pld.peer.map.ext_addr, SA_ALL); + TEST_SACMP(&peer.remote_addr, &msg->pld.peer.remote_addr, SA_ALL); + + out: + mem_deref(mb); + mem_deref(msg); + return err; +} + + +static int test_pcp_option(void) +{ + struct mbuf *mb; + struct pcp_option *opt = NULL; + struct sa addr; + static const uint8_t opt_pkt[] = { + 0x01, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, + 0x0a, 0x00, 0x00, 0x01 + }; + int err; + + mb = mbuf_alloc(64); + if (!mb) + return ENOMEM; + + err = sa_set_str(&addr, "10.0.0.1", 0); + if (err) + goto out; + + err = pcp_option_encode(mb, PCP_OPTION_THIRD_PARTY, &addr); + if (err) + goto out; + + TEST_MEMCMP(opt_pkt, sizeof(opt_pkt), mb->buf, mb->end); + + mb->pos = 0; + err = pcp_option_decode(&opt, mb); + + TEST_EQUALS(PCP_OPTION_THIRD_PARTY, opt->code); + TEST_SACMP(&addr, &opt->u.third_party, SA_ADDR); + + out: + mem_deref(opt); + mem_deref(mb); + return err; +} + + +int test_pcp(void) +{ + int err; + + err = test_pcp_request_loop(0); + err |= test_pcp_request_loop(4); + if (err) + return err; + + err = test_pcp_message(); + if (err) + return err; + + err = test_pcp_option(); + if (err) + return err; + + return err; +} diff --git a/test/remain.c b/test/remain.c new file mode 100644 index 000000000..9294b87f9 --- /dev/null +++ b/test/remain.c @@ -0,0 +1,147 @@ +/** + * @file remain.c Testcode for re main loop + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "remain" +#define DEBUG_LEVEL 5 +#include + + +struct data { + thrd_t tid; + mtx_t *mutex; + bool thread_started; + bool thread_exited; + unsigned tmr_called; + int err; +}; + + +static void tmr_handler(void *arg) +{ + struct data *data = arg; + int err = 0; + + mtx_lock(data->mutex); + + /* verify that timer is called from the new thread */ + TEST_ASSERT(0 != thrd_equal(data->tid, thrd_current())); + + ++data->tmr_called; + + out: + if (err) + data->err = err; + + mtx_unlock(data->mutex); + + re_cancel(); +} + + +static int thread_handler(void *arg) +{ + struct data *data = arg; + struct tmr tmr; + int err; + + data->thread_started = true; + + tmr_init(&tmr); + + err = re_thread_init(); + if (err) { + DEBUG_WARNING("re thread init: %m\n", err); + data->err = err; + return 0; + } + +#ifndef WIN32 + err = fd_setsize(-1); + TEST_ERR(err); +#endif + err = re_thread_init(); + ASSERT_TRUE(err == 0 || err == EALREADY); + + tmr_start(&tmr, 1, tmr_handler, data); + + /* run the main loop now */ + err = re_main(NULL); + +out: + if (err) + data->err = err; + tmr_cancel(&tmr); + + /* cleanup */ + tmr_debug(); + re_thread_close(); + + data->thread_exited = true; + + return 0; +} + + +static int test_remain_thread(void) +{ + struct data data; + int i, err; + + memset(&data, 0, sizeof(data)); + + err = mutex_alloc(&data.mutex); + if (err) + return err; + + err = thread_create_name(&data.tid, "remain", thread_handler, &data); + TEST_ERR(err); + + /* wait for timer to be called */ + for (i=0; i<500; i++) { + mtx_lock(data.mutex); + + if (data.tmr_called || data.err) { + mtx_unlock(data.mutex); + break; + } + + mtx_unlock(data.mutex); + + sys_msleep(1); + } + + data.mutex = mem_deref(data.mutex); + + /* wait for thread to end */ + thrd_join(data.tid, NULL); + + if (data.err) + return data.err; + + TEST_ASSERT(data.thread_started); + TEST_ASSERT(data.thread_exited); + TEST_EQUALS(1, data.tmr_called); + TEST_EQUALS(0, data.err); + + out: + mem_deref(data.mutex); + return err; +} + + +int test_remain(void) +{ + int err = 0; + + err = test_remain_thread(); + + return err; +} diff --git a/test/rtcp.c b/test/rtcp.c new file mode 100644 index 000000000..fb2175120 --- /dev/null +++ b/test/rtcp.c @@ -0,0 +1,193 @@ +/** + * @file rtcp.c Tests for RTCP + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "rtcp_test" +#define DEBUG_LEVEL 5 +#include + + +/* + * .------. RTP + * | RTP | <--------------- [ Packet Generator ] + * | RTCP | + * '------' + */ + + +#define GENERATOR_SSRC 0x00000001 + + +struct fixture { + struct rtp_sock *rtp; + struct sa rtp_addr; + size_t n_recv; + size_t num_packets; +}; + + +static int send_rtp_packet(struct udp_sock *us, const struct sa *dst, + uint16_t seq, uint32_t ssrc) +{ + struct rtp_header hdr; + struct mbuf *mb = mbuf_alloc(256); + int err; + + if (!mb) + return ENOMEM; + + memset(&hdr, 0, sizeof(hdr)); + hdr.ver = RTP_VERSION; + hdr.seq = seq; + hdr.ts = 0; + hdr.ssrc = ssrc; + + err = rtp_hdr_encode(mb, &hdr); + if (err) + goto out; + mbuf_fill(mb, 160, 0x00); + mb->pos = 0; + + err = udp_send(us, dst, mb); + if (err) + goto out; + + out: + mem_deref(mb); + return err; +} + + +static void rtp_recv(const struct sa *src, const struct rtp_header *hdr, + struct mbuf *mb, void *arg) +{ + struct fixture *f = arg; + (void)src; + (void)hdr; + (void)mb; + + ++f->n_recv; + + if (f->n_recv >= f->num_packets) { + re_cancel(); + } +} + + +static int fixture_init(struct fixture *f) +{ + int err; + + memset(f, 0, sizeof(*f)); + + err = sa_set_str(&f->rtp_addr, "127.0.0.1", 0); + TEST_ERR(err); + + err = rtp_listen(&f->rtp, IPPROTO_UDP, &f->rtp_addr, 10000, 49152, + true, rtp_recv, NULL, f); + if (err) + goto out; + + err = udp_local_get(rtp_sock(f->rtp), &f->rtp_addr); + TEST_ERR(err); + + out: + return err; +} + + +static void fixture_close(struct fixture *f) +{ + mem_deref(f->rtp); +} + + +static int test_loss(const uint16_t *seqv, size_t seqc, + int exp_lost) +{ + struct fixture fix, *f = &fix; + struct rtcp_stats stats; + uint32_t ssrc = GENERATOR_SSRC; + unsigned i; + int err, e; + + err = fixture_init(f); + if (err) + goto out; + + f->num_packets = seqc; + + /* no sources exist yet */ + e = rtcp_stats(f->rtp, ssrc, &stats); + TEST_EQUALS(ENOENT, e); + + /* start the RTP packet generator, send X number of RTP packets + * to the RTP-stack (fixture) + */ + for (i=0; irtp), &f->rtp_addr, + seq, ssrc); + if (err) + goto out; + } + + err = re_main_timeout(200); + TEST_ERR(err); + + err = rtcp_stats(f->rtp, ssrc, &stats); + if (err) { + if (err == ENOENT) + err = ENOMEM; + goto out; + } + + /* in OOM-test, detect if member/sender was not allocated */ + if (stats.rx.sent == 0 && + stats.rx.lost == 0 && + stats.rx.jit == 0) { + + err = ENOMEM; + goto out; + } + + /* verify expected packets sent and packet loss */ + TEST_EQUALS(seqc, stats.rx.sent); + TEST_EQUALS(exp_lost, stats.rx.lost); + + TEST_EQUALS(seqc, f->n_recv); + + out: + fixture_close(f); + return err; +} + + +int test_rtcp_packetloss(void) +{ + static const uint16_t seqv1[] = {0, 1, 2}; + static const uint16_t seqv2[] = {0,1,3,2,5,4}; + static const uint16_t seqv3[] = {65534, 65535, 0, 1}; + static const uint16_t seqv4[] = {65534, 0, 1}; + static const uint16_t seqv5[] = {65534, 1, 2}; + static const uint16_t seqv6[] = {65534, 1, 2, 65535}; + static const uint16_t seqv7[] = {1,2,8,9,10}; + int err = 0; + + err |= test_loss(seqv1, RE_ARRAY_SIZE(seqv1), 0); + err |= test_loss(seqv2, RE_ARRAY_SIZE(seqv2), 0); + err |= test_loss(seqv3, RE_ARRAY_SIZE(seqv3), 0); + err |= test_loss(seqv4, RE_ARRAY_SIZE(seqv4), 1); + err |= test_loss(seqv5, RE_ARRAY_SIZE(seqv5), 2); + err |= test_loss(seqv6, RE_ARRAY_SIZE(seqv6), 1); + err |= test_loss(seqv7, RE_ARRAY_SIZE(seqv7), 5); + + return err; +} diff --git a/test/rtmp.c b/test/rtmp.c new file mode 100644 index 000000000..3acb4cded --- /dev/null +++ b/test/rtmp.c @@ -0,0 +1,857 @@ +/** + * @file rtmp.c RTMP Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "rtmp" +#define DEBUG_LEVEL 5 +#include + + +#define NUM_MEDIA_PACKETS 4 + + +/* Force testing of Extended Timestamp */ +#define TS_OFFSET 0x00fffffe + + +#define DUMMY_STREAM_ID 42 + + +#define CHUNK_SIZE 4096 + + +enum mode { + MODE_PLAY, + MODE_PUBLISH, +}; + + +struct rtmp_endpoint { + struct rtmp_endpoint *other; + struct rtmp_conn *conn; + struct rtmp_stream *stream; + struct tcp_sock *ts; /* server only */ + struct tls *tls; + const char *tag; + enum mode mode; + uint32_t stream_id; + bool is_client; + unsigned n_estab; + unsigned n_cmd; + unsigned n_stream_cmd; + unsigned n_close; + unsigned n_ready; + unsigned n_play; + unsigned n_publish; + unsigned n_publish_start; + unsigned n_deletestream; + unsigned n_audio; + unsigned n_video; + unsigned n_data; + int err; +}; + + +static const uint8_t fake_audio_packet[CHUNK_SIZE + 6] = { + 0x5b, 0xb2, 0xfb, 0x11, 0x46, 0xe9 +}; + +static const uint8_t fake_video_packet[CHUNK_SIZE + 8] = { + 0xcb, 0x9c, 0xb5, 0x60, 0x7f, 0xe9, 0xbd, 0xe1 +}; + +static const char *fake_stream_name = "sample.mp4"; +static const char *fake_app_inst = "vod"; + + +static void endpoint_terminate(struct rtmp_endpoint *ep, int err) +{ + if (err) { + DEBUG_INFO("[ %s ] terminate: %m\n", ep->tag, err); + } + + ep->err = err; + re_cancel(); +} + + +/* criteria for test to be finished */ +static bool client_is_finished(const struct rtmp_endpoint *ep) +{ + switch (ep->mode) { + + case MODE_PLAY: + return ep->n_ready > 0 && + ep->n_audio >= NUM_MEDIA_PACKETS && + ep->n_video >= NUM_MEDIA_PACKETS; + + case MODE_PUBLISH: + return ep->n_ready > 0 && + ep->n_publish_start > 0; + } + + return false; +} + + +static bool server_is_finished(const struct rtmp_endpoint *ep) +{ + switch (ep->mode) { + + case MODE_PLAY: + return ep->n_play > 0; + + case MODE_PUBLISH: + return ep->n_publish > 0 && + ep->n_audio >= NUM_MEDIA_PACKETS && + ep->n_video >= NUM_MEDIA_PACKETS; + } + + return false; +} + + +static bool endpoints_are_finished(const struct rtmp_endpoint *ep) +{ + if (ep->is_client) { + return client_is_finished(ep) && + server_is_finished(ep->other); + } + else { + return client_is_finished(ep->other) && + server_is_finished(ep); + } +} + + +static int send_media(struct rtmp_endpoint *ep_cli) +{ + unsigned i; + int err = 0; + + /* Send some dummy media packets to server */ + for (i=0; istream, i, + fake_audio_packet, + sizeof(fake_audio_packet)); + if (err) + return err; + + err = rtmp_send_video(ep_cli->stream, + TS_OFFSET + i, + fake_video_packet, + sizeof(fake_video_packet)); + if (err) + return err; + } + + return 0; +} + + +static void stream_command_handler(const struct odict *msg, void *arg) +{ + struct rtmp_endpoint *ep = arg; + const char *name; + int err = 0; + + name = odict_string(msg, "0"); + + DEBUG_INFO("[%s] stream command: %s\n", ep->tag, name); + + TEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id); + + ++ep->n_stream_cmd; + + if (0 == str_casecmp(name, "play")) { + + const char *stream_name; + uint64_t tid; + uint32_t i; + + ++ep->n_play; + + if (!odict_get_number(msg, &tid, "1")) { + err = EPROTO; + goto out; + } + TEST_EQUALS(0, tid); + + stream_name = odict_string(msg, "3"); + TEST_STRCMP(fake_stream_name, strlen(fake_stream_name), + stream_name, str_len(stream_name)); + + err = rtmp_amf_data(ep->conn, DUMMY_STREAM_ID, + "|RtmpSampleAccess", + 2, + RTMP_AMF_TYPE_BOOLEAN, false, + RTMP_AMF_TYPE_BOOLEAN, false); + if (err) + goto out; + + /* Send some dummy media packets to client */ + + for (i=0; istream, i, + fake_audio_packet, + sizeof(fake_audio_packet)); + if (err) + goto out; + + err = rtmp_send_video(ep->stream, TS_OFFSET + i, + fake_video_packet, + sizeof(fake_video_packet)); + if (err) + goto out; + } + } + else if (0 == str_casecmp(name, "publish")) { + + const char *stream_name; + const char *code = "NetStream.Publish.Start"; + uint64_t tid; + + ++ep->n_publish; + + if (!odict_get_number(msg, &tid, "1")) { + err = EPROTO; + goto out; + } + TEST_EQUALS(0, tid); + + stream_name = odict_string(msg, "3"); + TEST_STRCMP(fake_stream_name, strlen(fake_stream_name), + stream_name, str_len(stream_name)); + + err = rtmp_amf_command(ep->conn, ep->stream_id, "onStatus", + 3, + RTMP_AMF_TYPE_NUMBER, (double)0, + RTMP_AMF_TYPE_NULL, + RTMP_AMF_TYPE_OBJECT, 2, + RTMP_AMF_TYPE_STRING, "level", "status", + RTMP_AMF_TYPE_STRING, "code", code); + if (err) + goto out; + } + else if (0 == str_casecmp(name, "onStatus")) { + + struct odict *obj; + const char *level; + + obj = odict_get_object(msg, "3"); + + level = odict_string(obj, "level"); + if (0 == str_casecmp(level, "status")) { + + const char *code = odict_string(obj, "code"); + const char *exp_code = "NetStream.Publish.Start"; + + ++ep->n_publish_start; + + TEST_STRCMP(exp_code, str_len(exp_code), + code, str_len(code)); + + err = rtmp_meta(ep->stream); + if (err) + goto out; + + err = send_media(ep); + if (err) + goto out; + } + else { + DEBUG_WARNING("unsupported level %s\n", level); + } + } + else { + DEBUG_NOTICE("[ %s ] stream: command not handled (%s)\n", + ep->tag, name); + err = ENOTSUP; + goto out; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void test_done(struct rtmp_endpoint *ep) +{ + struct rtmp_endpoint *client; + + if (ep->is_client) + client = ep; + else + client = ep->other; + + /* Force destruction here to test robustness */ + + client->stream = mem_deref(client->stream); +} + + +static void stream_control_handler(enum rtmp_event_type event, struct mbuf *mb, + void *arg) +{ + struct rtmp_endpoint *ep = arg; + int err = 0; + (void)mb; + + TEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id); + + DEBUG_INFO("[ %s ] got control event: event=%d (%s)\n", + ep->tag, event, rtmp_event_name(event)); + + switch (event) { + + default: + break; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void audio_handler(uint32_t timestamp, + const uint8_t *pld, size_t len, void *arg) +{ + struct rtmp_endpoint *ep = arg; + int err = 0; + + TEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id); + + TEST_EQUALS(ep->n_audio, timestamp); + + ++ep->n_audio; + + TEST_MEMCMP(fake_audio_packet, sizeof(fake_audio_packet), pld, len); + + /* Test complete ? */ + if (endpoints_are_finished(ep)) { + + test_done(ep); + return; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void video_handler(uint32_t timestamp, + const uint8_t *pld, size_t len, void *arg) +{ + struct rtmp_endpoint *ep = arg; + int err = 0; + + TEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id); + + TEST_EQUALS(TS_OFFSET + ep->n_video, timestamp); + + ++ep->n_video; + + TEST_MEMCMP(fake_video_packet, sizeof(fake_video_packet), pld, len); + + /* Test complete ? */ + if (endpoints_are_finished(ep)) { + + test_done(ep); + return; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void stream_data_handler(const struct odict *msg, void *arg) +{ + struct rtmp_endpoint *ep = arg; + const char *command; + bool ret; + bool value; + int err = 0; + + TEST_EQUALS(DUMMY_STREAM_ID, ep->stream_id); + + ++ep->n_data; + + command = odict_string(msg, "0"); + + if (ep->is_client) { + + TEST_STRCMP("|RtmpSampleAccess", 17, + command, str_len(command)); + + ret = odict_get_boolean(msg, &value, "1"); + TEST_ASSERT(ret); + TEST_ASSERT(!value); + + ret = odict_get_boolean(msg, &value, "2"); + TEST_ASSERT(ret); + TEST_ASSERT(!value); + } + else { + const struct odict_entry *e; + uint64_t num; + + TEST_STRCMP("@setDataFrame", 13, command, str_len(command)); + + e = odict_get_type(msg, ODICT_OBJECT, "2"); + TEST_ASSERT(e != NULL); + + ret = odict_get_number(odict_entry_object(e), &num, + "audiocodecid"); + TEST_ASSERT(ret); + TEST_EQUALS(10ULL, num); + + ret = odict_get_number(odict_entry_object(e), &num, + "videocodecid"); + TEST_ASSERT(ret); + TEST_EQUALS(7ULL, num); + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void stream_create_resp_handler(bool success, + const struct odict *msg, void *arg) +{ + struct rtmp_endpoint *ep = arg; + uint64_t stream_id; + int err; + + TEST_ASSERT(success); + + ++ep->n_ready; + + /* the stream-id was assigned by the server */ + if (!odict_get_number(msg, &stream_id, "3")) { + err = EPROTO; + goto out; + } + ep->stream_id = (uint32_t)stream_id; + + switch (ep->mode) { + + case MODE_PLAY: + err = rtmp_play(ep->stream, fake_stream_name); + if (err) + goto out; + break; + + case MODE_PUBLISH: + err = rtmp_publish(ep->stream, fake_stream_name); + if (err) + goto out; + break; + + default: + err = EPROTO; + goto out; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void estab_handler(void *arg) +{ + struct rtmp_endpoint *ep = arg; + int err = 0; + + DEBUG_INFO("[%s] Established\n", ep->tag); + + ++ep->n_estab; + + if (ep->is_client) { + + TEST_ASSERT(ep->stream == NULL); + + err = rtmp_stream_create(&ep->stream, ep->conn, + stream_create_resp_handler, + stream_command_handler, + stream_control_handler, + audio_handler, + video_handler, stream_data_handler, + ep); + if (err) + goto out; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +/* Server */ +static int server_send_reply(struct rtmp_conn *conn, const struct odict *req) +{ + const char *code = "NetConnection.Connect.Success"; + const char *descr = "Connection succeeded."; + int err; + + err = rtmp_amf_reply(conn, 0, true, req, + 2, + + RTMP_AMF_TYPE_OBJECT, 3, + RTMP_AMF_TYPE_STRING, "fmsVer", "FMS/3,5,7,7009", + RTMP_AMF_TYPE_NUMBER, "capabilities", 31.0, + RTMP_AMF_TYPE_NUMBER, "mode", 1.0, + + RTMP_AMF_TYPE_OBJECT, 6, + RTMP_AMF_TYPE_STRING, "level", "status", + RTMP_AMF_TYPE_STRING, "code", code, + RTMP_AMF_TYPE_STRING, "description", descr, + RTMP_AMF_TYPE_ECMA_ARRAY, "data", 1, + RTMP_AMF_TYPE_STRING, "version", "3,5,7,7009", + RTMP_AMF_TYPE_NUMBER, "clientid", 734806661.0, + RTMP_AMF_TYPE_NUMBER, "objectEncoding", 0.0); + + return err; +} + + +static void command_handler(const struct odict *msg, void *arg) +{ + struct rtmp_endpoint *ep = arg; + const char *name; + int err = 0; + + TEST_ASSERT(!ep->is_client); + + name = odict_string(msg, "0"); + + ++ep->n_cmd; + + if (0 == str_casecmp(name, "connect")) { + + const struct odict_entry *entry; + uint32_t window_ack_size = 32000; + const char *app; + + entry = odict_lookup(msg, "2"); + TEST_ASSERT(entry != NULL); + + app = odict_string(odict_entry_object(entry), "app"); + TEST_STRCMP(fake_app_inst, strlen(fake_app_inst), + app, str_len(app)); + + err = rtmp_control(ep->conn, RTMP_TYPE_WINDOW_ACK_SIZE, + (uint32_t)window_ack_size); + if (err) + goto out; + + err = server_send_reply(ep->conn, msg); + if (err) { + re_printf("rtmp: reply failed (%m)\n", err); + goto out; + } + } + else if (0 == str_casecmp(name, "createStream")) { + + uint32_t stream_id = DUMMY_STREAM_ID; + + TEST_ASSERT(ep->stream == NULL); + + ep->stream_id = stream_id; + + err = rtmp_stream_alloc(&ep->stream, ep->conn, stream_id, + stream_command_handler, + stream_control_handler, audio_handler, + video_handler, stream_data_handler, + ep); + if (err) { + goto out; + } + + err = rtmp_amf_reply(ep->conn, 0, true, msg, + 2, + RTMP_AMF_TYPE_NULL, + RTMP_AMF_TYPE_NUMBER, (double)stream_id); + if (err) { + re_printf("rtmp: reply failed (%m)\n", err); + goto out; + } + } + else if (0 == str_casecmp(name, "deleteStream")) { + + uint64_t stream_id; + + ++ep->n_deletestream; + + if (!odict_get_number(msg, &stream_id, "3")) { + err = EPROTO; + goto out; + } + + TEST_EQUALS(DUMMY_STREAM_ID, stream_id); + + if (stream_id == ep->stream_id) + ep->stream = mem_deref(ep->stream); + + /* re_main will be stopped when the + * TCP connection is closed + */ + + endpoint_terminate(ep, 0); + } + else { + DEBUG_NOTICE("[ %s ] command not handled (%s)\n", + ep->tag, name); + err = ENOTSUP; + goto out; + } + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static void close_handler(int err, void *arg) +{ + struct rtmp_endpoint *ep = arg; + + if (err) { + DEBUG_INFO("[ %s ] rtmp connection closed (%m)\n", + ep->tag, err); + } + + ++ep->n_close; + + endpoint_terminate(ep, err); +} + + +static void endpoint_destructor(void *data) +{ + struct rtmp_endpoint *ep = data; + + mem_deref(ep->stream); + mem_deref(ep->conn); + mem_deref(ep->tls); + mem_deref(ep->ts); +} + + +static struct rtmp_endpoint *rtmp_endpoint_alloc(enum mode mode, + bool is_client, bool secure) +{ + struct rtmp_endpoint *ep; + int err = 0; + + ep = mem_zalloc(sizeof(*ep), endpoint_destructor); + if (!ep) + return NULL; + + ep->is_client = is_client; + ep->mode = mode; + + if (secure) { + +#ifdef USE_TLS + char path[256]; + + re_snprintf(path, sizeof(path), "%s/server-ecdsa.pem", + test_datapath()); + + err = tls_alloc(&ep->tls, TLS_METHOD_SSLV23, + is_client ? NULL : path, NULL); + if (err) + goto out; + + /* Client: Add the server's certificate as a CA cert. + * This is required for authentication to work. + */ + if (is_client) { + + err = tls_add_ca(ep->tls, path); + if (err) + goto out; + } +#else + err = ENOSYS; + goto out; +#endif + } + + ep->tag = is_client ? "Client" : "Server"; + + out: + if (err) + return mem_deref(ep); + + return ep; +} + + +static void tcp_conn_handler(const struct sa *peer, void *arg) +{ + struct rtmp_endpoint *ep = arg; + int err; + (void)peer; + + err = rtmp_accept(&ep->conn, ep->ts, ep->tls, command_handler, + close_handler, ep); + if (err) + goto out; + + out: + if (err) + endpoint_terminate(ep, err); +} + + +static int test_rtmp_client_server_conn(enum mode mode, bool secure) +{ + struct rtmp_endpoint *cli, *srv; + struct sa srv_addr; + char uri[256]; + int err = 0; + + cli = rtmp_endpoint_alloc(mode, true, secure); + srv = rtmp_endpoint_alloc(mode, false, secure); + if (!cli || !srv) { + err = ENOMEM; + goto out; + } + + cli->other = srv; + srv->other = cli; + + err = sa_set_str(&srv_addr, "127.0.0.1", 0); + TEST_ERR(err); + + err = tcp_listen(&srv->ts, &srv_addr, tcp_conn_handler, srv); + if (err) + goto out; + + err = tcp_local_get(srv->ts, &srv_addr); + TEST_ERR(err); + + re_snprintf(uri, sizeof(uri), "rtmp%s://%J/%s/foo", + secure ? "s" : "", &srv_addr, fake_app_inst); + + err = rtmp_connect(&cli->conn, NULL, uri, cli->tls, estab_handler, + command_handler, close_handler, cli); + if (err) + goto out; + + err = re_main_timeout(1000); + if (err) + goto out; + + if (cli->err) { + err = cli->err; + goto out; + } + if (srv->err) { + err = srv->err; + goto out; + } + + TEST_EQUALS(1, cli->n_estab); + TEST_EQUALS(0, srv->n_estab); + TEST_EQUALS(0, cli->n_cmd); + TEST_EQUALS(3, srv->n_cmd); + + TEST_EQUALS(1, srv->n_stream_cmd); + + TEST_EQUALS(0, cli->n_close); + + TEST_EQUALS(1, cli->n_ready); + TEST_EQUALS(0, srv->n_ready); + TEST_EQUALS(0, cli->n_deletestream); + TEST_EQUALS(1, srv->n_deletestream); + + switch (mode) { + + case MODE_PLAY: + TEST_EQUALS(1, srv->n_play); + TEST_EQUALS(0, srv->n_publish); + + TEST_EQUALS(NUM_MEDIA_PACKETS, cli->n_audio); + TEST_EQUALS(NUM_MEDIA_PACKETS, cli->n_video); + TEST_EQUALS(1, cli->n_data); + TEST_EQUALS(0, srv->n_audio); + TEST_EQUALS(0, srv->n_video); + TEST_EQUALS(0, srv->n_data); + break; + + case MODE_PUBLISH: + TEST_EQUALS(0, srv->n_play); + TEST_EQUALS(1, srv->n_publish); + + TEST_EQUALS(0, cli->n_audio); + TEST_EQUALS(0, cli->n_video); + TEST_EQUALS(0, cli->n_data); + TEST_EQUALS(NUM_MEDIA_PACKETS, srv->n_audio); + TEST_EQUALS(NUM_MEDIA_PACKETS, srv->n_video); + TEST_EQUALS(1, srv->n_data); + break; + } + + out: + mem_deref(srv); + mem_deref(cli); + + return err; +} + + +int test_rtmp_play(void) +{ + int err = 0; + + err = test_rtmp_client_server_conn(MODE_PLAY, false); + if (err) + return err; + + return err; +} + + +int test_rtmp_publish(void) +{ + int err = 0; + + err = test_rtmp_client_server_conn(MODE_PUBLISH, false); + if (err) + return err; + + return err; +} + + +#ifdef USE_TLS +int test_rtmps_publish(void) +{ + int err = 0; + + err = test_rtmp_client_server_conn(MODE_PUBLISH, true); + if (err) + return err; + + return err; +} +#endif diff --git a/test/rtp.c b/test/rtp.c new file mode 100644 index 000000000..595aee6f7 --- /dev/null +++ b/test/rtp.c @@ -0,0 +1,702 @@ +/** + * @file rtp.c RTP/RTCP Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "rtptest" +#define DEBUG_LEVEL 5 +#include + + +enum {PAYLOAD_SIZE = 160}; + + +int test_rtp(void) +{ + struct rtp_sock *rtp = NULL; + struct mbuf *mb = NULL; + static const uint8_t payload[PAYLOAD_SIZE]; + int j; + int err; + + mb = mbuf_alloc(RTP_HEADER_SIZE); + if (!mb) + return ENOMEM; + + err = rtp_alloc(&rtp); + if (err) + goto out; + + for (j=0; j<100; j++) { + struct rtp_header hdr, hdr2; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.m = j & 0x01; + hdr.pt = j & 0x7f; + hdr.ts = 160 + j; + + mb->pos = mb->end = RTP_HEADER_SIZE; + err = mbuf_write_mem(mb, payload, sizeof(payload)); + if (err) + break; + + mb->pos = 0; + err = rtp_encode(rtp, false, hdr.m, hdr.pt, hdr.ts, mb); + if (err) + break; + + mb->pos = 0; + + err = rtp_decode(rtp, mb, &hdr2); + if (err) + break; + + if (hdr.m != hdr2.m) { + DEBUG_WARNING("marker bit mismatch (%d != %d)\n", + hdr.m, hdr2.m); + err = EBADMSG; + break; + } + + if (hdr.pt != hdr2.pt) { + DEBUG_WARNING("payload type mismatch (%u != %u)\n", + hdr.pt, hdr2.pt); + err = EBADMSG; + break; + } + + if (hdr.ts != hdr2.ts) { + DEBUG_WARNING("timestamp mismatch (%lu != %lu)\n", + hdr.ts, hdr2.ts); + err = EBADMSG; + break; + } + + if (hdr2.pad) { + DEBUG_WARNING("unexpected padding bit\n"); + err = EBADMSG; + break; + } + + if (hdr2.ext) { + DEBUG_WARNING("unexpected extension bit\n"); + err = EBADMSG; + break; + } + + if (RTP_HEADER_SIZE != mb->pos || + (RTP_HEADER_SIZE + PAYLOAD_SIZE) != mb->end) { + DEBUG_WARNING("invalid mbuf size (pos=%u end=%u)\n", + mb->pos, mb->end); + err = EBADMSG; + break; + } + + if (0 != memcmp(mbuf_buf(mb), payload, sizeof(payload))) { + DEBUG_WARNING("RTP payload mismatch\n"); + err = EBADMSG; + break; + } + } + + out: + mem_deref(rtp); + mem_deref(mb); + + return err; +} + + +static const uint8_t rtcp_msg[] = + /* SR */ + "\x81\xc8\x00\x0c" + "\x12\x34\x56\x78" + "\x00\x11\x22\x33" + "\x44\x55\x66\x77" + "\x22\x33\x22\x33" + "\x00\x00\x11\x11" + "\x00\x00\x22\x22" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + + /* RR */ + "\x81\xc9\x00\x07" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + "\x12\x34\x56\x78" + + /* SDES */ + "\x81\xca\x00\x03" + "\xde\xad\xbe\xef" + "\x01\x03\x61\x62" + "\x63\x00\x00\x00" + + /* BYE */ + "\x82\xcb\x00\x04" + "\x12\x34\x56\x78" + "\x00\xab\xcd\xef" + "\x04\x63\x69\x61" + "\x6f\x00\x00\x00" + + /* APP */ + "\x80\xcc\x00\x03" + "\x12\x34\x56\x78" + "\x6e\x61\x6d\x65" + "\x64\x61\x74\x61" + + /* FIR */ + "\x80\xc0\x00\x01" + "\x12\x34\x56\x78" + + /* NACK */ + "\x80\xc1\x00\x02" + "\x12\x34\x56\x78" + "\x89\xab\xcd\xef" + + /* RTPFB */ + "\x81\xcd\x00\x05" + "\x12\x34\x56\x78" + "\xfe\xdc\xba\x98" + "\x01\x23\x04\x56" + "\x01\x23\x04\x56" + "\x01\x23\x04\x56" + + /* PSFB - PLI */ + "\x81\xce\x00\x02" + "\x12\x34\x56\x78" + "\xfe\xdc\xba\x98" + + /* PSFB - SLI */ + "\x82\xce\x00\x04" + "\x12\x34\x56\x78" + "\xfe\xdc\xba\x98" + "\xca\xfe\xca\xfe" + "\xca\xfe\xca\xfe" + + ""; + + +static int encode_handler(struct mbuf *mb, void *arg) +{ + int err = 0; + size_t i; + + (void)arg; + + for (i=0; i<6 && !err; i++) + err = mbuf_write_u32(mb, htonl(0x12345678)); + + return err; +} + + +static int sdes_encode_handler(struct mbuf *mb, void *arg) +{ + (void)arg; + return rtcp_sdes_encode(mb, 0xdeadbeef, 1, RTCP_SDES_CNAME, "abc"); +} + + +static int gnack_encode(struct mbuf *mb, void *arg) +{ + int err = 0, n=3; + (void)arg; + + while (n--) { + err |= mbuf_write_u16(mb, htons(0x0123)); + err |= mbuf_write_u16(mb, htons(0x0456)); + } + + return err; +} + + +static int sli_encode(struct mbuf *mb, void *arg) +{ + int err = 0, n=2; + (void)arg; + + while (n--) { + err |= mbuf_write_u32(mb, htonl(0xcafecafe)); + } + + return err; +} + + +int test_rtcp_encode(void) +{ + struct mbuf *mb; + const size_t sz = sizeof(rtcp_msg) - 1; + const uint32_t srcv[2] = {0x12345678, 0x00abcdef}; + int err = 0; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + + err |= rtcp_encode(mb, RTCP_SR, 1, 0x12345678, 0x00112233, + 0x44556677, 0x22332233, 0x00001111, 0x00002222, + encode_handler, 0); + err |= rtcp_encode(mb, RTCP_RR, 1, 0x12345678, encode_handler, 0); + err |= rtcp_encode(mb, RTCP_SDES, 1, sdes_encode_handler, 0); + err |= rtcp_encode(mb, RTCP_BYE, 2, srcv, "ciao"); + err |= rtcp_encode(mb, RTCP_APP, 0, 0x12345678, "name", "data", + (size_t)4); + err |= rtcp_encode(mb, RTCP_FIR, 0, 0x12345678); + err |= rtcp_encode(mb, RTCP_NACK, 0, 0x12345678, 0x89ab, 0xcdef); + err |= rtcp_encode(mb, RTCP_RTPFB, RTCP_RTPFB_GNACK, + 0x12345678, 0xfedcba98, gnack_encode, 0); + err |= rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_PLI, + 0x12345678, 0xfedcba98, NULL, 0); + err |= rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_SLI, + 0x12345678, 0xfedcba98, sli_encode, 0); + if (err) + goto out; + + if (mb->end != sz) { + err = EPROTO; + } + if (0 != memcmp(mb->buf, rtcp_msg, mb->end)) { + err = EBADMSG; + } + + if (err) { + DEBUG_WARNING("encode error: %m\n", err); + hexdump(stderr, mb->buf, mb->end); + } + + mb->pos = 0; + while (mbuf_get_left(mb) >= 4 && !err) { + struct rtcp_msg *msg = NULL; + err = rtcp_decode(&msg, mb); + msg = mem_deref(msg); + } + if (err) + goto out; + + /* verify that rtcp_decode() read the whole buffer */ + TEST_EQUALS(mb->end, mb->pos); + + out: + mem_deref(mb); + return err; +} + + +static const uint8_t rtcp_sdes[] = + /* SDES */ + "\x83\xca\x00\x09" + "\x11\x22\x33\x44" + "\x01\x02\x41\x61" /* cname */ + "\x00\x00\x00\x00" + "\x55\x66\x77\x88" + "\x07\x02\x42\x62" /* note */ + "\x02\x01\x43\x00" /* name */ + "\xaa\xbb\xcc\xdd" + "\x04\x05\x31\x32" /* phone */ + "\x33\x34\x35\x00" + + /* APP */ + "\x80\xcc\x00\x03" + "\x12\x34\x56\x78" + "\x6e\x61\x6d\x65" + "\x64\x61\x74\x61" + ""; + + +static int test_rtcp_decode_badmsg(void) +{ + struct rtcp_msg *msg = NULL; + uint32_t ssrc = 0xcafebabe; + struct mbuf *mb; + int err = 0; + + mb = mbuf_alloc(128); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_SLI, + ssrc, ssrc, NULL, NULL); + if (err) + goto out; + + /* simulate a corrupt RTCP packet */ + mb->pos = 2; + (void)mbuf_write_u16(mb, htons(0)); + + mb->pos = 0; + + if (EBADMSG != rtcp_decode(&msg, mb)) { + err = EBADMSG; + goto out; + } + + out: + mem_deref(msg); + mem_deref(mb); + + return err; +} + + +int test_rtcp_decode(void) +{ + struct rtcp_msg *msg = NULL; + struct mbuf *mb; + int err = 0; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err |= mbuf_write_u8(mb, 0x55); /* overhead -- test padding */ + err |= mbuf_write_mem(mb, rtcp_sdes, sizeof(rtcp_sdes)); + err |= mbuf_write_u8(mb, 0xaa); /* junk */ + mb->pos = 1; + + /* SDES */ + err = rtcp_decode(&msg, mb); + TEST_ERR(err); + + ASSERT_EQ( 2, msg->hdr.version); + ASSERT_EQ( 0, msg->hdr.p); + ASSERT_EQ( 3, msg->hdr.count); + ASSERT_EQ(202, msg->hdr.pt); + ASSERT_EQ( 9, msg->hdr.length); + + const struct rtcp_sdes *sdes = &msg->r.sdesv[0]; + + ASSERT_EQ(0x11223344, sdes->src); + ASSERT_EQ(1, sdes->n); + + const struct rtcp_sdes_item *item = &sdes->itemv[0]; + + ASSERT_EQ(1, item->type); + ASSERT_EQ(2, item->length); + TEST_STRCMP("Aa", 2, item->data, item->length); + + msg = mem_deref(msg); + + /* APP */ + err = rtcp_decode(&msg, mb); + TEST_ERR(err); + + ASSERT_EQ( 2, msg->hdr.version); + ASSERT_EQ( 0, msg->hdr.p); + ASSERT_EQ( 0, msg->hdr.count); + ASSERT_EQ(204, msg->hdr.pt); + ASSERT_EQ( 3, msg->hdr.length); + + ASSERT_EQ( 0x12345678, msg->r.app.src); + TEST_STRCMP("name", 4, msg->r.app.name, 4); + TEST_STRCMP("data", 4, msg->r.app.data, msg->r.app.data_len); + + if (err) + goto out; + + err = test_rtcp_decode_badmsg(); + if (err) + return err; + + out: + mem_deref(msg); + mem_deref(mb); + + return err; +} + + +static int afb_encode_handler(struct mbuf *mb, void *arg) +{ + return mbuf_write_str(mb, arg); +} + + +int test_rtcp_encode_afb(void) +{ + uint32_t ssrc_packet_sender, ssrc_media_source; + const char *afb_payload = "AFB tull"; + struct rtcp_msg *msg = NULL; + struct mbuf *mb; + int err = 0; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + ssrc_packet_sender = 0xbad00bad; + ssrc_media_source = 0; /* always 0 */ + err = rtcp_encode(mb, RTCP_PSFB, RTCP_PSFB_AFB, + ssrc_packet_sender, ssrc_media_source, + afb_encode_handler, afb_payload); + if (err) + goto out; + + mb->pos = 0; + err = rtcp_decode(&msg, mb); + if (err) + goto out; + + if (msg->hdr.count != RTCP_PSFB_AFB) { + DEBUG_WARNING("expected AFB, got fmt=%u\n", msg->hdr.count); + err = EPROTO; + goto out; + } + + if (msg->r.fb.ssrc_packet != ssrc_packet_sender || + msg->r.fb.ssrc_media != ssrc_media_source) { + DEBUG_WARNING("error in SSRC encoding\n"); + err = EBADMSG; + goto out; + } + + if (!msg->r.fb.fci.afb || + mbuf_get_left(msg->r.fb.fci.afb) != strlen(afb_payload)) { + DEBUG_WARNING("error in AFB mbuf (left=%u, size=%u)\n", + mbuf_get_left(msg->r.fb.fci.afb), + strlen(afb_payload)); + err = EBADMSG; + goto out; + } + + if (0 != memcmp(mbuf_buf(msg->r.fb.fci.afb), + afb_payload, + strlen(afb_payload))) { + DEBUG_WARNING("error in AFB mbuf content\n"); + err = EBADMSG; + goto out; + } + + /* verify that rtcp_decode() read the whole buffer */ + TEST_EQUALS(mb->end, mb->pos); + + out: + mem_deref(mb); + mem_deref(msg); + return err; +} + + +struct rtp_test { + struct rtp_sock *rtp; + struct mbuf *mb; + uint32_t n; + uint32_t f; +}; + + +static void rtp_recv_handler(const struct sa *src, + const struct rtp_header *hdr, struct mbuf *mb, + void *arg) +{ + struct rtp_test *test = arg; + char bufs[5]; + char bufr[5]; + (void) src; + (void) hdr; + + mbuf_read_str(test->mb, bufs, sizeof(bufs)); + mbuf_read_str(mb, bufr, sizeof(bufr)); + + if (!strncmp(bufr, bufs, sizeof(bufs))) + test->n++; + else + test->f++; + + if (test->n + test->f == 2) + re_cancel(); + else + mbuf_advance(test->mb, RTP_HEADER_SIZE); +} + + +static int test_rtp_listen_priv(bool clear) +{ + struct rtp_test test; + struct sa sa; + size_t pos; + int err; + + sa_init(&sa, AF_INET); + memset(&test, 0, sizeof(test)); + err = rtp_listen(&test.rtp, IPPROTO_UDP, &sa, 1024, 49152, false, + rtp_recv_handler, NULL, &test); + TEST_ERR(err); + + test.mb = mbuf_alloc(2 * (RTP_HEADER_SIZE + 5)); + if (!test.mb) { + err = ENOMEM; + goto out; + } + + pos = RTP_HEADER_SIZE; + test.mb->pos = test.mb->end = pos; + mbuf_write_str(test.mb, "abcd"); + mbuf_write_u8(test.mb, 0); + test.mb->pos = pos; + sa_set_str(&sa, "127.0.0.1", sa_port(rtp_local(test.rtp))); + err = rtp_send(test.rtp, &sa, false, true, 0, 160, + tmr_jiffies_rt_usec(), test.mb); + TEST_ERR(err); + + pos = test.mb->end + RTP_HEADER_SIZE; + test.mb->pos = test.mb->end = pos; + mbuf_write_str(test.mb, "bcde"); + mbuf_write_u8(test.mb, 0); + test.mb->pos = pos; + err = rtp_send(test.rtp, &sa, false, false, 0, 320, + tmr_jiffies_rt_usec(), test.mb); + TEST_ERR(err); + + if (clear) { + err = rtp_clear(test.rtp); + TEST_ERR(err); + } + + test.mb->pos = RTP_HEADER_SIZE; + if (!clear) + (void)re_main_timeout(100); + + TEST_EQUALS(clear ? 0 : 2, test.n); + TEST_EQUALS(0, test.f); + +out: + mem_deref(test.rtp); + mem_deref(test.mb); + return err; +} + + +int test_rtp_listen(void) +{ + int err; + + err = test_rtp_listen_priv(false); + TEST_ERR(err); + + err = test_rtp_listen_priv(true); + TEST_ERR(err); +out: + return err; +} + + +int test_rtcp_twcc(void) +{ + /* + A RTCP transport-cc parser test. Done as part of WebRtcTransport + which uses it. TWCC rtcp packets have been extracted from what + Chrome sends using Wireshark and concatenated to form a single + compound packet. + */ + uint8_t packets[] = { + + /* First packet */ + 0xaf, 0xcd, 0x00, 0x07, 0xfa, 0x17, + 0xfa, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x03, 0x00, 0x09, 0x00, + 0x42, 0x6d, 0x00, 0xad, 0xe0, + 0x14, 0x18, 0x18, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + + /* Second packet */ + 0xaf, 0xcd, 0x00, 0x0c, 0xfa, 0x17, + 0xfa, 0x17, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x0c, 0x00, 0x1e, 0x00, 0x42, 0x6d, + 0x01, 0x96, 0xf7, 0xbb, 0xf3, 0x20, 0x02, + 0xb0, 0x14, 0x08, 0x58, 0x18, 0x00, + 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + + /* Third packet */ + 0xaf, 0xcd, 0x00, 0x07, 0xfa, 0x17, + 0xfa, 0x17, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x2a, 0x00, 0x0b, 0x00, + 0x42, 0x6e, 0x02, 0x9b, 0xf8, + 0xb8, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + + struct mbuf *buf = mbuf_alloc(sizeof(packets)); + + struct rtcp_msg *msg = NULL; + int err = 0; + + if (!buf) + return ENOMEM; + + mbuf_rewind(buf); + mbuf_write_mem(buf, packets, sizeof(packets)); + mbuf_set_pos(buf, 0); + + /* TWCC n=5 base=3 count=9 reftime=17005 fbcount=0 chunks=2 deltas=7 + (with padding 00 00 03) */ + err = rtcp_decode(&msg, buf); + TEST_ERR(err); + EXPECT_EQ(err, 0); + EXPECT_EQ(msg->hdr.count, RTCP_RTPFB_TWCC); + EXPECT_EQ(msg->r.fb.n, 5); + EXPECT_TRUE(msg->r.fb.fci.twccv != NULL); + EXPECT_EQ(msg->r.fb.fci.twccv->seq, 3); + EXPECT_EQ(msg->r.fb.fci.twccv->count, 9); + EXPECT_EQ(msg->r.fb.fci.twccv->reftime, 17005); + EXPECT_EQ(msg->r.fb.fci.twccv->fbcount, 0); + EXPECT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->chunks), 2); + EXPECT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->deltas), 7); + msg = mem_deref(msg); + + /* TWCC n=10 base=12 count=30 reftime=17005 fbcount=1 chunks=6 + deltas=23 (with padding 00 00 03) */ + err = rtcp_decode(&msg, buf); + TEST_ERR(err); + EXPECT_EQ(err, 0); + EXPECT_EQ(msg->hdr.count, RTCP_RTPFB_TWCC); + EXPECT_EQ(msg->r.fb.n, 10); + EXPECT_EQ(msg->r.fb.fci.twccv->seq, 12); + EXPECT_EQ(msg->r.fb.fci.twccv->count, 30); + EXPECT_EQ(msg->r.fb.fci.twccv->reftime, 17005); + EXPECT_EQ(msg->r.fb.fci.twccv->fbcount, 1); + EXPECT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->chunks), 6); + EXPECT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->deltas), 23); + msg = mem_deref(msg); + + /* TWCC n=5 base=42 count=11 reftime=17006 fbcount=2 chunks=2 + deltas=9 (with padding 01) */ + err = rtcp_decode(&msg, buf); + TEST_ERR(err); + EXPECT_EQ(err, 0); + EXPECT_EQ(msg->hdr.count, RTCP_RTPFB_TWCC); + EXPECT_EQ(msg->r.fb.n, 5); + EXPECT_EQ(msg->r.fb.fci.twccv->seq, 42); + EXPECT_EQ(msg->r.fb.fci.twccv->count, 11); + EXPECT_EQ(msg->r.fb.fci.twccv->reftime, 17006); + EXPECT_EQ(msg->r.fb.fci.twccv->fbcount, 2); + EXPECT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->chunks), 2); + EXPECT_EQ(mbuf_get_left(msg->r.fb.fci.twccv->deltas), 9); + msg = mem_deref(msg); + + /* Assert we have processed everything. */ + EXPECT_EQ(mbuf_get_left(buf), 0); + + out: + mem_deref(buf); + mem_deref(msg); + return err; +} diff --git a/test/rtpext.c b/test/rtpext.c new file mode 100644 index 000000000..f4d6bddf4 --- /dev/null +++ b/test/rtpext.c @@ -0,0 +1,71 @@ +/** + * @file rtpext.c RTP Header Extensions + * + * Copyright (C) 2010 - 2022 Alfred E. Heggestad + */ + +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "rtpext" +#define DEBUG_LEVEL 5 +#include + + +static const uint8_t packet_bytes[] = { + + /* Header */ + 0xbe, 0xde, 0x00, 0x01, + + /* First Extension */ + 0x40, 0x30, + + /* Second Extension */ + 0x10, 0xe0 +}; + + +int test_rtpext(void) +{ + struct mbuf *mb = mbuf_alloc(sizeof(packet_bytes)); + if (!mb) + return ENOMEM; + + int err = mbuf_write_mem(mb, packet_bytes, sizeof(packet_bytes)); + ASSERT_EQ(0, err); + + mb->pos = 0; + + /* decode header */ + uint16_t type = ntohs(mbuf_read_u16(mb)); + uint16_t words = ntohs(mbuf_read_u16(mb)); + + ASSERT_EQ(RTPEXT_TYPE_MAGIC, type); + ASSERT_EQ(1, words); + + size_t num_bytes = words * sizeof(uint32_t); + + ASSERT_EQ(num_bytes, mbuf_get_left(mb)); + + struct rtpext ext; + + /* First extension */ + err = rtpext_decode(&ext, mb); + ASSERT_EQ(0, err); + ASSERT_EQ(4, ext.id); + ASSERT_EQ(1, ext.len); + ASSERT_EQ(0x30, ext.data[0]); + + /* Second extension */ + err = rtpext_decode(&ext, mb); + ASSERT_EQ(0, err); + ASSERT_EQ(1, ext.id); + ASSERT_EQ(1, ext.len); + ASSERT_EQ(0xe0, ext.data[0]); + + out: + mem_deref(mb); + return err; +} diff --git a/test/sa.c b/test/sa.c new file mode 100644 index 000000000..f3eced9f9 --- /dev/null +++ b/test/sa.c @@ -0,0 +1,391 @@ +/** + * @file sa.c Socket address Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_sa" +#define DEBUG_LEVEL 5 +#include + + +int test_sa_cmp(void) +{ + const struct { + const char *host1; + uint16_t port1; + const char *host2; + uint16_t port2; + bool eq; + } testv[] = { + { + "unix:/test.sock", 0, + "unix:/test.sock", 0, + true + }, + { + "1.2.3.4", 12345, + "1.2.3.4", 12345, + true + }, + { + "1.2.3.4", 12345, + "1.2.3.5", 12345, + false + }, + { + "1.2.3.4", 12345, + "1.2.3.4", 12344, + false + }, +#ifdef HAVE_INET6 + { + "0:0:0:0:0:0:0:0", 123, + "::", 123, + true + }, + { + "0:0:0:0:0:0:0:1", 123, + "::1", 123, + true + }, + { + "0:0:0:0:0:0:1:1", 123, + "::1", 123, + false + }, + { + "2001:0:53aa:64c:0:fbff:ab2e:1eac", 2001, + "2001:0000:53aa:064c:0000:fbff:ab2e:1eac", 2001, + true + }, + { + "3001:0:53aa:64c:0:fbff:ab2e:1eac", 2001, + "2001:0000:53aa:064c:0000:fbff:ab2e:1eac", 2001, + false + }, + { + "192.168.1.1", 123, + "2001:0000:53aa:064c:0000:fbff:ab2e:1eac", 123, + false + }, + { /* IPv6-mapped IPv4-address */ + "::ffff:208.68.208.201", 3478, + "208.68.208.201", 3478, + true + }, + { + "fe80::215:58ff:fe2d:90ab", 3333, + "fe80:0000:0000:0000:0215:58ff:fe2d:90ab", 3333, + true + }, + { + "fe80::215:58ff:fe2d:90ab", 3333, + "fe80:0000:0000:0000:1215:58ff:fe2d:90ab", 3333, + false + }, +#endif + }; + size_t i; + int err = 0; + + for (i=0; i +#include +#include "test.h" + + +#define DEBUG_MODULE "test" +#define DEBUG_LEVEL 5 +#include + + +static const char ref_host[] = "1.2.3.4"; +static const uint16_t ref_port = 5004; +static const char ref_pt[] = "0"; +static const char *ref_cname = "PCMU"; +static const char *cname_speex = "speex"; +static const uint32_t ref_srate = 8000; +static const char ref_msg[] = + "v=0\r\n" + "o=- 1234 5678 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 5004 RTP/AVP 0 110\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:110 speex/16000/2\r\n" + "a=sendrecv\r\n"; + + +static const char *msgs[] = { + /* Counterpath */ + "v=0\n" + "o=- 1 2 IN IP4 84.209.220.122\r\n" + "s=\r\n" + "c=IN IP4 84.209.220.122\r\n" + "t=0 0\r\n" + "m=audio 24484 RTP/AVP 107 119 0 98 8 3 101\r\n" + "a=alt:1 2 : 8KiUNmDF 2AKrU/iZ 192.168.1.100 24484\r\n" + "a=alt:2 1 : uEmq9erD rG6uFpsK 84.209.220.122 24484\r\n" + "a=fmtp:101 0-15\r\n" + "a=rtpmap:107 BV32/16000\r\n" + "a=rtpmap:119 BV32-FEC/16000\r\n" + "a=rtpmap:98 iLBC/8000\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=sendrecv\r\n" + "a=x-rtp-session-id:EE42E5DC96034E1A95B8843DA28640E4\r\n" + "m=video 5040 RTP/AVP 115 34\r\n" + "a=alt:1 2 : 6zn66DoK 0TsJT2lQ 192.168.1.100 5040\r\n" + "a=alt:2 1 : JTVNzu+a 8pvqo0dE 84.209.220.122 5040\r\n" + "a=fmtp:115 QCIF=2 MAXBR=2180\r\n" + "a=fmtp:34 QCIF=2 MAXBR=2180\r\n" + "a=rtpmap:115 H263-1998/90000\r\n" + "a=rtpmap:34 H263/90000\r\n" + "a=sendrecv\r\n" + "a=x-rtp-session-id:E6856C1F08904D6B88B129266C82D351\r\n", + + /** Freeswitch 1.0rc1 */ + "v=0\r\n" + "o=FreeSWITCH 531003883936 28814208941 IN IP4 1.2.3.4\r\n" + "s=FreeSWITCH\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "a=sendrecv\r\n" + "m=audio 16610 RTP/AVP 8 99 13\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:99 telephone-event/8000\r\n" + "a=fmtp:99 0-16\r\n" + "a=rtpmap:13 CN/8000\r\n" + "a=ptime:20\r\n" + "a=nortpproxy:yes\r\n", + + /* newline termination */ + "v=0\n" + "o=- 531003883936 28814208941 IN IP4 1.2.3.4\n" + "s=-\n" + "t=0 0\n" + "m=audio 16610 RTP/AVP 8\n" + "c=IN IP4 1.2.3.4\n" + "a=rtpmap:8 PCMA/8000\n", + + /* Polycom */ + "v=0\r\n" + "o=- 1197975037 1197975037 IN IP4 192.168.9.74\r\n" + "s=Polycom IP Phone\r\n" + "c=IN IP4 192.168.9.74\r\n" + "t=0 0\r\n" + "m=audio 49200 RTP/AVP 8 0 9 18 96\r\n" + "a=sendrecv\r\n" + "a=crypto:1 AES_CM_128_HMAC_SHA1_80" + " inline:tMuyik1Aiiq9p4DQVHhAASSWDEP7K7wo0cICOn39\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:9 G722/8000\r\n" + "a=rtpmap:18 G729/8000\r\n" + "a=rtpmap:96 telephone-event/8000\r\n", + + /* Ekiga 3.0 */ + "v=0\r\n" + "o=- 1235562135 1235562135 IN IP4 192.168.1.55\r\n" + "s=Opal SIP Session\r\n" + "c=IN IP4 192.168.1.55\r\n" + "t=0 0\r\n" + "m=audio 5062 RTP/AVP 106 105 9 117 8 0 104 103 102 120 3 116 101\r\n" + "a=sendrecv\r\n" + "a=rtpmap:106 CELT/48000/1\r\n" + "a=rtpmap:105 CELT/32000/1\r\n" + "a=rtpmap:9 G722/8000/1\r\n" + "a=rtpmap:117 Speex/16000/1\r\n" + "a=fmtp:117 sr=16000,mode=any\r\n" + "a=rtpmap:8 PCMA/8000/1\r\n" + "a=rtpmap:0 PCMU/8000/1\r\n" + "a=rtpmap:104 G726-16/8000/1\r\n" + "a=rtpmap:103 G726-24/8000/1\r\n" + "a=rtpmap:102 G726-32/8000/1\r\n" + "a=rtpmap:120 G726-40/8000/1\r\n" + "a=rtpmap:3 gsm/8000/1\r\n" + "a=rtpmap:116 Speex/8000/1\r\n" + "a=fmtp:116 sr=8000,mode=any\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-16,32,36\r\n" + + /* LG */ + "v=0\r\n" + "o=LGN_IP_PHONE 29386 29386 IN IP4 192.168.1.97\r\n" + "s=SIP Call\r\n" + "c=IN IP4 85.112.135.82\r\n" + "t=0 0\r\n" + "m=audio 17412 RTP/AVP 9 8 0 18 101\r\n" + "c=IN IP4 85.112.135.82\r\n" + "b=AS:82\r\n" + "a=rtpmap:9 G722/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:18 G729/8000\r\n" + "a=fmtp:18 annexb=no\r\n" + "a=rtpmap:101 telephone-event/8000\r\n" + "a=fmtp:101 0-15\r\n" + "a=ptime:20\r\n" + "a=sendrecv\r\n" + "m=video 16512 RTP/AVP 98 102 34 105\r\n" + "c=IN IP4 85.112.135.82\r\n" + "b=TIAS:329000\r\n" + "b=AS:366\r\n" + "a=rtpmap:98 H264/90000\r\n" + "a=rtpmap:102 H264/90000\r\n" + "a=fmtp:98 profile-level-id=42800C; packetization-mode=0\r\n" + "a=fmtp:102 profile-level-id=42800C; packetization-mode=1\r\n" + "a=rtpmap:34 H263/90000\r\n" + "a=fmtp:34 CIF=1;QCIF=1\r\n" + "a=rtpmap:105 MP4V-ES/90000\r\n" +}; + + +/** Compare two SDP messages line-by-line (exclude owner) */ +static bool sdp_cmp(struct mbuf *mb, const char *msg) +{ + struct pl pl; + + if (!mb || !msg) + return false; + + pl.p = (char *)mb->buf; + pl.l = mb->end; + + while (pl.l && strlen(msg)) { + struct pl n1, v1, n2, v2; + + if (re_regex(pl.p, pl.l, + "[^=]1=[^\r\n]+", &n1, &v1)) + return false; + + if (re_regex(msg, strlen(msg), + "[^=]1=[^\r\n]+", &n2, &v2)) + return false; + + pl_advance(&pl, 2 + v1.l + 2); + msg += (2 + v2.l + 2); + + if (0 != pl_cmp(&n1, &n2)) { + DEBUG_WARNING("name mismatch: %r=%r\n", &n1, &v1); + return false; + } + + /* ignore owner */ + if (n1.p[0] == 'o') + continue; + + if (0 != pl_cmp(&v1, &v2)) { + DEBUG_WARNING("value mismatch: %r=%r\n", + &n1, &v1); + return false; + } + } + + if (pl.l) { + DEBUG_WARNING("%u bytes junk at end: %r\n", pl.l, &pl); + } + + if (strlen(msg)) { + DEBUG_WARNING("%u bytes junk at end: %s\n", strlen(msg), msg); + } + + return !pl.l && !strlen(msg); +} + + +int test_sdp_all(void) +{ + struct sdp_session *sess = NULL; + struct sdp_media *audio = NULL; + struct mbuf *desc = NULL; + struct sa ref; + const struct sdp_format *rc = NULL, *sc; + struct sa laddr; + int err; + + (void)sa_set_str(&laddr, ref_host, 0); + + err = sdp_session_alloc(&sess, &laddr); + if (err) + goto out; + + err = sdp_media_add(&audio, sess, sdp_media_audio, 5004, + sdp_proto_rtpavp); + if (err) + goto out; + + err = sdp_format_add(NULL, audio, false, ref_pt, ref_cname, + ref_srate, 1, NULL, NULL, NULL, false, NULL); + err |= sdp_format_add(NULL, audio, false, "110", cname_speex, + 16000, 2, NULL, NULL, NULL, false, NULL); + if (err) + goto out; + + /* find codec - expected */ + sc = sdp_media_format(audio, true, NULL, 0, "PCMU", 8000, 1); + if (!sc) { + DEBUG_WARNING("codec not found\n"); + err = ENOENT; + goto out; + } + + sc = sdp_media_format(audio, true, NULL, 110, "Speex", 16000, 2); + if (!sc) { + DEBUG_WARNING("codec not found: speex\n"); + err = ENOENT; + goto out; + } + + /* find codec - not expected */ + sc = sdp_media_format(audio, true, NULL, -1, "Speex", 8000, 1); + if (sc) { + DEBUG_WARNING("unexpected codec found\n"); + err = EINVAL; + goto out; + } + + err = sdp_encode(&desc, sess, true); + if (err) + goto out; + + if (!sdp_cmp(desc, ref_msg)) { + DEBUG_WARNING("ref: %s\n", ref_msg); + DEBUG_WARNING("sdp: %b\n", desc->buf, desc->end); + err = EBADMSG; + goto out; + } + + err = sdp_decode(sess, desc, false); + if (err) + goto out; + + rc = sdp_media_rformat(audio, NULL); + if (!rc) { + err = ENOENT; + goto out; + } + + err = sa_set_str(&ref, ref_host, ref_port); + if (err) + goto out; + + err = EINVAL; + + if (!sa_cmp(sdp_media_raddr(audio), &ref, SA_ALL)) + goto out; + + if (0 != strcmp(rc->id, ref_pt)) + goto out; + + if (0 != strcmp(ref_cname, rc->name)) + goto out; + + if (rc->srate != ref_srate) + goto out; + + err = 0; + + out: + mem_deref(audio); + mem_deref(sess); + mem_deref(desc); + + return err; +} + + +/** + * Test parsing of various SDP messages from various vendors + */ +int test_sdp_parse(void) +{ + struct sdp_session *sess = NULL; + struct sdp_media *audio; + struct mbuf *mb; + struct sa laddr; + uint32_t i; + int err; + + mb = mbuf_alloc(2048); + if (!mb) + return ENOMEM; + + sa_init(&laddr, AF_INET); + + for (i=0; ipos = 0; + + err = sdp_decode(sess, mb, true); + if (err) + goto out; + } + + out: + mem_deref(sess); + mem_deref(mb); + + return err; +} + + +struct oa { + struct sdp_session *alice, *bob; +}; + + +static int oa_init(struct oa *oa) +{ + struct sa laddr; + int err; + + if (!oa->alice) { + (void)sa_set_str(&laddr, "1.2.3.4", 0); + err = sdp_session_alloc(&oa->alice, &laddr); + if (err) + return err; + } + if (!oa->bob) { + (void)sa_set_str(&laddr, "5.6.7.8", 0); + err = sdp_session_alloc(&oa->bob, &laddr); + if (err) + return err; + } + + return 0; +} + + +static void oa_reset(struct oa *oa) +{ + oa->alice = mem_deref(oa->alice); + oa->bob = mem_deref(oa->bob); +} + + +static int oa_addmedia(struct oa *oa, bool local, + const char *mname, uint16_t port, const char *transp, + enum sdp_dir dir, uint32_t ncodec, ...) +{ + struct sdp_media *m; + va_list ap; + int err; + + err = oa_init(oa); + if (err) + return err; + + err = sdp_media_add(&m, local ? oa->alice : oa->bob, + mname, port, transp); + if (err) + return err; + + sdp_media_set_ldir(m, dir); + + va_start(ap, ncodec); + + while (ncodec--) { + const char *id = va_arg(ap, char *); + const char *cname = va_arg(ap, char *); + int srate = va_arg(ap, int); + + err = sdp_format_add(NULL, m, false, id, cname, srate, 1, + NULL, NULL, NULL, false, NULL); + if (err) + break; + } + + va_end(ap); + + return err; +} + + +static int oa_offeranswer(struct oa *oa, const char *offer, const char *answer) +{ + struct mbuf *mbo = NULL, *mba = NULL; + int err = 0; + + /* create and send offer, compare offer */ + err = sdp_encode(&mbo, oa->alice, true); + if (err) + goto out; + + if (!sdp_cmp(mbo, offer)) { + DEBUG_WARNING("offer failed:\n%b", mbo->buf, mbo->end); + err = EBADMSG; + goto out; + } + + /* bob decodes offer */ + err = sdp_decode(oa->bob, mbo, true); + if (err) + goto out; + + /* create and send answer, compare answer */ + err = sdp_encode(&mba, oa->bob, false); + if (err) + goto out; + + if (!sdp_cmp(mba, answer)) { + DEBUG_WARNING("answer failed:\n%b", mba->buf, mba->end); + err = EBADMSG; + goto out; + } + + err = sdp_decode(oa->alice, mba, false); + + out: + oa_reset(oa); + mem_deref(mbo); + mem_deref(mba); + + return err; +} + + +/* RFC 4317 - section 2.1 */ +static int rfc4317_section2_1(struct oa *oa) +{ + int err = 0; + + err |= oa_addmedia(oa, 1, "audio", 49170, "RTP/AVP", SDP_SENDRECV, 3, + "0", "PCMU", 8000, + "8", "PCMA", 8000, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 1, "video", 51372, "RTP/AVP", SDP_SENDRECV, 2, + "31", "H261", 90000, + "32", "MPV", 90000); + err |= oa_addmedia(oa, 0, "audio", 49174, "RTP/AVP", SDP_SENDRECV, 1, + "0", "PCMU", 8000); + err |= oa_addmedia(oa, 0, "video", 49170, "RTP/AVP", SDP_SENDRECV, 1, + "32", "MPV", 90000); + if (err) + return err; + + err = oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=video 51372 RTP/AVP 31 32\r\n" + "a=rtpmap:31 H261/90000\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49174 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 49170 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n"); + return err; +} + + +/* RFC 4317 - section 2.2 */ +static int rfc4317_section2_2(struct oa *oa) +{ + int err = 0; + + err |= oa_addmedia(oa, 1, "audio", 49170, "RTP/AVP", SDP_SENDRECV, 3, + "0", "PCMU", 8000, + "8", "PCMA", 8000, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 1, "video", 51372, "RTP/AVP", SDP_SENDRECV, 2, + "31", "H261", 90000, + "32", "MPV", 90000); + err |= oa_addmedia(oa, 0, "audio", 49172, "RTP/AVP", SDP_SENDRECV, 2, + "0", "PCMU", 8000, + "8", "PCMA", 8000); + if (err) + return err; + + return oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=video 51372 RTP/AVP 31 32\r\n" + "a=rtpmap:31 H261/90000\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49172 RTP/AVP 0 8\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=sendrecv\r\n" + "m=video 0 RTP/AVP 0\r\n" + ); +} + + +/* RFC 4317 - section 2.4 */ +static int rfc4317_section2_4(struct oa *oa) +{ + int err = 0; + + err |= oa_addmedia(oa, 1, "audio", 49170, "RTP/AVP", SDP_SENDRECV, 2, + "0", "PCMU", 8000, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 1, "audio", 49172, "RTP/AVP", SDP_SENDRECV, 1, + "98", "telephone-event", 8000); + err |= oa_addmedia(oa, 0, "audio", 49172, "RTP/AVP", SDP_SENDRECV, 1, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 0, "audio", 49174, "RTP/AVP", SDP_RECVONLY, 1, + "98", "telephone-event", 8000); + if (err) + return err; + + err = oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=audio 49172 RTP/AVP 98\r\n" + "a=rtpmap:98 telephone-event/8000\r\n" + "a=sendrecv\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49172 RTP/AVP 97\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=audio 49174 RTP/AVP 98\r\n" + "a=rtpmap:98 telephone-event/8000\r\n" + "a=recvonly\r\n"); + return err; +} + + +/* RFC 4317 - section 5.1 */ +static int rfc4317_section5_1(struct oa *oa) +{ + int err = 0; + + err |= oa_init(oa); + + if (err) + return err; + + err = oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n"); + return err; +} + + +/** Test SDP Offer/Answer examples in RFC 4317 */ +int test_sdp_oa(void) +{ + struct oa oa; + int err = 0; + + memset(&oa, 0, sizeof(oa)); + + err |= rfc4317_section2_1(&oa); + err |= rfc4317_section2_2(&oa); + err |= rfc4317_section2_4(&oa); + err |= rfc4317_section5_1(&oa); + + oa_reset(&oa); + + return err; +} + + +/** Test BFCP in SDP -- RFC 4583 */ +int test_sdp_bfcp(void) +{ + static const char *msg_offer = + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=application 50000 TCP/BFCP *\r\n" + "a=sendrecv\r\n" + "a=setup:passive\r\n" + "a=connection:new\r\n" + "a=floorctrl:s-only\r\n" + "a=confid:4321\r\n" + "a=userid:1234\r\n" + "a=floorid:1 m-stream:10\r\n" + "a=floorid:2 m-stream:11\r\n" + "m=audio 50002 RTP/AVP 0\r\n" + "a=sendrecv\r\n" + "a=label:10\r\n" + "m=video 50004 RTP/AVP 31\r\n" + "a=sendrecv\r\n" + "a=label:11\r\n" + ; + struct sdp_session *alice = NULL, *bob = NULL; + struct sdp_media *bfcp, *audio, *video; + struct mbuf *mbo = NULL, *mba = NULL; + struct sa laddr; + int err; + + /* create sessions */ + (void)sa_set_str(&laddr, "1.2.3.4", 0); + err = sdp_session_alloc(&alice, &laddr); + if (err) + goto out; + + err = sdp_media_add(&bfcp, alice, "application", 50000, "TCP/BFCP"); + if (err) + goto out; + + err |= sdp_media_set_lattr(bfcp, true, "setup", "passive"); + err |= sdp_media_set_lattr(bfcp, true, "connection", "new"); + err |= sdp_media_set_lattr(bfcp, true, "floorctrl", "s-only"); + err |= sdp_media_set_lattr(bfcp, true, "confid", "4321"); + err |= sdp_media_set_lattr(bfcp, true, "userid", "1234"); + err |= sdp_media_set_lattr(bfcp, false, "floorid", "1 m-stream:10"); + sdp_media_del_lattr(bfcp, "floorid"); /* test attr delete */ + err |= sdp_media_set_lattr(bfcp, false, "floorid", "1 m-stream:10"); + err |= sdp_media_set_lattr(bfcp, false, "floorid", "2 m-stream:11"); + if (err) + goto out; + + err = sdp_media_add(&audio, alice, "audio", 50002, "RTP/AVP"); + if (err) + goto out; + + err = sdp_media_add(&video, alice, "video", 50004, "RTP/AVP"); + if (err) + goto out; + + err |= sdp_media_set_lattr(audio, true, "label", "10"); + err |= sdp_media_set_lattr(video, true, "label", "11"); + if (err) + goto out; + + err = sdp_format_add(NULL, bfcp, false, "*", NULL, 0, 0, + NULL, NULL, NULL, false, NULL); + err |= sdp_format_add(NULL, audio, false, "0", NULL, 0, 0, + NULL, NULL, NULL, false, NULL); + err |= sdp_format_add(NULL, video, false, "31", NULL, 0, 0, + NULL, NULL, NULL, false, NULL); + if (err) + goto out; + + /* create and send offer, compare offer */ + err = sdp_encode(&mbo, alice, true); + if (err) + goto out; + + if (!sdp_cmp(mbo, msg_offer)) { + DEBUG_WARNING("offer failed:\n%b", mbo->buf, mbo->end); + err = EBADMSG; + goto out; + } + + out: + mem_deref(alice); + mem_deref(bob); + mem_deref(mbo); + mem_deref(mba); + + return err; +} + + +int test_sdp_extmap(void) +{ + static const char *extmapv[3] = { + "extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level", + "extmap:2/sendrecv http://example.com/ext.htm#xmeta short", + "extmap:4096/recvonly URI-gps-string" + }; + struct sdp_extmap ext; + int err = 0; + + /* extmap 1 */ + err = sdp_extmap_decode(&ext, extmapv[0]); + TEST_EQUALS(0, err); + TEST_STRCMP("urn:ietf:params:rtp-hdrext:ssrc-audio-level", + strlen("urn:ietf:params:rtp-hdrext:ssrc-audio-level"), + ext.name.p, ext.name.l); + TEST_ASSERT(!pl_isset(&ext.attrs)); + TEST_EQUALS(SDP_SENDRECV, ext.dir); + TEST_ASSERT(!ext.dir_set); + TEST_EQUALS(1, ext.id); + + /* extmap 2 */ + err = sdp_extmap_decode(&ext, extmapv[1]); + TEST_EQUALS(0, err); + TEST_STRCMP("http://example.com/ext.htm#xmeta", + strlen("http://example.com/ext.htm#xmeta"), + ext.name.p, ext.name.l); + TEST_STRCMP("short", strlen("short"), + ext.attrs.p, ext.attrs.l); + TEST_EQUALS(SDP_SENDRECV, ext.dir); + TEST_ASSERT(ext.dir_set); + TEST_EQUALS(2, ext.id); + + /* extmap 3 */ + err = sdp_extmap_decode(&ext, extmapv[2]); + TEST_EQUALS(0, err); + TEST_STRCMP("URI-gps-string", + strlen("URI-gps-string"), + ext.name.p, ext.name.l); + TEST_ASSERT(!pl_isset(&ext.attrs)); + TEST_EQUALS(SDP_RECVONLY, ext.dir); + TEST_ASSERT(ext.dir_set); + TEST_EQUALS(4096, ext.id); + + out: + return err; +} + + +static int disabled_local_medialine(struct oa *oa) +{ + int err = 0; + + err |= oa_addmedia(oa, 1, "audio", 49170, "RTP/AVP", SDP_SENDRECV, 3, + "0", "PCMU", 8000, + "8", "PCMA", 8000, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 1, "video", 51372, "RTP/AVP", SDP_INACTIVE, 2, + "31", "H261", 90000, + "32", "MPV", 90000); + err |= oa_addmedia(oa, 0, "audio", 49174, "RTP/AVP", SDP_SENDRECV, 1, + "0", "PCMU", 8000); + err |= oa_addmedia(oa, 0, "video", 49170, "RTP/AVP", SDP_SENDRECV, 1, + "32", "MPV", 90000); + if (err) + return err; + + err = oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=video 51372 RTP/AVP 31 32\r\n" + "a=rtpmap:31 H261/90000\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=inactive\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49174 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 49170 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=inactive\r\n"); + return err; +} + + +static int disabled_remote_medialine(struct oa *oa) +{ + int err = 0; + + err |= oa_addmedia(oa, 1, "audio", 49170, "RTP/AVP", SDP_SENDRECV, 3, + "0", "PCMU", 8000, + "8", "PCMA", 8000, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 1, "video", 51372, "RTP/AVP", SDP_SENDRECV, 2, + "31", "H261", 90000, + "32", "MPV", 90000); + err |= oa_addmedia(oa, 0, "audio", 49174, "RTP/AVP", SDP_SENDRECV, 1, + "0", "PCMU", 8000); + err |= oa_addmedia(oa, 0, "video", 49170, "RTP/AVP", SDP_INACTIVE, 1, + "32", "MPV", 90000); + if (err) + return err; + + err = oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=video 51372 RTP/AVP 31 32\r\n" + "a=rtpmap:31 H261/90000\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49174 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 49170 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=inactive\r\n"); + return err; +} + + +static int reject_video_medialine(struct oa *oa) +{ + int err = 0; + + err |= oa_addmedia(oa, 1, "audio", 49170, "RTP/AVP", SDP_SENDRECV, 3, + "0", "PCMU", 8000, + "8", "PCMA", 8000, + "97", "iLBC", 8000); + err |= oa_addmedia(oa, 1, "video", 51372, "RTP/AVP", SDP_SENDRECV, 2, + "31", "H261", 90000, + "32", "MPV", 90000); + err |= oa_addmedia(oa, 0, "audio", 49174, "RTP/AVP", SDP_SENDRECV, 1, + "0", "PCMU", 8000); + + if (err) + return err; + + err = oa_offeranswer(oa, + "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=video 51372 RTP/AVP 31 32\r\n" + "a=rtpmap:31 H261/90000\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n" + , + "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49174 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 0 RTP/AVP 0\r\n"); + return err; +} + + +int test_sdp_disabled_rejected(void) +{ + struct oa oa; + int err = 0; + + memset(&oa, 0, sizeof(oa)); + + err |= disabled_local_medialine(&oa); + err |= disabled_remote_medialine(&oa); + err |= reject_video_medialine(&oa); + + oa_reset(&oa); + + return err; +} diff --git a/test/sha.c b/test/sha.c new file mode 100644 index 000000000..42f3b11b2 --- /dev/null +++ b/test/sha.c @@ -0,0 +1,70 @@ +/** + * @file sha.c SHA Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testsha1" +#define DEBUG_LEVEL 4 +#include + + +static const char *test_data[] = { + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + + /* 64 bytes */ + "9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda", + + /* 96 bytes */ + "9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda" + "9293haoijsdlasjd9ehr98wehrlsihdf", + + /* 128 bytes */ + "9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda" + "9293haoijsdlasjd82halsdlkajsdlkjasldkjasldjlskjd9ehr98wehrlsihdd", + + /* 256 bytes */ + "9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda" + "9293haoijsdlasjd82halsdlkajsdlkjasldkjasldjlskjd9ehr98wehrlsihdd" + "9293haoijsdlasjd9ehr98wehrlsihdflskidjflaisjdlaisdjalsdkjasdlsda" + "9293haoijsdlasjd82halsdlkajsdlkjasldkjasldjlskjd9ehr98wehrlsihdd", +}; +static const char *test_results[] = { + "a9993e364706816aba3e25717850c26c9cd0d89d", + "84983e441c3bd26ebaae4aa1f95129e5e54670f1", + "105104a6ee22de58c0888d2f9cdd56d95c14d4e7", + "9962f530d85f354304efcf35ceaa29a279a3208d", + "17307171329ed5aeaccf4cd4f6d02223a69af9fb", + "4f051b5c4fcd0916df00f9c9dbab8608cd3355a7"}; + + +int test_sha1(void) +{ + uint32_t k; + uint8_t digest[20]; + char output[80]; + + for (k = 0; k < RE_ARRAY_SIZE(test_data); k++) { + + sha1((uint8_t *)test_data[k], strlen(test_data[k]), digest); + + (void)re_snprintf(output, sizeof(output), "%02w", digest, + sizeof(digest)); + if (strcmp(output, test_results[k])) { + DEBUG_WARNING("* hash of \"%s\" incorrect:\n", + test_data[k]); + DEBUG_WARNING("\t%s returned\n", output); + DEBUG_WARNING("\t%s is correct\n", test_results[k]); + return EINVAL; + } + } + + /* success */ + return 0; +} diff --git a/test/sip.c b/test/sip.c new file mode 100644 index 000000000..66dd65c43 --- /dev/null +++ b/test/sip.c @@ -0,0 +1,865 @@ +/** + * @file sip.c SIP Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "siptest" +#define DEBUG_LEVEL 5 +#include + + +static int sip_addr_encode(const struct sip_addr *addr, struct mbuf *mb) +{ + bool anglebr = addr->dname.p || addr->uri.params.p; + int err; + + if (addr->dname.p) { + err = mbuf_printf(mb, "\"%r\" ", &addr->dname); + if (err) + return err; + } + + if (anglebr) { + err = mbuf_write_u8(mb, '<'); + if (err) + return err; + } + + err = mbuf_printf(mb, "%H", uri_encode, &addr->uri); + if (err) + return err; + + if (anglebr) { + err = mbuf_write_u8(mb, '>'); + if (err) + return err; + } + + if (addr->params.p) { + err = mbuf_write_pl(mb, &addr->params); + if (err) + return err; + } + + return 0; +} + + +int test_sip_addr(void) +{ + const char *addrv[] = { + + /* With Display name */ + "\"Agmund Bolt\" ", + "\"Agmund Bolt\" ", + "\"Agmund Bolt\" ", + "\"Agmund Bolt\" ", + "\"Agmund Bolt\" ", + "\"Agmund Bolt\" ", + + "", + "", + "", + "", + "", + "", + "", + "", + + /* RFC 3261 */ + "", + "", + "", + "", + "", + "", + + /* With address parameters */ + "\"Agmund Bolt\" " + ";tag=foo123", + "\"Agmund Bolt\" " + ";tag=foo123;bar=9d7j3", + +#ifdef HAVE_INET6 + /* RFC 5118 - SIP Torture Test Messages for IPv6 */ + "\"Caller\" ", + "\"Caller\" ", +#endif + + /* gruu */ + "\"hei\" " + ";expires=3845" + ";gruu=\"sip:alfred1@devel.sip.su.se" + ";opaque=t49sgjnwu8;gruu\"" + ";+sip.instance=" + "\"\"" + ";reg-id=1", + }; + struct sip_addr addr; + struct mbuf mb; + int err = EINVAL; + size_t i; + + mbuf_init(&mb); + + for (i=0; ivia.tp != testv[i].tp) { + DEBUG_WARNING("%u: via transp: '%s' != '%s'\n", + i, sip_transp_name(msg->via.tp), + sip_transp_name(testv[i].tp)); + goto out; + } + + /* Numeric IP address */ + if (ipaddr) { + if (!sa_cmp(&msg->via.addr, &addr, SA_ALL)) { + DEBUG_WARNING("%u: via addr: addr=%J\n", + i, &msg->via.addr); + err = EINVAL; + goto out; + } + } + else { + err = pl_strcmp(&msg->via.sentby, testv[i].host); + if (err) { + DEBUG_WARNING("%u: via uri: sentby='%r'\n", + i, &msg->via.sentby); + goto out; + } + + if (sa_port(&msg->via.addr) != testv[i].port) { + DEBUG_WARNING("%u: via: port mismatch (%u)\n", + i, sa_port(&msg->via.addr)); + err = EINVAL; + goto out; + } + } + + err = pl_strcmp(&msg->via.branch, testv[i].branch); + if (err) { + DEBUG_WARNING("%u: via branch: '%r' != '%s'\n", + i, &msg->via.branch, testv[i].branch); + goto out; + } + + msg = mem_deref(msg); + } + + err = 0; + out: + mem_deref(mb); + mem_deref(msg); + return err; +} + + +struct apply { + uint32_t n; + int err; +}; + + +static bool apply_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, + void *arg) +{ + const char *ref = "SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123"; + struct apply *apply = arg; + + (void)msg; + (void)arg; + + if (hdr->id != SIP_HDR_VIA) { + apply->err = EINVAL; + return true; + } + + apply->err = pl_strcmp(&hdr->val, ref); + if (apply->err) { + return true; + } + + ++apply->n; + + return false; +} + + +int test_sip_apply(void) +{ + struct { + uint32_t n; + const char *msg; + } testv[] = { + {0, + "Tull: tull\r\n"}, + {1, + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n"}, + {1, + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n"}, + {2, + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n" + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n" + }, + {2, + "Via: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123," + " SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\r\n" + }, + {2, + "v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123," + " SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\r\n" + }, + {2, + "v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123" + ",SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\r\n" + }, + {3, + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n" + "v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\r\n" + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n" + }, + {3, + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n" + "Tull: tull\r\n" + "v: SIP/2.0/UDP 123.45.67.89:12345;branch=z9hG4bK123\r\n" + "Via: SIP/2.0/UDP 123.45.67.89:12345" + ";branch=z9hG4bK123\r\n" + }, + }; + struct sip_msg *msg = NULL; + struct mbuf *mb; + int err = 0; + size_t i; + + mb = mbuf_alloc(1024); + if (!mb) { + err = ENOMEM; + goto out; + } + + for (i=0; i\r\n" + "t: Bob \r\n" + "f: Alice \r\n" + " ;tag=1928301774\r\n" + "Call-ID : a84b4c76e66710@pc33.atlanta.com\r\n" + "CSeq : 314159 INVITE\r\n" + "Contact: \r\n" + "Content-Type: application/sdp\r\n" + "Content-Length: 142\r\n" + "\r\n"; + const char hdr_maxf[] = "70"; + const char hdr_from[] = "sip:alice@atlanta.com"; + const char hdr_to[] = "sip:bob@biloxi.com"; + const char hdr_callid[] = "a84b4c76e66710@pc33.atlanta.com"; + struct mbuf *mb; + struct sip_msg *msg = NULL; + int err = EINVAL; + + mb = mbuf_alloc(1024); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = mbuf_write_str(mb, str_raw); + if (err) + goto out; + + mbuf_set_pos(mb, 0); + err = sip_msg_decode(&msg, mb); + if (err) { + goto out; + } + + /* Max-Forwards */ + err = pl_strcmp(&msg->maxfwd, hdr_maxf); + if (err) + goto out; + + /* From */ + err = pl_strcmp(&msg->from.auri, hdr_from); + if (err) { + DEBUG_WARNING("from header mismatch (%r)\n", &msg->from.auri); + goto out; + } + + /* To */ + err = pl_strcmp(&msg->to.auri, hdr_to); + if (err) { + DEBUG_WARNING("to header mismatch\n"); + goto out; + } + + /* Call-ID */ + err = pl_strcmp(&msg->callid, hdr_callid); + if (err) { + DEBUG_WARNING("callid header mismatch\n"); + goto out; + } + + /* CSeq */ + if (314159 != msg->cseq.num) { + DEBUG_WARNING("cseq number mismatch\n"); + goto out; + } + err = pl_strcmp(&msg->cseq.met, "INVITE"); + if (err) { + DEBUG_WARNING("cseq method mismatch\n"); + goto out; + } + + /* Content-Type */ + if (!msg_ctype_cmp(&msg->ctyp, "application", "sdp")) { + DEBUG_WARNING("content type mismatch (%r/%r)\n", + &msg->ctyp.type, &msg->ctyp.subtype); + err = EBADMSG; + goto out; + } + + /* Content-Length */ + if (142 != pl_u32(&msg->clen)) { + DEBUG_WARNING("content length mismatch\n"); + err = EINVAL; + goto out; + } + + err = 0; + + out: + mem_deref(mb); + mem_deref(msg); + return err; +} + + +static bool count_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, + void *arg) +{ + (void)hdr; + (void)msg; + ++(*(uint32_t *)arg); + return false; +} + + +static uint32_t xhdr_count(const struct sip_msg *msg, const char *name) +{ + uint32_t n = 0; + sip_msg_xhdr_apply(msg, true, name, count_handler, &n); + return n; +} + + +int test_sip_hdr(void) +{ + const char str[] = + "REGISTER sip:telio.no SIP/2.0\r\n" + "Via: SIP/2.0/UDP 85.119.136.184:5080" + " ;branch=z9hG4bKe282.0c5b6835.0;i=2b505\r\n" + "Via: SIP/2.0/TCP 172.17.18.219:5060;received=85.0.35.235" + " ;branch=z9hG4bK6ec163d6cebbbe491e1940b91.1;rport=49505\r\n" + "Call-ID: 2e60298e76751681@172.17.18.219\r\n" + "CSeq: 67139 REGISTER\r\n" + "Contact: \r\n" + "From: ;tag=1ea582725e044bf6\r\n" + "To: \r\n" + "Max-Forwards: 16\r\n" + "Allow: INVITE,ACK,CANCEL,BYE,UPDATE,INFO,OPTIONS\r\n" + "User-Agent: TANDBERG/67 (F7.2 PAL)\r\n" + "Expires: 3600\r\n" + "Supported: replaces,100rel,timer\r\n" + "Content-Length: 0\r\n" + "\r\n"; + struct mbuf *mb; + struct sip_msg *msg = NULL; + int err = EINVAL; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + err = mbuf_write_str(mb, str); + if (err) + goto out; + + mbuf_set_pos(mb, 0); + err = sip_msg_decode(&msg, mb); + if (err) + goto out; + + if (xhdr_count(msg, "Call-ID") != 1) { + err = EBADMSG; + goto out; + } + + if (xhdr_count(msg, "Supported") != 3) { + err = EBADMSG; + goto out; + } + + if (xhdr_count(msg, "Allow") != 7) { + err = EBADMSG; + goto out; + } + + if (xhdr_count(msg, "NonExisting") != 0) { + err = EBADMSG; + goto out; + } + + + out: + mem_deref(msg); + mem_deref(mb); + + return err; +} + + +/** SIP Authenticated Request */ +struct sip_req { + struct sip_request *req; + struct sip_auth *auth; + struct sip_dialog *dlg; + struct sip *sip; +}; + + +static int do_sip_drequestf(struct sa *laddr) +{ + struct sip_req *sr; + int err; + char uri[64]; + char touri[64]; + + sr = mem_zalloc(sizeof(*sr), NULL); + if (!sr) + return ENOMEM; + + err = re_snprintf(uri, 64, "sip:%J;transport=UDP", laddr); + err |= re_snprintf(touri, 64, "sip:test@%J", laddr); + + err = sip_dialog_alloc(&sr->dlg, uri, + touri, NULL, + touri, NULL, 0); + TEST_ERR(err); + + err = sip_auth_alloc(&sr->auth, NULL, NULL, false); + TEST_ERR(err); + + err = sip_alloc(&sr->sip, NULL, 32, 32, 32, "retest", NULL, NULL); + TEST_ERR(err); + + err = sip_transp_add(sr->sip, SIP_TRANSP_UDP, laddr); + TEST_ERR(err); + + err = sip_drequestf(&sr->req, sr->sip, true, "REGISTER", sr->dlg, 0, + sr->auth, NULL, NULL, NULL, ""); + TEST_ERR(err); + +out: + mem_deref(sr->dlg); + mem_deref(sr->auth); + mem_deref(sr->sip); + mem_deref(sr); + + return err; +} + + +int test_sip_drequestf(void) +{ + int err; + struct sa laddr; + + err = sa_set_str(&laddr, "127.0.0.1", 0); + TEST_ERR(err); + + err = do_sip_drequestf(&laddr); + TEST_ERR(err); + +out: + return err; +} + + +int test_sip_drequestf_network(void) +{ + struct sa laddr; + int err = 0; + + sa_init(&laddr, AF_INET6); + + if (0 == net_if_getlinklocal(NULL, AF_INET6, &laddr)) { + + err = do_sip_drequestf(&laddr); + TEST_ERR(err); + } + +out: + return err; +} + + +#ifdef USE_TLS +struct sip_transp_tls { + struct sip *sip; + struct tls *tls; + struct uri uri; + const char *ccert_cn; +}; + + +int test_sip_transp_add_client_cert(void) +{ + struct sip_transp_tls *stt; + struct sa laddr; + int err; + char clientcert[256]; + char cafile[256]; + + const char *user = "abcd"; + const char *scheme = "sip"; + const char *host = "localhost"; + const uint16_t port = 5061; + + memset(clientcert, 0, sizeof(clientcert)); + (void)re_snprintf(clientcert, sizeof(clientcert), "%s/client.pem", + test_datapath()); + + stt = mem_zalloc(sizeof(*stt), NULL); + if (!stt) + return ENOMEM; + + pl_set_str(&stt->uri.user, user); + pl_set_str(&stt->uri.scheme, scheme); + pl_set_str(&stt->uri.host, host); + stt->uri.port = port; + + + err = sa_set_str(&laddr, "127.0.0.1", 0); + TEST_ERR(err); + + err = tls_alloc(&stt->tls, TLS_METHOD_SSLV23, NULL, NULL); + TEST_ERR(err); + + (void)re_snprintf(cafile, sizeof(cafile), "%s/server-ecdsa.pem", + test_datapath()); + + err = tls_add_ca(stt->tls, cafile); + TEST_ERR(err); + + err = sip_alloc(&stt->sip, NULL, 32, 32, 32, "retest", NULL, NULL); + TEST_ERR(err); + + err = sip_transp_add(stt->sip, SIP_TRANSP_TLS, &laddr, stt->tls); + TEST_ERR(err); + + /* actuall test cases */ + err = sip_transp_add_ccert(NULL, &stt->uri, clientcert); + if (err == EINVAL) { + err = 0; + goto out; + } + TEST_ERR(err); + + err = sip_transp_add_ccert(stt->sip, NULL, clientcert); + if (err == EINVAL) { + err = 0; + goto out; + } + TEST_ERR(err); + + err = sip_transp_add_ccert(stt->sip, &stt->uri, NULL); + if (err == EINVAL) { + err = 0; + goto out; + } + TEST_ERR(err); + + err = sip_transp_add_ccert(stt->sip, &stt->uri, clientcert); + TEST_EQUALS(0, err); + + out: + mem_deref(stt->sip); + mem_deref(stt->tls); + mem_deref(stt); + + return err; +} +#endif diff --git a/test/sipauth.c b/test/sipauth.c new file mode 100644 index 000000000..4c8d86b76 --- /dev/null +++ b/test/sipauth.c @@ -0,0 +1,95 @@ +/** + * @file sipauth.c SIP Auth testcode + */ +#include +#include "test.h" + + +#define DEBUG_MODULE "test_sipauth" +#define DEBUG_LEVEL 5 +#include + + +static int auth_handler(char **user, char **pass, const char *rlm, void *arg) +{ + (void)user; + (void)pass; + (void)rlm; + (void)arg; + + return 0; +} + + +static int test_sip_auth_encode(void) +{ + int err; + struct mbuf *mb, *mb_enc; + struct sip_auth *auth = NULL; + char buf[1024] = {0}; + struct sip_msg *msg = NULL; + const char met[] = "REGISTER"; + const char uri[] = ""; + const char str_raw[] = + "SIP/2.0 401 Unauthorized\r\n" + "Via: SIP/2.0/TLS " + "10.0.0.1:37589;branch=z9hG4bK5625ce6f310a0fc8;rport=13718;" + "received=10.0.0.2\r\n" + "WWW-Authenticate: Digest realm=\"example.net\", " + "nonce=\"YZlVk2GZVGegVBZVKaMHpnxmUA+QyoSl\"\r\n" + "Content-Length: 0\r\n\r\n"; + + mb = mbuf_alloc(2048); + if (!mb) + return ENOMEM; + + mb_enc = mbuf_alloc(2048); + if (!mb_enc) { + mem_deref(mb); + return ENOMEM; + } + + err = sip_auth_alloc(&auth, auth_handler, NULL, false); + TEST_ERR(err); + + err = mbuf_write_str(mb, str_raw); + TEST_ERR(err); + + mbuf_set_pos(mb, 0); + + err = sip_msg_decode(&msg, mb); + TEST_ERR(err); + + err = sip_auth_authenticate(auth, msg); + TEST_ERR(err); + + err = sip_auth_encode(mb_enc, auth, met, uri); + TEST_ERR(err); + + mbuf_set_pos(mb_enc, 0); + mbuf_read_str(mb_enc, buf, mbuf_get_left(mb_enc)); + + err = re_regex(buf, str_len(buf), "algorithm=MD5"); + TEST_ERR(err); + +out: + mem_deref(mb); + mem_deref(mb_enc); + if (msg) + mem_deref(msg); + if (auth) + mem_deref(auth); + return err; +} + + +int test_sip_auth(void) +{ + int err; + + err = test_sip_auth_encode(); + TEST_ERR(err); + +out: + return err; +} diff --git a/test/sipevent.c b/test/sipevent.c new file mode 100644 index 000000000..2ee2d1fa6 --- /dev/null +++ b/test/sipevent.c @@ -0,0 +1,439 @@ +/** + * @file sipevent.c SIP Event regression testcode + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_sipevent" +#define DEBUG_LEVEL 5 +#include + + +/* + * A is the subscriber to event changes from B + * + * .-----. .-----. + * | A | | B | + * '-----' '-----' + * (Subscriber) (Notifier) + * + * ----- SUBSCRIBE -----> + * + * <----- 200 OK -------- + * + * + * + * + * <------- X somekind of event happened + * <----- NOTIFY -------- + * X <-- + * ------- 200 OK ------> + * + */ + + +struct agent { + struct agent *peer; + struct sip *sip; + struct sipevent_sock *sock; + struct sipsub *sub; + struct sipnot *not; + char name[32]; + bool exited; + char uri[256]; + unsigned subc; + unsigned notc; + unsigned closec; + int err; +}; + + +static const char *test_event = "my-event"; + + +static void complete(struct agent *ag, int err) +{ + ag->err = err; + re_cancel(); +} + + +static int send_notify(struct agent *ag) +{ + const char *aor = "tull"; + struct mbuf *mb; + int err; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + err = mbuf_printf(mb, + "\r\n" + "\r\n" + " \r\n" + " \r\n" + " \r\n" + " %s\r\n" + " \r\n" + " %s\r\n" + " \r\n" + "\r\n" + ,aor, "open", aor); + if (err) + goto out; + + mb->pos = 0; + + err = sipevent_notify(ag->not, mb, SIPEVENT_ACTIVE, 0, 0); + if (err) { + DEBUG_WARNING("presence: notify to %s failed (%m)\n", + aor, err); + } + + out: + mem_deref(mb); + return err; +} + + +static void sipnot_close_handler(int err, const struct sip_msg *msg, + void *arg) +{ + struct agent *ag = arg; + (void)msg; + + DEBUG_WARNING("[ %s ] sip notification closed (%m)\n", + ag->name, err); + + ++ag->closec; + + complete(ag, err); +} + + +/* + * Agent `B' -- the Notifier + * + * Handle incoming SUBSCRIBE message from A + +SUBSCRIBE sip:b@127.0.0.1:20000 SIP/2.0. +Via: SIP/2.0/UDP 127.0.0.1:10000;branch=z9hG4bKf7f2e9e48bbaea6b;rport. +Contact: . +Max-Forwards: 70. +To: . +From: "a" ;tag=d3d00d9fb5ee45d5. +Call-ID: 26ac32ea11a58011. +CSeq: 37745 SUBSCRIBE. +User-Agent: a. +Event: my-event. +Expires: 600. +Content-Length: 0. + + + */ +static bool subscribe_handler(const struct sip_msg *msg, void *arg) +{ + struct agent *ag = arg; + struct agent *peer = ag->peer; + const struct sip_hdr *hdr; + struct sipevent_event se; + int err = 0; + + DEBUG_INFO("[ %s ] recv SIP msg (%r)\n", ag->name, &msg->met); + + ++ag->subc; + + TEST_ASSERT(msg != NULL); + TEST_STRCMP("SUBSCRIBE", 9U, msg->met.p, msg->met.l); + hdr = sip_msg_hdr(msg, SIP_HDR_CONTACT); + TEST_ASSERT(hdr != NULL); + TEST_STRCMP(ag->uri, strlen(ag->uri), msg->to.auri.p, msg->to.auri.l); + TEST_STRCMP(peer->uri, strlen(peer->uri), + msg->from.auri.p, msg->from.auri.l); + TEST_STRCMP("SUBSCRIBE", 9U, msg->cseq.met.p, msg->cseq.met.l); + TEST_ASSERT(pl_u32(&msg->expires) > 0); + + hdr = sip_msg_hdr(msg, SIP_HDR_EVENT); + if (!hdr) { + err = EPROTO; + goto out; + } + + err = sipevent_event_decode(&se, &hdr->val); + if (err) + goto out; + + if (pl_strcasecmp(&se.event, test_event)) { + DEBUG_WARNING("presence: unexpected event '%r'\n", &se.event); + err = EPROTO; + goto out; + } + + err = sipevent_accept(&ag->not, ag->sock, msg, NULL, &se, 200, "OK", + 600, 600, 600, ag->name, "application/pidf", + NULL, NULL, false, + sipnot_close_handler, ag, NULL); + if (err) + goto out; + + err = send_notify(ag); + if (err) + goto out; + + out: + if (err) { + complete(ag, err); + } + + return true; +} + + +/* + * Agent `A' -- the Subscriber + * + * handle incoming NOTIFY messages from B + * + +NOTIFY sip:a@127.0.0.1:10000 SIP/2.0. +Via: SIP/2.0/UDP 127.0.0.1:20000;branch=z9hG4bK056ce9e0dabdea16;rport. +Contact: . +Max-Forwards: 70. +To: "a" ;tag=b8c2a38adecb1bea. +From: ;tag=426f09aed9f32053. +Call-ID: f3b22da3f4223935. +CSeq: 7364 NOTIFY. +User-Agent: b. +Event: my-event. +Subscription-State: active;expires=600. +Content-Type: application/pidf. +Content-Length: 417. + + */ +static void sipsub_notify_handler(struct sip *sip, const struct sip_msg *msg, + void *arg) +{ + struct agent *ag = arg; + struct agent *peer = ag->peer; + const struct sip_hdr *hdr; + struct sipevent_substate substate; + int err = 0; + (void)sip; + + DEBUG_INFO("[ %s ] subscriber -- incoming notify\n", ag->name); + + TEST_ASSERT(NULL != ag->sub); + + ++ag->notc; + + /* verify the SIP message */ + TEST_ASSERT(msg != NULL); + TEST_STRCMP("NOTIFY", 6U, msg->met.p, msg->met.l); + hdr = sip_msg_hdr(msg, SIP_HDR_CONTACT); + TEST_ASSERT(hdr != NULL); + TEST_STRCMP(ag->uri, strlen(ag->uri), msg->to.auri.p, msg->to.auri.l); + TEST_STRCMP(peer->uri, strlen(peer->uri), + msg->from.auri.p, msg->from.auri.l); + TEST_STRCMP("NOTIFY", 6U, msg->cseq.met.p, msg->cseq.met.l); + hdr = sip_msg_hdr(msg, SIP_HDR_EVENT); + TEST_ASSERT(hdr != NULL); + TEST_STRCMP(test_event, str_len(test_event), hdr->val.p, hdr->val.l); + + hdr = sip_msg_hdr(msg, SIP_HDR_SUBSCRIPTION_STATE); + TEST_ASSERT(hdr != NULL); + err = sipevent_substate_decode(&substate, &hdr->val); + TEST_ERR(err); + + /* verify that state is active */ + TEST_EQUALS(SIPEVENT_ACTIVE, substate.state); + TEST_ASSERT(pl_u32(&substate.expires) > 0); + + sip_treply(NULL, sip, msg, 200, "OK"); + + complete(ag, 0); + return; + + out: + if (err) + complete(ag, err); +} + + +static void sipsub_close_handler(int err, const struct sip_msg *msg, + const struct sipevent_substate *substate, + void *arg) +{ + struct agent *ag = arg; + (void)msg; + (void)substate; + + DEBUG_WARNING("[ %s ] subscriber -- closed (%m)\n", ag->name, err); + + ++ag->closec; + + complete(ag, err); +} + + +static void exit_handler(void *arg) +{ + struct agent *ag = arg; + + ag->exited = true; + + if (ag->peer->exited) + re_cancel(); +} + + +static void destructor(void *data) +{ + struct agent *ag = data; + + mem_deref(ag->sub); + mem_deref(ag->not); + + mem_deref(ag->sock); + + sip_close(ag->sip, true); + mem_deref(ag->sip); +} + + +static int agent_alloc(struct agent **agp, const char *name, + const struct sa *laddr) +{ + struct sa sa; + struct agent *ag; + int err; + + ag = mem_zalloc(sizeof(*ag), destructor); + if (!ag) + return ENOMEM; + + str_ncpy(ag->name, name, sizeof(ag->name)); + + err = sip_alloc(&ag->sip, NULL, 32, 32, 32, + name, exit_handler, ag); + if (err) + goto out; + + err = sip_transp_add(ag->sip, SIP_TRANSP_UDP, laddr); + if (err) + goto out; + + err = sip_transp_laddr(ag->sip, &sa, SIP_TRANSP_UDP, NULL); + if (err) + goto out; + + err = sipevent_listen(&ag->sock, ag->sip, 32, 32, + subscribe_handler, ag); + if (err) + goto out; + + re_snprintf(ag->uri, sizeof(ag->uri), "sip:%s@%J", name, &sa); + +#if 0 + re_printf("agent %s (%s)\n", name, ag->uri); +#endif + + out: + if (err) + mem_deref(ag); + else + *agp = ag; + + return err; +} + + +static int agent_subscribe(struct agent *ag, struct agent *peer) +{ + if (!ag || !peer) + return EINVAL; + + return sipevent_subscribe(&ag->sub, ag->sock, peer->uri, ag->name, + ag->uri, test_event, NULL, 600, ag->name, + NULL, 0, NULL, NULL, false, + NULL, sipsub_notify_handler, + sipsub_close_handler, ag, NULL); +} + + +static int do_sipevent(struct sa *laddr) +{ + struct agent *a = NULL, *b = NULL; + int err = 0; + + err |= agent_alloc(&a, "a", laddr); + err |= agent_alloc(&b, "b", laddr); + if (err) + goto out; + + a->peer = b; + b->peer = a; + + err = agent_subscribe(a, b); + if (err) + goto out; + + err = re_main_timeout(500); + if (err) + goto out; + + TEST_ERR(a->err); + TEST_ERR(b->err); + + TEST_EQUALS(0, a->subc); + TEST_EQUALS(1, a->notc); + TEST_EQUALS(0, a->closec); + + TEST_EQUALS(1, b->subc); + TEST_EQUALS(0, b->notc); + TEST_EQUALS(0, b->closec); + + out: + mem_deref(b); + mem_deref(a); + + return err; +} + + +int test_sipevent(void) +{ + int err; + struct sa laddr; + + err = sa_set_str(&laddr, "127.0.0.1", 0); + TEST_ERR(err); + + err = do_sipevent(&laddr); + +out: + return err; +} + + +int test_sipevent_network(void) +{ + struct sa laddr; + int err = 0; + + sa_init(&laddr, AF_INET6); + + if (0 == net_if_getlinklocal(NULL, AF_INET6, &laddr)) { + + err = do_sipevent(&laddr); + } + + return err; +} diff --git a/test/sipreg.c b/test/sipreg.c new file mode 100644 index 000000000..d3c397cb2 --- /dev/null +++ b/test/sipreg.c @@ -0,0 +1,212 @@ +/** + * @file sipreg.c SIP Register client regression testcode + * + * Copyright (C) 2010 - 2015 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_sipreg" +#define DEBUG_LEVEL 5 +#include + + +#define LOCAL_PORT 0 +#define LOCAL_SECURE_PORT 0 + + +struct test { + enum sip_transp tp; + unsigned n_resp; + uint16_t srcport; + int err; +}; + + +static void exit_handler(void *arg) +{ + (void)arg; + re_cancel(); +} + + +static int sipstack_fixture(struct sip **sipp) +{ + struct sa laddr, laddrs; + struct sip *sip = NULL; + struct tls *tls = NULL; +#ifdef USE_TLS + char cafile[256]; +#endif + int err; + + (void)sa_set_str(&laddr, "127.0.0.1", LOCAL_PORT); + (void)sa_set_str(&laddrs, "127.0.0.1", LOCAL_SECURE_PORT); + + err = sip_alloc(&sip, NULL, 32, 32, 32, "retest", exit_handler, NULL); + if (err) + goto out; + + err |= sip_transp_add(sip, SIP_TRANSP_UDP, &laddr); + err |= sip_transp_add(sip, SIP_TRANSP_TCP, &laddr); + if (err) + goto out; + +#ifdef USE_TLS + /* TLS-context for client -- no certificate needed */ + err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + re_snprintf(cafile, sizeof(cafile), "%s/server-ecdsa.pem", + test_datapath()); + + err = tls_add_ca(tls, cafile); + if (err) + goto out; + + err |= sip_transp_add(sip, SIP_TRANSP_TLS, &laddrs, tls); + if (err) + goto out; +#endif + + out: + mem_deref(tls); + if (err) + mem_deref(sip); + else + *sipp = sip; + + return err; +} + + +static void sip_resp_handler(int err, const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + + if (err) { + test->err = err; + re_cancel(); + return; + } + + ++test->n_resp; + + /* verify the SIP response message */ + TEST_ASSERT(msg != NULL); + TEST_EQUALS(200, msg->scode); + TEST_STRCMP("REGISTER", 8, msg->cseq.met.p, msg->cseq.met.l); + TEST_EQUALS(test->tp, msg->tp); + if (test->srcport) + TEST_EQUALS(test->srcport, sa_port(&msg->dst)); + + out: + if (err) + test->err = err; + re_cancel(); +} + + +static int reg_test(enum sip_transp tp, bool deprecated, uint16_t srcport) +{ + struct test test; + struct sip_server *srv = NULL; + struct sipreg *reg = NULL; + struct sip *sip = NULL; + char reg_uri[256]; + int err; + + memset(&test, 0, sizeof(test)); + test.tp = tp; + test.srcport = srcport; + + err = sip_server_alloc(&srv); + if (err) + goto out; + + err = sipstack_fixture(&sip); + if (err) + goto out; + + err = sip_server_uri(srv, reg_uri, sizeof(reg_uri), tp); + if (err) + goto out; + + if (deprecated) { + err = sipreg_register(®, sip, reg_uri, + "sip:x@test", NULL, + "sip:x@test", + 3600, "x", NULL, 0, 0, NULL, NULL, false, + sip_resp_handler, &test, NULL, NULL); + } + else { + err = sipreg_alloc(®, sip, reg_uri, + "sip:x@test", NULL, + "sip:x@test", + 3600, "x", NULL, 0, 0, NULL, NULL, false, + sip_resp_handler, &test, NULL, NULL); + if (srcport) + sipreg_set_srcport(reg, srcport); + + err |= sipreg_send(reg); + } + + if (err) + goto out; + + err = re_main_timeout(1000); + TEST_ERR(err); + + if (test.err) { + err = test.err; + TEST_ERR(err); + } + + TEST_ASSERT(srv->n_register_req > 0); + TEST_ASSERT(test.n_resp > 0); + + out: + mem_deref(reg); + + sip_close(sip, true); + mem_deref(sip); + + mem_deref(srv); + + return err; +} + + +int test_sipreg_udp(void) +{ + int err; + + err = reg_test(SIP_TRANSP_UDP, true, 0); + err |= reg_test(SIP_TRANSP_UDP, false, 0); + return err; +} + + +int test_sipreg_tcp(void) +{ + int err; + + err = reg_test(SIP_TRANSP_TCP, true, 0); + err |= reg_test(SIP_TRANSP_TCP, false, 0); + return err; +} + + +#ifdef USE_TLS +int test_sipreg_tls(void) +{ + int err; + + err = reg_test(SIP_TRANSP_TLS, true, 0); + err |= reg_test(SIP_TRANSP_TLS, false, 0); + return err; +} +#endif diff --git a/test/sipsess.c b/test/sipsess.c new file mode 100644 index 000000000..babca686a --- /dev/null +++ b/test/sipsess.c @@ -0,0 +1,1321 @@ +/** + * @file sipsess.c SIP Session regression testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_sipsess" +#define DEBUG_LEVEL 5 +#include + + +typedef void (prack_func)(void *arg); + + +enum sdp_neg_state { + INITIAL = 0, + OFFER_RECEIVED, + ANSWER_RECEIVED, + EARLY_CONFIRMED +}; + + +enum rel100_state { + REL100_NONE = 0, + REL100_SUPPORTED = 1, + REL100_REQUIRE = 2 +}; + + +enum connect_action { + CONN_PROGRESS = 1, + CONN_PROGR_ANS = 2, + CONN_ANSWER = 4, + CONN_BUSY = 8 +}; + + +struct test { + struct sip *sip; + struct sipsess_sock *sock; + struct sipsess *a; + struct sipsess *b; + struct tmr ans_tmr; + bool estab_a; + bool estab_b; + bool answr_a; + bool answr_b; + bool progr_a; + bool progr_b; + bool offer_a; + bool offer_b; + enum rel100_mode rel100_a; + enum rel100_mode rel100_b; + enum sdp_neg_state sdp_state; + enum rel100_state rel100_state_a; + enum rel100_state rel100_state_b; + enum connect_action conn_action; + prack_func *prack_action; + int progr_ret_code; + int answ_ret_code; + bool upd_a; + bool upd_b; + struct mbuf *desc; + bool blind_transfer; + uint16_t altaddr_port; + int err; +}; + + +const char sdp_a[] = "v=0\r\n" + "o=alice 2890844526 2890844526 IN IP4 1.2.3.4\r\n" + "s=-\r\n" + "c=IN IP4 1.2.3.4\r\n" + "t=0 0\r\n" + "m=audio 49170 RTP/AVP 0 8 97\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=rtpmap:8 PCMA/8000\r\n" + "a=rtpmap:97 iLBC/8000\r\n" + "a=sendrecv\r\n" + "m=video 51372 RTP/AVP 31 32\r\n" + "a=rtpmap:31 H261/90000\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n"; +const char sdp_b[] = "v=0\r\n" + "o=bob 2808844564 2808844564 IN IP4 5.6.7.8\r\n" + "s=-\r\n" + "c=IN IP4 5.6.7.8\r\n" + "t=0 0\r\n" + "m=audio 49174 RTP/AVP 0\r\n" + "a=rtpmap:0 PCMU/8000\r\n" + "a=sendrecv\r\n" + "m=video 49170 RTP/AVP 32\r\n" + "a=rtpmap:32 MPV/90000\r\n" + "a=sendrecv\r\n"; + + +static void stop_test(void) +{ + re_cancel(); +} + + +static void abort_test(struct test *test, int err) +{ + test->err = err; + re_cancel(); +} + + +static void exit_handler(void *arg) +{ + (void)arg; + re_cancel(); +} + + +static void send_answer_b(void *arg) +{ + struct test *test = arg; + int err; + + err = sipsess_answer(test->b, 200, "Answering", NULL, NULL); + if (err) { + abort_test(test, err); + } +} + + +static void send_update_a(void *arg) +{ + struct test *test = arg; + struct mbuf *desc; + int err; + + desc = mbuf_alloc(sizeof(sdp_a)); + if (!desc) { + err = ENOMEM; + goto out; + } + err = mbuf_write_str(desc, sdp_a); + TEST_ERR(err); + + mbuf_set_pos(desc, 0); + + err = sipsess_modify(test->a, desc); + TEST_ERR(err); + +out: + mem_deref(desc); + if (err) + abort_test(test, err); + +} + + +static void send_update_b(void *arg) +{ + struct test *test = arg; + struct mbuf *desc; + int err; + + desc = mbuf_alloc(sizeof(sdp_b)); + if (!desc) { + err = ENOMEM; + goto out; + } + err = mbuf_write_str(desc, sdp_b); + TEST_ERR(err); + + mbuf_set_pos(desc, 0); + + err = sipsess_modify(test->b, desc); + TEST_ERR(err); + +out: + mem_deref(desc); + if (err) + abort_test(test, err); + +} + + +static int desc_handler(struct mbuf **descp, const struct sa *src, + const struct sa *dst, void *arg) +{ + struct test *test = arg; + (void)src; + (void)dst; + + test->desc = mbuf_alloc(1); + if (!test->desc) + return ENOMEM; + + *descp = test->desc; + return 0; +} + + +static int desc_handler_a(struct mbuf **descp, const struct sa *src, + const struct sa *dst, void *arg) +{ + struct mbuf *desc; + int err = 0; + (void)src; + (void)dst; + (void)arg; + + desc = mbuf_alloc(sizeof(sdp_a)); + if (!desc) { + err = ENOMEM; + goto out; + } + + err = mbuf_write_str(desc, sdp_a); + if (err) + goto out; + + mbuf_set_pos(desc, 0); + *descp = desc; + +out: + return err; +} + + +static int offer_handler_a(struct mbuf **descp, const struct sip_msg *msg, + void *arg) +{ + struct test *test = arg; + (void)descp; + (void)msg; + + if (test->sdp_state == INITIAL || test->sdp_state == EARLY_CONFIRMED) + test->sdp_state = OFFER_RECEIVED; + + if (!pl_strcmp(&msg->met, "UPDATE")) + test->upd_a = true; + + test->offer_a = true; + + return 0; +} + + +static int offer_handler_b(struct mbuf **descp, const struct sip_msg *msg, + void *arg) +{ + struct test *test = arg; + (void)descp; + (void)msg; + + if (test->sdp_state == INITIAL || test->sdp_state == EARLY_CONFIRMED) + test->sdp_state = OFFER_RECEIVED; + + if (!pl_strcmp(&msg->met, "UPDATE")) + test->upd_b = true; + + test->offer_b = true; + + return 0; +} + + +static int answer_handler_a(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + test->answr_a = true; + if (mbuf_get_left(msg->mb)) + test->sdp_state = ANSWER_RECEIVED; + + if (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, "100rel")) + test->rel100_a |= REL100_SUPPORTED; + + if (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, "100rel")) + test->rel100_a |= REL100_REQUIRE; + + if (!pl_strcmp(&msg->cseq.met, "UPDATE")) { + if (msg->scode < 200 || msg->scode > 299) { + abort_test(test, msg->scode); + return msg->scode; + } + + tmr_start(&test->ans_tmr, 0, send_answer_b, test); + } + + return 0; +} + + +static int answer_handler_b(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + test->answr_b = true; + if (mbuf_get_left(msg->mb)) + test->sdp_state = ANSWER_RECEIVED; + + if (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, "100rel")) + test->rel100_state_b |= REL100_SUPPORTED; + + if (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, "100rel")) + test->rel100_state_b |= REL100_REQUIRE; + + if (!pl_strcmp(&msg->cseq.met, "UPDATE")) { + if (msg->scode < 200 || msg->scode > 299) { + abort_test(test, msg->scode); + return msg->scode; + } + + tmr_start(&test->ans_tmr, 0, send_answer_b, test); + } + + return 0; +} + + +static void progr_handler_a(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + (void)msg; + + test->progr_a = true; +} + + +static void prack_handler(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + (void)msg; + + if (test->sdp_state == ANSWER_RECEIVED) + test->sdp_state = EARLY_CONFIRMED; + + if (test->prack_action) + tmr_start(&test->ans_tmr, 0, test->prack_action, test); +} + + +static void estab_handler_a(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + (void)msg; + + test->estab_a = true; + if (test->estab_b) + stop_test(); +} + + +static void estab_handler_b(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + (void)msg; + + test->estab_b = true; + if (test->estab_a) + stop_test(); +} + + +static void close_handler(int err, const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + (void)msg; + + if (!err && test->conn_action == CONN_BUSY) + err = EBUSY; + + abort_test(test, err ? err : ENOMEM); +} + + +static void conn_handler(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + struct mbuf *desc; + int err; + char *hdrs = test->rel100_b == REL100_REQUIRED ? + "Require: 100rel\r\n" : ""; + + if (mbuf_get_left(msg->mb)) { + test->sdp_state = OFFER_RECEIVED; + test->offer_b = true; + } + + if (sip_msg_hdr_has_value(msg, SIP_HDR_SUPPORTED, "100rel")) + test->rel100_state_b |= REL100_SUPPORTED; + + if (sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, "100rel")) + test->rel100_state_b |= REL100_REQUIRE; + + desc = mbuf_alloc(sizeof(sdp_b)); + if (!desc) { + abort_test(test, ENOMEM); + return; + } + + err = mbuf_write_str(desc, sdp_b); + if (err) { + abort_test(test, err); + return; + } + + mbuf_set_pos(desc, 0); + test->desc = desc; + + if (test->conn_action & CONN_PROGRESS + || test->conn_action & CONN_PROGR_ANS) { + err = sipsess_accept(&test->b, test->sock, msg, 183, + "Progress", test->rel100_b, "b", + "application/sdp", desc, NULL, NULL, false, + offer_handler_b, answer_handler_b, + estab_handler_b, NULL, NULL, close_handler, + test, hdrs); + if (err != test->progr_ret_code) { + test->progr_ret_code = err; + goto out; + } + + if (err) + mem_deref(desc); + + err = sipsess_set_prack_handler(test->b, prack_handler); + if (err) + abort_test(test, err); + } + + if (test->conn_action & CONN_PROGR_ANS) { + err = sipsess_answer(test->b, 200, "Answering", NULL, NULL); + if (err != test->answ_ret_code) { + test->answ_ret_code = err; + goto out; + } + } + else if (test->conn_action & CONN_ANSWER) { + err = sipsess_accept(&test->b, test->sock, msg, 200, "OK", + test->rel100_b, "b", "application/sdp", + NULL, NULL, NULL, false, offer_handler_b, + answer_handler_b, estab_handler_b, NULL, NULL, + close_handler, test, hdrs); + if (err != test->answ_ret_code) { + test->answ_ret_code = err; + goto out; + } + } + else if (test->conn_action & CONN_BUSY) { + err = sipsess_accept(&test->b, test->sock, msg, 180, + "Ringing", test->rel100_b, "b", + "application/sdp", NULL, NULL, NULL, false, + offer_handler_b, answer_handler_b, + estab_handler_b, NULL, NULL, close_handler, + test, hdrs); + if (err != test->answ_ret_code) { + test->answ_ret_code = err; + goto out; + } + err |= sipsess_reject(test->b, 486, "Busy Here", NULL); + if (err != test->answ_ret_code) { + test->answ_ret_code = err; + goto out; + } + } + + if (test->conn_action & (CONN_ANSWER | CONN_PROGR_ANS | CONN_BUSY)) + mem_deref(desc); + + return; + +out: + mem_deref(desc); + abort_test(test, err); +} + + +static void conn_transfer_handler(const struct sip_msg *msg, void *arg) +{ + struct test *test = arg; + int err = 0; + + if (test->blind_transfer) { + conn_handler(msg, arg); + } + else { + err = sip_replyf(test->sip, msg, 302, "Moved Temporarily", + "Contact: \"alt retest\" " + "\r\n\r\n", test->altaddr_port); + if (err) { + abort_test(test, err); + } + } + + return; +} + + +static void redirect_handler(const struct sip_msg *msg, const char *uri, + void *arg) +{ + struct test *test = arg; + + (void) msg; + (void) uri; + + test->blind_transfer = true; +} + + +int test_sipsess(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_ENABLED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_ANSWER; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + if (err) + goto out; + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + if (err) + goto out; + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + if (err) + goto out; + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, &test); + if (err) + goto out; + + err = str_x64dup(&callid, rand_u64()); + if (err) + goto out; + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler, + offer_handler_a, answer_handler_a, NULL, + estab_handler_a, NULL, NULL, + close_handler, &test, NULL); + mem_deref(callid); + if (err) + goto out; + + err = re_main_timeout(200); + if (err) + goto out; + + if (test.err) { + err = test.err; + goto out; + } + + /* okay here -- verify */ + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.desc); + ASSERT_TRUE(test.answr_a); + ASSERT_TRUE(!test.offer_b); + + out: + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + return err; +} + + +int test_sipsess_reject(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_DISABLED; + test.rel100_b = REL100_DISABLED; + test.conn_action = CONN_BUSY; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler, + offer_handler_a, answer_handler_a, NULL, + estab_handler_a, NULL, NULL, + close_handler, &test, NULL); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + + /* okay here -- verify */ + ASSERT_TRUE(test.err == EBUSY); + ASSERT_TRUE(!test.estab_a); + ASSERT_TRUE(!test.estab_b); + ASSERT_TRUE(test.desc); + ASSERT_TRUE(!test.answr_a); + ASSERT_TRUE(!test.offer_b); + +out: + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + return err; +} + + +int test_sipsess_blind_transfer(void) +{ + struct test test; + struct sa laddr, altaddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_ENABLED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_ANSWER; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_transfer_handler, + &test); + TEST_ERR(err); + + (void)sa_set_str(&altaddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &altaddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &altaddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + test.altaddr_port = sa_port(&altaddr); + + err = str_x64dup(&callid, rand_u64()); + if (err) + goto out; + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler, + offer_handler_a, answer_handler_a, NULL, + estab_handler_a, NULL, NULL, + close_handler, &test, NULL); + mem_deref(callid); + TEST_ERR(err); + + err = sipsess_set_redirect_handler(test.a, redirect_handler); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + + if (test.err) { + err = test.err; + TEST_ERR(err); + } + + /* okay here -- verify */ + ASSERT_TRUE(test.blind_transfer); + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.desc); + ASSERT_TRUE(test.answr_a); + ASSERT_TRUE(!test.offer_b); + + out: + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + return err; +} + +int test_sipsess_100rel_caller_require(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_REQUIRED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_PROGRESS; + test.prack_action = send_answer_b; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, + &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler_a, + offer_handler_a, answer_handler_a, + progr_handler_a, estab_handler_a, NULL, + NULL, close_handler, &test, + "Require: 100rel\r\n"); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + + if (test.err) { + err = test.err; + TEST_ERR(err); + } + + /* okay here -- verify */ + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.desc); + ASSERT_TRUE(test.offer_b); + ASSERT_TRUE(!test.answr_b); + ASSERT_TRUE(test.progr_a); + ASSERT_TRUE(test.rel100_state_b & REL100_REQUIRE); + ASSERT_TRUE((test.rel100_state_b & REL100_SUPPORTED) == 0); + ASSERT_TRUE(test.sdp_state == EARLY_CONFIRMED); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + mem_deref(test.desc); + + return err; +} + + +int test_sipsess_100rel_supported(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_ENABLED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_PROGRESS; + test.prack_action = send_answer_b; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, + &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler_a, + offer_handler_a, answer_handler_a, + progr_handler_a, estab_handler_a, NULL, + NULL, close_handler, &test, + "Supported: 100rel\r\n"); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + + if (test.err) { + err = test.err; + TEST_ERR(err); + } + + /* okay here -- verify */ + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.desc); + ASSERT_TRUE(test.answr_a); + ASSERT_TRUE(!test.offer_a); + ASSERT_TRUE(test.offer_b); + ASSERT_TRUE(!test.answr_b); + ASSERT_TRUE(test.progr_a); + ASSERT_TRUE(test.rel100_state_b & REL100_SUPPORTED); + ASSERT_TRUE((test.rel100_state_b & REL100_REQUIRE) == 0); + ASSERT_TRUE(test.sdp_state == EARLY_CONFIRMED); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + mem_deref(test.desc); + + return err; +} + + +int test_sipsess_100rel_answer_not_allowed(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_ENABLED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_PROGR_ANS; + test.answ_ret_code = EINVAL; + test.prack_action = send_answer_b; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler_a, + offer_handler_a, answer_handler_a, + progr_handler_a, estab_handler_a, NULL, + NULL, close_handler, &test, + "Supported: 100rel\r\n"); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + TEST_ERR(test.err); + + TEST_ERR(test.progr_ret_code); + ASSERT_TRUE(test.answ_ret_code == EINVAL); + + /* okay here -- verify */ + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.progr_a); + ASSERT_TRUE(test.rel100_state_b & REL100_SUPPORTED); + ASSERT_TRUE((test.rel100_state_b & REL100_REQUIRE) == 0); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + return err; +} + + +int test_sipsess_100rel_420(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_REQUIRED; + test.rel100_b = REL100_DISABLED; + test.conn_action = CONN_PROGRESS; + test.progr_ret_code = -1; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, + &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler, + offer_handler_a, answer_handler_a, NULL, + estab_handler_a, NULL, NULL, + close_handler, &test, + "Require: 100rel\r\n"); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + ASSERT_TRUE(test.err == EINVAL); + + /* okay here -- verify */ + ASSERT_TRUE(!test.b); + ASSERT_TRUE(!test.estab_a); + ASSERT_TRUE(!test.estab_b); + ASSERT_TRUE(test.desc); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + return err; +} + + +int test_sipsess_100rel_421(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_DISABLED; + test.rel100_b = REL100_REQUIRED; + test.conn_action = CONN_PROGRESS; + test.progr_ret_code = -1; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, + &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler, + offer_handler_a, answer_handler_a, NULL, + estab_handler_a, NULL, NULL, + close_handler, &test, NULL); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + ASSERT_TRUE(test.err == EINVAL); + + /* okay here -- verify */ + ASSERT_TRUE(!test.b); + ASSERT_TRUE(!test.estab_a); + ASSERT_TRUE(!test.estab_b); + ASSERT_TRUE(test.desc); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + return err; +} + + +int test_sipsess_update_uac(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + struct mbuf *desc_a = NULL; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_ENABLED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_PROGRESS; + test.prack_action = send_update_a; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, + &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler_a, + offer_handler_a, answer_handler_a, + progr_handler_a, estab_handler_a, NULL, + NULL, close_handler, &test, + "Supported: 100rel\r\n"); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + + if (test.err) { + err = test.err; + TEST_ERR(err); + } + + /* okay here -- verify */ + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.answr_a); + ASSERT_TRUE(!test.answr_b); + ASSERT_TRUE(!test.offer_a); + ASSERT_TRUE(test.offer_b); + ASSERT_TRUE(test.progr_a); + ASSERT_TRUE(test.upd_b); + ASSERT_TRUE(!test.upd_a); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + mem_deref(desc_a); + mem_deref(test.desc); + + return err; +} + + +int test_sipsess_update_uas(void) +{ + struct test test; + struct sa laddr; + char to_uri[256]; + struct mbuf *desc_a = NULL; + int err; + uint16_t port; + char *callid; + + memset(&test, 0, sizeof(test)); + + test.rel100_a = REL100_ENABLED; + test.rel100_b = REL100_ENABLED; + test.conn_action = CONN_PROGRESS; + test.prack_action = send_update_b; + + err = sip_alloc(&test.sip, NULL, 32, 32, 32, + "retest", exit_handler, NULL); + TEST_ERR(err); + + (void)sa_set_str(&laddr, "127.0.0.1", 0); + err = sip_transp_add(test.sip, SIP_TRANSP_UDP, &laddr); + TEST_ERR(err); + + err = sip_transp_laddr(test.sip, &laddr, SIP_TRANSP_UDP, NULL); + TEST_ERR(err); + + port = sa_port(&laddr); + + err = sipsess_listen(&test.sock, test.sip, 32, conn_handler, + &test); + TEST_ERR(err); + + err = str_x64dup(&callid, rand_u64()); + TEST_ERR(err); + + /* Connect to "b" */ + (void)re_snprintf(to_uri, sizeof(to_uri), "sip:b@127.0.0.1:%u", port); + err = sipsess_connect(&test.a, test.sock, to_uri, NULL, + "sip:a@127.0.0.1", "a", NULL, 0, + "application/sdp", NULL, NULL, false, + callid, desc_handler_a, + offer_handler_a, answer_handler_a, + progr_handler_a, estab_handler_a, NULL, + NULL, close_handler, &test, + "Supported: 100rel\r\n"); + mem_deref(callid); + TEST_ERR(err); + + err = re_main_timeout(200); + TEST_ERR(err); + + if (test.err) { + err = test.err; + TEST_ERR(err); + } + + /* okay here -- verify */ + ASSERT_TRUE(test.estab_a); + ASSERT_TRUE(test.estab_b); + ASSERT_TRUE(test.answr_a); + ASSERT_TRUE(test.answr_b); + ASSERT_TRUE(test.offer_a); + ASSERT_TRUE(test.offer_b); + ASSERT_TRUE(test.progr_a); + ASSERT_TRUE(test.upd_a); + ASSERT_TRUE(!test.upd_b); + +out: + tmr_cancel(&test.ans_tmr); + test.a = mem_deref(test.a); + test.b = mem_deref(test.b); + + sipsess_close_all(test.sock); + test.sock = mem_deref(test.sock); + + sip_close(test.sip, false); + test.sip = mem_deref(test.sip); + + mem_deref(desc_a); + mem_deref(test.desc); + + return err; +} diff --git a/test/srtp.c b/test/srtp.c new file mode 100644 index 000000000..2e45c3ea6 --- /dev/null +++ b/test/srtp.c @@ -0,0 +1,1212 @@ +/** + * @file srtp.c SRTP Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "srtptest" +#define DEBUG_LEVEL 5 +#include + + +#define SSRC 0x31323334 + + +enum { + SALT_LEN_CTR = 14 +}; + + +static const uint8_t fixed_payload[20] = { + 0x55, 0x55, 0x55, 0x55, + 0x11, 0x11, 0x11, 0x11, + 0xee, 0xee, 0xee, 0xee, + 0x11, 0x11, 0x11, 0x11, + 0x55, 0x55, 0x55, 0x55, +}; + + +static size_t get_keylen(enum srtp_suite suite) +{ + switch (suite) { + + case SRTP_AES_CM_128_HMAC_SHA1_32: return 16; + case SRTP_AES_CM_128_HMAC_SHA1_80: return 16; + case SRTP_AES_256_CM_HMAC_SHA1_32: return 32; + case SRTP_AES_256_CM_HMAC_SHA1_80: return 32; + case SRTP_AES_128_GCM: return 16; + case SRTP_AES_256_GCM: return 32; + default: return 0; + } +} + + +static size_t get_saltlen(enum srtp_suite suite) +{ + switch (suite) { + + case SRTP_AES_CM_128_HMAC_SHA1_32: return 14; + case SRTP_AES_CM_128_HMAC_SHA1_80: return 14; + case SRTP_AES_256_CM_HMAC_SHA1_32: return 14; + case SRTP_AES_256_CM_HMAC_SHA1_80: return 14; + case SRTP_AES_128_GCM: return 12; + case SRTP_AES_256_GCM: return 12; + default: return 0; + } +} + + +static size_t get_taglen(enum srtp_suite suite) +{ + switch (suite) { + + case SRTP_AES_CM_128_HMAC_SHA1_32: return 4; + case SRTP_AES_CM_128_HMAC_SHA1_80: return 10; + case SRTP_AES_256_CM_HMAC_SHA1_32: return 4; + case SRTP_AES_256_CM_HMAC_SHA1_80: return 10; + case SRTP_AES_128_GCM: return 16; + case SRTP_AES_256_GCM: return 16; + default: return 0; + } +} + + +/* + * RFC 3711 B.2. AES-CM Test Vectors + */ +static int test_srtp_aescm128(void) +{ + uint8_t k_e[16], iv[16]; + struct aes *aes = NULL; + uint8_t keystream[16], nulldata[16]; + size_t i; + int err = 0; + static const struct { + const char *counter; + const char *keystream; + } testv[] = { + {"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0000", + "E03EAD0935C95E80E166B16DD92B4EB4"}, + + {"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0001", + "D23513162B02D0F72A43A2FE4A5F97AB"}, + + {"F0F1F2F3F4F5F6F7F8F9FAFBFCFD0002", + "41E95B3BB0A2E8DD477901E4FCA894C0"}, + }; + + memset(nulldata, 0, sizeof(nulldata)); + + err |= str_hex(k_e, sizeof(k_e), "2B7E151628AED2A6ABF7158809CF4F3C"); + err |= str_hex(iv, sizeof(iv), "F0F1F2F3F4F5F6F7F8F9FAFBFCFD0000"); + if (err) + return err; + + err = aes_alloc(&aes, AES_MODE_CTR, k_e, 128, iv); + if (err) + return err; + + for (i=0; ipos = mb->end = offset; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.ver = RTP_VERSION; + hdr.seq = seq++; + hdr.ssrc = SSRC; + + err = rtp_hdr_encode(mb, &hdr); + if (err) + break; + + memcpy(hdrbuf, &mb->buf[mb->pos-12], 12); + + err = mbuf_write_mem(mb, fixed_payload, sizeof(fixed_payload)); + if (err) + break; + + end = mb->end; + + /* tx */ + mb->pos = offset; + err = srtp_encrypt(ctx_tx, mb); + if (err) + break; + + TEST_EQUALS(offset, mb->pos); + TEST_EQUALS(end + tag_len, mb->end); + + /* verify that srtp_encrypt() did not tamper with RTP header */ + TEST_MEMCMP(hdrbuf, sizeof(hdrbuf), &mb->buf[offset], 12); + + /* rx */ + mb->pos = offset; + err = srtp_decrypt(ctx_rx, mb); + if (err) { + DEBUG_WARNING("srtp_decrypt: %m\n", err); + break; + } + + TEST_EQUALS(offset, mb->pos); + TEST_EQUALS(end, mb->end); + + mb->pos = offset + RTP_HEADER_SIZE; + + TEST_MEMCMP(fixed_payload, sizeof(fixed_payload), + mbuf_buf(mb), mbuf_get_left(mb)); + } + + out: + mem_deref(ctx_tx); + mem_deref(ctx_rx); + mem_deref(mb); + + return err; +} + + +static int test_srtcp_loop(size_t offset, enum srtp_suite suite, + enum rtcp_type type) +{ + struct srtp *ctx_tx = NULL, *ctx_rx = NULL; + struct mbuf *mb1 = NULL, *mb2 = NULL; + const size_t key_len = get_keylen(suite); + const size_t salt_len = get_saltlen(suite); + const size_t tag_len = get_taglen(suite); + unsigned i; + int err = 0; + + static const uint8_t master_key[16+16+14] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + }; + + mb1 = mbuf_alloc(1024); + mb2 = mbuf_alloc(1024); + if (!mb1 || !mb2) { + err = ENOMEM; + goto out; + } + + err = srtp_alloc(&ctx_tx, suite, master_key, key_len + salt_len, 0); + err |= srtp_alloc(&ctx_rx, suite, master_key, key_len + salt_len, 0); + if (err) + goto out; + + for (i=0; i<10; i++) { + + const uint32_t srcv[2] = {0x12345678, 0x00abcdef}; + size_t end; + + mb1->pos = mb1->end = offset; + mb2->pos = mb2->end = offset; + + if (type == RTCP_BYE) { + err = rtcp_encode(mb1, RTCP_BYE, 2, srcv, "ciao"); + } + else if (type == RTCP_RR) { + err = rtcp_encode(mb1, RTCP_RR, 0, srcv[0], + NULL, NULL); + } + else { + re_printf("unknown type %d\n", type); + err = EINVAL; + break; + } + + if (err) + break; + + end = mb1->end; + + mb1->pos = offset; + (void)mbuf_write_mem(mb2, mbuf_buf(mb1), mbuf_get_left(mb1)); + mb2->pos = offset; + + /* tx */ + mb1->pos = offset; + err = srtcp_encrypt(ctx_tx, mb1); + if (err) + break; + + TEST_EQUALS(offset, mb1->pos); + TEST_ASSERT(mb1->end != end); + TEST_EQUALS((mbuf_get_left(mb2) + 4 + tag_len), + mbuf_get_left(mb1)); + + /* rx */ + mb1->pos = offset; + err = srtcp_decrypt(ctx_rx, mb1); + if (err) + break; + + TEST_EQUALS(offset, mb1->pos); + TEST_EQUALS(end, mb1->end); + TEST_MEMCMP(mbuf_buf(mb2), mbuf_get_left(mb2), + mbuf_buf(mb1), mbuf_get_left(mb1)); + } + + out: + mem_deref(ctx_tx); + mem_deref(ctx_rx); + mem_deref(mb1); + mem_deref(mb2); + + return err; +} + + +/* + * Reference SRTP-packet generated by libsrtp + * + * cipher: AES-CM-128 + * auth: HMAC-SHA1 80-bits tag + * master key: 0x22222222222222222222222222222222 + * master salt: 0x4444444444444444444444444444 + * SSRC: 0x01020304 + * Seq: 0x0001 + * RTP payload: 0xa5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5 + */ +static const char *srtp_libsrtp = + "800000010000000001020304" + "f5b44b7e3ad4eb057bc6480c45df6547bb70bcc2" + "7b136e1f3d3a62821b15"; + + +static int test_srtp_libsrtp(void) +{ + uint8_t pkt[12+20+10]; + struct srtp *srtp_enc = NULL; + static const uint8_t mast_key[16+14] = + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44"; + static const uint8_t rtp_payload[20] = + "\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5" + "\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5\xa5"; + struct mbuf *mb; + struct rtp_header hdr; + int err = 0; + + memset(&hdr, 0, sizeof(hdr)); + hdr.ver = RTP_VERSION; + hdr.ssrc = 0x01020304; + hdr.seq = 0x0001; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = str_hex(pkt, sizeof(pkt), srtp_libsrtp); + if (err) + goto out; + + err = srtp_alloc(&srtp_enc, SRTP_AES_CM_128_HMAC_SHA1_80, + mast_key, sizeof(mast_key), 0); + if (err) + goto out; + + err = rtp_hdr_encode(mb, &hdr); + err |= mbuf_write_mem(mb, rtp_payload, sizeof(rtp_payload)); + if (err) + goto out; + mb->pos = 0; + + err = srtp_encrypt(srtp_enc, mb); + if (err) + goto out; + + TEST_MEMCMP(pkt, sizeof(pkt), mb->buf, mb->end); + + out: + mem_deref(srtp_enc); + mem_deref(mb); + return err; +} + + +/* + * Reference SRTCP-packet generated by libsrtp + * + * cipher: AES-CM-128 + * auth: HMAC-SHA1 32-bits tag + * master key: 0x22222222222222222222222222222222 + * master salt: 0x4444444444444444444444444444 + * SSRC: 0x01020304 + * RTCP packet: BYE-message + */ +static const char *srtcp_libsrtp = + "81cb00020102030487c9fcdb80000001e9442fcc"; +/* ^^^^^^^^________ + * index tag + */ + + +static int test_srtcp_libsrtp(void) +{ + uint8_t pkt[12+4+4]; + struct srtp *srtp_enc = NULL; + static const uint8_t mast_key[16+14] = + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x22\x22\x22\x22\x22\x22\x22\x22" + "\x44\x44\x44\x44\x44\x44\x44" + "\x44\x44\x44\x44\x44\x44\x44"; + const uint32_t srcv[1] = {0x01020304}; + struct mbuf *mb; + int err = 0; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = str_hex(pkt, sizeof(pkt), srtcp_libsrtp); + if (err) + goto out; + + err = srtp_alloc(&srtp_enc, SRTP_AES_CM_128_HMAC_SHA1_32, + mast_key, sizeof(mast_key), 0); + if (err) + goto out; + + err = rtcp_encode(mb, RTCP_BYE, 1, srcv, "b"); + if (err) + goto out; + + mb->pos = 0; + + err = srtcp_encrypt(srtp_enc, mb); + if (err) + goto out; + + TEST_MEMCMP(pkt, sizeof(pkt), mb->buf, mb->end); + + out: + mem_deref(srtp_enc); + mem_deref(mb); + return err; +} + + +static int send_rtp_packet(struct srtp *srtp, struct mbuf *mb, uint16_t seq) +{ + struct rtp_header hdr; + size_t len; + int err; + + memset(&hdr, 0, sizeof(hdr)); + + hdr.ver = RTP_VERSION; + hdr.seq = seq; + hdr.ssrc = SSRC; + + mb->pos = mb->end = 0; + err = rtp_hdr_encode(mb, &hdr); + err |= mbuf_write_mem(mb, fixed_payload, sizeof(fixed_payload)); + if (err) + return err; + + len = mb->end; + + mb->pos = 0; + err = srtp_encrypt(srtp, mb); + if (err) + return err; + + TEST_EQUALS(0, mb->pos); + TEST_ASSERT(mb->end > len); + + out: + return err; +} + + +static int recv_srtp_packet(struct srtp *srtp, struct mbuf *mb) +{ + const size_t len = mb->end; + int err = 0; + + mb->pos = 0; + err = srtp_decrypt(srtp, mb); + if (err) + return err; + + TEST_EQUALS(0, mb->pos); + TEST_ASSERT(mb->end < len); + TEST_MEMCMP(fixed_payload, sizeof(fixed_payload), + mb->buf + 12, mb->end - 12); + out: + return err; +} + + +static int test_srtp_replay(enum srtp_suite suite) +{ + static const uint8_t key[32+14] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + }; + struct srtp *ctx = NULL; + struct mbuf *mb = NULL; + const size_t key_len = get_keylen(suite); + const size_t salt_len = get_saltlen(suite); + int e, err = 0; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + err = srtp_alloc(&ctx, suite, key, key_len + salt_len, 0); + if (err) + goto out; + + /* send/receive one RTP packet first */ + err = send_rtp_packet(ctx, mb, 42); + if (err) + goto out; + + err = srtp_decrypt(ctx, mb); + if (err) + goto out; + + /* then send/receive the same packet again, + expect replay protection */ + err = send_rtp_packet(ctx, mb, 42); + if (err) + goto out; + + e = srtp_decrypt(ctx, mb); + TEST_EQUALS(EALREADY, e); + + out: + mem_deref(ctx); + mem_deref(mb); + + return err; +} + + +static int test_seq_loop(const uint16_t *seqv, size_t seqn) +{ + static const uint8_t key[16+14] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + }; + struct srtp *srtp_tx = NULL, *srtp_rx = NULL; + struct mbuf *mb; + size_t i; + int err; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + /* note: we must use two separate SRTP instances here, + since the SSRC is the same */ + err = srtp_alloc(&srtp_tx, SRTP_AES_CM_128_HMAC_SHA1_32, + key, sizeof(key), 0); + err |= srtp_alloc(&srtp_rx, SRTP_AES_CM_128_HMAC_SHA1_32, + key, sizeof(key), 0); + if (err) + goto out; + + for (i=0; iend; + + for (i=0; ipos = 0; mb->end = i; + (void)srtp_encrypt(ctx, mb); + + mb->pos = 0; mb->end = i; + (void)srtp_decrypt(ctx, mb); + } + + out: + mem_deref(ctx); + mem_deref(mb); + + return err; +} + + +/* verify that we dont crash on random input */ +static int test_srtcp_random(enum srtp_suite suite) +{ + struct srtp *ctx = NULL; + struct mbuf *mb = NULL; + const size_t key_len = get_keylen(suite); + const size_t salt_len = get_saltlen(suite); + const uint32_t srcv[2] = {0x12345678, 0x00abcdef}; + size_t sz, i; + int err = 0; + + static const uint8_t master_key[16+16+14] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + }; + + mb = mbuf_alloc(1024); + if (!mb) + return ENOMEM; + + err = srtp_alloc(&ctx, suite, master_key, key_len + salt_len, 0); + if (err) + goto out; + + err = rtcp_encode(mb, RTCP_BYE, 2, srcv, "ciao"); + if (err) + goto out; + + err = mbuf_fill(mb, 0xd5, 32); + if (err) + goto out; + + sz = mb->end; + + for (i=0; ipos = 0; + mb->end = i; + (void)srtcp_encrypt(ctx, mb); + + mb->pos = 0; + mb->end = i; + (void)srtcp_decrypt(ctx, mb); + } + + out: + mem_deref(ctx); + mem_deref(mb); + + return err; +} + + +static int test_srtp_unauth(enum srtp_suite suite) +{ + struct srtp *srtp_tx, *srtp_rx; + struct mbuf *mb = NULL; + const size_t key_len = get_keylen(suite); + const size_t salt_len = get_saltlen(suite); + int err = 0; + + static const uint8_t master_key[32 + SALT_LEN_CTR] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + }; + + mb = mbuf_alloc(32); + if (!mb) + return ENOMEM; + + err = srtp_alloc(&srtp_tx, suite, master_key, key_len+salt_len, 0); + err |= srtp_alloc(&srtp_rx, suite, master_key, key_len+salt_len, 0); + if (err) + goto out; + + err = send_rtp_packet(srtp_tx, mb, 3); + if (err) + goto out; + + /* flip bits in the auth-tag to force authentication error */ + mb->buf[mb->end - 1] ^= 0x55; + + err = recv_srtp_packet(srtp_rx, mb); + + TEST_EQUALS(EAUTH, err); + + err = 0; + + out: + mem_deref(srtp_tx); + mem_deref(srtp_rx); + mem_deref(mb); + + return err; +} + + +/* + * Special test for Unencrypted SRTCP. This is a special case in + * SDES, See RFC 4568 section 6.3.2 + */ +static int test_unencrypted_srtcp(void) +{ + struct srtp *srtp = NULL; + struct mbuf *mb1 = NULL, *mb2 = NULL; + enum srtp_suite suite = SRTP_AES_CM_128_HMAC_SHA1_32; + const size_t tag_len = get_taglen(suite); + size_t end; + uint32_t v; + bool ep; + int err = 0; + + const uint32_t srcv[2] = {0x12345678, 0x00abcdef}; + static const uint8_t master_key[16+14] = { + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, + }; + + mb1 = mbuf_alloc(1024); + mb2 = mbuf_alloc(1024); + if (!mb1 || !mb2) { + err = ENOMEM; + goto out; + } + + err = srtp_alloc(&srtp, suite, master_key, 16 + SALT_LEN_CTR, + SRTP_UNENCRYPTED_SRTCP); + if (err) + goto out; + + err = rtcp_encode(mb1, RTCP_BYE, 2, srcv, "ciao"); + err |= rtcp_encode(mb2, RTCP_BYE, 2, srcv, "ciao"); + if (err) + goto out; + + end = mb1->end; + + /* tx */ + mb1->pos = 0; + err = srtcp_encrypt(srtp, mb1); + if (err) + goto out; + + TEST_EQUALS(0, mb1->pos); + + mb1->pos = end; + v = ntohl(mbuf_read_u32(mb1)); + ep = (v >> 31) & 1; + + mb1->pos = 0; + + /* verify that RTCP packet is not encrypted */ + TEST_ASSERT(ep == false); + TEST_MEMCMP(mb2->buf, mb2->end, mb1->buf, mb1->end - 4 - tag_len); + + /* rx */ + err = srtcp_decrypt(srtp, mb1); + if (err) + goto out; + + TEST_EQUALS(0, mb1->pos); + TEST_MEMCMP(mb2->buf, mb2->end, mb1->buf, mb1->end); + + out: + mem_deref(srtp); + mem_deref(mb1); + mem_deref(mb2); + + return err; +} + + +static bool have_srtp(void) +{ + static const uint8_t nullkey[30]; + struct srtp *srtp = NULL; + int err; + + err = srtp_alloc(&srtp, SRTP_AES_CM_128_HMAC_SHA1_32, + nullkey, sizeof(nullkey), 0); + + mem_deref(srtp); + + return err != ENOSYS; +} + + +/* + * test low-level code first, then high-level at the end + */ +int test_srtp(void) +{ + int err = 0; + + /* XXX: find a better solution for optional SRTP. + perhaps only register this test if SRTP is available? */ + if (!have_srtp()) { + (void)re_printf("skipping SRTP test\n"); + return ESKIPPED; + } + + err = test_srtp_aescm128(); + err |= test_srtp_aescm256(); + if (err) + return err; + + err |= test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, 3); + err |= test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_80, 3); + err |= test_srtp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_32, 3); + err |= test_srtp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_80, 3); + if (err) + return err; + + err |= test_srtp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_32, 3); + err |= test_srtp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_80, 3); + if (err) + return err; + + err |= test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, 65530); + err |= test_srtp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_80, 65530); + if (err) + return err; + + err = test_srtp_libsrtp(); + if (err) + return err; + + err = test_srtp_replay(SRTP_AES_CM_128_HMAC_SHA1_32); + if (err) + return err; + + err = test_srtp_reordering_and_wrap(); + if (err) + return err; + + err = test_srtp_unauth(SRTP_AES_CM_128_HMAC_SHA1_32); + if (err) + return err; + + err = test_srtp_random(SRTP_AES_CM_128_HMAC_SHA1_32); + if (err) + return err; + + return err; +} + + +int test_srtcp(void) +{ + int err = 0; + + if (!have_srtp()) { + (void)re_printf("skipping SRTCP test\n"); + return ESKIPPED; + } + + err |= test_srtcp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, RTCP_BYE); + err |= test_srtcp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_80, RTCP_BYE); + err |= test_srtcp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_32, RTCP_BYE); + err |= test_srtcp_loop(0, SRTP_AES_256_CM_HMAC_SHA1_80, RTCP_BYE); + err |= test_srtcp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_32, RTCP_BYE); + err |= test_srtcp_loop(4, SRTP_AES_CM_128_HMAC_SHA1_80, RTCP_BYE); + err |= test_srtcp_loop(0, SRTP_AES_CM_128_HMAC_SHA1_32, RTCP_RR); + if (err) + return err; + + err = test_srtcp_libsrtp(); + if (err) + return err; + + err = test_unencrypted_srtcp(); + if (err) + return err; + + err = test_srtcp_random(SRTP_AES_CM_128_HMAC_SHA1_32); + if (err) + return err; + + return err; +} + + +int test_srtp_gcm(void) +{ + int err = 0; + + err |= test_srtp_loop(0, SRTP_AES_128_GCM, 3); + if (err) + return err; + + err |= test_srtp_loop(0, SRTP_AES_256_GCM, 3); + err |= test_srtp_loop(0, SRTP_AES_256_GCM, 65530); + if (err) + return err; + + err = test_srtp_unauth(SRTP_AES_256_GCM); + if (err) + return err; + + err = test_srtp_replay(SRTP_AES_128_GCM); + if (err) + return err; + + err = test_srtp_random(SRTP_AES_128_GCM); + if (err) + return err; + + return err; +} + + +int test_srtcp_gcm(void) +{ + int err = 0; + + err |= test_srtcp_loop(0, SRTP_AES_128_GCM, RTCP_BYE); + err |= test_srtcp_loop(0, SRTP_AES_256_GCM, RTCP_BYE); + err |= test_srtcp_loop(4, SRTP_AES_128_GCM, RTCP_BYE); + err |= test_srtcp_loop(0, SRTP_AES_128_GCM, RTCP_RR); + if (err) + return err; + + err = test_srtcp_random(SRTP_AES_128_GCM); + if (err) + return err; + + return err; +} diff --git a/test/stun.c b/test/stun.c new file mode 100644 index 000000000..4ff07a91e --- /dev/null +++ b/test/stun.c @@ -0,0 +1,598 @@ +/** + * @file stun.c STUN Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_stun" +#define DEBUG_LEVEL 5 +#include + + +#define NATTED (true) + + +/* + * Test vectors from RFC 5769 + */ + +static const uint8_t tid[] = + "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"; +static const char *username = "evtj:h6vY"; +static const struct pl password = PL("VOkJxbRl1RmTxUk/WvJxBt"); + +static const unsigned char req[] = + "\x00\x01\x00\x58" + "\x21\x12\xa4\x42" + "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" + "\x80\x22\x00\x10" + "STUN test client" + "\x00\x24\x00\x04" + "\x6e\x00\x01\xff" + "\x80\x29\x00\x08" + "\x93\x2f\xf9\xb1\x51\x26\x3b\x36" + "\x00\x06\x00\x09" + "\x65\x76\x74\x6a\x3a\x68\x36\x76\x59\x20\x20\x20" + "\x00\x08\x00\x14" + "\x9a\xea\xa7\x0c\xbf\xd8\xcb\x56\x78\x1e\xf2\xb5" + "\xb2\xd3\xf2\x49\xc1\xb5\x71\xa2" + "\x80\x28\x00\x04" + "\xe5\x7a\x3b\xcf"; + +static const unsigned char respv4[] = + "\x01\x01\x00\x3c" + "\x21\x12\xa4\x42" + "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" + "\x80\x22\x00\x0b" + "\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20" + "\x00\x20\x00\x08" + "\x00\x01\xa1\x47\xe1\x12\xa6\x43" + "\x00\x08\x00\x14" + "\x2b\x91\xf5\x99\xfd\x9e\x90\xc3\x8c\x74\x89\xf9" + "\x2a\xf9\xba\x53\xf0\x6b\xe7\xd7" + "\x80\x28\x00\x04" + "\xc0\x7d\x4c\x96"; + +#ifdef HAVE_INET6 +static const unsigned char respv6[] = + "\x01\x01\x00\x48" + "\x21\x12\xa4\x42" + "\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae" + "\x80\x22\x00\x0b" + "\x74\x65\x73\x74\x20\x76\x65\x63\x74\x6f\x72\x20" + "\x00\x20\x00\x14" + "\x00\x02\xa1\x47" + "\x01\x13\xa9\xfa\xa5\xd3\xf1\x79" + "\xbc\x25\xf4\xb5\xbe\xd2\xb9\xd9" + "\x00\x08\x00\x14" + "\xa3\x82\x95\x4e\x4b\xe6\x7b\xf1\x17\x84\xc9\x7c" + "\x82\x92\xc2\x75\xbf\xe3\xed\x41" + "\x80\x28\x00\x04" + "\xc8\xfb\x0b\x4c"; +#endif + + +static const uint32_t ice_prio = 0x6e0001ff; +static const uint64_t ice_contr = 0x932ff9b151263b36ULL; +static const char *client_sw = "STUN test client"; +static const char *server_sw = "test vector"; + + +int test_stun_req(void) +{ + struct stun_msg *msg = NULL; + struct mbuf *mb; + struct stun_attr *attr; + int err; + + mb = mbuf_alloc(1024); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, + tid, NULL, + (uint8_t *)password.p, password.l, true, + 0x20, 4, + STUN_ATTR_SOFTWARE, client_sw, + STUN_ATTR_PRIORITY, &ice_prio, + STUN_ATTR_CONTROLLED, &ice_contr, + STUN_ATTR_USERNAME, username); + if (err) + goto out; + + TEST_MEMCMP(req, sizeof(req)-1, mb->buf, mb->end); + + /* Decode STUN message */ + mb->pos = 0; + err = stun_msg_decode(&msg, mb, NULL); + if (err) + goto out; + + if (STUN_CLASS_REQUEST != stun_msg_class(msg)) + goto bad; + + if (STUN_METHOD_BINDING != stun_msg_method(msg)) + goto out; + + err = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l); + if (err) + goto out; + + err = stun_msg_chk_fingerprint(msg); + if (err) + goto out; + + attr = stun_msg_attr(msg, STUN_ATTR_PRIORITY); + if (!attr || ice_prio != attr->v.priority) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_CONTROLLED); + if (!attr || ice_contr != attr->v.controlled) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_USERNAME); + if (!attr || strcmp(username, attr->v.username)) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); + if (!attr || strcmp(client_sw, attr->v.software)) + goto bad; + + goto out; + + bad: + err = EBADMSG; + + out: + mem_deref(msg); + mem_deref(mb); + return err; +} + + +static int test_resp(const struct pl *resp, const struct sa *addr) +{ + struct stun_msg *msg = NULL; + struct stun_attr *attr; + struct mbuf *mb = NULL; + int err; + + mb = mbuf_alloc(1024); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_SUCCESS_RESP, + tid, NULL, + (uint8_t *)password.p, password.l, true, + 0x20, 2, + STUN_ATTR_SOFTWARE, server_sw, + STUN_ATTR_XOR_MAPPED_ADDR, addr); + if (err) + goto out; + + if (resp->l != mb->end || + 0 != memcmp(mb->buf, resp->p, mb->end)) { + err = EBADMSG; + DEBUG_WARNING("compare failed (%J)\n", addr); + (void)re_printf("msg: [%02w]\n", mb->buf, mb->end); + (void)re_printf("ref: [%02w]\n", resp->p, resp->l); + goto out; + } + + /* Decode STUN message */ + mb->pos = 0; + err = stun_msg_decode(&msg, mb, NULL); + if (err) + goto out; + + if (STUN_CLASS_SUCCESS_RESP != stun_msg_class(msg)) + goto bad; + + if (STUN_METHOD_BINDING != stun_msg_method(msg)) + goto bad; + + err = stun_msg_chk_mi(msg, (uint8_t *)password.p, password.l); + if (err) + goto out; + + err = stun_msg_chk_fingerprint(msg); + if (err) + goto out; + + attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); + if (!attr || !sa_cmp(&attr->v.xor_mapped_addr, addr, SA_ALL)) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_SOFTWARE); + if (!attr || strcmp(server_sw, attr->v.software)) + goto bad; + + goto out; + + bad: + err = EBADMSG; + + out: + mem_deref(msg); + mem_deref(mb); + return err; +} + + +int test_stun_resp(void) +{ + struct sa maddr; + struct pl resp; + int err; + + resp.p = (char *)respv4; + resp.l = sizeof(respv4) - 1; + err = sa_set_str(&maddr, "192.0.2.1", 32853); + if (err) + return err; + err = test_resp(&resp, &maddr); + if (err) + return err; + +#ifdef HAVE_INET6 + resp.p = (char *)respv6; + resp.l = sizeof(respv6) - 1; + err = sa_set_str(&maddr, "2001:db8:1234:5678:11:2233:4455:6677", + 32853); + if (err) + return err; + err = test_resp(&resp, &maddr); +#endif + + return err; +} + + +static const unsigned char reqltc[] = + "\x00\x01\x00\x60" + "\x21\x12\xa4\x42" + "\x78\xad\x34\x33\xc6\xad\x72\xc0\x29\xda\x41\x2e" + "\x00\x06\x00\x12" + "\xe3\x83\x9e\xe3\x83\x88\xe3\x83\xaa\xe3\x83\x83" + "\xe3\x82\xaf\xe3\x82\xb9\x00\x00" + "\x00\x15\x00\x1c" + "\x66\x2f\x2f\x34\x39\x39\x6b\x39\x35\x34\x64\x36" + "\x4f\x4c\x33\x34\x6f\x4c\x39\x46\x53\x54\x76\x79" + "\x36\x34\x73\x41" + "\x00\x14\x00\x0b" + "\x65\x78\x61\x6d\x70\x6c\x65\x2e\x6f\x72\x67\x00" + "\x00\x08\x00\x14" + "\xf6\x70\x24\x65\x6d\xd6\x4a\x3e\x02\xb8\xe0\x71" + "\x2e\x85\xc9\xa2\x8c\xa8\x96\x66"; + +static const uint8_t tid_ltc[] = + "\x78\xad\x34\x33\xc6\xad\x72\xc0\x29\xda\x41\x2e"; +/* Username: "" + (without quotes) unaffected by SASLprep[RFC4013] processing */ +static const char *username_ltc = + "\xe3\x83\x9e\xe3\x83\x88\xe3\x83" + "\xaa\xe3\x83\x83\xe3\x82\xaf\xe3" + "\x82\xb9"; +/* Password: "TheMtr"" resp "TheMatrIX" (without + quotes) before resp after SASLprep processing */ +static const char *password_ltc = "TheMatrIX"; +static const char *nonce_ltc = "f//499k954d6OL34oL9FSTvy64sA"; +static const char *realm_ltc = "example.org"; + + +int test_stun_reqltc(void) +{ + struct stun_msg *msg = NULL; + struct stun_attr *attr; + struct mbuf *mb; + uint8_t md5_hash[MD5_SIZE]; + int r, err; + + mb = mbuf_alloc(1024); + if (!mb) { + err = ENOMEM; + goto out; + } + + /* use long-term credentials */ + err = md5_printf(md5_hash, "%s:%s:%s", username_ltc, realm_ltc, + password_ltc); + if (err) + goto out; + + err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, + tid_ltc, NULL, + md5_hash, sizeof(md5_hash), + false, 0x00, 3, + STUN_ATTR_USERNAME, username_ltc, + STUN_ATTR_NONCE, nonce_ltc, + STUN_ATTR_REALM, realm_ltc); + if (err) + goto out; + + r = memcmp(mb->buf, reqltc, mb->end); + if ((sizeof(reqltc)-1) != mb->end || 0 != r) { + err = EBADMSG; + DEBUG_WARNING("compare failed (r=%d)\n", r); + (void)re_printf("msg: [%02w]\n", mb->buf, mb->end); + (void)re_printf("ref: [%02w]\n", reqltc, sizeof(reqltc)-1); + goto out; + } + + /* Decode STUN message */ + mb->pos = 0; + err = stun_msg_decode(&msg, mb, NULL); + if (err) + goto out; + + if (STUN_CLASS_REQUEST != stun_msg_class(msg)) + goto bad; + + if (STUN_METHOD_BINDING != stun_msg_method(msg)) + goto bad; + + err = stun_msg_chk_mi(msg, md5_hash, sizeof(md5_hash)); + if (err) + goto out; + + if (EPROTO != stun_msg_chk_fingerprint(msg)) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_USERNAME); + if (!attr || strcmp(username_ltc, attr->v.username)) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_NONCE); + if (!attr || strcmp(nonce_ltc, attr->v.nonce)) + goto bad; + + attr = stun_msg_attr(msg, STUN_ATTR_REALM); + if (!attr || strcmp(realm_ltc, attr->v.realm)) + goto bad; + + goto out; + + bad: + err = EBADMSG; + + out: + mem_deref(msg); + mem_deref(mb); + return err; +} + + +struct test { + struct stun *stun; + struct udp_sock *us; + struct sa mapped_addr; + size_t n_resp; + int err; +}; + + +static void stun_resp_handler(int err, uint16_t scode, const char *reason, + const struct stun_msg *msg, void *arg) +{ + struct test *test = arg; + struct stun_attr *attr; + (void)reason; + + if (err) + goto out; + + ++test->n_resp; + + /* verify STUN response */ + EXPECT_EQ(0, scode); + TEST_EQUALS(0x0101, stun_msg_type(msg)); + TEST_EQUALS(STUN_CLASS_SUCCESS_RESP, stun_msg_class(msg)); + TEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg)); + TEST_EQUALS(0, stun_msg_chk_fingerprint(msg)); + + attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); + TEST_ASSERT(attr != NULL); + + test->mapped_addr = attr->v.sa; + + out: + if (err) + test->err = err; + + /* done */ + re_cancel(); +} + + +static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct test *test = arg; + (void)src; + + (void)stun_recv(test->stun, mb); +} + + +static int test_stun_request(int proto, bool natted) +{ + struct stunserver *srv = NULL; + struct stun_ctrans *ct = NULL; + struct nat *nat = NULL; + struct test test; + struct sa laddr, public_addr; + int err; + + memset(&test, 0, sizeof(test)); + + err = stunserver_alloc(&srv); + if (err) + goto out; + + err = stun_alloc(&test.stun, NULL, NULL, NULL); + if (err) + goto out; + + if (proto == IPPROTO_UDP) { + err = sa_set_str(&laddr, "127.0.0.1", 0); + TEST_ERR(err); + + err = udp_listen(&test.us, &laddr, udp_recv_handler, &test); + if (err) + goto out; + err = udp_local_get(test.us, &laddr); + TEST_ERR(err); + } + + if (natted) { + err = sa_set_str(&public_addr, "4.5.6.7", 0); + TEST_ERR(err); + + err = nat_alloc(&nat, NAT_INBOUND_SNAT, srv->us, &public_addr); + if (err) + goto out; + + sa_set_port(&public_addr, sa_port(&laddr)); + } + else if (proto == IPPROTO_UDP) { + public_addr = laddr; + } + + err = stun_request(&ct, test.stun, proto, test.us, + stunserver_addr(srv, proto), 0, + STUN_METHOD_BINDING, NULL, 0, true, + stun_resp_handler, &test, 0); + if (err) + goto out; + + TEST_ASSERT(ct != NULL); + + err = re_main_timeout(100); + if (err) + goto out; + + if (srv->err) { + err = srv->err; + goto out; + } + if (test.err) { + err = test.err; + goto out; + } + + /* verify results */ + TEST_ASSERT(srv->nrecv >= 1); + TEST_EQUALS(1, test.n_resp); + + if (proto == IPPROTO_UDP) { + TEST_SACMP(&public_addr, &test.mapped_addr, SA_ALL); + } + + out: + mem_deref(test.stun); + mem_deref(test.us); + mem_deref(nat); + mem_deref(srv); + + return err; +} + + +static int test_stun_req_attributes(void) +{ + struct stun_msg *msg = NULL; + struct mbuf *mb; + struct stun_attr *attr; + const uint64_t rsv_token = 0x1100c0ffee; + const uint32_t lifetime = 3600; + const uint16_t chan = 0x4000; + const uint8_t req_addr_fam = AF_INET; + int err; + + mb = mbuf_alloc(1024); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = stun_msg_encode(mb, STUN_METHOD_BINDING, STUN_CLASS_REQUEST, + tid, NULL, NULL, 0, false, + 0x00, 4, + STUN_ATTR_REQ_ADDR_FAMILY, &req_addr_fam, + STUN_ATTR_CHANNEL_NUMBER, &chan, + STUN_ATTR_LIFETIME, &lifetime, + STUN_ATTR_RSV_TOKEN, &rsv_token); + if (err) + goto out; + + /* Decode STUN message */ + mb->pos = 0; + err = stun_msg_decode(&msg, mb, NULL); + if (err) + goto out; + + TEST_EQUALS(STUN_CLASS_REQUEST, stun_msg_class(msg)); + TEST_EQUALS(STUN_METHOD_BINDING, stun_msg_method(msg)); + + /* verify integer attributes of different sizes */ + + /* 8-bit */ + attr = stun_msg_attr(msg, STUN_ATTR_REQ_ADDR_FAMILY); + TEST_ASSERT(attr != NULL); + TEST_EQUALS(req_addr_fam, attr->v.req_addr_family); + + /* 16-bit */ + attr = stun_msg_attr(msg, STUN_ATTR_CHANNEL_NUMBER); + TEST_ASSERT(attr != NULL); + TEST_EQUALS(chan, attr->v.channel_number); + + /* 32-bit */ + attr = stun_msg_attr(msg, STUN_ATTR_LIFETIME); + TEST_ASSERT(attr != NULL); + TEST_EQUALS(lifetime, attr->v.lifetime); + + /* 64-bit */ + attr = stun_msg_attr(msg, STUN_ATTR_RSV_TOKEN); + TEST_ASSERT(attr != NULL); + TEST_EQUALS(rsv_token, attr->v.rsv_token); + + out: + mem_deref(msg); + mem_deref(mb); + return err; +} + + +/* + * Send a STUN Binding Request to the mock STUN-Server, + * and expect a STUN Binding Response. + */ +int test_stun(void) +{ + int err = 0; + + err = test_stun_request(IPPROTO_UDP, false); + if (err) + return err; + + err = test_stun_request(IPPROTO_UDP, NATTED); + if (err) + return err; + + err = test_stun_request(IPPROTO_TCP, false); + if (err) + return err; + + err = test_stun_req_attributes(); + if (err) + return err; + + return err; +} diff --git a/test/sys.c b/test/sys.c new file mode 100644 index 000000000..ab1bafa6b --- /dev/null +++ b/test/sys.c @@ -0,0 +1,206 @@ +/** + * @file sys.c System Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "test.h" + + +#define DEBUG_MODULE "test_sys" +#define DEBUG_LEVEL 4 +#include + + +int test_sys_endian(void) +{ + uint16_t s_le, s_ho; + uint8_t *s = (uint8_t *)&s_le; + uint32_t l_le, l_ho; + uint8_t *l = (uint8_t *)&l_le; + uint64_t ll0, ll1 = 0x0102030405060708ULL; + + /* Little endian: LSB first - 0x1234 + * + * 0x0000: 0x34 + * 0x0001: 0x12 + */ + + s[0] = 0x34; + s[1] = 0x12; + + s_ho = sys_ltohs(s_le); + if (0x1234 != s_ho) { + DEBUG_WARNING("endian short: 0x%04x\n", s_ho); + return EINVAL; + } + + if (s_le != sys_htols(s_ho)) { + DEBUG_WARNING("sys_htols failed: 0x%04x\n", sys_htols(s_ho)); + return EINVAL; + } + + /* 0x12345678 + * + * 0x0000: 0x78 + * 0x0001: 0x56 + * 0x0002: 0x34 + * 0x0003: 0x12 + */ + + l[0] = 0x78; + l[1] = 0x56; + l[2] = 0x34; + l[3] = 0x12; + + l_ho = sys_ltohl(l_le); + if (0x12345678 != l_ho) { + DEBUG_WARNING("endian long: 0x%08x\n", l_ho); + return EINVAL; + } + + if (l_le != sys_htoll(l_ho)) { + DEBUG_WARNING("sys_htoll failed: 0x%08x\n", sys_htoll(l_ho)); + return EINVAL; + } + + /* Test 64-bit */ + ll0 = sys_ntohll(sys_htonll(ll1)); + + if (ll0 != ll1) { + DEBUG_WARNING("endian long-long: 0x%llx\n", ll0); + return EINVAL; + } + + return 0; +} + + +int test_sys_rand(void) +{ + char str[64]; + uint8_t buf[64]; + size_t i; + int err = 0; + + volatile uint16_t u16 = rand_u16(); + volatile uint32_t u32 = rand_u32(); + volatile uint64_t u64 = rand_u64(); + char ch = rand_char(); + + (void)u16; + (void)u32; + (void)u64; + + TEST_ASSERT(ch > 0); + TEST_ASSERT(isprint(ch)); + + rand_str(str, sizeof(str)); + rand_bytes(buf, sizeof(buf)); + + for (i = 0; i < (sizeof(str)-1); i++) { + TEST_ASSERT(str[i] > 0); + TEST_ASSERT(isprint(str[i])); + } + + out: + return err; +} + + +int test_sys_fs_isdir(void) +{ + int err = 0; + bool ret; + char path[256]; + char file[256]; + char *wpath = "/some/path/to/nothing"; + + re_snprintf(path, sizeof(path), "%s", test_datapath()); + re_snprintf(file, sizeof(file), "%s/menu.json", test_datapath()); + + ret = fs_isdir(path); + TEST_EQUALS(true, ret); + + ret = fs_isdir(NULL); + TEST_EQUALS(false, ret); + + ret = fs_isdir(wpath); + TEST_EQUALS(false, ret); + + ret = fs_isdir(file); + TEST_EQUALS(false, ret); + + out: + return err; +} + + +int test_sys_fs_isfile(void) +{ + int err = 0; + bool ret; + char path[256]; + char file[256]; + char *wpath = "/some/path/to/nothing"; + + re_snprintf(path, sizeof(path), "%s", test_datapath()); + re_snprintf(file, sizeof(file), "%s/menu.json", test_datapath()); + + ret = fs_isfile(file); + TEST_EQUALS(true, ret); + + ret = fs_isfile(NULL); + TEST_EQUALS(false, ret); + + ret = fs_isfile(wpath); + TEST_EQUALS(false, ret); + + ret = fs_isfile(path); + TEST_EQUALS(false, ret); + + out: + return err; +} + + +int test_sys_fs_fopen(void) +{ + char filename[256]; + FILE *file; + int err; + + /* Use a unique filename to avoid clash when running + * multiple instances of test + */ + re_snprintf(filename, sizeof(filename), + "retest_fs_fopen-%llu", rand_u64()); + + err = fs_fopen(&file, filename, "w+"); + TEST_ERR(err); + TEST_EQUALS(true, fs_isfile(filename)); + + err = fclose(file); + TEST_ERR(err); + + /* Try reopen */ + err = fs_fopen(&file, filename, "w+"); + TEST_ERR(err); + + err = fclose(file); + TEST_ERR(err); + +#ifdef WIN32 + (void)_unlink(filename); +#else + (void)unlink(filename); +#endif + +out: + return err; +} diff --git a/test/tcp.c b/test/tcp.c new file mode 100644 index 000000000..2619da449 --- /dev/null +++ b/test/tcp.c @@ -0,0 +1,229 @@ +/** + * @file tcp.c TCP testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "tcptest" +#define DEBUG_LEVEL 5 +#include + + +/* + * .------. .------. + * |Client| |Server| + * '------' '------' + * + * <------ tcp_listen() + * tcp_connect() ---> + * ------------- TCP [SYN] -----------> + * -----> tcp_conn_h + * + * <----- tcp_accept() + * <-------- TCP [SYN, ACK] ---------- + * --------- TCP [ACK] --------------> + * tcp_estab_h <--- + * + * tcp_send() ===> + * ======= TCP [PSH, ACK] ==========> + * <======= TCP [ACK] =============== + * =====> tcp_recv_h + */ + +struct tcp_test { + struct tcp_sock *ts; + struct tcp_conn *tc; + struct tcp_conn *tc2; + int err; +}; + + +static const char *ping = "ping from client to server\n"; +static const char *pong = "pong from server 2 client\n"; + + +static void destructor(void *arg) +{ + struct tcp_test *tt = arg; + + mem_deref(tt->tc2); + mem_deref(tt->tc); + mem_deref(tt->ts); +} + + +static void abort_test(struct tcp_test *tt, int err) +{ + if (err) { + tt->err = err; + } + + re_cancel(); +} + + +static int send_data(struct tcp_conn *tc, const char *data) +{ + struct mbuf mb; + int err; + + mbuf_init(&mb); + + err = mbuf_write_str(&mb, data); + if (err) + goto out; + + mb.pos = 0; + err = tcp_send(tc, &mb); + if (err) + goto out; + + out: + mbuf_reset(&mb); + return err; +} + + +static bool mbuf_compare(const struct mbuf *mb, const char *str) +{ + if (mbuf_get_left(mb) != strlen(str)) { + DEBUG_WARNING("compare: mbuf=%u str=%u (bytes)\n", + mbuf_get_left(mb), strlen(str)); + return false; + } + + if (0 != memcmp(mbuf_buf(mb), str, strlen(str))) { + DEBUG_WARNING("compare: mbuf=[%b] str=[%s]\n", + mbuf_buf(mb), mbuf_get_left(mb), str); + return false; + } + + return true; +} + + +static void tcp_server_recv_handler(struct mbuf *mb, void *arg) +{ + struct tcp_test *tt = arg; + int err; + + DEBUG_INFO("Server: TCP Receive data (%u bytes)\n", + mbuf_get_left(mb)); + + if (!mbuf_compare(mb, ping)) { + abort_test(tt, EBADMSG); + return; + } + + err = send_data(tt->tc2, pong); + if (err) + abort_test(tt, err); +} + + +static void tcp_server_close_handler(int err, void *arg) +{ + struct tcp_test *tt = arg; + abort_test(tt, err); +} + + +static void tcp_server_conn_handler(const struct sa *peer, void *arg) +{ + struct tcp_test *tt = arg; + int err; + + (void)peer; + + DEBUG_INFO("Server: Incoming CONNECT from %J\n", peer); + + err = tcp_accept(&tt->tc2, tt->ts, NULL, tcp_server_recv_handler, + tcp_server_close_handler, tt); + if (err) { + abort_test(tt, err); + return; + } +} + + +static void tcp_client_estab_handler(void *arg) +{ + struct tcp_test *tt = arg; + int err; + + DEBUG_INFO("Client: TCP Established\n"); + + err = send_data(tt->tc, ping); + if (err) + abort_test(tt, err); +} + + +static void tcp_client_recv_handler(struct mbuf *mb, void *arg) +{ + struct tcp_test *tt = arg; + + DEBUG_INFO("Client: TCP receive: %u bytes\n", mbuf_get_left(mb)); + + if (!mbuf_compare(mb, pong)) { + abort_test(tt, EBADMSG); + return; + } + + abort_test(tt, 0); +} + + +static void tcp_client_close_handler(int err, void *arg) +{ + struct tcp_test *tt = arg; + DEBUG_NOTICE("Client: TCP Close (%m)\n", err); + + abort_test(tt, err); +} + + +int test_tcp(void) +{ + struct tcp_test *tt; + struct sa srv; + int err; + + tt = mem_zalloc(sizeof(*tt), destructor); + if (!tt) + return ENOMEM; + + err = sa_set_str(&srv, "127.0.0.1", 0); + if (err) + goto out; + + err = tcp_listen(&tt->ts, &srv, tcp_server_conn_handler, tt); + if (err) + goto out; + + err = tcp_local_get(tt->ts, &srv); + if (err) + goto out; + + err = tcp_connect(&tt->tc, &srv, tcp_client_estab_handler, + tcp_client_recv_handler, tcp_client_close_handler, + tt); + if (err) + goto out; + + err = re_main_timeout(500); + if (err) + goto out; + + if (tt->err) + err = tt->err; + + out: + mem_deref(tt); + + return err; +} diff --git a/test/telev.c b/test/telev.c new file mode 100644 index 000000000..250eb59b6 --- /dev/null +++ b/test/telev.c @@ -0,0 +1,77 @@ +/** + * @file telev.c Testcode for Telephone-event + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +int test_telev(void) +{ + static const char digits[] = "1234567890ABCD*#"; + struct telev *tlv = NULL; + struct mbuf *mb; + bool marker, expect_end = false; + char digit; + size_t i; + int err; + + mb = mbuf_alloc(512); + if (!mb) + return ENOMEM; + + err = telev_alloc(&tlv, 1); + if (err) + goto out; + + /* Encode all digits */ + for (i=0; ipos = 0; + i = 0; + while (mbuf_get_left(mb) && i +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_IO_H +#include +#endif +#include +#include +#ifdef HAVE_SYS_TIME_H +#include +#endif +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test" +#define DEBUG_LEVEL 5 +#include + + +#ifdef WIN32 +#define open _open +#define read _read +#define write _write +#define close _close +#define dup _dup +#define dup2 _dup2 +#define fileno _fileno +#endif + + +typedef int (test_exec_h)(void); + +struct test { + test_exec_h *exec; + const char *name; +}; + +#define TEST(a) {a, #a} + +static const struct test tests[] = { + TEST(test_aac), + TEST(test_aes), + TEST(test_aes_gcm), + TEST(test_aubuf), + TEST(test_aulevel), + TEST(test_auresamp), + TEST(test_async), + TEST(test_av1), + TEST(test_base64), + TEST(test_bfcp), + TEST(test_bfcp_bin), + TEST(test_bfcp_udp), + TEST(test_bfcp_tcp), + TEST(test_conf), + TEST(test_crc32), + TEST(test_dns_hdr), + TEST(test_dns_rr), + TEST(test_dns_dname), + TEST(test_dsp), +#ifdef USE_TLS + TEST(test_dtls), + TEST(test_dtls_srtp), +#endif + TEST(test_dtmf), + TEST(test_fir), + TEST(test_fmt_gmtime), + TEST(test_fmt_human_time), + TEST(test_fmt_timestamp), + TEST(test_fmt_param), + TEST(test_fmt_pl), + TEST(test_fmt_pl_u32), + TEST(test_fmt_pl_u64), + TEST(test_fmt_pl_i32), + TEST(test_fmt_pl_i64), + TEST(test_fmt_pl_x3264), + TEST(test_fmt_pl_float), + TEST(test_fmt_print), + TEST(test_fmt_regex), + TEST(test_fmt_snprintf), + TEST(test_fmt_str), + TEST(test_fmt_str_bool), + TEST(test_fmt_str_error), + TEST(test_fmt_str_itoa), + TEST(test_fmt_unicode), + TEST(test_fmt_unicode_decode), + TEST(test_g711_alaw), + TEST(test_g711_ulaw), + TEST(test_h264), + TEST(test_h264_sps), + TEST(test_h264_packet), + TEST(test_h265), + TEST(test_h265_packet), + TEST(test_hash), + TEST(test_hmac_sha1), + TEST(test_hmac_sha256), + TEST(test_http), + TEST(test_http_loop), + TEST(test_http_large_body), + TEST(test_http_conn), + TEST(test_http_conn_large_body), +#ifdef USE_TLS + TEST(test_https_loop), + TEST(test_http_client_set_tls), + TEST(test_https_large_body), +#endif + TEST(test_httpauth_chall), + TEST(test_httpauth_resp), + TEST(test_ice_cand), + TEST(test_ice_loop), + TEST(test_jbuf), + TEST(test_jbuf_adaptive), + TEST(test_json), + TEST(test_json_file), + TEST(test_json_unicode), + TEST(test_json_bad), + TEST(test_json_array), + TEST(test_list), + TEST(test_list_flush), + TEST(test_list_ref), + TEST(test_list_sort), + TEST(test_mbuf), + TEST(test_md5), + TEST(test_mem), + TEST(test_mem_reallocarray), + TEST(test_mem_secure), + TEST(test_net_if), + TEST(test_mqueue), + TEST(test_odict), + TEST(test_odict_array), + TEST(test_pcp), + TEST(test_remain), + TEST(test_rtmp_play), + TEST(test_rtmp_publish), +#ifdef USE_TLS + TEST(test_rtmps_publish), +#endif + TEST(test_rtp), + TEST(test_rtpext), + TEST(test_rtcp_encode), + TEST(test_rtcp_encode_afb), + TEST(test_rtcp_decode), + TEST(test_rtcp_packetloss), + TEST(test_rtcp_twcc), + TEST(test_sa_class), + TEST(test_sa_cmp), + TEST(test_sa_decode), + TEST(test_sa_ntop), + TEST(test_sa_pton), + TEST(test_sa_pton_linklocal), + TEST(test_sdp_all), + TEST(test_sdp_bfcp), + TEST(test_sdp_parse), + TEST(test_sdp_oa), + TEST(test_sdp_extmap), + TEST(test_sdp_disabled_rejected), + TEST(test_sha1), + TEST(test_sip_addr), + TEST(test_sip_auth), + TEST(test_sip_drequestf), + TEST(test_sip_apply), + TEST(test_sip_hdr), + TEST(test_sip_param), + TEST(test_sip_parse), + TEST(test_sip_via), +#ifdef USE_TLS + TEST(test_sip_transp_add_client_cert), +#endif + TEST(test_sipevent), + TEST(test_sipsess), + TEST(test_sipsess_reject), + TEST(test_sipsess_blind_transfer), + TEST(test_sipsess_100rel_caller_require), + TEST(test_sipsess_100rel_supported), + TEST(test_sipsess_100rel_answer_not_allowed), + TEST(test_sipsess_100rel_420), + TEST(test_sipsess_100rel_421), + TEST(test_sipsess_update_uac), + TEST(test_sipsess_update_uas), + TEST(test_srtp), + TEST(test_srtcp), + TEST(test_srtp_gcm), + TEST(test_srtcp_gcm), + TEST(test_stun_req), + TEST(test_stun_resp), + TEST(test_stun_reqltc), + TEST(test_stun), + TEST(test_sys_endian), + TEST(test_sys_rand), + TEST(test_sys_fs_isdir), + TEST(test_sys_fs_isfile), + TEST(test_sys_fs_fopen), + TEST(test_tcp), + TEST(test_telev), +#ifdef USE_TLS + TEST(test_tls), + TEST(test_tls_ec), + TEST(test_tls_selfsigned), + TEST(test_tls_certificate), + TEST(test_tls_false_cafile_path), + TEST(test_tls_cli_conn_change_cert), + TEST(test_tls_session_reuse_tls_v12), + TEST(test_tls_sni), +#endif + TEST(test_trice_cand), + TEST(test_trice_candpair), + TEST(test_trice_checklist), + TEST(test_trice_loop), + TEST(test_try_into), + TEST(test_turn), + TEST(test_turn_tcp), + TEST(test_udp), + TEST(test_unixsock), + TEST(test_uri), + TEST(test_uri_encode), + TEST(test_uri_headers), + TEST(test_uri_user), + TEST(test_uri_params_headers), + TEST(test_uri_escape), + TEST(test_vid), + TEST(test_vidconv), + TEST(test_vidconv_scaling), + TEST(test_vidconv_pixel_formats), + TEST(test_websock), + TEST(test_trace), + TEST(test_thread), + +#ifdef USE_TLS + /* combination tests: */ + TEST(test_dtls_turn), +#endif +}; + + +static const struct test tests_integration[] = { + TEST(test_dns_cache_http_integration), + TEST(test_dns_http_integration), + TEST(test_dns_integration), + TEST(test_net_dst_source_addr_get), + TEST(test_rtp_listen), + TEST(test_sip_drequestf_network), + TEST(test_sipevent_network), + TEST(test_sipreg_tcp), +#ifdef USE_TLS + TEST(test_sipreg_tls), +#endif + TEST(test_sipreg_udp), + TEST(test_tmr_jiffies), + TEST(test_tmr_jiffies_usec), + TEST(test_turn_thread), +}; + + +#ifdef DATA_PATH +static char datapath[256] = DATA_PATH; +#else +static char datapath[256] = "./data"; +#endif + + +static uint32_t timeout_override; +static int dup_stdout; +static int dup_stderr; + +enum test_mode test_mode = TEST_NONE; + + +static void hide_output(void) +{ + dup_stdout = dup(fileno(stdout)); + dup_stderr = dup(fileno(stderr)); + int fd_out = open("stdout.out", O_WRONLY | O_CREAT); + int fd_err = open("stderr.out", O_WRONLY | O_CREAT); + (void)dup2(fd_out, fileno(stdout)); + (void)dup2(fd_err, fileno(stderr)); +} + + +static void restore_output(int err) +{ + FILE *f = NULL; + char line[1024]; + + fflush(stdout); + fflush(stderr); + + /* Restore stdout/stderr */ + (void)dup2(dup_stdout, fileno(stdout)); + (void)dup2(dup_stderr, fileno(stderr)); + + if (!err) + goto out; + + f = fopen("stdout.out", "r"); + if (!f) + goto out; + + while (fgets(line, sizeof(line), f)) { + re_fprintf(stdout, "%s", line); + } + (void)fclose(f); + + + f = fopen("stderr.out", "r"); + if (!f) + goto out; + + while (fgets(line, sizeof(line), f)) { + re_fprintf(stderr, "%s", line); + } + (void)fclose(f); + +out: +#ifdef WIN32 + (void)_unlink("stdout.out"); + (void)_unlink("stderr.out"); +#else + (void)unlink("stdout.out"); + (void)unlink("stderr.out"); +#endif +} + + +static const struct test *find_test(const char *name) +{ + size_t i; + + for (i=0; i time + */ +static int testcase_oom(const struct test *test, int levels, bool verbose) +{ + int i; + int err = 0; + + if (verbose) + (void)re_fprintf(stderr, " %-26s: ", test->name); + + /* All memory levels */ + for (i=0; iexec(); + if (err == 0) { + /* success, stop now */ + break; + } + else if (err == ENOMEM) { + /* OOM, as expected */ + err = 0; + } + else if (err == ETIMEDOUT) { + /* test timed out, stop now */ + err = 0; + goto out; + } + else if (err == ENOSYS) { + err = 0; + break; + } + else if (err == ESKIPPED) { + err = 0; + break; + } + else { + DEBUG_WARNING("oom: %s: unexpected error code at" + " %d blocks free (%m)\n", + test->name, i, err); + goto out; + } + } + + out: + if (verbose) + (void)re_fprintf(stderr, "oom max %d\n", i); + + return err; +} + + +int test_oom(const char *name, bool verbose) +{ + size_t i; + const int levels = 64; + int err = 0; + + test_mode = TEST_MEMORY; + + if (!verbose) + hide_output(); + + (void)re_fprintf(stderr, "oom tests %u levels: \n", levels); + + if (name) { + const struct test *test = find_test(name); + if (!test) { + (void)re_fprintf(stderr, "no such test: %s\n", name); + err = ENOENT; + goto out; + } + + err = testcase_oom(test, levels, verbose); + } + else { + /* All test cases */ + for (i=0; iexec(); + if (err) { + DEBUG_WARNING("%s: test failed (%m)\n", name, err); + goto out; + } + } + else { + unsigned n_skipped = 0; + + for (i=0; iexec(); + if (err) + return err; + + usec_stop = tmr_jiffies_usec(); + + if ((usec_stop - usec_start) > DRYRUN_USEC) + break; + } + + usec_avg = 1.0 * (usec_stop - usec_start) / (double)i; + + n = usec_avg ? (size_t)(REPEATS_USEC / usec_avg) : 0; + n = min(REPEATS_MAX, max(n, REPEATS_MIN)); + + /* now for the real measurement */ + usec_start = tmr_jiffies_usec(); + for (i=0; iexec(); + if (err) + return err; + } + usec_stop = tmr_jiffies_usec(); + + if (usec_stop <= usec_start) { + DEBUG_WARNING("perf: cannot measure, test is too fast\n"); + return EINVAL; + } + + usec_avg = (1.0 * (usec_stop - usec_start)) / i; + + if (usec_avgp) + *usec_avgp = usec_avg; + + re_printf("%-32s: %10.2f usec [%6u repeats]\n", + test->name, usec_avg, i); + + return 0; +} + + +struct timing { + const struct test *test; + uint64_t nsec_avg; +}; + + +/* + * The comparison function must return an integer less than, equal to, + * or greater than zero if the first argument is considered to be + * respectively less than, equal to, or greater than the second. + * + * If two members compare as equal, their order in the sorted array + * is undefined. + */ +static int timing_cmp(const void *p1, const void *p2) +{ + const struct timing *v1 = p1; + const struct timing *v2 = p2; + + if (v1->nsec_avg < v2->nsec_avg) + return 1; + else if (v1->nsec_avg > v2->nsec_avg) + return -1; + else + return 0; +} + + +int test_perf(const char *name, bool verbose) +{ + int err = 0; + unsigned i; + (void)verbose; + + test_mode = TEST_PERF; + + if (name) { + const struct test *test; + + test = find_test(name); + if (!test) { + (void)re_fprintf(stderr, "no such test: %s\n", name); + return ENOENT; + } + + err = testcase_perf(test, NULL); + if (err) + return err; + } + else { + struct timing timingv[RE_ARRAY_SIZE(tests)]; + + memset(&timingv, 0, sizeof(timingv)); + + /* All test cases */ + for (i=0; itest = &tests[i]; + + err = testcase_perf(&tests[i], + &usec_avg); + + if (!verbose) + restore_output(err); + + if (err) { + if (err == ESKIPPED || err == ENOSYS) { + re_printf("skipped: %s\n", + tests[i].name); + tim->test = NULL; + continue; + } + DEBUG_WARNING("perf: %s failed (%m)\n", + tests[i].name, err); + return err; + } + + tim->nsec_avg = (uint64_t)(1000.0 * usec_avg); + } + + /* sort the timing table by average time */ + qsort(timingv, RE_ARRAY_SIZE(timingv), sizeof(timingv[0]), + timing_cmp); + + re_fprintf(stderr, + "\nsorted by average timing (slowest on top):\n"); + + for (i=0; insec_avg / 1000.0; + + if (!tim->test) + continue; + + re_fprintf(stderr, "%-34s: %10.2f usec\n", + tim->test->name, usec_avg); + } + re_fprintf(stderr, "\n"); + } + + return err; +} + + +int test_reg(const char *name, bool verbose) +{ + int err; + + test_mode = TEST_REGULAR; + + timeout_override = 10000; + + (void)re_fprintf(stderr, "regular tests: "); + err = test_unit(name, verbose); + if (err) + return err; + (void)re_fprintf(stderr, "\x1b[32mOK\x1b[;m\n"); + + timeout_override = 0; + + return err; +} + + +struct thread { + const struct test *test; + thrd_t tid; + int err; +}; + + +static int thread_handler(void *arg) +{ + struct thread *thr = arg; + int err; + + err = re_thread_init(); + if (err) { + DEBUG_WARNING("thread: re_thread_init failed %m\n", err); + thr->err = err; + return 0; + } + + err = thr->test->exec(); + if (err) { + if (err == ESKIPPED) { + err = 0; + } + else { + DEBUG_WARNING("%s: test failed (%m)\n", + thr->test->name, err); + } + } + + re_thread_close(); + + /* safe to write it, main thread is waiting for us */ + thr->err = err; + + return 0; +} + + +/* Run all test-cases in multiple threads */ +int test_multithread(void) +{ +#define NUM_REPEAT 2 +#define NUM_TOTAL (NUM_REPEAT * RE_ARRAY_SIZE(tests)) + + struct thread threadv[NUM_TOTAL]; + size_t test_index=0; + size_t i; + int err = 0; + + test_mode = TEST_THREAD; + + timeout_override = 15000; + + memset(threadv, 0, sizeof(threadv)); + + (void)re_fprintf(stderr, "multithread: %u tests" + " with %d repeats (total %u threads): ", + RE_ARRAY_SIZE(tests), NUM_REPEAT, NUM_TOTAL); + + for (i=0; iname, + threadv[i].err, threadv[i].err); + err = threadv[i].err; + } + } + + if (err) + return err; + (void)re_fprintf(stderr, "\x1b[32mOK\x1b[;m\n"); + + timeout_override = 0; + + return err; +} + + +void test_listcases(void) +{ + size_t i, n, nh; + + n = RE_ARRAY_SIZE(tests); + nh = (n+1)/2; + + (void)re_printf("\n%u test cases:\n", n); + + for (i=0; i= alen; + + if (wrong) + (void)re_fprintf(f, "\x1b[35m"); + (void)re_fprintf(f, " %02x", ebuf[pos]); + if (wrong) + (void)re_fprintf(f, "\x1b[;m"); + } + else + (void)re_fprintf(f, " "); + } + + (void)re_fprintf(f, " "); + + for (j=0; jname) + return EINVAL; + + (void)re_fprintf(stderr, " %-24s: ", test->name); + + if (test->exec) + err = test->exec(); + + if (err) + DEBUG_WARNING(" %-24s: NOK: %m\n", test->name, err); + else + (void)re_fprintf(stderr, "\x1b[32mOK\x1b[;m\n"); + + return err; + } + + for (i=0; iname) + continue; + + (void)re_fprintf(stderr, " %-28s: ", test->name); + + if (test->exec) + err = test->exec(); + + if (err) { + DEBUG_WARNING(" %-24s: NOK: %m\n", test->name, err); + break; + } + else { + (void)re_fprintf(stderr, "\x1b[32mOK\x1b[;m\t\n"); + } + } + + return err; +} diff --git a/test/test.h b/test/test.h new file mode 100644 index 000000000..9c32cddc8 --- /dev/null +++ b/test/test.h @@ -0,0 +1,541 @@ +/** + * @file test.h Interface to regression testcode + * + * Copyright (C) 2010 Creytiv.com + */ + +enum test_mode { + TEST_NONE, + TEST_REGULAR, + TEST_MEMORY, + TEST_PERF, + TEST_THREAD +}; + +/* + * Global test mode + */ +extern enum test_mode test_mode; + +/* + * Special negative error code for a skipped test + */ +#define ESKIPPED (-1000) + +#define TEST_EINVAL(func, ...) \ + err = func(__VA_ARGS__); \ + if (err != EINVAL) \ + goto out; + +#define TEST_EQUALS(expected, actual) \ + if ((expected) != (actual)) { \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_EQUALS: %s:%u: %s():" \ + " expected=%d(0x%x), actual=%d(0x%x)\n", \ + __FILE__, __LINE__, __func__, \ + (expected), (expected), \ + (actual), (actual)); \ + err = EINVAL; \ + goto out; \ + } + +#define TEST_NOT_EQUALS(expected, actual) \ + if ((expected) == (actual)) { \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_NOT_EQUALS: %s:%u:" \ + " expected=%d != actual=%d\n", \ + __FILE__, __LINE__, \ + (expected), (actual)); \ + err = EINVAL; \ + goto out; \ + } + +#define TEST_MEMCMP(expected, expn, actual, actn) \ + if (expn != actn || \ + 0 != memcmp((expected), (actual), (expn))) { \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_MEMCMP: %s:%u:" \ + " %s(): failed\n", \ + __FILE__, __LINE__, __func__); \ + test_hexdump_dual(stderr, \ + expected, expn, \ + actual, actn); \ + err = EINVAL; \ + goto out; \ + } + +#define TEST_STRCMP(expected, expn, actual, actn) \ + if (expn != actn || \ + 0 != memcmp((expected), (actual), (expn))) { \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_STRCMP: %s:%u:" \ + " failed\n", \ + __FILE__, __LINE__); \ + (void)re_fprintf(stderr, \ + "expected string: (%zu bytes)\n" \ + "\"%b\"\n", \ + (size_t)(expn), \ + (expected), (size_t)(expn)); \ + (void)re_fprintf(stderr, \ + "actual string: (%zu bytes)\n" \ + "\"%b\"\n", \ + (size_t)(actn), \ + (actual), (size_t)(actn)); \ + err = EINVAL; \ + goto out; \ + } + +#define TEST_ASSERT(actual) \ + if (!(actual)) { \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_ASSERT: %s:%u:" \ + " actual=%d\n", \ + __FILE__, __LINE__, \ + (actual)); \ + err = EINVAL; \ + goto out; \ + } + +#define TEST_ERR(err) \ + if ((err)) { \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_ERR: %s:%u:" \ + " (%m)\n", \ + __FILE__, __LINE__, \ + (err)); \ + goto out; \ + } + +#define TEST_SACMP(expect, actual, flags) \ + if (!sa_cmp((expect), (actual), (flags))) { \ + \ + (void)re_fprintf(stderr, "\n"); \ + DEBUG_WARNING("TEST_SACMP: %s:%u:" \ + " %s(): failed\n", \ + __FILE__, __LINE__, __func__); \ + DEBUG_WARNING("expected: %J\n", (expect)); \ + DEBUG_WARNING("actual: %J\n", (actual)); \ + err = EADDRNOTAVAIL; \ + goto out; \ + } + +/* + * NOTE: try to reuse macros from Gtest. + */ + +#define ASSERT_EQ(expected, actual) \ + if ((expected) != (actual)) { \ + DEBUG_WARNING("ASSERT_EQ: %s:%u: %s():" \ + " expected=%d(0x%x), actual=%d(0x%x)\n", \ + __FILE__, __LINE__, __func__, \ + (expected), (expected), \ + (actual), (actual)); \ + err = EINVAL; \ + goto out; \ + } + +#define ASSERT_DOUBLE_EQ(expected, actual, prec) \ + if (!test_cmp_double((expected), (actual), (prec))) { \ + DEBUG_WARNING("selftest: ASSERT_DOUBLE_EQ: %s:%u:" \ + " expected=%f, actual=%f\n", \ + __FILE__, __LINE__, \ + (double)(expected), (double)(actual)); \ + err = EINVAL; \ + goto out; \ + } + +#define ASSERT_TRUE(cond) \ + if (!(cond)) { \ + DEBUG_WARNING("ASSERT_TRUE: %s:%u:\n", \ + __FILE__, __LINE__); \ + err = EINVAL; \ + goto out; \ + } + +#define EXPECT_EQ ASSERT_EQ +#define EXPECT_TRUE ASSERT_TRUE + + +/* Module API */ +int test_aac(void); +int test_aes(void); +int test_aes_gcm(void); +int test_aubuf(void); +int test_aulevel(void); +int test_auresamp(void); +int test_async(void); +int test_av1(void); +int test_base64(void); +int test_bfcp(void); +int test_bfcp_bin(void); +int test_bfcp_udp(void); +int test_bfcp_tcp(void); +int test_conf(void); +int test_crc32(void); +int test_dns_hdr(void); +int test_dns_integration(void); +int test_dns_rr(void); +int test_dns_dname(void); +int test_dsp(void); +int test_dtmf(void); +int test_fir(void); +int test_fmt_gmtime(void); +int test_fmt_timestamp(void); +int test_fmt_human_time(void); +int test_fmt_param(void); +int test_fmt_pl(void); +int test_fmt_pl_i32(void); +int test_fmt_pl_i64(void); +int test_fmt_pl_u32(void); +int test_fmt_pl_u64(void); +int test_fmt_pl_x3264(void); +int test_fmt_pl_float(void); +int test_fmt_print(void); +int test_fmt_regex(void); +int test_fmt_snprintf(void); +int test_fmt_str(void); +int test_fmt_str_bool(void); +int test_fmt_str_error(void); +int test_fmt_str_itoa(void); +int test_fmt_unicode(void); +int test_fmt_unicode_decode(void); +int test_g711_alaw(void); +int test_g711_ulaw(void); +int test_h264(void); +int test_h264_sps(void); +int test_h264_packet(void); +int test_h265(void); +int test_h265_packet(void); +int test_hash(void); +int test_hmac_sha1(void); +int test_hmac_sha256(void); +int test_http(void); +int test_http_loop(void); +int test_http_large_body(void); +int test_http_conn(void); +int test_http_conn_large_body(void); +int test_dns_http_integration(void); +int test_dns_cache_http_integration(void); +#ifdef USE_TLS +int test_https_loop(void); +int test_http_client_set_tls(void); +int test_https_large_body(void); +#endif +int test_httpauth_chall(void); +int test_httpauth_resp(void); +int test_ice_loop(void); +int test_ice_cand(void); +int test_jbuf(void); +int test_jbuf_adaptive(void); +int test_json(void); +int test_json_bad(void); +int test_json_file(void); +int test_json_unicode(void); +int test_json_array(void); +int test_list(void); +int test_list_flush(void); +int test_list_ref(void); +int test_list_sort(void); +int test_mbuf(void); +int test_md5(void); +int test_mem(void); +int test_mem_reallocarray(void); +int test_mem_secure(void); +int test_mqueue(void); +int test_net_if(void); +int test_net_dst_source_addr_get(void); +int test_odict(void); +int test_odict_array(void); +int test_pcp(void); +int test_trice_cand(void); +int test_trice_candpair(void); +int test_trice_checklist(void); +int test_trice_loop(void); +int test_remain(void); +int test_rtmp_play(void); +int test_rtmp_publish(void); +#ifdef USE_TLS +int test_rtmps_publish(void); +#endif +int test_rtp(void); +int test_rtp_listen(void); +int test_rtpext(void); +int test_rtcp_encode(void); +int test_rtcp_encode_afb(void); +int test_rtcp_decode(void); +int test_rtcp_packetloss(void); +int test_rtcp_twcc(void); +int test_sa_class(void); +int test_sa_cmp(void); +int test_sa_decode(void); +int test_sa_ntop(void); +int test_sa_pton(void); +int test_sa_pton_linklocal(void); +int test_sdp_all(void); +int test_sdp_bfcp(void); +int test_sdp_parse(void); +int test_sdp_oa(void); +int test_sdp_extmap(void); +int test_sdp_disabled_rejected(void); +int test_sha1(void); +int test_sip_addr(void); +int test_sip_auth(void); +int test_sip_drequestf(void); +int test_sip_apply(void); +int test_sip_hdr(void); +int test_sip_msg(void); +int test_sip_param(void); +int test_sip_parse(void); +int test_sip_via(void); +#ifdef USE_TLS +int test_sip_transp_add_client_cert(void); +#endif +int test_sipevent(void); +int test_sipreg_udp(void); +int test_sipreg_tcp(void); +#ifdef USE_TLS +int test_sipreg_tls(void); +#endif +int test_sipsess(void); +int test_sipsess_reject(void); +int test_sipsess_blind_transfer(void); +int test_sipsess_100rel_caller_require(void); +int test_sipsess_100rel_supported(void); +int test_sipsess_100rel_answer_not_allowed(void); +int test_sipsess_100rel_420(void); +int test_sipsess_100rel_421(void); +int test_sipsess_update_uac(void); +int test_sipsess_update_uas(void); +int test_srtp(void); +int test_srtcp(void); +int test_srtp_gcm(void); +int test_srtcp_gcm(void); +int test_stun_req(void); +int test_stun_resp(void); +int test_stun_reqltc(void); +int test_stun(void); +int test_sys_endian(void); +int test_sys_rand(void); +int test_sys_fs_isdir(void); +int test_sys_fs_isfile(void); +int test_sys_fs_fopen(void); +int test_tcp(void); +int test_telev(void); +int test_thread(void); +int test_tmr_jiffies(void); +int test_tmr_jiffies_usec(void); +int test_try_into(void); +int test_turn(void); +int test_turn_tcp(void); +int test_turn_thread(void); +int test_udp(void); +int test_unixsock(void); +int test_uri(void); +int test_uri_encode(void); +int test_uri_headers(void); +int test_uri_user(void); +int test_uri_params_headers(void); +int test_uri_escape(void); +int test_vid(void); +int test_vidconv(void); +int test_vidconv_scaling(void); +int test_vidconv_pixel_formats(void); +int test_websock(void); +int test_trace(void); +#ifdef USE_TLS +int test_dtls(void); +int test_dtls_srtp(void); +int test_tls(void); +int test_tls_ec(void); +int test_tls_selfsigned(void); +int test_tls_certificate(void); +int test_tls_false_cafile_path(void); +int test_tls_cli_conn_change_cert(void); +int test_tls_session_reuse_tls_v12(void); +int test_tls_session_reuse(void); +int test_tls_sni(void); +#endif + +#ifdef USE_TLS +int test_dtls_turn(void); +#endif + + +#ifdef USE_TLS +extern const char test_certificate_ecdsa[]; +#endif + +/* Integration tests */ +int test_integration(const char *name, bool verbose); +int test_sipevent_network(void); +int test_sip_drequestf_network(void); + +/* High-level API */ +int test_reg(const char *name, bool verbose); +int test_oom(const char *name, bool verbose); +int test_perf(const char *name, bool verbose); +int test_multithread(void); +void test_listcases(void); + + +void test_hexdump_dual(FILE *f, + const void *ep, size_t elen, + const void *ap, size_t alen); +bool test_cmp_double(double a, double b, double precision); +int re_main_timeout(uint32_t timeout_ms); +int test_load_file(struct mbuf *mb, const char *filename); +int test_write_file(struct mbuf *mb, const char *filename); +void test_set_datapath(const char *path); +const char *test_datapath(void); + + +/* + * Mock objects + */ + + +struct pf { + struct udp_helper *uh; + struct udp_sock *us; + char name[16]; +}; + +int pf_create(struct pf **pfp, struct udp_sock *us, const char *name); + + +struct stunserver { + struct udp_sock *us; + struct tcp_sock *ts; + struct tcp_conn *tc; + struct mbuf *mb; + struct sa laddr; + struct sa laddr_tcp; + struct sa paddr; + uint32_t nrecv; + int err; +}; + +int stunserver_alloc(struct stunserver **stunp); +const struct sa *stunserver_addr(const struct stunserver *stun, int proto); + + +struct turnserver { + struct udp_sock *us; + struct sa laddr; + struct tcp_sock *ts; + struct sa laddr_tcp; + struct tcp_conn *tc; + struct sa paddr; + struct mbuf *mb; + struct udp_sock *us_relay; + struct sa cli; + struct sa relay; + + struct channel { + uint16_t nr; + struct sa peer; + } chanv[4]; + size_t chanc; + + struct sa permv[4]; + size_t permc; + + size_t n_allocate; + size_t n_createperm; + size_t n_chanbind; + size_t n_send; + size_t n_raw; + size_t n_recv; +}; + +int turnserver_alloc(struct turnserver **turnp); + + +enum natbox_type { + NAT_INBOUND_SNAT, /* NOTE: must be installed on receiving socket */ + NAT_FIREWALL, +}; + +/** + * A simple NAT-box that can be hooked onto a UDP-socket. + * + * The NAT behaviour is port-preserving and will rewrite the source + * IP-address to the public address. + */ +struct nat { + enum natbox_type type; + struct sa public_addr; + struct udp_helper *uh; + struct udp_sock *us; + struct sa bindingv[16]; + size_t bindingc; +}; + +int nat_alloc(struct nat **natp, enum natbox_type type, + struct udp_sock *us, const struct sa *public_addr); + + +/* + * TCP Server + */ + +enum behavior { + BEHAVIOR_NORMAL, + BEHAVIOR_REJECT +}; + +struct tcp_server { + struct tcp_sock *ts; + enum behavior behavior; + struct sa laddr; +}; + +int tcp_server_alloc(struct tcp_server **srvp, enum behavior behavior); + + +/* + * SIP Server + */ + +struct sip_server { + struct sip *sip; + struct sip_lsnr *lsnr; + bool terminate; + + unsigned n_register_req; +}; + +int sip_server_alloc(struct sip_server **srvp); +int sip_server_uri(struct sip_server *srv, char *uri, size_t sz, + enum sip_transp tp); + + +/* + * Packet fuzzing + */ + +struct fuzz; + +int fuzz_register_tcpconn(struct fuzz **fuzzp, struct tcp_conn *tc); + + +/* + * Mock DNS-Server + */ + +struct dns_server { + struct udp_sock *us; + struct sa addr; + struct list rrl; + bool rotate; +}; + +int dns_server_alloc(struct dns_server **srvp, bool rotate); +int dns_server_add_a(struct dns_server *srv, const char *name, uint32_t addr, + int64_t ttl); +int dns_server_add_aaaa(struct dns_server *srv, const char *name, + const uint8_t *addr); +int dns_server_add_srv(struct dns_server *srv, const char *name, + uint16_t pri, uint16_t weight, uint16_t port, + const char *target); +void dns_server_flush(struct dns_server *srv); diff --git a/test/thread.c b/test/thread.c new file mode 100644 index 000000000..c7bc09603 --- /dev/null +++ b/test/thread.c @@ -0,0 +1,51 @@ +/** + * @file src/thread.c re threads + * + * Copyright (C) 2022 Sebastian Reimers + */ + +#include +#include "test.h" + +#define DEBUG_MODULE "thread" +#define DEBUG_LEVEL 5 +#include + + +static int thread(void *id) +{ + int n = *(int *)id; + + if (n != 42) + return thrd_error; + + return EPROTO; +} + + +int test_thread(void) +{ + thrd_t thr; + int err; + int id; + + err = thread_create_name(&thr, "test1", NULL, NULL); + TEST_EQUALS(EINVAL, err); + + id = 23; + err = thread_create_name(&thr, "test2", thread, (void *)&id); + TEST_ERR(err); + thrd_join(thr, &err); + TEST_EQUALS(thrd_error, err); + + id = 42; + err = thread_create_name(&thr, "test3", thread, (void *)&id); + TEST_ERR(err); + thrd_join(thr, &err); + TEST_EQUALS(EPROTO, err); + + err = 0; + +out: + return err; +} diff --git a/test/tls.c b/test/tls.c new file mode 100644 index 000000000..29acc6afd --- /dev/null +++ b/test/tls.c @@ -0,0 +1,648 @@ +/** + * @file tls.c TLS testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "tlstest" +#define DEBUG_LEVEL 5 +#include + + +struct tls_test { + struct tls *tls; + struct tls *tls2; + struct tls_conn *sc_cli; + struct tls_conn *sc_srv; + struct tcp_sock *ts; + struct tcp_conn *tc_cli; + struct tcp_conn *tc_srv; + enum tls_keytype keytype; + bool estab_cli; + bool estab_srv; + bool send_done_cli; + size_t recv_cli; + size_t recv_srv; + int err; +}; + + +static const char *payload = "0123456789"; + + +static void check(struct tls_test *tt, int err) +{ + if (tt->err == 0) + tt->err = err; + + if (tt->err) + re_cancel(); +} + + +static void can_send(struct tls_test *tt) +{ + struct mbuf *mb; + int err = 0; + + if (!tt->estab_cli || !tt->estab_srv || tt->send_done_cli) + return; + + mb = mbuf_alloc(256); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = mbuf_write_str(mb, payload); + if (err) + goto out; + + mb->pos = 0; + err = tcp_send(tt->tc_cli, mb); + + if (!err) + tt->send_done_cli = true; + + out: + mem_deref(mb); + + check(tt, err); +} + + +static void client_estab_handler(void *arg) +{ + struct tls_test *tt = arg; + int err = 0; + + tt->estab_cli = true; + can_send(tt); + + check(tt, err); +} + + +static void client_recv_handler(struct mbuf *mb, void *arg) +{ + struct tls_test *tt = arg; + int err = 0; + + if (!tt->estab_cli) { + (void)re_fprintf(stderr, "unexpected data received" + " on client [%02w]\n", + mbuf_buf(mb), mbuf_get_left(mb)); + check(tt, EPROTO); + } + + ++tt->recv_cli; + + TEST_MEMCMP(payload, strlen(payload), + mbuf_buf(mb), mbuf_get_left(mb)); + + out: + check(tt, err); + + /* We are done */ + re_cancel(); +} + + +static void client_close_handler(int err, void *arg) +{ + struct tls_test *tt = arg; + + if (!tt->estab_cli) + check(tt, err); +} + + +static void server_estab_handler(void *arg) +{ + struct tls_test *tt = arg; + tt->estab_srv = true; + can_send(tt); +} + + +static void server_recv_handler(struct mbuf *mb, void *arg) +{ + struct tls_test *tt = arg; + int err = 0; + + if (!tt->estab_srv) { + check(tt, EPROTO); + return; + } + + ++tt->recv_srv; + + TEST_MEMCMP(payload, strlen(payload), + mbuf_buf(mb), mbuf_get_left(mb)); + + /* echo */ + err = tcp_send(tt->tc_srv, mb); + if (err) { + DEBUG_WARNING("server: tcp_send error (%m)\n", err); + } + + out: + check(tt, err); +} + + +static void server_close_handler(int err, void *arg) +{ + struct tls_test *tt = arg; + + if (!tt->estab_cli) + check(tt, err); +} + + +static void server_conn_handler(const struct sa *peer, void *arg) +{ + struct tls_test *tt = arg; + int err; + (void)peer; + + err = tcp_accept(&tt->tc_srv, tt->ts, server_estab_handler, + server_recv_handler, server_close_handler, tt); + check(tt, err); + + err = tls_start_tcp(&tt->sc_srv, tt->tls, tt->tc_srv, 0); + check(tt, err); +} + + +static int test_tls_base(enum tls_keytype keytype, bool add_ca, int exp_verr, + bool test_sess_reuse, int forced_version) +{ + struct tls_test tt; + struct sa srv; + int err, verr; + unsigned long int i, rounds = 1 + (unsigned long int) test_sess_reuse; + + memset(&tt, 0, sizeof(tt)); + + tt.keytype = keytype; + + err = sa_set_str(&srv, "127.0.0.1", 0); + if (err) + goto out; + + err = tls_alloc(&tt.tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + if (forced_version >= 0) { + TEST_EQUALS(0, + tls_set_min_proto_version(tt.tls, forced_version)); + TEST_EQUALS(0, + tls_set_max_proto_version(tt.tls, forced_version)); + } + + switch (keytype) { + + case TLS_KEYTYPE_EC: + err = tls_set_certificate(tt.tls, test_certificate_ecdsa, + strlen(test_certificate_ecdsa)); + if (err) + goto out; + break; + + default: + err = EINVAL; + goto out; + } + + if (add_ca) { + char cafile[256]; + + re_snprintf(cafile, sizeof(cafile), "%s/server-ecdsa.pem", + test_datapath()); + + err = tls_add_ca(tt.tls, cafile); + if (err) + goto out; + } + + err = tcp_listen(&tt.ts, &srv, server_conn_handler, &tt); + if (err) + goto out; + + err = tcp_sock_local_get(tt.ts, &srv); + if (err) + goto out; + + err = tls_set_session_reuse(tt.tls, test_sess_reuse); + if (err) + goto out; + + for (i = 0; i < rounds; i++) { + tt.send_done_cli = false; + err = tcp_connect(&tt.tc_cli, &srv, client_estab_handler, + client_recv_handler, client_close_handler, &tt); + if (err) + goto out; + + err = tls_start_tcp(&tt.sc_cli, tt.tls, tt.tc_cli, 0); + if (err) + goto out; + + if (exp_verr == 0) { + + err = tls_set_verify_server(tt.sc_cli, "127.0.0.1"); + if (err) + goto out; + } + + err = re_main_timeout(800); + if (err) + goto out; + + if (tt.err) { + err = tt.err; + goto out; + } + + TEST_EQUALS(true, tt.estab_cli); + TEST_EQUALS(true, tt.estab_srv); + TEST_EQUALS(1, tt.recv_cli); + TEST_EQUALS(1+i, tt.recv_srv); + + verr = tls_peer_verify(tt.sc_cli); + TEST_EQUALS(exp_verr, verr); + + if (test_sess_reuse) { + TEST_EQUALS(i == 0 ? false : true, + tls_session_reused(tt.sc_cli)); + } + + tt.sc_cli = mem_deref(tt.sc_cli); + tt.sc_srv = mem_deref(tt.sc_srv); + tt.tc_cli = mem_deref(tt.tc_cli); + tt.tc_srv = mem_deref(tt.tc_srv); + tt.estab_cli = false; + tt.estab_srv = false; + tt.recv_cli = 0; + } + + out: + /* NOTE: close context first */ + mem_deref(tt.tls); + + mem_deref(tt.sc_cli); + mem_deref(tt.sc_srv); + mem_deref(tt.tc_cli); + mem_deref(tt.tc_srv); + mem_deref(tt.ts); + + return err; +} + + +int test_tls_session_reuse_tls_v12(void) +{ + return test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, true, + TLS1_2_VERSION); +} + + +/* TLS v1.3 session reuse is not yet supported by libre */ +int test_tls_session_reuse(void) +{ + return test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, true, -1); +} + + +int test_tls(void) +{ + return test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, false, -1); +} + + +int test_tls_ec(void) +{ + int err; + + err = test_tls_base(TLS_KEYTYPE_EC, false, EAUTH, false, -1); + if (err) + return err; + + err = test_tls_base(TLS_KEYTYPE_EC, true, 0, false, -1); + if (err) + return err; + + return err; +} + + +int test_tls_selfsigned(void) +{ + struct tls *tls = NULL; + uint8_t fp[32]; + int err; + + err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + err = tls_set_selfsigned_ec(tls, "re_ec@test", "unknown"); + TEST_EQUALS(err, ENOTSUP); + + err = tls_set_selfsigned_ec(tls, "re_ec@test", "prime256v1"); + TEST_ERR(err); + + /* verify fingerprint of the self-signed certificate */ + err = tls_fingerprint(tls, TLS_FINGERPRINT_SHA256, fp, sizeof(fp)); + TEST_ERR(err); + + out: + mem_deref(tls); + return err; +} + + +int test_tls_certificate(void) +{ + struct tls *tls = NULL; + static const uint8_t test_fingerprint[32] = + "\x50\x5d\x95\x2b\xef\x5b\x6f\x7f" + "\x2b\x4a\xa8\x1b\xdd\xe1\x99\xfd" + "\x4e\xb5\xc1\x04\xe7\x67\xa7\x48" + "\xb1\xf1\x66\x35\x98\xdc\x84\xc6"; + uint8_t fp[32]; + struct mbuf *mb = NULL; + int err; + + err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + mb = mbuf_alloc(20); + if (!mb) { + err = ENOMEM; + goto out; + } + + err = tls_get_subject(tls, mb); + TEST_EQUALS(ENOENT, err); + + err = tls_set_certificate(tls, test_certificate_ecdsa, + strlen(test_certificate_ecdsa)); + TEST_EQUALS(0, err); + + /* verify fingerprint of the certificate */ + err = tls_fingerprint(tls, TLS_FINGERPRINT_SHA256, fp, sizeof(fp)); + TEST_ERR(err); + + TEST_MEMCMP(test_fingerprint, sizeof(test_fingerprint), + fp, sizeof(fp)); + + err = tls_get_subject(tls, mb); + TEST_ERR(err); + + out: + mem_deref(tls); + mem_deref(mb); + return err; +} + + +int test_tls_false_cafile_path(void) +{ + int err = 0; + struct tls *tls = NULL; + const char *cafile_wrong = "/some/path/to/wrong/file.crt"; + const char *capath_wrong = "/some/path/to/nothing"; + + err = tls_alloc(&tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + err = tls_add_cafile_path(tls, NULL, NULL); + TEST_EQUALS(EINVAL, err); + + err = tls_add_cafile_path(tls, cafile_wrong, NULL); + TEST_EQUALS(ENOENT, err); + + err = tls_add_cafile_path(tls, NULL, capath_wrong); + TEST_EQUALS(ENOTDIR, err); + + err = tls_add_cafile_path(tls, cafile_wrong, capath_wrong); + TEST_EQUALS(ENOTDIR, err); + + err = 0; + + out: + mem_deref(tls); + return err; +} + + +int test_tls_cli_conn_change_cert(void) +{ + struct tls_test tt; + struct sa srv; + int err; + char clientcert[256]; + char clientcert_cn[256]; + char *exp_clientcert_cn = "Mr Retest Client Cert"; + + memset(&tt, 0, sizeof(tt)); + + tt.keytype = TLS_KEYTYPE_EC; + + err = sa_set_str(&srv, "127.0.0.1", 0); + if (err) + goto out; + + err = tls_alloc(&tt.tls, TLS_METHOD_SSLV23, NULL, NULL); + if (err) + goto out; + + tls_set_verify_client(tt.tls); + + err = tls_set_certificate(tt.tls, test_certificate_ecdsa, + strlen(test_certificate_ecdsa)); + if (err) + goto out; + + err = tcp_listen(&tt.ts, &srv, server_conn_handler, &tt); + if (err) + goto out; + + err = tcp_sock_local_get(tt.ts, &srv); + if (err) + goto out; + + err = tcp_connect(&tt.tc_cli, &srv, client_estab_handler, + client_recv_handler, client_close_handler, &tt); + if (err) + goto out; + + err = tls_start_tcp(&tt.sc_cli, tt.tls, tt.tc_cli, 0); + if (err) + goto out; + + /* actuall test cases*/ + err = tls_conn_change_cert(tt.sc_cli, NULL); + TEST_EQUALS(EINVAL, err); + + err = tls_conn_change_cert(NULL, clientcert); + TEST_EQUALS(EINVAL, err); + + memset(clientcert, 0, sizeof(clientcert)); + (void)re_snprintf(clientcert, sizeof(clientcert), + "%s/not_a_file.pem", test_datapath()); + + int ret = tls_conn_change_cert(tt.sc_cli, clientcert); + ASSERT_TRUE(ret==EINVAL || ret==ENOENT); + + memset(clientcert, 0, sizeof(clientcert)); + (void)re_snprintf(clientcert, sizeof(clientcert), + "%s/client_wrongkey.pem", test_datapath()); + + err = tls_conn_change_cert(tt.sc_cli, clientcert); + TEST_EQUALS(EKEYREJECTED, err); + + memset(clientcert, 0, sizeof(clientcert)); + (void)re_snprintf(clientcert, sizeof(clientcert), "%s/client.pem", + test_datapath()); + + err = tls_conn_change_cert(tt.sc_cli, clientcert); + if (err) + goto out; + + err = re_main_timeout(800); + if (err) + goto out; + + err = tls_peer_common_name(tt.sc_srv, clientcert_cn, + sizeof(clientcert_cn)); + if (err) { + if (!tt.sc_srv) { + TEST_EQUALS(EINVAL, err); + err = 0; + goto out; + } + } + + TEST_STRCMP(exp_clientcert_cn, strlen(exp_clientcert_cn), + clientcert_cn, strlen(clientcert_cn)); + + if (tt.err) { + err = tt.err; + goto out; + } + + out: + /* NOTE: close context first */ + mem_deref(tt.tls); + + mem_deref(tt.sc_cli); + mem_deref(tt.sc_srv); + mem_deref(tt.tc_cli); + mem_deref(tt.tc_srv); + mem_deref(tt.ts); + + return err; +} + + +/** + * SNI Test + * + * - UAS opens a TLS connection to UAC by sending a client hello with SNI + * - UAC chooses a certificate with matching SNI + * - UAC verifies the chain and common name of the UAS + * - UAS verifies the chain and common name of the UAC + * + * @return 0 if success, otherwise errorcode + */ +int test_tls_sni(void) +{ + struct tls_test tt; + struct sa srv; + int err; + const char *dp = test_datapath(); + char path[256]; + + memset(&tt, 0, sizeof(tt)); + + err = sa_set_str(&srv, "127.0.0.1", 0); + TEST_ERR(err); + + /* UAC global not matching cert (test checks that it is not used) */ + re_snprintf(path, sizeof(path), "%s/client.pem", dp); + err = tls_alloc(&tt.tls, TLS_METHOD_SSLV23, path, NULL); + TEST_ERR(err); + + /* UAS cert + intermediate CA */ + re_snprintf(path, sizeof(path), "%s/sni/server-interm.pem", dp); + err = tls_alloc(&tt.tls2, TLS_METHOD_SSLV23, path, NULL); + TEST_ERR(err); + + /* set root CA at UAC and UAS */ + re_snprintf(path, sizeof(path), "%s/sni/root-ca.pem", dp); + err = tls_add_ca(tt.tls, path); + err |= tls_add_ca(tt.tls2, path); + TEST_ERR(err); + + /* UAC cert + intermediate CA */ + re_snprintf(path, sizeof(path), "%s/sni/client-interm.pem", dp); + err = tls_add_certf(tt.tls, path, "retest.server.org"); + TEST_ERR(err); + + /* UAC listens (as TLS server)*/ + err = tcp_listen(&tt.ts, &srv, server_conn_handler, &tt); + TEST_ERR(err); + + err = tcp_sock_local_get(tt.ts, &srv); + TEST_ERR(err); + + /* UAS connects to UAC (as TLS client) */ + err = tcp_connect(&tt.tc_cli, &srv, client_estab_handler, + client_recv_handler, client_close_handler, &tt); + TEST_ERR(err); + + err = tls_start_tcp(&tt.sc_cli, tt.tls2, tt.tc_cli, 0); + TEST_ERR(err); + + err = tls_set_verify_server(tt.sc_cli, "retest.client.org"); + TEST_ERR(err); + + err = re_main_timeout(800); + TEST_ERR(err); + + if (tt.err) { + err = tt.err; + goto out; + } + + ASSERT_TRUE(tt.estab_cli); + ASSERT_TRUE(tt.estab_srv); + ASSERT_EQ(1, tt.recv_cli); + ASSERT_EQ(1, tt.recv_srv); + + err = tls_peer_verify(tt.sc_cli); + ASSERT_EQ(0, err); + + out: + /* NOTE: close context first */ + mem_deref(tt.tls); + mem_deref(tt.tls2); + mem_deref(tt.sc_cli); + mem_deref(tt.sc_srv); + mem_deref(tt.tc_cli); + mem_deref(tt.tc_srv); + mem_deref(tt.ts); + + return err; +} diff --git a/test/tmr.c b/test/tmr.c new file mode 100644 index 000000000..06cc66b81 --- /dev/null +++ b/test/tmr.c @@ -0,0 +1,50 @@ +/** + * @file tmr.c Timers testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testtmr" +#define DEBUG_LEVEL 5 +#include + + +int test_tmr_jiffies(void) +{ + uint64_t tmr_start, tmr_end, diff; + int err = 0; + + tmr_start = tmr_jiffies(); + sys_msleep(1); + tmr_end = tmr_jiffies(); + diff = tmr_end - tmr_start; + + TEST_ASSERT(diff >= 1); + TEST_ASSERT(diff < 50); + +out: + return err; +} + + +int test_tmr_jiffies_usec(void) +{ + uint64_t tmr_start, diff; + int i; + int err = 0; + + tmr_start = tmr_jiffies_usec(); + diff = 0; + for (i = 0; i < 100000 && !diff; i++) + diff = tmr_jiffies_usec() - tmr_start; + + TEST_ASSERT(diff >= 1); + TEST_ASSERT(diff < 1000); + +out: + return err; +} diff --git a/test/trace.c b/test/trace.c new file mode 100644 index 000000000..be85c828e --- /dev/null +++ b/test/trace.c @@ -0,0 +1,71 @@ +/** + * @file trace.c Trace testcode + */ + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "test.h" + +#define DEBUG_MODULE "test_trace" +#define DEBUG_LEVEL 5 +#include + +static void test_loop(int count) +{ + int i; + + for (i=0; i < count; i++) { + RE_TRACE_INSTANT_I("test", "Instant", i); + } +} + +int test_trace(void) +{ + int err; + + if (test_mode == TEST_THREAD) + return ESKIPPED; + + err = re_trace_init("test_trace.json"); + TEST_ERR(err); + + RE_TRACE_PROCESS_NAME("retest"); + RE_TRACE_THREAD_NAME("test_trace"); + RE_TRACE_BEGIN("test", "Test Loop Start"); + + test_loop(100); + + RE_TRACE_BEGIN("test", "Flush"); + err = re_trace_flush(); + TEST_ERR(err); + + RE_TRACE_END("test", "Flush"); + + test_loop(25); + + RE_TRACE_BEGIN_FUNC(); + + err = re_trace_flush(); + TEST_ERR(err); + + RE_TRACE_END_FUNC(); + + RE_TRACE_END("test", "Test Loop End"); + + err = re_trace_close(); + TEST_ERR(err); + + /* Test TRACE after close - should do nothing */ + RE_TRACE_BEGIN("test", "test after close"); + +#ifdef WIN32 + (void)_unlink("test_trace.json"); +#else + (void)unlink("test_trace.json"); +#endif + +out: + return err; +} diff --git a/test/trice.c b/test/trice.c new file mode 100644 index 000000000..8ba3fdcaa --- /dev/null +++ b/test/trice.c @@ -0,0 +1,1160 @@ +/** + * @file trice.c Trickle-ICE Testcode + * + * Copyright (C) 2010 - 2022 Alfred E. Heggestad + */ + +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_trice" +#define DEBUG_LEVEL 5 +#include + + +#define DEBUG 0 +#define COMPID 1 + + +struct fixture { + struct trice *icem; + struct sa laddr; + bool controlling; + char lufrag[8]; + char lpwd[24]; + char rufrag[8]; + char rpwd[24]; + + struct trice *icem2; + + /* result: */ + int err; + + unsigned n_expected_estabh; + bool cancel_on_both; + + /* counters: */ + unsigned n_estabh; + unsigned n_failh; + unsigned n_estabh2; + unsigned n_failh2; + + /* NAT */ + struct nat *nat; + struct nat *nat2; +}; + + +/* + * Helper macros + */ + + +#define FIXTURE_INIT \ + struct fixture _f, *f = &_f; \ + int err = fixture_init(f); \ + if (err) \ + goto out; \ + + +/* todo: 'addr' used as 'base_addr' (hack) */ +#define ADD_LOCAL_SRFLX_CANDIDATE(proto, prio, addr) \ + \ + do { \ + struct ice_lcand *_lcand; \ + \ + err = trice_lcand_add(&_lcand, f->icem, \ + COMPID, (proto), \ + (prio), (addr), (addr), \ + ICE_CAND_TYPE_SRFLX, (addr), \ + 0, NULL, 0); \ + if (err) goto out; \ + TEST_ASSERT(_lcand != NULL); \ + \ + } while (0); + +#define add_local_udp_candidate_use(addr) \ + \ + do { \ + struct ice_lcand *_lcand; \ + uint32_t _prio; \ + \ + _prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1); \ + \ + err = trice_lcand_add(&_lcand, f->icem, 1, \ + IPPROTO_UDP, _prio, \ + addr, NULL, \ + ICE_CAND_TYPE_HOST, NULL, \ + 0, NULL, 0); \ + if (err) goto out; \ + TEST_ASSERT(_lcand != NULL); \ + \ + } while (0); + +#define add_local_tcp_candidate_use(addr, tcptype) \ + \ + do { \ + struct ice_lcand *_lcand; \ + uint32_t _prio; \ + \ + _prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1); \ + \ + err = trice_lcand_add(&_lcand, f->icem, 1, \ + IPPROTO_TCP, _prio, \ + addr, NULL, \ + ICE_CAND_TYPE_HOST, NULL, \ + tcptype, NULL, 0); \ + if (err) goto out; \ + TEST_ASSERT(_lcand != NULL); \ + \ + } while (0); + + +#define add_local_tcp_candidate_use2(addr, tcptype) \ + \ + do { \ + struct ice_lcand *_lcand; \ + uint32_t _prio; \ + \ + _prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1); \ + \ + err = trice_lcand_add(&_lcand, f->icem2, 1, \ + IPPROTO_TCP, _prio, \ + addr, NULL, \ + ICE_CAND_TYPE_HOST, NULL, \ + tcptype, NULL, 0); \ + if (err) goto out; \ + TEST_ASSERT(_lcand != NULL); \ + \ + } while (0); + + +#define ADD_REMOTE_HOST_CANDIDATE(addr) \ + \ + do { \ + uint32_t _prio; \ + \ + _prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, 1); \ + \ + err = trice_rcand_add(NULL, f->icem, \ + 1, "FND", \ + IPPROTO_UDP, \ + _prio, \ + addr, \ + ICE_CAND_TYPE_HOST, 0); \ + if (err) goto out; \ + \ + } while (0); + +#define CHECKLIST_START(fix) \ + err = trice_checklist_start((fix)->icem, NULL, 1, \ + ice_estab_handler, \ + ice_failed_handler, (fix)); \ + TEST_ERR(err); \ + + +static void fixture_abort(struct fixture *f, int err); + + +static bool verify_sorted(const struct list *pairl) +{ + struct le *le; + uint64_t pprio = 0; + + if (!pairl) + return false; + + for (le = list_head(pairl); le; le = le->next) { + + struct ice_candpair *pair = le->data; + + if (!pprio) { + pprio = pair->pprio; + continue; + } + + if (pair->pprio > pprio) { + DEBUG_WARNING("unsorted list: %llu > %llu\n", + pair->pprio, pprio); + return false; + } + } + + return true; +} + + +static bool are_both_established(const struct fixture *f) +{ + if (!f) + return false; + return f->n_estabh > 0 && f->n_estabh2 > 0; +} + + +static void ice_estab_handler(struct ice_candpair *pair, + const struct stun_msg *msg, void *arg) +{ + struct fixture *f = arg; + int err = 0; + + ++f->n_estabh; + + /* TODO: save candidate-pairs, and compare in the test */ + + TEST_ASSERT(msg != NULL); + TEST_ASSERT(pair != NULL); + TEST_ASSERT(pair->lcand != NULL); + TEST_ASSERT(pair->rcand != NULL); + TEST_ASSERT(pair->valid); + TEST_EQUALS(ICE_CANDPAIR_SUCCEEDED, pair->state); + TEST_ERR(pair->err); + TEST_EQUALS(0, pair->scode); + + TEST_ASSERT((ICE_CAND_TYPE_HOST == pair->rcand->attr.type) || + (ICE_CAND_TYPE_PRFLX == pair->rcand->attr.type)); + + /* exit criteria */ + if (f->n_expected_estabh && f->n_estabh >= f->n_expected_estabh) { + fixture_abort(f, 0); + } + + if (f->cancel_on_both && are_both_established(f)) { + fixture_abort(f, 0); + } + + out: + if (err) + fixture_abort(f, err); +} + + +static void ice_failed_handler(int err, uint16_t scode, + struct ice_candpair *pair, void *arg) +{ + struct fixture *f = arg; + (void)err; + (void)scode; + (void)pair; + + ++f->n_failh; + + if (trice_checklist_iscompleted(f->icem)) { + re_cancel(); + } +} + + +static void ice_estab_handler2(struct ice_candpair *pair, + const struct stun_msg *msg, void *arg) +{ + struct fixture *f = arg; + (void)pair; + (void)msg; + + ++f->n_estabh2; + + if (f->cancel_on_both && are_both_established(f)) { + fixture_abort(f, 0); + } +} + + +static void ice_failed_handler2(int err, uint16_t scode, + struct ice_candpair *pair, void *arg) +{ + struct fixture *f = arg; + (void)err; + (void)scode; + (void)pair; + + re_printf(" ~ ice2 closed (%m)\n", err); + + ++f->n_failh2; + +#if 0 + re_cancel(); +#endif +} + + +static int fixture_init(struct fixture *f) +{ + const struct trice_conf conf = { + .debug = DEBUG, + .enable_prflx = true + }; + int err; + + if (!f) + return EINVAL; + + memset(f, 0, sizeof(*f)); + + f->controlling = true; + + rand_str(f->lufrag, sizeof(f->lufrag)); + rand_str(f->lpwd, sizeof(f->lpwd)); + rand_str(f->rufrag, sizeof(f->rufrag)); + rand_str(f->rpwd, sizeof(f->rpwd)); + + err = trice_alloc(&f->icem, &conf, + f->controlling ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED, + f->lufrag, f->lpwd); + TEST_ERR(err); + + TEST_ASSERT(f->icem != NULL); + + err = trice_set_remote_ufrag(f->icem, f->rufrag); + TEST_ERR(err); + + err = trice_set_remote_pwd(f->icem, f->rpwd); + TEST_ERR(err); + + err = sa_set_str(&f->laddr, "127.0.0.1", 0); + TEST_ERR(err); + + out: + return err; +} + + +static void fixture_close(struct fixture *f) +{ + if (!f) + return; + + f->nat = mem_deref(f->nat); + f->nat2 = mem_deref(f->nat2); + + f->icem2 = mem_deref(f->icem2); + f->icem = mem_deref(f->icem); +} + + +static void fixture_abort(struct fixture *f, int err) +{ + f->err = err; + re_cancel(); +} + + +/* ... TEST CASES ... */ + + +static int candidate_local_udp(void) +{ + struct ice_lcand *lcand; + FIXTURE_INIT; + + err = trice_lcand_add(&lcand, f->icem, 1, IPPROTO_UDP, + 1234, &f->laddr, NULL, + ICE_CAND_TYPE_HOST, NULL, 0, NULL, 0); + if (err) + goto out; + + /* verify the new local candidate */ + TEST_ASSERT(lcand != NULL); + TEST_ASSERT(str_isset(lcand->attr.foundation)); + TEST_EQUALS(1, lcand->attr.compid); + TEST_EQUALS(IPPROTO_UDP, lcand->attr.proto); + TEST_EQUALS(1234, lcand->attr.prio); + TEST_SACMP(&f->laddr, &lcand->attr.addr, SA_ADDR); + TEST_ASSERT(sa_isset(&lcand->attr.addr, SA_PORT)); + TEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type); + + TEST_ASSERT(list_contains(trice_lcandl(f->icem), &lcand->le)); + TEST_ASSERT(lcand->icem == f->icem); + TEST_ASSERT(lcand->us != NULL); + TEST_ASSERT(lcand->uh != NULL); + TEST_ASSERT(lcand->ts == NULL); + + out: + fixture_close(f); + return err; +} + + +static int candidate_local_tcp(enum ice_tcptype tcptype) +{ + struct ice_lcand *lcand; + FIXTURE_INIT; + + err = trice_lcand_add(&lcand, f->icem, 1, IPPROTO_TCP, + 1234, &f->laddr, NULL, + ICE_CAND_TYPE_HOST, NULL, tcptype, NULL, 0); + if (err) + goto out; + + /* verify the new local candidate */ + TEST_ASSERT(lcand != NULL); + TEST_ASSERT(str_isset(lcand->attr.foundation)); + TEST_EQUALS(1, lcand->attr.compid); + TEST_EQUALS(IPPROTO_TCP, lcand->attr.proto); + TEST_EQUALS(1234, lcand->attr.prio); + TEST_SACMP(&f->laddr, &lcand->attr.addr, SA_ADDR); + if (tcptype == ICE_TCP_ACTIVE) { + TEST_ASSERT(!sa_isset(&lcand->attr.addr, SA_PORT)); + } + else { + TEST_ASSERT(sa_isset(&lcand->attr.addr, SA_PORT)); + } + TEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type); + + TEST_ASSERT(list_contains(trice_lcandl(f->icem), &lcand->le)); + TEST_ASSERT(lcand->icem == f->icem); + TEST_ASSERT(lcand->us == NULL); + TEST_ASSERT(lcand->uh == NULL); + if (tcptype == ICE_TCP_ACTIVE) { + TEST_ASSERT(lcand->ts == NULL); + } + else { + TEST_ASSERT(lcand->ts != NULL); + } + + out: + fixture_close(f); + return err; +} + + +static int candidate_add_5_local(int proto) +{ + int i; + FIXTURE_INIT; + + for (i=0; i<5; i++) { + struct sa addr; + char buf[64]; + + re_snprintf(buf, sizeof(buf), "10.0.0.%u", i+1); + + sa_set_str(&addr, buf, 1000+i); + + ADD_LOCAL_SRFLX_CANDIDATE(proto, 0, &addr) + } + + TEST_EQUALS(5, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(0, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(0, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + TEST_EQUALS(0, f->n_estabh); + + out: + fixture_close(f); + return err; +} + + +static int candidate_find_local_candidate(void) +{ + struct sa addr; + struct ice_lcand *cand; + FIXTURE_INIT; + + sa_set_str(&addr, "1.2.3.4", 1234); + + /* should not exist now */ + cand = trice_lcand_find(f->icem, -1, 1, IPPROTO_UDP, &addr); + TEST_ASSERT(cand == NULL); + + ADD_LOCAL_SRFLX_CANDIDATE(IPPROTO_UDP, 0x7e0000ff, &addr); + + cand = trice_lcand_find(f->icem, -1, 1, IPPROTO_UDP, &addr); + TEST_ASSERT(cand != NULL); + + TEST_EQUALS(ICE_CAND_TYPE_SRFLX, cand->attr.type); + TEST_EQUALS(0x7e0000ff, cand->attr.prio); + TEST_ASSERT(str_isset(cand->attr.foundation)); + TEST_EQUALS(1, cand->attr.compid); + TEST_SACMP(&addr, &cand->attr.addr, SA_ALL); + TEST_EQUALS(IPPROTO_UDP, cand->attr.proto); + + out: + fixture_close(f); + return err; +} + + +static int candidate_add_5_remote_candidates(void) +{ + int i; + FIXTURE_INIT; + + for (i=0; i<5; i++) { + struct sa addr; + char buf[64]; + + re_snprintf(buf, sizeof(buf), "10.0.0.%u", i+1); + + sa_set_str(&addr, buf, 1234); + + ADD_REMOTE_HOST_CANDIDATE(&addr); + } + + TEST_EQUALS(0, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(5, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(0, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + TEST_EQUALS(0, f->n_estabh); + + out: + fixture_close(f); + return err; +} + + +static int candidate_find_remote_candidate(void) +{ + struct sa addr; + struct ice_rcand *cand; + FIXTURE_INIT; + + sa_set_str(&addr, "1.2.3.4", 1234); + + /* should not exist now */ + cand = trice_rcand_find(f->icem, 1, IPPROTO_UDP, &addr); + TEST_ASSERT(cand == NULL); + + ADD_REMOTE_HOST_CANDIDATE(&addr); + + cand = trice_rcand_find(f->icem, 1, IPPROTO_UDP, &addr); + TEST_ASSERT(cand != NULL); + + TEST_EQUALS(ICE_CAND_TYPE_HOST, cand->attr.type); + TEST_EQUALS(0x7e0000ff, cand->attr.prio); + TEST_ASSERT(str_isset(cand->attr.foundation)); + TEST_EQUALS(1, cand->attr.compid); + TEST_SACMP(&addr, &cand->attr.addr, SA_ALL); + TEST_EQUALS(IPPROTO_UDP, cand->attr.proto); + + out: + fixture_close(f); + return err; +} + + +static int candidate_add_2_local_and_2_remote_candidates(void) +{ + struct sa laddr, raddr; + int i; + FIXTURE_INIT; + + sa_set_str(&laddr, "10.0.0.1", 0); + sa_set_str(&raddr, "10.0.0.2", 0); + + for (i=0; i<2; i++) { + + sa_set_port(&laddr, 10000+i); + sa_set_port(&raddr, 20000+i); + + ADD_LOCAL_SRFLX_CANDIDATE(IPPROTO_UDP, 1234, &laddr) + + ADD_REMOTE_HOST_CANDIDATE(&raddr); + } + + TEST_EQUALS(2, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(2, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(4, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + TEST_EQUALS(0, f->n_estabh); + + TEST_ASSERT(verify_sorted(trice_checkl(f->icem))); + + out: + fixture_close(f); + return err; +} + + +static int candidate_2_local_duplicates(int proto, + uint32_t prio1, uint32_t prio2) +{ + struct sa laddr; + struct ice_lcand *lcand; + FIXTURE_INIT; + + sa_set_str(&laddr, "10.0.0.3", 1002); + + TEST_EQUALS(0, list_count(trice_lcandl(f->icem))); + + /* add one with Low Priority */ + ADD_LOCAL_SRFLX_CANDIDATE(proto, prio1, &laddr); + + TEST_EQUALS(1, list_count(trice_lcandl(f->icem))); + + /* add one with High Priority */ + ADD_LOCAL_SRFLX_CANDIDATE(proto, prio2, &laddr); + + TEST_EQUALS(1, list_count(trice_lcandl(f->icem))); + + /* verify that local candidate has the HIGH prio */ + lcand = trice_lcand_find(f->icem, -1, 1, proto, &laddr); + TEST_ASSERT(lcand != NULL); + TEST_EQUALS(max(prio1, prio2), lcand->attr.prio); + + out: + fixture_close(f); + return err; +} + + +static int candidate_local_host_and_srflx_with_base(void) +{ + struct fixture f; + struct sa laddr, srflx; + struct ice_lcand *lcand; + int err = 0; + + err = fixture_init(&f); + if (err) + goto out; + + sa_set_str(&laddr, "127.0.0.1", 0); + sa_set_str(&srflx, "46.45.1.1", 1002); + + err = trice_lcand_add(&lcand, f.icem, COMPID, IPPROTO_UDP, + 1234, &laddr, NULL, + ICE_CAND_TYPE_HOST, NULL, 0, NULL, 0); + TEST_ERR(err); + TEST_ASSERT(lcand != NULL); + + laddr = lcand->attr.addr; + + err = trice_lcand_add(NULL, f.icem, COMPID, IPPROTO_UDP, + 1234, &srflx, &laddr, + ICE_CAND_TYPE_SRFLX, &laddr, 0, NULL, 0); + TEST_ERR(err); + + TEST_EQUALS(2, list_count(trice_lcandl(f.icem))); + + /* verify */ + lcand = trice_lcand_find(f.icem, ICE_CAND_TYPE_HOST, COMPID, + IPPROTO_UDP, &lcand->attr.addr); + TEST_ASSERT(lcand != NULL); + TEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type); + TEST_SACMP(&laddr, &lcand->attr.addr, SA_ALL); + + lcand = trice_lcand_find(f.icem, ICE_CAND_TYPE_SRFLX, COMPID, + IPPROTO_UDP, &srflx); + TEST_ASSERT(lcand != NULL); + TEST_EQUALS(ICE_CAND_TYPE_SRFLX, lcand->attr.type); + TEST_SACMP(&srflx, &lcand->attr.addr, SA_ALL); + TEST_SACMP(&laddr, &lcand->base_addr, SA_ALL); + + out: + fixture_close(&f); + return err; +} + + +/* 4.1.3. Eliminating Redundant Candidates */ +static int candidate_verify_redundant_with_public_ip(void) +{ + struct sa laddr, raddr; + struct ice_lcand *lcand; + uint32_t prio; + FIXTURE_INIT; + + sa_set_str(&laddr, "127.0.0.1", 0); + sa_set_str(&raddr, "10.0.0.4", 1002); + + prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, COMPID); + err = trice_lcand_add(&lcand, f->icem, COMPID, IPPROTO_UDP, + prio, &laddr, NULL, + ICE_CAND_TYPE_HOST, NULL, 0, + NULL, 0); + TEST_ERR(err); + TEST_ASSERT(lcand != NULL); + + laddr = lcand->attr.addr; + + prio = ice_cand_calc_prio(ICE_CAND_TYPE_SRFLX, 0, COMPID); + err = trice_lcand_add(NULL, f->icem, COMPID, IPPROTO_UDP, + prio, + &lcand->attr.addr, &lcand->attr.addr, + ICE_CAND_TYPE_SRFLX, + &lcand->attr.addr, + 0, NULL, 0); + TEST_ERR(err); + + ADD_REMOTE_HOST_CANDIDATE(&raddr); + + TEST_EQUALS(1, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(1, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(1, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + /* verify the local candidate */ + lcand = list_ledata(list_head(trice_lcandl(f->icem))); + TEST_EQUALS(ICE_CAND_TYPE_HOST, lcand->attr.type); + TEST_SACMP(&laddr, &lcand->attr.addr, SA_ALL); + + out: + fixture_close(f); + return err; +} + + +/* ... testcases for candidate pairs ... */ + + +static int candpair_add_1_local_and_1_remote_candidate_and_create_pair(void) +{ + struct sa addr; + FIXTURE_INIT; + + sa_set_str(&addr, "10.0.0.5", 1000); + + ADD_LOCAL_SRFLX_CANDIDATE(IPPROTO_UDP, 1234, &addr); + + ADD_REMOTE_HOST_CANDIDATE(&addr); + + /* the checklist is formated automatically */ + + TEST_EQUALS(1, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(1, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(1, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + TEST_EQUALS(0, f->n_estabh); + + out: + fixture_close(f); + return err; +} + + +static int candpair_combine_ipv4_ipv6_udp_tcp(void) +{ + struct sa addr, addr6; + FIXTURE_INIT; + + sa_set_str(&addr, "10.0.0.6", 1000); + sa_set_str(&addr6, "::1", 6000); + + err |= trice_lcand_add(0, f->icem, 1, IPPROTO_UDP, 1234, + &addr, &addr, ICE_CAND_TYPE_SRFLX, &addr, 0, + NULL, 0); + err |= trice_lcand_add(0, f->icem, 1, IPPROTO_TCP, 1234, + &addr, &addr, ICE_CAND_TYPE_SRFLX, &addr, + ICE_TCP_ACTIVE, + NULL, 0); + err |= trice_lcand_add(0, f->icem, 1, IPPROTO_UDP, 1234, + &addr6, &addr6, ICE_CAND_TYPE_SRFLX, &addr6, + 0, NULL, 0); + err |= trice_lcand_add(0, f->icem, 1, IPPROTO_TCP, 1234, + &addr6, &addr6, ICE_CAND_TYPE_SRFLX, &addr6, + ICE_TCP_ACTIVE, + NULL, 0); + TEST_ERR(err); + + ADD_REMOTE_HOST_CANDIDATE(&addr); + err |= trice_rcand_add(NULL, f->icem, 1, + "FND", IPPROTO_TCP, 1234, + &addr, ICE_CAND_TYPE_HOST, + ICE_TCP_PASSIVE); + if (err) goto out; + + ADD_REMOTE_HOST_CANDIDATE(&addr6); + err |= trice_rcand_add(NULL, f->icem, 1, + "FND", IPPROTO_TCP, 1234, + &addr6, ICE_CAND_TYPE_HOST, + ICE_TCP_PASSIVE); + if (err) goto out; + + TEST_EQUALS(4, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(4, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(4, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + TEST_EQUALS(0, f->n_estabh); + TEST_EQUALS(0, f->n_failh); + + out: + fixture_close(f); + return err; +} + + +static int candpair_add_many_verify_sorted(void) +{ + struct fixture f; + struct sa laddr, raddr; + int i, err = 0; + + err = fixture_init(&f); + if (err) + goto out; + + sa_set_str(&laddr, "10.0.0.7", 0); + sa_set_str(&raddr, "10.0.0.8", 0); + + for (i=0; i<4; i++) { + + uint8_t compid = 1 + i%2; + + sa_set_port(&laddr, 10000+i); + sa_set_port(&raddr, 20000+i); + + err = trice_lcand_add(0, f.icem, compid, IPPROTO_UDP, + i*1000, &laddr, &laddr, + ICE_CAND_TYPE_SRFLX, &laddr, 0, + NULL, 0); + TEST_ERR(err); + + err = trice_rcand_add(0, f.icem, compid, "FND", + IPPROTO_UDP, i*2000, + &raddr, ICE_CAND_TYPE_HOST, 0); + TEST_ERR(err); + } + + TEST_EQUALS(4, list_count(trice_lcandl(f.icem))); + TEST_EQUALS(4, list_count(trice_rcandl(f.icem))); + TEST_EQUALS(8, list_count(trice_checkl(f.icem))); + TEST_EQUALS(0, list_count(trice_validl(f.icem))); + + TEST_ASSERT(verify_sorted(trice_checkl(f.icem))); + + out: + fixture_close(&f); + return err; +} + + +static int candpair_test_pruning(void) +{ + struct sa srflx_addr, remote_addr; + struct ice_lcand *lcand; + uint32_t prio; + FIXTURE_INIT; + + err |= sa_set_str(&srflx_addr, "95.1.2.3", 50000); + err |= sa_set_str(&remote_addr, "10.0.0.9", 10000); + TEST_ERR(err); + + prio = ice_cand_calc_prio(ICE_CAND_TYPE_SRFLX, 0, COMPID); + + add_local_udp_candidate_use(&f->laddr); + + lcand = trice_lcand_find(f->icem, -1, COMPID, + IPPROTO_UDP, NULL); + TEST_ASSERT(lcand != NULL); + + err = trice_lcand_add(&lcand, f->icem, COMPID, IPPROTO_UDP, + prio, &srflx_addr, &lcand->attr.addr, + ICE_CAND_TYPE_SRFLX, &lcand->attr.addr, + 0, NULL, 0); + TEST_ERR(err); + TEST_ASSERT(lcand != NULL); + + ADD_REMOTE_HOST_CANDIDATE(&remote_addr); + + /* verify that SRFLX candpair was pruned + */ + TEST_EQUALS(2, list_count(trice_lcandl(f->icem))); + TEST_EQUALS(1, list_count(trice_rcandl(f->icem))); + TEST_EQUALS(1, list_count(trice_checkl(f->icem))); + TEST_EQUALS(0, list_count(trice_validl(f->icem))); + + out: + fixture_close(f); + return err; +} + + +static int checklist_verify_states(void) +{ + struct fixture f; + int err = 0; + + err = fixture_init(&f); + if (err) + goto out; + + TEST_EQUALS(false, trice_checklist_isrunning(f.icem)); + + /* Start -- Running */ + CHECKLIST_START(&f); + TEST_EQUALS(true, trice_checklist_isrunning(f.icem)); + + /* Stop */ + trice_checklist_stop(f.icem); + TEST_EQUALS(false, trice_checklist_isrunning(f.icem)); + + out: + fixture_close(&f); + return err; +} + + +static int exchange_candidates(struct trice *dst, const struct trice *src) +{ + struct le *le; + int err = 0; + + for (le = list_head(trice_lcandl(src)); le; le = le->next) { + + struct ice_cand_attr *cand = le->data; + + err = trice_rcand_add(NULL, dst, cand->compid, + cand->foundation, cand->proto, + cand->prio, &cand->addr, + cand->type, cand->tcptype); + if (err) + return err; + } + + return err; +} + + +static int checklist_tcp_simple(enum ice_tcptype tcptype) +{ + struct le *le; + FIXTURE_INIT; + +#if 0 + trice_conf(f.icem)->debug = true; + trice_conf(f.icem)->trace = true; +#endif + + err = trice_alloc(&f->icem2, NULL, + !f->controlling ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED, + f->rufrag, f->rpwd); + TEST_ERR(err); + + err |= trice_set_remote_ufrag(f->icem2, f->lufrag); + err |= trice_set_remote_pwd(f->icem2, f->lpwd); + TEST_ERR(err); + + add_local_tcp_candidate_use(&f->laddr, tcptype); + add_local_tcp_candidate_use2(&f->laddr, ice_tcptype_reverse(tcptype)); + + err = exchange_candidates(f->icem, f->icem2); + err |= exchange_candidates(f->icem2, f->icem); + TEST_ERR(err); + + CHECKLIST_START(f); + + err = trice_checklist_start(f->icem2, NULL, 1, + ice_estab_handler2, ice_failed_handler2, f); + TEST_ERR(err); + + f->n_expected_estabh = 1; + + err = re_main_timeout(1000); + if (err) + goto out; + + TEST_ERR(f->err); + +#if 0 + re_printf("\n\n%H\n", trice_debug, f->icem); +#endif + + TEST_ASSERT(f->n_estabh > 0); + + TEST_ASSERT(list_count(trice_lcandl(f->icem)) >= 1); + TEST_ASSERT(list_count(trice_rcandl(f->icem)) >= 1); + TEST_EQUALS(1, list_count(trice_validl(f->icem))); + + for (le = list_head(trice_validl(f->icem)); le; le = le->next) { + struct ice_candpair *pair = le->data; + struct ice_lcand *lcand = pair->lcand; + + TEST_ASSERT(pair->valid); + + TEST_EQUALS(ICE_CANDPAIR_SUCCEEDED, pair->state); + TEST_EQUALS(0, pair->err); + TEST_EQUALS(0, pair->scode); + + TEST_EQUALS(IPPROTO_TCP, lcand->attr.proto); + TEST_EQUALS(tcptype, lcand->attr.tcptype); + + TEST_EQUALS(IPPROTO_TCP, pair->rcand->attr.proto); + TEST_EQUALS(ice_tcptype_reverse(tcptype), + pair->rcand->attr.tcptype); + } + + out: + fixture_close(f); + return err; +} + + +int test_trice_cand(void) +{ + int err = 0; + + err |= candidate_local_udp(); + err |= candidate_local_tcp(ICE_TCP_ACTIVE); + err |= candidate_local_tcp(ICE_TCP_PASSIVE); + err |= candidate_local_tcp(ICE_TCP_SO); + err |= candidate_add_5_local(IPPROTO_UDP); + err |= candidate_add_5_local(IPPROTO_TCP); + err |= candidate_find_local_candidate(); + err |= candidate_add_5_remote_candidates(); + err |= candidate_find_remote_candidate(); + err |= candidate_add_2_local_and_2_remote_candidates(); + err |= candidate_2_local_duplicates(IPPROTO_UDP, 100, 200); + err |= candidate_2_local_duplicates(IPPROTO_UDP, 200, 100); +#if 0 + err |= candidate_2_local_duplicates(IPPROTO_TCP, 100, 200); +#endif + err |= candidate_local_host_and_srflx_with_base(); + err |= candidate_verify_redundant_with_public_ip(); + + return err; +} + + +int test_trice_candpair(void) +{ + int err = 0; + + err |= candpair_add_1_local_and_1_remote_candidate_and_create_pair(); + err |= candpair_combine_ipv4_ipv6_udp_tcp(); + err |= candpair_add_many_verify_sorted(); + err |= candpair_test_pruning(); + + return err; +} + + +int test_trice_checklist(void) +{ + int err = 0; + + err |= checklist_verify_states(); + TEST_ERR(err); + err |= checklist_tcp_simple(ICE_TCP_ACTIVE); + TEST_ERR(err); + err |= checklist_tcp_simple(ICE_TCP_PASSIVE); + TEST_ERR(err); + +out: + return err; +} + + +static int checklist_udp_loop(bool fw_a, bool fw_b) +{ + struct ice_lcand *lcand, *lcand2; + struct sa laddr2; + uint32_t prio; + FIXTURE_INIT; + +#if 0 + trice_conf(f->icem)->debug = true; + trice_conf(f->icem)->trace = true; +#endif + + sa_set_str(&laddr2, "127.0.0.1", 0); + + err = trice_alloc(&f->icem2, NULL, + !f->controlling ? ICE_ROLE_CONTROLLING : ICE_ROLE_CONTROLLED, + f->rufrag, f->rpwd); + TEST_ERR(err); + + err |= trice_set_remote_ufrag(f->icem2, f->lufrag); + err |= trice_set_remote_pwd(f->icem2, f->lpwd); + TEST_ERR(err); + + /* add local HOST candidates */ + + add_local_udp_candidate_use(&f->laddr); + + lcand = trice_lcand_find2(f->icem, + ICE_CAND_TYPE_HOST, AF_INET); + TEST_ASSERT(lcand != NULL); + + /* install NAT/Firewall */ + if (fw_a) { + err = nat_alloc(&f->nat, NAT_FIREWALL, lcand->us, NULL); + if (err) { + re_printf("nat failed\n"); + goto out; + } + } + + prio = ice_cand_calc_prio(ICE_CAND_TYPE_HOST, 0, COMPID); + err = trice_lcand_add(&lcand2, f->icem2, COMPID, IPPROTO_UDP, + prio, &laddr2, NULL, + ICE_CAND_TYPE_HOST, NULL, 0, NULL, 0); + if (err) + goto out; + + /* install NAT/Firewall */ + if (fw_b) { + err = nat_alloc(&f->nat2, NAT_FIREWALL, lcand2->us, NULL); + if (err) { + re_printf("nat failed\n"); + goto out; + } + } + + err = exchange_candidates(f->icem, f->icem2); + err |= exchange_candidates(f->icem2, f->icem); + TEST_ERR(err); + + f->cancel_on_both = true; + + CHECKLIST_START(f); + + /* NOTE: slow checklist */ + err = trice_checklist_start(f->icem2, NULL, 10, + ice_estab_handler2, ice_failed_handler2, f); + TEST_ERR(err); + + err = re_main_timeout(2000); + if (err) + goto out; + + TEST_ERR(f->err); + +#if 0 + re_printf("\nA:\n%H\n", trice_debug, f->icem); + re_printf("\nB:\n%H\n", trice_debug, f->icem2); +#endif + + TEST_ASSERT(f->n_estabh > 0); + + TEST_ASSERT(list_count(trice_lcandl(f->icem)) >= 1); + TEST_ASSERT(list_count(trice_rcandl(f->icem)) >= 1); + TEST_EQUALS(1, list_count(trice_validl(f->icem))); + + if (fw_a) { + TEST_ASSERT(f->nat->bindingc >= 1); + } + + out: + fixture_close(f); + return err; +} + + +int test_trice_loop(void) +{ + int err = 0; + + err = checklist_udp_loop(0, 0); + TEST_ERR(err); + + err = checklist_udp_loop(0, 1); + TEST_ERR(err); + + err = checklist_udp_loop(1, 0); + TEST_ERR(err); + + err = checklist_udp_loop(1, 1); + TEST_ERR(err); + +out: + return err; +} diff --git a/test/turn.c b/test/turn.c new file mode 100644 index 000000000..0dda8943d --- /dev/null +++ b/test/turn.c @@ -0,0 +1,535 @@ +/** + * @file turn.c TURN testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_turn" +#define DEBUG_LEVEL 5 +#include + + +enum rx_state { + RX_NULL = 0, + RX_DETACH, + RX_ATTACH, + RX_READY, + RX_CLOSE +}; + + +struct turntest { + struct turnc *turnc; + struct turnserver *turnsrv; + struct udp_sock *us_cli; + struct udp_sock *us_peer; + struct tcp_conn *tc; + struct sa cli; + struct sa peer; + struct mbuf *mb; + struct tmr tmr; + uint32_t lifetime; + RE_ATOMIC enum rx_state rx_state; + thrd_t thr; + int proto; + int err; + + size_t n_alloc_resp; + size_t n_chan_resp; + size_t n_peer_recv; +}; + + +static const char *test_payload = "guten tag Herr TURN server"; + + +static void destructor(void *arg) +{ + struct turntest *tt = arg; + + /* NOTE: must be derefed before udp socket */ + mem_deref(tt->turnc); + + mem_deref(tt->us_cli); + mem_deref(tt->us_peer); + mem_deref(tt->tc); + mem_deref(tt->mb); + mem_deref(tt->turnsrv); +} + + +static bool is_complete(struct turntest *tt) +{ + return tt->n_chan_resp >= 1 && tt->n_peer_recv >= 2; +} + + +static void complete_test(struct turntest *tt, int err) +{ + tt->err = err; + re_cancel(); +} + + +/* send a UDP payload via TURN-Server to a UDP-peer */ +static int send_payload(struct turntest *tt, size_t offset, + const struct sa *dst, const char *str) +{ + struct mbuf *mb = mbuf_alloc(offset + str_len(str)); + int err; + + if (!mb) + return ENOMEM; + + mb->pos = offset; + + err = mbuf_write_str(mb, str); + if (err) + goto out; + + mb->pos = offset; + + switch (tt->proto) { + + case IPPROTO_UDP: + err = udp_send(tt->us_cli, dst, mb); + break; + + case IPPROTO_TCP: + err = turnc_send(tt->turnc, dst, mb); + break; + } + + out: + mem_deref(mb); + + return err; +} + + +static void turnc_chan_handler(void *arg) +{ + struct turntest *tt = arg; + int err = 0; + + ++tt->n_chan_resp; + + err |= send_payload(tt, 4, &tt->peer, test_payload); + if (err) { + DEBUG_WARNING("failed to send payload (%m)\n", err); + complete_test(tt, err); + } + + if (err) + complete_test(tt, err); +} + + +static void turnc_handler(int err, uint16_t scode, const char *reason, + const struct sa *relay_addr, + const struct sa *mapped_addr, + const struct stun_msg *msg, + void *arg) +{ + struct turntest *tt = arg; + (void)reason; + (void)msg; + + ++tt->n_alloc_resp; + + if (err) { + complete_test(tt, err); + return; + } + + if (scode) { + complete_test(tt, EPROTO); + return; + } + + TEST_SACMP(&tt->turnsrv->relay, relay_addr, SA_ALL); + TEST_SACMP(&tt->cli, mapped_addr, SA_ALL); + + /* Permission is needed for sending data */ + err = turnc_add_perm(tt->turnc, &tt->peer, NULL, NULL); + if (err) + goto out; + + err |= send_payload(tt, 36, &tt->peer, test_payload); + if (err) { + DEBUG_WARNING("failed to send payload (%m)\n", err); + complete_test(tt, err); + } + + err = turnc_add_chan(tt->turnc, &tt->peer, + turnc_chan_handler, tt); + if (err) + goto out; + + out: + if (err) + complete_test(tt, err); +} + + +static void peer_udp_recv(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct turntest *tt = arg; + int err = 0; + (void)src; + + ++tt->n_peer_recv; + + err = udp_send(tt->us_peer, src, mb); + TEST_ERR(err); + + TEST_MEMCMP(test_payload, strlen(test_payload), + mbuf_buf(mb), mbuf_get_left(mb)); + + if (re_atomic_rlx(&tt->rx_state) > RX_NULL) + return; + + out: + if (err || is_complete(tt)) + complete_test(tt, err); +} + + +static void cli_udp_recv(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct turntest *tt = arg; + (void)src; + + if (re_atomic_rlx(&tt->rx_state) == RX_DETACH) { + udp_thread_detach(tt->us_cli); + re_atomic_rlx_set(&tt->rx_state, RX_ATTACH); + } + + udp_send(tt->us_cli, src, mb); +} + + +static void data_handler(struct turntest *tt, struct mbuf *mb) +{ + (void)tt; + (void)mb; +} + + +static void tcp_estab_handler(void *arg) +{ + struct turntest *tt = arg; + int err; + + err = tcp_conn_local_get(tt->tc, &tt->cli); + if (err) + goto out; + + err = turnc_alloc(&tt->turnc, NULL, IPPROTO_TCP, tt->tc, + 0, &tt->turnsrv->laddr_tcp, + "username", "password", tt->lifetime, + turnc_handler, tt); + if (err) + goto out; + + out: + if (err) + complete_test(tt, err); +} + + +static void tcp_recv_handler(struct mbuf *mb, void *arg) +{ + struct turntest *tl = arg; + int err = 0; + + if (tl->mb) { + size_t pos; + + pos = tl->mb->pos; + + tl->mb->pos = tl->mb->end; + + err = mbuf_write_mem(tl->mb, mbuf_buf(mb),mbuf_get_left(mb)); + if (err) + goto out; + + tl->mb->pos = pos; + } + else { + tl->mb = mem_ref(mb); + } + + for (;;) { + + size_t len, pos, end; + struct sa src; + uint16_t typ; + + if (mbuf_get_left(tl->mb) < 4) + break; + + typ = ntohs(mbuf_read_u16(tl->mb)); + len = ntohs(mbuf_read_u16(tl->mb)); + + if (typ < 0x4000) + len += STUN_HEADER_SIZE; + else if (typ < 0x8000) + len += 4; + else { + err = EBADMSG; + goto out; + } + + tl->mb->pos -= 4; + + if (mbuf_get_left(tl->mb) < len) + break; + + pos = tl->mb->pos; + end = tl->mb->end; + + tl->mb->end = pos + len; + + err = turnc_recv(tl->turnc, &src, tl->mb); + if (err) + goto out; + + if (mbuf_get_left(tl->mb)) + data_handler(tl, tl->mb); + + /* 4 byte alignment */ + while (len & 0x03) + ++len; + + tl->mb->pos = pos + len; + tl->mb->end = end; + + if (tl->mb->pos >= tl->mb->end) { + tl->mb = mem_deref(tl->mb); + break; + } + } + + out: + if (err) + complete_test(tl, err); +} + + +static void tcp_close_handler(int err, void *arg) +{ + struct turntest *tt = arg; + + if (err == ECONNRESET) { + re_printf("translate ECONNRESET -> ENOMEM\n"); + err = ENOMEM; + } + + if (err) + complete_test(tt, err); +} + + +static int turntest_alloc(struct turntest **ttp, int proto, uint32_t lifetime) +{ + struct turntest *tt; + struct sa laddr; + int err; + + tt = mem_zalloc(sizeof(*tt), destructor); + if (!tt) + return ENOMEM; + + tt->proto = proto; + tt->lifetime = lifetime; + + err = sa_set_str(&laddr, "127.0.0.1", 0); + if (err) + goto out; + + if (proto == IPPROTO_UDP) { + err |= udp_listen(&tt->us_cli, &laddr, cli_udp_recv, tt); + if (err) + goto out; + + err = udp_local_get(tt->us_cli, &tt->cli); + if (err) + goto out; + } + + err = udp_listen(&tt->us_peer, &laddr, peer_udp_recv, tt); + if (err) + goto out; + + err = udp_local_get(tt->us_peer, &tt->peer); + if (err) + goto out; + + err = turnserver_alloc(&tt->turnsrv); + if (err) + goto out; + + switch (proto) { + + case IPPROTO_UDP: + err = turnc_alloc(&tt->turnc, NULL, proto, tt->us_cli, + 0, &tt->turnsrv->laddr, + "username", "password", lifetime, + turnc_handler, tt); + break; + + case IPPROTO_TCP: + err = tcp_connect(&tt->tc, &tt->turnsrv->laddr_tcp, + tcp_estab_handler, tcp_recv_handler, + tcp_close_handler, tt); + break; + } + if (err) + goto out; + + out: + if (err) + mem_deref(tt); + else if (ttp) + *ttp = tt; + + return err; +} + + +int test_turn(void) +{ + struct turntest *tt; + int err; + + err = turntest_alloc(&tt, IPPROTO_UDP, 600); + if (err) + return err; + + err = re_main_timeout(200); + TEST_ERR(err); + + err = tt->err; + TEST_ERR(err); + + /* verify results after test is complete */ + + TEST_EQUALS(1, tt->n_alloc_resp); + TEST_EQUALS(1, tt->n_chan_resp); + TEST_EQUALS(2, tt->n_peer_recv); + + TEST_ASSERT(tt->turnsrv->n_allocate >= 1); + TEST_ASSERT(tt->turnsrv->n_chanbind >= 1); + TEST_ASSERT(tt->turnsrv->n_raw >= 1); + TEST_EQUALS(1, tt->turnsrv->n_send); + + out: + mem_deref(tt); + + return err; +} + + +int test_turn_tcp(void) +{ + struct turntest *tt; + int err; + + err = turntest_alloc(&tt, IPPROTO_TCP, 600); + if (err) + return err; + + err = re_main_timeout(200); + TEST_ERR(err); + + err = tt->err; + TEST_ERR(err); + + /* verify results after test is complete */ + + TEST_EQUALS(1, tt->n_alloc_resp); + TEST_EQUALS(1, tt->n_chan_resp); + TEST_EQUALS(2, tt->n_peer_recv); + + TEST_ASSERT(tt->turnsrv->n_allocate >= 1); + TEST_ASSERT(tt->turnsrv->n_chanbind >= 1); + TEST_ASSERT(tt->turnsrv->n_raw >= 1); + TEST_EQUALS(1, tt->turnsrv->n_send); + + out: + mem_deref(tt); + + return err; +} + + +static void tmr_handler(void *arg) +{ + struct turntest *tt = arg; + + if (re_atomic_rlx(&tt->rx_state) == RX_CLOSE) + re_cancel(); + + if (re_atomic_rlx(&tt->rx_state) == RX_ATTACH) { + udp_thread_attach(tt->us_cli); + re_atomic_rlx_set(&tt->rx_state, RX_READY); + } + + tmr_start(&tt->tmr, 0, tmr_handler, tt); +} + + +static int turn_thread(void *arg) +{ + struct turntest *tt = arg; + int err; + + re_thread_init(); + + tmr_init(&tt->tmr); + tmr_start(&tt->tmr, 0, tmr_handler, tt); + + err = re_main(NULL); + TEST_ERR(err); + + tmr_cancel(&tt->tmr); + +out: + re_thread_close(); + return err; +} + + +int test_turn_thread(void) +{ + struct turntest *tt; + int err; + + err = turntest_alloc(&tt, IPPROTO_UDP, 0); + if (err) + return err; + + re_atomic_rlx_set(&tt->rx_state, RX_DETACH); + + thread_create_name(&tt->thr, "test_turn_thread", turn_thread, tt); + + re_main_timeout(500); + + re_atomic_rlx_set(&tt->rx_state, RX_CLOSE); + thrd_join(tt->thr, &err); + TEST_ERR(err); + + err = tt->err; + TEST_ERR(err); + +out: + mem_deref(tt); + + return err; +} diff --git a/test/udp.c b/test/udp.c new file mode 100644 index 000000000..22a8d3243 --- /dev/null +++ b/test/udp.c @@ -0,0 +1,215 @@ +/** + * @file udp.c UDP testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "udptest" +#define DEBUG_LEVEL 5 +#include + + +struct udp_test { + struct udp_sock *usc; + struct udp_sock *uss; + struct udp_helper *uh; + struct sa cli; + struct sa srv; + int tindex; + int err; +}; + + +static const char *data0 = "data from client to server"; + + +static void destructor(void *arg) +{ + struct udp_test *ut = arg; + mem_deref(ut->uh); + mem_deref(ut->usc); + mem_deref(ut->uss); +} + + +static int send_data(struct udp_sock *us, const struct sa *peer, + const char *data) +{ + struct mbuf *mb = mbuf_alloc(strlen(data) + 1); + int err; + if (!mb) + return ENOMEM; + + (void)mbuf_write_str(mb, data); + mb->pos = 0; + + err = udp_send(us, peer, mb); + + mem_deref(mb); + + return err; +} + + +static bool mbuf_compare(const struct mbuf *mb, const char *str) +{ + if (mbuf_get_left(mb) != strlen(str)) + return false; + + if (0 != memcmp(mbuf_buf(mb), str, strlen(str))) + return false; + + return true; +} + + +static void udp_recv_client(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct udp_test *ut = arg; + + switch (ut->tindex++) { + + case 0: + if (!mbuf_compare(mb, data0)) { + ut->err = EBADMSG; + break; + } + if (!sa_cmp(src, &ut->srv, SA_ALL)) { + ut->err = EPROTO; + break; + } + break; + + default: + ut->err = ERANGE; + break; + } + + if (ut->tindex >= 1) + re_cancel(); +} + + +/* Echo server */ +static void udp_recv_server(const struct sa *src, struct mbuf *mb, void *arg) +{ + struct udp_test *ut = arg; + int err; + + err = udp_send(ut->uss, src, mb); + if (err) + ut->err = err; +} + + +static bool udp_helper_send(int *err, struct sa *dst, + struct mbuf *mb, void *arg) +{ + struct udp_test *ut = arg; + const size_t pos = mb->pos; + + if (!sa_cmp(dst, &ut->srv, SA_ALL)) { + *err = EPROTO; + return false; + } + + if (!mbuf_compare(mb, data0)) { + *err = EBADMSG; + return false; + } + + /* Append a fake protocol trailer */ + mb->pos = mb->end; + *err = mbuf_write_str(mb, "ABCD"); + + mb->pos = pos; + + return false; +} + + +static bool udp_helper_recv(struct sa *src, struct mbuf *mb, void *arg) +{ + struct udp_test *ut = arg; + + if (!sa_cmp(src, &ut->srv, SA_ALL)) + ut->err = EPROTO; + + mb->end -= 4; + + if (!mbuf_compare(mb, data0)) + ut->err = EBADMSG; + + return false; +} + + +int test_udp(void) +{ + struct udp_sock *uss2; + struct udp_test *ut; + int layer = 0; + int err; + + ut = mem_zalloc(sizeof(*ut), destructor); + if (!ut) + return ENOMEM; + + err = sa_set_str(&ut->cli, "127.0.0.1", 0); + err |= sa_set_str(&ut->srv, "127.0.0.1", 0); + if (err) + goto out; + + err = udp_listen(&ut->usc, &ut->cli, udp_recv_client, ut); + err |= udp_listen(&ut->uss, &ut->srv, udp_recv_server, ut); + if (err) + goto out; + + udp_rxbuf_presz_set(ut->uss, 16); + + err = udp_local_get(ut->usc, &ut->cli); + err |= udp_local_get(ut->uss, &ut->srv); + if (err) + goto out; + + TEST_ASSERT(NULL == udp_helper_find(ut->usc, layer)); + + err = udp_register_helper(&ut->uh, ut->usc, layer, + udp_helper_send, udp_helper_recv, ut); + if (err) + goto out; + + TEST_ASSERT(NULL != udp_helper_find(ut->usc, layer)); + + /* expect failure */ + if (!udp_listen(&uss2, &ut->srv, udp_recv_client, ut)) { + err = EBUSY; + goto out; + } + + /* Send from connected client UDP socket */ + err = udp_connect(ut->usc, &ut->srv); + if (err) + goto out; + + /* Start test */ + err = send_data(ut->usc, &ut->srv, data0); + if (err) + goto out; + + err = re_main_timeout(100); + if (err) + goto out; + + if (ut->err) + err = ut->err; + + out: + mem_deref(ut); + + return err; +} diff --git a/test/unixsock.c b/test/unixsock.c new file mode 100644 index 000000000..70d81026b --- /dev/null +++ b/test/unixsock.c @@ -0,0 +1,60 @@ +/** + * @file src/unixsock.c Unix domain sockets + * + * Copyright (C) 2022 Sebastian Reimers + */ + +#ifdef HAVE_UNISTD_H +#include +#endif +#include +#include "test.h" +#ifdef WIN32 +#define unlink _unlink +#endif + +#define DEBUG_MODULE "unixsock" +#define DEBUG_LEVEL 5 +#include + + +static void http_req_handler(struct http_conn *conn, + const struct http_msg *msg, void *arg) +{ + (void)conn; + (void)msg; + (void)arg; +} + + +int test_unixsock(void) +{ + struct sa srv; + re_sock_t fd = RE_BAD_SOCK; + struct http_sock *sock; + int err; + char filename[32]; + char socket[128]; + + rand_str(filename, sizeof(filename)); + re_snprintf(socket, sizeof(socket), "unix:http_%s.sock", filename); + + err = sa_set_str(&srv, socket, 0); + TEST_ERR(err); + + err = unixsock_listen_fd(&fd, &srv); + TEST_ERR(err); + + err = http_listen_fd(&sock, fd, http_req_handler, NULL); + TEST_ERR(err); + + mem_deref(sock); + + err = unlink(&socket[5]); + TEST_ERR(err); + +out: + if (err) + (void)unlink(&socket[5]); + return err; +} diff --git a/test/uri.c b/test/uri.c new file mode 100644 index 000000000..83c4785c4 --- /dev/null +++ b/test/uri.c @@ -0,0 +1,439 @@ +/** + * @file uri.c URI Testcode + * + * Copyright (C) 2010 Creytiv.com + */ +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "testuri" +#define DEBUG_LEVEL 5 +#include + + +int test_uri(void) +{ + const char *uriv[] = { + "sip:user:pass@host:5060;transport=udp", + "sip:user@host:5060;transport=udp", + "sip:user@host:5060", + "sip:host:5060", + "sip:host", + "sip:user@81.187.91.2:28481", + "sip:user@81.187.91.2:28481", + "sip:81.187.91.2:28481", + "sips:81.187.91.2:28481", + "sip:192.168.43.83:443/wss/;transport=wss", + + /* RFC 3261 */ + "sip:alice@atlanta.com", + "sip:alice:secretword@atlanta.com;transport=tcp", + "sips:alice@atlanta.com?subject=project%20&priority=urgent", + "sip:+1-212-555-1212:1234@gateway.com;user=phone", + "sips:1212@gateway.com", + "sip:alice@192.0.2.4", + "sip:atlanta.com;method=REGISTER?to=alice%40atlanta.com", + "sip:alice;day=tuesday@atlanta.com", + +#ifdef HAVE_INET6 + /* IPv6 */ + "sip:[::1];transport=udp", + "sip:[::1]:1234;transport=udp", + "sip:user@[::1];transport=udp", + "sip:user:pass@[::1];transport=udp", + "sip:user@[::1]:1234;transport=udp", + "sip:user:pass@[::1]:1234;transport=udp", + "sip:user@[::1]:1234;transport=udp?foo=bar", + "sip:user:pass@[::1]:1234;transport=udp?foo=bar", + + /* draft-ietf-sipping-ipv6-torture-tests-00 */ + "sip:[2001:db8::10]", + "sip:[2001:db8::10:5070]", + "sip:[2001:db8::10]:5070", + "sip:user@[2001:db8::10]", +#endif + }; + struct uri uri; + struct mbuf mb; + int err = EINVAL; + size_t i; + + mbuf_init(&mb); + + for (i=0; i") + } + }; + struct mbuf mbe, mbd; + int err = EINVAL; + size_t i; + + mbuf_init(&mbd); + mbuf_init(&mbe); + + for (i=0; i +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "vid" +#define DEBUG_LEVEL 5 +#include + + +static const enum vidfmt fmtv[VID_FMT_N] = { + VID_FMT_YUV420P, + VID_FMT_YUYV422, + VID_FMT_UYVY422, + VID_FMT_RGB32, + VID_FMT_ARGB, + VID_FMT_NV12, + VID_FMT_NV21, + VID_FMT_YUV444P, + VID_FMT_YUV422P, +}; + + +static int test_vidsz_cmp(void) +{ + struct vidsz a = {64, 64}, b = {64, 64}, c = {32, 32}; + int err = 0; + + TEST_ASSERT(!vidsz_cmp(NULL, NULL)); + TEST_ASSERT( vidsz_cmp(&a, &a)); + TEST_ASSERT( vidsz_cmp(&a, &b)); + TEST_ASSERT(!vidsz_cmp(&a, &c)); + + out: + return err; +} + + +static int test_vidrect_cmp(void) +{ + struct vidrect a = {10, 10, 30, 30}; + struct vidrect b = {10, 10, 30, 30}; + struct vidrect c = {10, 10, 40, 40}; + struct vidrect d = {20, 20, 30, 30}; + int err = 0; + + TEST_ASSERT(!vidrect_cmp(NULL, NULL)); + TEST_ASSERT( vidrect_cmp(&a, &a)); + TEST_ASSERT( vidrect_cmp(&a, &b)); + TEST_ASSERT(!vidrect_cmp(&a, &c)); + TEST_ASSERT(!vidrect_cmp(&a, &d)); + + out: + return err; +} + + +static int test_vidframe_size(void) +{ + static const struct vidsz vidsz = {32, 32}; + size_t i; + int err = 0; + + for (i=0; ilinesize[0]); + TEST_ASSERT(vidsz_cmp(&vidsz, &vf->size)); + TEST_EQUALS(fmtv[i], vf->fmt); + + vf = mem_deref(vf); + } + + out: + mem_deref(vf); + return err; +} + + +/* + * Create one RGB32 pixel in native endianess + */ +#define RGB32(r, g, b) (r)<<16 | (g)<<8 | (b) + + +/* + * Test a RGB32 Video-frame with 2 x 2 pixels and 3 RGB pixel + * + * .--+---- + * |RG| + * |B | + * +--+---- + * | + * | + */ +static int test_vidframe_rgb32_2x2_red(void) +{ + struct vidframe vf; + struct vidsz sz = {2, 2}; + uint8_t buf[2*4 + 2*4]; + const uint32_t pix[2][2] = { + + { RGB32(255U, 0, 0), RGB32(0, 255U, 0) }, + { RGB32(0, 0, 255U), RGB32(0, 0, 0) } + }; + int err = 0; + + memset(buf, 0, sizeof(buf)); + vidframe_init_buf(&vf, VID_FMT_RGB32, &sz, buf); + + TEST_EQUALS(buf, vf.data[0]); + TEST_EQUALS(NULL, vf.data[1]); + TEST_EQUALS(NULL, vf.data[2]); + TEST_EQUALS(NULL, vf.data[3]); + + TEST_EQUALS(8, vf.linesize[0]); + TEST_EQUALS(0, vf.linesize[1]); + TEST_EQUALS(0, vf.linesize[2]); + TEST_EQUALS(0, vf.linesize[3]); + + TEST_EQUALS(2, vf.size.w); + TEST_EQUALS(2, vf.size.h); + + TEST_EQUALS(VID_FMT_RGB32, vf.fmt); + + vidframe_draw_point(&vf, 0, 0, 255, 0, 0); + vidframe_draw_point(&vf, 1, 0, 0, 255, 0); + vidframe_draw_point(&vf, 0, 1, 0, 0, 255); + vidframe_draw_point(&vf, 1, 1, 0, 0, 0); + + TEST_MEMCMP(pix[0], sizeof(pix[0]), vf.data[0], 8); + TEST_MEMCMP(pix[1], sizeof(pix[1]), vf.data[0] + vf.linesize[0], 8); + + out: + return err; +} + + +static int test_vidframe_yuv_2x2_white(enum vidfmt fmt, unsigned chroma) +{ + struct vidframe *vf; + struct vidsz sz = {2, 2}; + const uint8_t ypix[4] = {235, 235, 235, 235}; + const uint8_t uvpix[4] = {128, 128, 128, 128}; + const unsigned chroma_sq = chroma * chroma; + int err; + + err = vidframe_alloc(&vf, fmt, &sz); + if (err) + return err; + + vidframe_fill(vf, 255, 255, 255); + + TEST_NOT_EQUALS(NULL, vf->data[0]); + TEST_NOT_EQUALS(NULL, vf->data[1]); + TEST_NOT_EQUALS(NULL, vf->data[2]); + TEST_EQUALS(NULL, vf->data[3]); + + TEST_EQUALS(2, vf->linesize[0]); + TEST_EQUALS(chroma, vf->linesize[1]); + TEST_EQUALS(chroma, vf->linesize[2]); + TEST_EQUALS(0, vf->linesize[3]); + + TEST_EQUALS(2, vf->size.w); + TEST_EQUALS(2, vf->size.h); + + TEST_EQUALS(fmt, vf->fmt); + + TEST_ASSERT(chroma_sq <= sizeof(uvpix)); + TEST_MEMCMP(ypix, sizeof(ypix), vf->data[0], 4); + TEST_MEMCMP(uvpix, chroma_sq, vf->data[1], chroma_sq); + TEST_MEMCMP(uvpix, chroma_sq, vf->data[2], chroma_sq); + + out: + mem_deref(vf); + + return err; +} + + +static int test_vid_draw(void) +{ + static const struct vidsz vidsz = {320, 240}; + struct vidframe *vf = NULL, *vf2 = NULL; + int err = 0; + + static const enum vidfmt drawfmtv[] = { + VID_FMT_YUV420P, + VID_FMT_NV12, + VID_FMT_NV21, + VID_FMT_YUV444P, + VID_FMT_YUV422P, + }; + + for (size_t i=0; ifmt == VID_FMT_YUV422P) { + + ASSERT_EQ(320, vf->linesize[0]); + ASSERT_EQ(160, vf->linesize[1]); + ASSERT_EQ(160, vf->linesize[2]); + ASSERT_EQ( 0, vf->linesize[3]); + } + + for (unsigned x=0; x +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "vidconv" +#define DEBUG_LEVEL 5 +#include + + +/* + * http://en.wikipedia.org/wiki/YCbCr + * + * ITU-R BT.601 conversion + * + * Digital YCbCr can derived from digital R'dG'dB'd + * (8 bits per sample, each using the full range with + * zero representing black and 255 representing white) + * according to the following equations: + */ + + +#if 0 +static double rgb2y_ref(int r, int g, int b) +{ + return 16.0 + (65.738*r)/256 + (129.057*g)/256 + (25.064*b)/256; +} + +static double rgb2u_ref(int r, int g, int b) +{ + return 128 - 37.945*r/256 - 74.494*g/256 + 112.439*b/256; +} + +static double rgb2v_ref(int r, int g, int b) +{ + return 128 + 112.439*r/256 - 94.154*g/256 - 18.285*b/256; +} +#endif + + +#define CLIP(X) ( (X) > 255 ? 255 : (X) < 0 ? 0 : X) + + +/* RGB -> YUV */ +#define RGB2Y(R, G, B) \ + CLIP(( ( 66 * (R) + 129 * (G) + 25 * (B) + 128) >> 8) + 16) +#define RGB2U(R, G, B) \ + CLIP(( ( -38 * (R) - 74 * (G) + 112 * (B) + 128) >> 8) + 128) +#define RGB2V(R, G, B) \ + CLIP(( ( 112 * (R) - 94 * (G) - 18 * (B) + 128) >> 8) + 128) + + +#define test_vid_rgb2yuv_color(r, g, b) \ + \ + TEST_EQUALS(RGB2Y(r, g, b), rgb2y(r, g, b)); \ + TEST_EQUALS(RGB2U(r, g, b), rgb2u(r, g, b)); \ + TEST_EQUALS(RGB2V(r, g, b), rgb2v(r, g, b)); + + +static int test_vid_rgb2yuv(void) +{ + int r, g, b; + int err = 0; + + /* combine 2 color components */ + for (r=0; r<256; r++) { + for (g=0; g<256; g++) { + test_vid_rgb2yuv_color(r, g, 0); + } + } + + for (r=0; r<256; r++) { + for (b=0; b<256; b++) { + test_vid_rgb2yuv_color(r, 0, b); + } + } + + for (g=0; g<256; g++) { + for (b=0; b<256; b++) { + test_vid_rgb2yuv_color(0, g, b); + } + } + + out: + return err; +} + + +static bool vidframe_cmp(const struct vidframe *a, const struct vidframe *b) +{ + int i; + + if (!a || !b) + return false; + + if (a->fmt != b->fmt) + return false; + + for (i=0; i<4; i++) { + + if (a->linesize[i] != b->linesize[i]) + return false; + + if (!a->data[i] != !b->data[i]) + return false; + + if (a->data[i] && b->data[i]) { + + if (memcmp(a->data[i], b->data[i], a->linesize[i])) + return false; + } + } + + return vidsz_cmp(&a->size, &b->size); +} + + +static void vidframe_dump(const struct vidframe *f) +{ + int i; + + if (!f) + return; + + (void)re_printf("vidframe %u x %u:\n", f->size.w, f->size.h); + + for (i=0; i<4; i++) { + + if (f->linesize[i] && f->data[i]) { + + (void)re_printf("%d: %u bytes [%w]\n", + i, f->linesize[i], f->data[i], + (size_t)min(f->linesize[i], 16)); + } + } +} + + +static void write_pattern(uint8_t *buf, size_t len) +{ + size_t i; + + for (i=0; idata[i]) + write_pattern(f0->data[i], f0->linesize[i]); + } + + vidconv(f1, f0, &rect1); + vidconv(f2, f1, &rect2); + + if (!vidframe_cmp(f2, f0)) { + + vidframe_dump(f0); + vidframe_dump(f2); + + err = EBADMSG; + goto out; + } + + out: + mem_deref(f2); + mem_deref(f1); + mem_deref(f0); + + return err; +} + + +/* + * verify that pixel conversion between different planar and packed + * pixel formats is working + */ +int test_vidconv_pixel_formats(void) +{ + struct plane { + size_t sz; + const char *data; + }; + static const struct test { + enum vidfmt src_fmt; + struct plane src_planev[3]; + enum vidfmt dst_fmt; + struct plane dst_planev[3]; + } testv [] = { + + /* UYVY422 to YUV420P */ + { + VID_FMT_UYVY422, + { {32, + "\x20\x00\x30\x01" + "\x21\x02\x31\x03" + "\x20\x04\x30\x05" + "\x21\x06\x31\x07" + "\x22\x08\x32\x09" + "\x23\x0a\x33\x0b" + "\x22\x0c\x32\x0d" + "\x23\x0e\x33\x0f"}, + {0,0}, + {0,0} + }, + + VID_FMT_YUV420P, + { {16, + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"}, + {4, "\x20\x21\x22\x23"}, + {4, "\x30\x31\x32\x33"} + }, + }, + + /* NV12 to YUV420P */ + { + VID_FMT_NV12, + { {16, + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"}, + {8, "\x20\x30\x21\x31\x22\x32\x23\x33"}, + {0,0} + }, + + VID_FMT_YUV420P, + { {16, + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"}, + {4, "\x20\x21\x22\x23"}, + {4, "\x30\x31\x32\x33"} + }, + }, + + /* YUV420P to NV12 */ + { + VID_FMT_YUV420P, + { {16, + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"}, + {4, "\x20\x21\x22\x23"}, + {4, "\x30\x31\x32\x33"} + }, + + VID_FMT_NV12, + { {16, + "\x00\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"}, + {8, "\x20\x30\x21\x31\x22\x32\x23\x33"}, + {0,0} + }, + }, + +#if 0 + /* RGB32 to YUV444P */ + { + VID_FMT_RGB32, + { { (16*4), + "\x00\x00\x00\x00" "\x00\x00\x00\x00" /* black */ + "\xff\x00\x00\x00" "\xff\x00\x00\x00" /* red */ + "\x00\xff\x00\x00" "\x00\xff\x00\x00" /* green */ + "\x00\x00\xff\x00" "\x00\x00\xff\x00" /* blue */ + "\xff\x00\xff\x00" "\xff\x00\xff\x00" + "\x00\xff\xff\x00" "\x00\xff\xff\x00" + "\xff\xff\x00\x00" "\xff\xff\x00\x00" + "\xff\xff\xff\xff" "\xff\xff\xff\xff"},/* white */ + + {0, ""}, + {0, ""} + }, + + VID_FMT_YUV444P, + { {16, + "\x10\x10\x29\x29" "\x90\x90\x52\x52" + "\x6b\x6b\xd2\xd2" "\xa9\xa9\xeb\xeb"}, + {16, + "\x80\x80\xf0\xf0" "\x36\x36\x5a\x5a" + "\xca\xca\x10\x10" "\xa6\xa6\x80\x80"}, + {16, + "\x80\x80\x6e\x6e" "\x22\x22\xf0\xf0" + "\xde\xde\x92\x92" "\x10\x10\x80\x80"}, + }, + }, +#endif + + }; + struct vidframe *fsrc = NULL, *fdst = NULL; + const struct vidsz sz = {4, 4}; + unsigned i, p; + int err = 0; + + for (i=0; isrc_fmt), + vidfmt_name(test->dst_fmt)); +#endif + + err |= vidframe_alloc(&fsrc, test->src_fmt, &sz); + err |= vidframe_alloc(&fdst, test->dst_fmt, &sz); + if (err) + goto out; + + for (p=0; p<3; p++) { + if (test->src_planev[p].sz) { + memcpy(fsrc->data[p], + test->src_planev[p].data, + test->src_planev[p].sz); + } + } + + vidconv(fdst, fsrc, 0); + + for (p=0; p<3; p++) { + + size_t size = test->dst_planev[p].sz; + + TEST_MEMCMP(test->dst_planev[p].data, + test->dst_planev[p].sz, + fdst->data[p], + size); + } + + fdst = mem_deref(fdst); + fsrc = mem_deref(fsrc); + } + + out: + mem_deref(fsrc); + mem_deref(fdst); + + return err; +} + + +int test_vidconv(void) +{ + int err; + + err = test_vid_rgb2yuv(); + if (err) + return err; + + return err; +} + + +int test_vidconv_scaling(void) +{ + int err = 0; + + err = test_vidconv_scaling_base(VID_FMT_YUV420P); + err |= test_vidconv_scaling_base(VID_FMT_NV12); + if (err) + return err; + + return err; +} diff --git a/test/websock.c b/test/websock.c new file mode 100644 index 000000000..7d0bee346 --- /dev/null +++ b/test/websock.c @@ -0,0 +1,223 @@ +/** + * @file websock.c Websockets Testcode + * + * Copyright (C) 2010 Creytiv.com + */ + +#include +#include +#include "test.h" + + +#define DEBUG_MODULE "test_websock" +#define DEBUG_LEVEL 5 +#include + + +struct test { + struct websock *ws; + struct websock_conn *wc_cli; + struct websock_conn *wc_srv; + uint32_t n_estab_cli; + uint32_t n_recv_cli; + uint32_t n_recv_srv; + int err; +}; + +static const char test_payload[] = "hello websockets"; +static const char custom_useragent[] = "Retest v0.1"; + + +static void abort_test(struct test *t, int err) +{ + t->err = err; + re_cancel(); +} + + +static void done(struct test *t) +{ + t->wc_cli = mem_deref(t->wc_cli); + t->wc_srv = mem_deref(t->wc_srv); + + websock_shutdown(t->ws); +} + + +static void srv_websock_recv_handler(const struct websock_hdr *hdr, + struct mbuf *mb, void *arg) +{ + struct test *test = arg; + int err; + + test->n_recv_srv++; + + /* ECHO */ + err = websock_send(test->wc_srv, hdr->opcode, + "%b", mbuf_buf(mb), mbuf_get_left(mb)); + if (err) + abort_test(test, err); +} + + +static void srv_websock_close_handler(int err, void *arg) +{ + struct test *test = arg; + (void)test; + (void)err; +} + + +static void websock_shutdown_handler(void *arg) +{ + abort_test(arg, 0); +} + + +static void http_req_handler(struct http_conn *conn, + const struct http_msg *msg, void *arg) +{ + struct test *test = arg; + int err; + + TEST_ASSERT(http_msg_hdr_has_value(msg, HTTP_HDR_USER_AGENT, + custom_useragent)); + + err = websock_accept(&test->wc_srv, test->ws, conn, msg, + 0, srv_websock_recv_handler, + srv_websock_close_handler, test); + out: + if (err) + abort_test(test, err); +} + + +static void cli_websock_estab_handler(void *arg) +{ + struct test *test = arg; + int err; + + test->n_estab_cli++; + + err = websock_send(test->wc_cli, WEBSOCK_TEXT, test_payload); + if (err) + abort_test(test, err); +} + + +static void cli_websock_recv_handler(const struct websock_hdr *hdr, + struct mbuf *mb, void *arg) +{ + struct test *test = arg; + int err = 0; + + test->n_recv_cli++; + + TEST_EQUALS(WEBSOCK_TEXT, hdr->opcode); + + TEST_STRCMP(test_payload, strlen(test_payload), + mbuf_buf(mb), mbuf_get_left(mb)); + + done(test); + + out: + if (err) + abort_test(test, err); +} + + +static void cli_websock_close_handler(int err, void *arg) +{ + struct test *test = arg; + (void)test; + (void)err; + + /* translate error code */ + if (err) { + abort_test(test, ENOMEM); + } +} + + +static int test_websock_loop(void) +{ + struct http_sock *httpsock = NULL; + struct http_cli *http_cli = NULL; + struct dnsc *dnsc = NULL; + struct sa srv, dns; + struct test test; + char uri[256]; + int err = 0; + + memset(&test, 0, sizeof(test)); + + err |= sa_set_str(&srv, "127.0.0.1", 0); + err |= sa_set_str(&dns, "127.0.0.1", 53); /* note: unused */ + if (err) + goto out; + + err = http_listen(&httpsock, &srv, http_req_handler, &test); + if (err) + goto out; + + err = tcp_sock_local_get(http_sock_tcp(httpsock), &srv); + if (err) + goto out; + + err = dnsc_alloc(&dnsc, NULL, &dns, 1); + if (err) + goto out; + + err = http_client_alloc(&http_cli, dnsc); + if (err) + goto out; + + err = websock_alloc(&test.ws, websock_shutdown_handler, &test); + if (err) + goto out; + + (void)re_snprintf(uri, sizeof(uri), + "http://127.0.0.1:%u/", sa_port(&srv)); + err = websock_connect(&test.wc_cli, test.ws, + http_cli, uri, 0, + cli_websock_estab_handler, + cli_websock_recv_handler, + cli_websock_close_handler, &test, + "User-Agent: %s\r\n", custom_useragent); + if (err) + goto out; + + err = re_main_timeout(500); + if (err) + goto out; + + if (test.err) { + err = test.err; + goto out; + } + + /* verify results after traffic is successfully done */ + TEST_EQUALS(1, test.n_estab_cli); + TEST_EQUALS(1, test.n_recv_cli); + TEST_EQUALS(1, test.n_recv_srv); + + out: + mem_deref(httpsock); + mem_deref(test.wc_cli); + mem_deref(test.ws); + mem_deref(test.wc_srv); + mem_deref(http_cli); + mem_deref(dnsc); + + return err; +} + + +int test_websock(void) +{ + int err = 0; + + err |= test_websock_loop(); + + return err; +} From 131c649bde48c580105efc56dca0b3af13f4bf90 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 16:00:17 +0100 Subject: [PATCH 02/16] test: try to skip find_package --- test/CMakeLists.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5fdfe32c3..e402d9195 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,7 +19,7 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # Module/Package Includes # -find_package(RE REQUIRED) +#find_package(RE REQUIRED) ############################################################################## # @@ -59,10 +59,9 @@ endif() include_directories( ../include . - ${RE_INCLUDE_DIRS} ) -find_package(re CONFIG REQUIRED HINTS ../re/cmake) +find_package(re CONFIG REQUIRED HINTS ../cmake) ############################################################################## # @@ -161,7 +160,7 @@ endif() # Main target object # -set(LINKLIBS re ${RE_LIBRARIES} ${OPENSSL_LIBRARIES}) +set(LINKLIBS re ${OPENSSL_LIBRARIES}) if(WIN32) list(APPEND LINKLIBS qwave iphlpapi wsock32 ws2_32) else() From 95769f661dc2fbade86aa3a9c1548104d55966bd Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 16:09:18 +0100 Subject: [PATCH 03/16] test: fix clang warning --- test/combo/dtls_turn.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/combo/dtls_turn.c b/test/combo/dtls_turn.c index 385c082d3..ca849235e 100644 --- a/test/combo/dtls_turn.c +++ b/test/combo/dtls_turn.c @@ -416,7 +416,10 @@ int test_dtls_turn(void) goto out; /* connect the 2 agents */ - a->peer = b; b->peer = a; + if (a) + a->peer = b; + if (b) + b->peer = a; /* start it! */ err = re_main_timeout(1000); From f2fc1819d24013fc0d749a3d9234abe7c465e542 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 16:11:20 +0100 Subject: [PATCH 04/16] ci: temp reduce coverage --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index eb04204b3..ef3d61076 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -57,7 +57,7 @@ jobs: - name: coverage check run: | - min_cov="57.0" + min_cov="40.0" cov=$(~/.local/bin/gcovr -r . -s | grep lines | awk '{ print $2 }' | sed 's/%//') echo "Coverage: ${cov}% (min $min_cov%)" exit $(echo "$cov < $min_cov" | bc -l) From 566e8e63920352b48a8321a4794ebd95cbfa1016 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 16:19:47 +0100 Subject: [PATCH 05/16] test: fix another clang warning --- test/combo/dtls_turn.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/test/combo/dtls_turn.c b/test/combo/dtls_turn.c index ca849235e..eba23e09e 100644 --- a/test/combo/dtls_turn.c +++ b/test/combo/dtls_turn.c @@ -402,7 +402,7 @@ static bool have_dtls_support(enum tls_method method) int test_dtls_turn(void) { - struct agent *a=0, *b=0; + struct agent *a = NULL, *b = NULL; int err = 0; if (!have_dtls_support(TLS_METHOD_DTLSV1)) { @@ -410,8 +410,10 @@ int test_dtls_turn(void) return ESKIPPED; } - err |= agent_alloc(&a, 0, true, true, true); - err |= agent_alloc(&b, 0, false, false, false); + err = agent_alloc(&a, 0, true, true, true); + if (err) + goto out; + err = agent_alloc(&b, 0, false, false, false); if (err) goto out; @@ -426,8 +428,12 @@ int test_dtls_turn(void) if (err) goto out; - TEST_EQUALS(0, a->err); - TEST_EQUALS(0, b->err); + if (a) { + TEST_EQUALS(0, a->err); + } + if (b) { + TEST_EQUALS(0, b->err); + } /* verify results after test is complete */ err |= agent_verify(a); From 48f7d7be2a2c749942cfcc720938a814e0c7bd6c Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 16:21:38 +0100 Subject: [PATCH 06/16] ci: update valgrind --- .github/workflows/valgrind.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml index f285a80e3..aae633f1b 100644 --- a/.github/workflows/valgrind.yml +++ b/.github/workflows/valgrind.yml @@ -25,16 +25,6 @@ jobs: run: | cmake -B build && cmake --build build -j - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: retest - repo: https://github.com/baresip/retest - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - name: retest run: | - cd .. - cd retest - cmake -B build && cmake --build build -j - valgrind --leak-check=full --show-reachable=yes --error-exitcode=42 ./build/retest -r -v + valgrind --leak-check=full --show-reachable=yes --error-exitcode=42 ./build/test/retest -r -v From 042a0f038ab7f4a2bb1872e91b24cb5c935f0233 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:04:01 +0100 Subject: [PATCH 07/16] ci: update build --- .github/workflows/build.yml | 10 +--------- test/test.c | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f181a6191..7ac68a25f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -58,14 +58,6 @@ jobs: run: | cmake -B build && cmake --build build - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: retest - repo: https://github.com/baresip/retest.git - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - name: retest run: | - cd .. - cd retest && make && ./retest -r + ./build/test/retest -r -v diff --git a/test/test.c b/test/test.c index 1e1eb8c99..f111ba79b 100644 --- a/test/test.c +++ b/test/test.c @@ -264,7 +264,7 @@ static const struct test tests_integration[] = { #ifdef DATA_PATH static char datapath[256] = DATA_PATH; #else -static char datapath[256] = "./data"; +static char datapath[256] = "./test/data"; #endif From a31d2d309e9fb72317c8170465bdb9da8388ef4b Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:14:32 +0100 Subject: [PATCH 08/16] test: fix clang warnings --- test/rtp.c | 1 + test/sip.c | 4 ++-- test/sipevent.c | 6 ++++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/test/rtp.c b/test/rtp.c index 595aee6f7..5f37ed1b3 100644 --- a/test/rtp.c +++ b/test/rtp.c @@ -370,6 +370,7 @@ int test_rtcp_decode(void) err |= mbuf_write_u8(mb, 0x55); /* overhead -- test padding */ err |= mbuf_write_mem(mb, rtcp_sdes, sizeof(rtcp_sdes)); err |= mbuf_write_u8(mb, 0xaa); /* junk */ + TEST_ERR(err); mb->pos = 1; /* SDES */ diff --git a/test/sip.c b/test/sip.c index 66dd65c43..1808a973c 100644 --- a/test/sip.c +++ b/test/sip.c @@ -711,8 +711,8 @@ static int do_sip_drequestf(struct sa *laddr) if (!sr) return ENOMEM; - err = re_snprintf(uri, 64, "sip:%J;transport=UDP", laddr); - err |= re_snprintf(touri, 64, "sip:test@%J", laddr); + re_snprintf(uri, sizeof(uri), "sip:%J;transport=UDP", laddr); + re_snprintf(touri, sizeof(touri), "sip:test@%J", laddr); err = sip_dialog_alloc(&sr->dlg, uri, touri, NULL, diff --git a/test/sipevent.c b/test/sipevent.c index 2ee2d1fa6..12a09e810 100644 --- a/test/sipevent.c +++ b/test/sipevent.c @@ -373,8 +373,10 @@ static int do_sipevent(struct sa *laddr) struct agent *a = NULL, *b = NULL; int err = 0; - err |= agent_alloc(&a, "a", laddr); - err |= agent_alloc(&b, "b", laddr); + err = agent_alloc(&a, "a", laddr); + if (err) + goto out; + err = agent_alloc(&b, "b", laddr); if (err) goto out; From 63f20b9f21b1ff8d635bb9dc9ae34ad5b079cace Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:15:36 +0100 Subject: [PATCH 09/16] ci: update coverage --- .github/workflows/coverage.yml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ef3d61076..ebc380087 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -29,22 +29,13 @@ jobs: - name: make run: | - cmake -B build -DCMAKE_C_FLAGS="--coverage" && cmake --build build -j - - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: retest - repo: https://github.com/baresip/retest.git - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' + cmake -B build -DCMAKE_C_FLAGS="--coverage" -DCMAKE_EXE_LINKER_FLAGS="--coverage" + cmake --build build -j - name: retest run: | - cd .. - cd retest; cmake -B build -DCMAKE_EXE_LINKER_FLAGS="--coverage" && \ - cmake --build build -j && \ - ./build/retest -a -v && \ - ./build/retest -r -m select -v + ./build/test/retest -a -v + ./build/test/retest -r -m select -v - name: gcov run: | From 4ac6e1e2b3f22d2768e3a05504401a8c72deb785 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:18:38 +0100 Subject: [PATCH 10/16] ci: update sanitzers --- .github/workflows/sanitizers.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 31b4840fa..1719cf1ea 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -43,14 +43,6 @@ jobs: run: | cmake -B build -DHAVE_THREADS= && cmake --build build -j - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: retest - repo: https://github.com/baresip/retest.git - secret: ${{ secrets.GITHUB_TOKEN }} - working-directory: '../.' - - name: retest run: | - cd .. - cd retest && make && ./retest -riv + ./build/test/retest -riv From 85e56ea0d0e01c6311b0131aef3683ea2eaa5038 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:24:02 +0100 Subject: [PATCH 11/16] test: fix clang warnings --- test/http.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/http.c b/test/http.c index 682bc46bc..4c09b72d7 100644 --- a/test/http.c +++ b/test/http.c @@ -440,6 +440,7 @@ static int test_http_loop_base(bool secure, const char *met, bool http_conn, err |= sa_set_str(&srv, "127.0.0.1", 0); err |= sa_set_str(&dns, "127.0.0.1", 53); /* note: unused */ + TEST_ERR(err); if (secure) { @@ -587,6 +588,7 @@ static int test_http_loop_base(bool secure, const char *met, bool http_conn, out: mem_deref(t.mb_body); + mem_deref(mb_body); mem_deref(req); mem_deref(conn); mem_deref(cli); From 445bcd1c960af16a394a935c95bb95be9af39a1a Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:31:54 +0100 Subject: [PATCH 12/16] test: add flags to open --- test/test.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.c b/test/test.c index f111ba79b..97293d0f8 100644 --- a/test/test.c +++ b/test/test.c @@ -279,8 +279,8 @@ static void hide_output(void) { dup_stdout = dup(fileno(stdout)); dup_stderr = dup(fileno(stderr)); - int fd_out = open("stdout.out", O_WRONLY | O_CREAT); - int fd_err = open("stderr.out", O_WRONLY | O_CREAT); + int fd_out = open("stdout.out", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); + int fd_err = open("stderr.out", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); (void)dup2(fd_out, fileno(stdout)); (void)dup2(fd_err, fileno(stderr)); } From a4ca0a10b83647a143509bd52382050540b5fada Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 17:48:49 +0100 Subject: [PATCH 13/16] test: fix open mode for windows --- test/test.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/test.c b/test/test.c index 97293d0f8..a02ec9c7f 100644 --- a/test/test.c +++ b/test/test.c @@ -279,8 +279,15 @@ static void hide_output(void) { dup_stdout = dup(fileno(stdout)); dup_stderr = dup(fileno(stderr)); - int fd_out = open("stdout.out", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); - int fd_err = open("stderr.out", O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR); + +#ifdef WIN32 + mode_t mode = _S_IREAD | _S_IWRITE; +#else + mode_t mode = S_IWUSR | S_IRUSR; +#endif + + int fd_out = open("stdout.out", O_WRONLY | O_CREAT, mode); + int fd_err = open("stderr.out", O_WRONLY | O_CREAT, mode); (void)dup2(fd_out, fileno(stdout)); (void)dup2(fd_err, fileno(stderr)); } From 672c461e7892c295c054bc458adfd6a7770afa13 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Thu, 16 Feb 2023 18:02:51 +0100 Subject: [PATCH 14/16] win32: use int for mode --- test/test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test.c b/test/test.c index a02ec9c7f..c0da5da9b 100644 --- a/test/test.c +++ b/test/test.c @@ -281,7 +281,7 @@ static void hide_output(void) dup_stderr = dup(fileno(stderr)); #ifdef WIN32 - mode_t mode = _S_IREAD | _S_IWRITE; + int mode = _S_IREAD | _S_IWRITE; #else mode_t mode = S_IWUSR | S_IRUSR; #endif From 21c0c91452b6827afa97bc6c1335d28bee9bc5c7 Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Fri, 17 Feb 2023 09:11:42 +0100 Subject: [PATCH 15/16] coverage: reset min_cov --- .github/workflows/coverage.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index ebc380087..6e8033d56 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -48,7 +48,7 @@ jobs: - name: coverage check run: | - min_cov="40.0" + min_cov="57.0" cov=$(~/.local/bin/gcovr -r . -s | grep lines | awk '{ print $2 }' | sed 's/%//') echo "Coverage: ${cov}% (min $min_cov%)" exit $(echo "$cov < $min_cov" | bc -l) From afd124ab3362bc2eb64fa77e12329445377e20ff Mon Sep 17 00:00:00 2001 From: "Alfred E. Heggestad" Date: Fri, 17 Feb 2023 13:25:23 +0100 Subject: [PATCH 16/16] ci: update mingw --- .github/workflows/mingw.yml | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/.github/workflows/mingw.yml b/.github/workflows/mingw.yml index 50e2930ad..6df82244b 100644 --- a/.github/workflows/mingw.yml +++ b/.github/workflows/mingw.yml @@ -22,22 +22,12 @@ jobs: - uses: actions/checkout@v3 # needed for pr checkout - - uses: sreimers/pr-dependency-action@v0.5 - with: - name: retest - repo: https://github.com/baresip/retest - secret: ${{ secrets.GITHUB_TOKEN }} - - uses: sreimers/pr-dependency-action@v0.5 with: name: baresip-win32 repo: https://github.com/baresip/baresip-win32 secret: ${{ secrets.GITHUB_TOKEN }} - - name: "baresip-win32 repo" - run: | - mv retest baresip-win32/ - - uses: actions/checkout@v3 with: path: baresip-win32/re @@ -63,7 +53,7 @@ jobs: - uses: actions/upload-artifact@v2 with: name: retest-exe - path: baresip-win32/retest/build/retest.exe + path: baresip-win32/re/build/test/retest.exe retention-days: 1 wintest: @@ -75,10 +65,10 @@ jobs: - uses: actions/download-artifact@v2 - uses: sreimers/pr-dependency-action@v0.5 with: - name: retest - repo: https://github.com/baresip/retest + name: re + repo: https://github.com/baresip/re secret: ${{ secrets.GITHUB_TOKEN }} - name: "run retest.exe" - run: mv retest-exe/retest.exe retest/ && cd retest && ./retest.exe -v + run: mv retest-exe/retest.exe re/ && cd re && ./retest.exe -v shell: bash