diff --git a/CMakeLists.txt b/CMakeLists.txt index 25c71078..2592ffbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,7 @@ set(TESTS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/test/conflict.c ${CMAKE_CURRENT_SOURCE_DIR}/test/bind.c ${CMAKE_CURRENT_SOURCE_DIR}/test/ufrag.c + ${CMAKE_CURRENT_SOURCE_DIR}/test/tcp.c ) source_group("Test Files" FILES "${TESTS_SOURCES}") diff --git a/include/juice/juice.h b/include/juice/juice.h index 1416dd90..74e4b32f 100644 --- a/include/juice/juice.h +++ b/include/juice/juice.h @@ -90,6 +90,12 @@ typedef enum juice_concurrency_mode { JUICE_CONCURRENCY_MODE_THREAD, // Each connection runs in its own thread } juice_concurrency_mode_t; +typedef enum juice_ice_tcp_mode { + JUICE_ICE_TCP_MODE_NONE = 0, // ICE-TCP is disabled + JUICE_ICE_TCP_MODE_ACTIVE, // ICE-TCP will operate as a client + JUICE_ICE_TCP_MODE_PASSIVE, // ICE-TCP will operate as a server +} juice_ice_tcp_mode_t; + typedef struct juice_config { juice_concurrency_mode_t concurrency_mode; @@ -104,6 +110,8 @@ typedef struct juice_config { uint16_t local_port_range_begin; uint16_t local_port_range_end; + juice_ice_tcp_mode_t ice_tcp_mode; + juice_cb_state_changed_t cb_state_changed; juice_cb_candidate_t cb_candidate; juice_cb_gathering_done_t cb_gathering_done; diff --git a/test/main.c b/test/main.c index 35e97f02..7d57d525 100644 --- a/test/main.c +++ b/test/main.c @@ -22,6 +22,7 @@ int test_turn(void); int test_conflict(void); int test_bind(void); int test_ufrag(void); +int test_tcp(void); #ifndef NO_SERVER int test_server(void); @@ -30,6 +31,12 @@ int test_server(void); int main(int argc, char **argv) { juice_set_log_level(JUICE_LOG_LEVEL_WARN); + printf("\nRunning TCP test...\n"); + if (test_tcp()) { + fprintf(stderr, "TCP test failed\n"); + return -2; + } + printf("\nRunning CRC32 implementation test...\n"); if (test_crc32()) { fprintf(stderr, "CRC32 implementation test failed\n"); diff --git a/test/tcp.c b/test/tcp.c new file mode 100644 index 00000000..ccd3a126 --- /dev/null +++ b/test/tcp.c @@ -0,0 +1,220 @@ +/** + * Copyright (c) 2020 Paul-Louis Ageneau + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "juice/juice.h" + +#include +#include +#include +#include + +#ifdef _WIN32 +#include +static void sleep(unsigned int secs) { Sleep(secs * 1000); } +#else +#include // for sleep +#endif + +#define BUFFER_SIZE 4096 + +static juice_agent_t *agent1; +static juice_agent_t *agent2; + +static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr); +static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr); + +static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr); +static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr); + +static void on_gathering_done1(juice_agent_t *agent, void *user_ptr); +static void on_gathering_done2(juice_agent_t *agent, void *user_ptr); + +static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr); +static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr); + +int test_tcp() { + juice_set_log_level(JUICE_LOG_LEVEL_DEBUG); + + // Agent 1: Create agent + juice_config_t config1; + memset(&config1, 0, sizeof(config1)); + + config1.cb_state_changed = on_state_changed1; + config1.cb_candidate = on_candidate1; + config1.cb_gathering_done = on_gathering_done1; + config1.cb_recv = on_recv1; + config1.user_ptr = NULL; + config1.ice_tcp_mode = JUICE_ICE_TCP_MODE_PASSIVE; + + agent1 = juice_create(&config1); + + // Agent 2: Create agent + juice_config_t config2; + memset(&config2, 0, sizeof(config2)); + + config2.cb_state_changed = on_state_changed2; + config2.cb_candidate = on_candidate2; + config2.cb_gathering_done = on_gathering_done2; + config2.cb_recv = on_recv2; + config2.user_ptr = NULL; + config1.ice_tcp_mode = JUICE_ICE_TCP_MODE_ACTIVE; + + // agent2 = juice_create(&config2); + + // Agent 1: Generate local description + char sdp1[JUICE_MAX_SDP_STRING_LEN]; + juice_get_local_description(agent1, sdp1, JUICE_MAX_SDP_STRING_LEN); + printf("Local description 1:\n%s\n", sdp1); + + // // Agent 2: Receive description from agent 1 + // juice_set_remote_description(agent2, sdp1); + + // // Agent 2: Generate local description + // char sdp2[JUICE_MAX_SDP_STRING_LEN]; + // juice_get_local_description(agent2, sdp2, JUICE_MAX_SDP_STRING_LEN); + // printf("Local description 2:\n%s\n", sdp2); + + // // Agent 1: Receive description from agent 2 + // juice_set_remote_description(agent1, sdp2); + + // Agent 1: Gather candidates (and send them to agent 2) + juice_gather_candidates(agent1); + sleep(2); + + // // Agent 2: Gather candidates (and send them to agent 1) + // juice_gather_candidates(agent2); + // sleep(2); + + // -- Connection should be finished -- + + // // Check states + // juice_state_t state1 = juice_get_state(agent1); + // juice_state_t state2 = juice_get_state(agent2); + // bool success = (state1 == JUICE_STATE_COMPLETED && state2 == JUICE_STATE_COMPLETED); + + // // Retrieve candidates + // char local[JUICE_MAX_CANDIDATE_SDP_STRING_LEN]; + // char remote[JUICE_MAX_CANDIDATE_SDP_STRING_LEN]; + // if (success &= + // (juice_get_selected_candidates(agent1, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote, + // JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) { + // printf("Local candidate 1: %s\n", local); + // printf("Remote candidate 1: %s\n", remote); + // if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) || + // (!strstr(remote, "typ host") && !strstr(remote, "typ prflx"))) + // success = false; // local connection should be possible + // } + // if (success &= + // (juice_get_selected_candidates(agent2, local, JUICE_MAX_CANDIDATE_SDP_STRING_LEN, remote, + // JUICE_MAX_CANDIDATE_SDP_STRING_LEN) == 0)) { + // printf("Local candidate 2: %s\n", local); + // printf("Remote candidate 2: %s\n", remote); + // if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) || + // (!strstr(remote, "typ host") && !strstr(remote, "typ prflx"))) + // success = false; // local connection should be possible + // } + + // // Retrieve addresses + // char localAddr[JUICE_MAX_ADDRESS_STRING_LEN]; + // char remoteAddr[JUICE_MAX_ADDRESS_STRING_LEN]; + // if (success &= (juice_get_selected_addresses(agent1, localAddr, JUICE_MAX_ADDRESS_STRING_LEN, + // remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) { + // printf("Local address 1: %s\n", localAddr); + // printf("Remote address 1: %s\n", remoteAddr); + // } + // if (success &= (juice_get_selected_addresses(agent2, localAddr, JUICE_MAX_ADDRESS_STRING_LEN, + // remoteAddr, JUICE_MAX_ADDRESS_STRING_LEN) == 0)) { + // printf("Local address 2: %s\n", localAddr); + // printf("Remote address 2: %s\n", remoteAddr); + // } + + // // Agent 1: destroy + // juice_destroy(agent1); + + // // Agent 2: destroy + // juice_destroy(agent2); + + // if (success) { + // printf("Success\n"); + // return 0; + // } else { + // printf("Failure\n"); + // return -1; + // } + // + return -1; +} + +// Agent 1: on state changed +static void on_state_changed1(juice_agent_t *agent, juice_state_t state, void *user_ptr) { +// printf("State 1: %s\n", juice_state_to_string(state)); +// +// if (state == JUICE_STATE_CONNECTED) { +// // Agent 1: on connected, send a message +// const char *message = "Hello from 1"; +// juice_send(agent, message, strlen(message)); +// } +} +// +// Agent 2: on state changed +static void on_state_changed2(juice_agent_t *agent, juice_state_t state, void *user_ptr) { +// printf("State 2: %s\n", juice_state_to_string(state)); +// if (state == JUICE_STATE_CONNECTED) { +// // Agent 2: on connected, send a message +// const char *message = "Hello from 2"; +// juice_send(agent, message, strlen(message)); +// } +} + +// Agent 1: on local candidate gathered +static void on_candidate1(juice_agent_t *agent, const char *sdp, void *user_ptr) { +// printf("Candidate 1: %s\n", sdp); +// +// // Agent 2: Receive it from agent 1 +// juice_add_remote_candidate(agent2, sdp); +} +// +// Agent 2: on local candidate gathered +static void on_candidate2(juice_agent_t *agent, const char *sdp, void *user_ptr) { +// printf("Candidate 2: %s\n", sdp); +// +// // Agent 1: Receive it from agent 2 +// juice_add_remote_candidate(agent1, sdp); +} + +// Agent 1: on local candidates gathering done +static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) { +// printf("Gathering done 1\n"); +// juice_set_remote_gathering_done(agent2); // optional +} + +// Agent 2: on local candidates gathering done +static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) { +// printf("Gathering done 2\n"); +// juice_set_remote_gathering_done(agent1); // optional +} + +// Agent 1: on message received +static void on_recv1(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) { +// char buffer[BUFFER_SIZE]; +// if (size > BUFFER_SIZE - 1) +// size = BUFFER_SIZE - 1; +// memcpy(buffer, data, size); +// buffer[size] = '\0'; +// printf("Received 1: %s\n", buffer); +} +// +// Agent 2: on message received +static void on_recv2(juice_agent_t *agent, const char *data, size_t size, void *user_ptr) { +// char buffer[BUFFER_SIZE]; +// if (size > BUFFER_SIZE - 1) +// size = BUFFER_SIZE - 1; +// memcpy(buffer, data, size); +// buffer[size] = '\0'; +// printf("Received 2: %s\n", buffer); +}