Use sourcecode for libjuice

This commit is contained in:
dijunkun
2023-07-13 16:58:20 +08:00
parent ef6a04dc97
commit 8cd87a2646
76 changed files with 14877 additions and 179 deletions

45
thirdparty/libjuice/test/base64.c vendored Normal file
View File

@@ -0,0 +1,45 @@
/**
* 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 "base64.h"
#include <stdint.h>
#include <string.h>
#define BUFFER_SIZE 1024
int test_base64(void) {
const char *str = "Man is distinguished, not only by his reason, but by this singular passion "
"from other animals, which is a lust of the mind, that by a perseverance of "
"delight in the continued and indefatigable generation of knowledge, exceeds "
"the short vehemence of any carnal pleasure.";
const char *expected =
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIH"
"Bhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBw"
"ZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb2"
"4gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4"
"=";
char buffer1[BUFFER_SIZE];
if (BASE64_ENCODE(str, strlen(str), buffer1, BUFFER_SIZE) <= 0)
return -1;
if (strcmp(buffer1, expected) != 0)
return -1;
char buffer2[BUFFER_SIZE];
int len = BASE64_DECODE(buffer1, buffer2, BUFFER_SIZE);
if (len <= 0)
return -1;
buffer2[len] = '\0';
if (strcmp(buffer2, str) != 0)
return -1;
return 0;
}

225
thirdparty/libjuice/test/bind.c vendored Normal file
View File

@@ -0,0 +1,225 @@
/**
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // for sleep
#endif
#define BUFFER_SIZE 4096
#define BIND_ADDRESS "127.0.0.1"
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_bind() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Agent 1: Create agent
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
config1.bind_address = BIND_ADDRESS;
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;
agent1 = juice_create(&config1);
// Agent 2: Create agent
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
config2.bind_address = BIND_ADDRESS;
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;
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 --
bool success = true;
/*
// 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 (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);
}
// 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;
}
}
// 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);
// Filter host candidates for the bind address
if(!strstr(sdp, "host") || !strstr(sdp, BIND_ADDRESS))
return;
// 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);
// Filter host candidates for the bind address
if(!strstr(sdp, "host") || !strstr(sdp, BIND_ADDRESS))
return;
// 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);
}

203
thirdparty/libjuice/test/conflict.c vendored Normal file
View File

@@ -0,0 +1,203 @@
/**
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // 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_conflict() {
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;
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;
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: 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);
// Setting the remote description now on both agents will hint them both into controlling mode,
// creating an ICE role conflict
// Agent 1: Receive description from agent 2
juice_set_remote_description(agent1, sdp2);
// Agent 2: Receive description from agent 1
juice_set_remote_description(agent2, sdp1);
// 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
}
// 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;
}
}
// 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);
}

228
thirdparty/libjuice/test/connectivity.c vendored Normal file
View File

@@ -0,0 +1,228 @@
/**
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // 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_connectivity() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Agent 1: Create agent
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
// STUN server example
config1.stun_server_host = "stun.l.google.com";
config1.stun_server_port = 19302;
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;
agent1 = juice_create(&config1);
// Agent 2: Create agent
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
// STUN server example
config2.stun_server_host = "stun.l.google.com";
config2.stun_server_port = 19302;
// Port range example
config2.local_port_range_begin = 60000;
config2.local_port_range_end = 61000;
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;
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;
}
}
// 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);
}

22
thirdparty/libjuice/test/crc32.c vendored Normal file
View File

@@ -0,0 +1,22 @@
/**
* 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 "crc32.h"
#include <stdint.h>
#include <string.h>
int test_crc32(void) {
const char *str = "The quick brown fox jumps over the lazy dog";
uint32_t expected = 0x414fa339;
if (CRC32(str, strlen(str)) != expected)
return -1;
return 0;
}

95
thirdparty/libjuice/test/gathering.c vendored Normal file
View File

@@ -0,0 +1,95 @@
/**
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // for sleep
#endif
#define BUFFER_SIZE 4096
static juice_agent_t *agent;
static bool success = false;
static bool done = false;
static void on_state_changed(juice_agent_t *agent, juice_state_t state, void *user_ptr);
static void on_candidate(juice_agent_t *agent, const char *sdp, void *user_ptr);
static void on_gathering_done(juice_agent_t *agent, void *user_ptr);
int test_gathering() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Create agent
juice_config_t config;
memset(&config, 0, sizeof(config));
// STUN server example
config.stun_server_host = "stun.l.google.com";
config.stun_server_port = 19302;
config.cb_state_changed = on_state_changed;
config.cb_candidate = on_candidate;
config.cb_gathering_done = on_gathering_done;
config.user_ptr = NULL;
agent = juice_create(&config);
// Generate local description
char sdp[JUICE_MAX_SDP_STRING_LEN];
juice_get_local_description(agent, sdp, JUICE_MAX_SDP_STRING_LEN);
printf("Local description:\n%s\n", sdp);
// Gather candidates
juice_gather_candidates(agent);
// Wait until gathering done
int secs = 10;
while (secs-- && !done && !success)
sleep(1);
// Destroy
juice_destroy(agent);
if (success) {
printf("Success\n");
return 0;
} else {
printf("Failure\n");
return -1;
}
}
// On state changed
static void on_state_changed(juice_agent_t *agent, juice_state_t state, void *user_ptr) {
printf("State: %s\n", juice_state_to_string(state));
}
// On local candidate gathered
static void on_candidate(juice_agent_t *agent, const char *sdp, void *user_ptr) {
printf("Candidate: %s\n", sdp);
// Success if a valid srflx candidate is emitted
if (strstr(sdp, " typ srflx raddr 0.0.0.0 rport 0"))
success = true;
}
// On local candidates gathering done
static void on_gathering_done(juice_agent_t *agent, void *user_ptr) {
printf("Gathering done\n");
done = true;
}

110
thirdparty/libjuice/test/main.c vendored Normal file
View File

@@ -0,0 +1,110 @@
/**
* 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 <stdio.h>
int test_crc32(void);
int test_base64(void);
int test_stun(void);
int test_connectivity(void);
int test_thread(void);
int test_mux(void);
int test_notrickle(void);
int test_gathering(void);
int test_turn(void);
int test_conflict(void);
int test_bind(void);
#ifndef NO_SERVER
int test_server(void);
#endif
int main(int argc, char **argv) {
juice_set_log_level(JUICE_LOG_LEVEL_WARN);
printf("\nRunning CRC32 implementation test...\n");
if (test_crc32()) {
fprintf(stderr, "CRC32 implementation test failed\n");
return -2;
}
printf("\nRunning base64 implementation test...\n");
if (test_base64()) {
fprintf(stderr, "base64 implementation test failed\n");
return -2;
}
printf("\nRunning STUN parsing implementation test...\n");
if (test_stun()) {
fprintf(stderr, "STUN parsing implementation test failed\n");
return -3;
}
printf("\nRunning candidates gathering test...\n");
if (test_gathering()) {
fprintf(stderr, "Candidates gathering test failed\n");
return -1;
}
printf("\nRunning connectivity test...\n");
if (test_connectivity()) {
fprintf(stderr, "Connectivity test failed\n");
return -1;
}
// Disabled as the Open Relay TURN server is unreliable
/*
printf("\nRunning TURN connectivity test...\n");
if (test_turn()) {
fprintf(stderr, "TURN connectivity test failed\n");
return -1;
}
*/
printf("\nRunning thread-mode connectivity test...\n");
if (test_thread()) {
fprintf(stderr, "Thread-mode connectivity test failed\n");
return -1;
}
printf("\nRunning mux-mode connectivity test...\n");
if (test_mux()) {
fprintf(stderr, "Mux-mode connectivity test failed\n");
return -1;
}
printf("\nRunning non-trickled connectivity test...\n");
if (test_notrickle()) {
fprintf(stderr, "Non-trickled connectivity test failed\n");
return -1;
}
printf("\nRunning connectivity test with role conflict...\n");
if (test_conflict()) {
fprintf(stderr, "Connectivity test with role conflict failed\n");
return -1;
}
printf("\nRunning connectivity test with bind address...\n");
if (test_bind()) {
fprintf(stderr, "Connectivity test with bind address failed\n");
return -1;
}
#ifndef NO_SERVER
printf("\nRunning server test...\n");
if (test_server()) {
fprintf(stderr, "Server test failed\n");
return -1;
}
#endif
return 0;
}

226
thirdparty/libjuice/test/mux.c vendored Normal file
View File

@@ -0,0 +1,226 @@
/**
* Copyright (c) 2022 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // 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);
bool endswith(const char *str, const char *suffix) {
size_t str_len = strlen(str);
size_t suffix_len = strlen(suffix);
return str_len >= suffix_len && memcmp(str + str_len - suffix_len, suffix, suffix_len) == 0;
}
int test_mux() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Agent 1: Create agent
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
config1.stun_server_host = "stun.l.google.com";
config1.stun_server_port = 19302;
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;
agent1 = juice_create(&config1);
// Agent 2: Create agent in mux mode on port 60000
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
config2.concurrency_mode = JUICE_CONCURRENCY_MODE_MUX;
config2.local_port_range_begin = 60000;
config2.local_port_range_end = 60000;
config2.cb_state_changed = on_state_changed1;
config2.cb_candidate = on_candidate1;
config2.cb_gathering_done = on_gathering_done1;
config2.cb_recv = on_recv1;
config2.user_ptr = NULL;
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);
success &= endswith(remoteAddr, ":60000");
}
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);
success &= endswith(localAddr, ":60000");
}
// 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;
}
}
// 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);
}

201
thirdparty/libjuice/test/notrickle.c vendored Normal file
View File

@@ -0,0 +1,201 @@
/**
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // 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_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_notrickle() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Agent 1: Create agent
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
// STUN server example
config1.stun_server_host = "stun.l.google.com";
config1.stun_server_port = 19302;
config1.cb_state_changed = on_state_changed1;
config1.cb_gathering_done = on_gathering_done1;
config1.cb_recv = on_recv1;
config1.user_ptr = NULL;
agent1 = juice_create(&config1);
// Agent 2: Create agent
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
// STUN server example
config2.stun_server_host = "stun.l.google.com";
config2.stun_server_port = 19302;
config2.cb_state_changed = on_state_changed2;
config2.cb_gathering_done = on_gathering_done2;
config2.cb_recv = on_recv2;
config2.user_ptr = NULL;
agent2 = juice_create(&config2);
// Agent 1: Gather candidates
juice_gather_candidates(agent1);
sleep(4);
// -- 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 (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);
}
// 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 ((!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_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);
if ((!strstr(local, "typ host") && !strstr(local, "typ prflx")) ||
(!strstr(remote, "typ host") && !strstr(remote, "typ prflx")))
success = false; // local connection should be possible
}
// 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;
}
}
// 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 candidates gathering done
static void on_gathering_done1(juice_agent_t *agent, void *user_ptr) {
printf("Gathering done 1\n");
// 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: Gather candidates
juice_gather_candidates(agent2);
}
// Agent 2: on local candidates gathering done
static void on_gathering_done2(juice_agent_t *agent, void *user_ptr) {
printf("Gathering done 2\n");
// 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: 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);
}

274
thirdparty/libjuice/test/server.c vendored Normal file
View File

@@ -0,0 +1,274 @@
/**
* 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/.
*/
#ifndef NO_SERVER
#include "juice/juice.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // for sleep
#endif
#define BUFFER_SIZE 4096
#define TURN_USERNAME1 "server_test1"
#define TURN_PASSWORD1 "79874638521694"
#define TURN_USERNAME2 "server_test2"
#define TURN_PASSWORD2 "36512189907731"
static juice_server_t *server;
static juice_agent_t *agent1;
static juice_agent_t *agent2;
static bool srflx_success = false;
static bool relay_success = false;
static bool success = false;
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_server() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Create server
juice_server_credentials_t credentials[1];
memset(&credentials, 0, sizeof(credentials));
credentials[0].username = TURN_USERNAME1;
credentials[0].password = TURN_PASSWORD1;
juice_server_config_t server_config;
memset(&server_config, 0, sizeof(server_config));
server_config.port = 3478;
server_config.credentials = credentials;
server_config.credentials_count = 1;
server_config.max_allocations = 100;
server_config.realm = "Juice test server";
server = juice_server_create(&server_config);
if(juice_server_get_port(server) != 3478) {
printf("juice_server_get_port failed\n");
juice_server_destroy(server);
return -1;
}
// Added credentials example
juice_server_credentials_t added_credentials[1];
memset(&added_credentials, 0, sizeof(added_credentials));
added_credentials[0].username = TURN_USERNAME2;
added_credentials[0].password = TURN_PASSWORD2;
juice_server_add_credentials(server, added_credentials, 60000); // 60s
// Agent 1: Create agent
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
// Set STUN server
config1.stun_server_host = "localhost";
config1.stun_server_port = 3478;
// Set TURN server
juice_turn_server_t turn_server1;
memset(&turn_server1, 0, sizeof(turn_server1));
turn_server1.host = "localhost";
turn_server1.port = 3478;
turn_server1.username = TURN_USERNAME1;
turn_server1.password = TURN_PASSWORD1;
config1.turn_servers = &turn_server1;
config1.turn_servers_count = 1;
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;
agent1 = juice_create(&config1);
// Agent 2: Create agent
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
// Set STUN server
config2.stun_server_host = "localhost";
config2.stun_server_port = 3478;
// Set TURN server
juice_turn_server_t turn_server2;
memset(&turn_server2, 0, sizeof(turn_server2));
turn_server2.host = "localhost";
turn_server2.port = 3478;
turn_server2.username = TURN_USERNAME2;
turn_server2.password = TURN_PASSWORD2;
config2.turn_servers = &turn_server2;
config2.turn_servers_count = 1;
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;
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 --
// Agent 1: destroy
juice_destroy(agent1);
// Agent 2: destroy
juice_destroy(agent2);
// Destroy server
juice_server_destroy(server);
if (srflx_success && relay_success && success) {
printf("Success\n");
return 0;
} else {
printf("Failure\n");
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);
// Success if a valid srflx candidate is emitted
if (strstr(sdp, " typ srflx raddr 0.0.0.0 rport 0"))
srflx_success = true;
// Success if a valid relay candidate is emitted
if (strstr(sdp, " typ relay raddr 0.0.0.0 rport 0"))
relay_success = true;
// Filter relayed candidates
if (!strstr(sdp, "relay"))
return;
// 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);
// Success if a valid srflx candidate is emitted
if (strstr(sdp, " typ srflx raddr 0.0.0.0 rport 0"))
srflx_success = true;
// Success if a valid relay candidate is emitted
if (strstr(sdp, " typ relay raddr 0.0.0.0 rport 0"))
relay_success = true;
// Filter relayed candidates
if (!strstr(sdp, "relay"))
return;
// 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);
success = true;
}
// 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);
success = true;
}
#endif // ifndef NO_SERVER

155
thirdparty/libjuice/test/stun.c vendored Normal file
View File

@@ -0,0 +1,155 @@
/**
* 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 "stun.h"
#include <stdint.h>
#include <string.h>
int test_stun(void) {
stun_message_t msg;
uint8_t message1[] = {
0x00, 0x01, 0x00, 0x58, // Request type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0xb7, 0xe7, 0xa7, 0x01, // Transaction ID
0xbc, 0x34, 0xd6, 0x86, //
0xfa, 0x87, 0xdf, 0xae, //
0x80, 0x22, 0x00, 0x10, // SOFTWARE attribute header
0x53, 0x54, 0x55, 0x4e, //
0x20, 0x74, 0x65, 0x73, //
0x74, 0x20, 0x63, 0x6c, //
0x69, 0x65, 0x6e, 0x74, //
0x00, 0x24, 0x00, 0x04, // PRIORITY attribute header
0x6e, 0x00, 0x01, 0xff, //
0x80, 0x29, 0x00, 0x08, // ICE-CONTROLLED attribute header
0x93, 0x2f, 0xf9, 0xb1, //
0x51, 0x26, 0x3b, 0x36, //
0x00, 0x06, 0x00, 0x09, // USERNAME attribute header
0x65, 0x76, 0x74, 0x6a, //
0x3a, 0x68, 0x36, 0x76, //
0x59, 0x20, 0x20, 0x20, //
0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY attribute header
0x9a, 0xea, 0xa7, 0x0c, //
0xbf, 0xd8, 0xcb, 0x56, //
0x78, 0x1e, 0xf2, 0xb5, //
0xb2, 0xd3, 0xf2, 0x49, //
0xc1, 0xb5, 0x71, 0xa2, //
0x80, 0x28, 0x00, 0x04, // FINGERPRINT attribute header
0xe5, 0x7a, 0x3b, 0xcf, //
};
memset(&msg, 0, sizeof(msg));
if (_juice_stun_read(message1, sizeof(message1), &msg) <= 0)
return -1;
if(msg.msg_class != STUN_CLASS_REQUEST || msg.msg_method != STUN_METHOD_BINDING)
return -1;
if (memcmp(msg.transaction_id, message1 + 8, 12) != 0)
return -1;
if (msg.priority != 0x6e0001ff)
return -1;
if (msg.ice_controlled != 0x932ff9b151263b36LL)
return -1;
if (!msg.has_integrity)
return -1;
if (!_juice_stun_check_integrity(message1, sizeof(message1), &msg, "VOkJxbRl1RmTxUk/WvJxBt"))
return -1;
if(msg.error_code != 0)
return -1;
// The test vector in RFC 8489 is completely wrong
// See https://www.rfc-editor.org/errata_search.php?rfc=8489
uint8_t message2[] = {
0x00, 0x01, 0x00, 0x90, // Request type and message length
0x21, 0x12, 0xa4, 0x42, // Magic cookie
0x78, 0xad, 0x34, 0x33, // Transaction ID
0xc6, 0xad, 0x72, 0xc0, //
0x29, 0xda, 0x41, 0x2e, //
0x00, 0x1e, 0x00, 0x20, // USERHASH attribute header
0x4a, 0x3c, 0xf3, 0x8f, // Userhash value (32 bytes)
0xef, 0x69, 0x92, 0xbd, //
0xa9, 0x52, 0xc6, 0x78, //
0x04, 0x17, 0xda, 0x0f, //
0x24, 0x81, 0x94, 0x15, //
0x56, 0x9e, 0x60, 0xb2, //
0x05, 0xc4, 0x6e, 0x41, //
0x40, 0x7f, 0x17, 0x04, //
0x00, 0x15, 0x00, 0x29, // NONCE attribute header
0x6f, 0x62, 0x4d, 0x61, // Nonce value and padding (3 bytes)
0x74, 0x4a, 0x6f, 0x73, //
0x32, 0x41, 0x41, 0x41, //
0x43, 0x66, 0x2f, 0x2f, //
0x34, 0x39, 0x39, 0x6b, //
0x39, 0x35, 0x34, 0x64, //
0x36, 0x4f, 0x4c, 0x33, //
0x34, 0x6f, 0x4c, 0x39, //
0x46, 0x53, 0x54, 0x76, //
0x79, 0x36, 0x34, 0x73, //
0x41, 0x00, 0x00, 0x00, //
0x00, 0x14, 0x00, 0x0b, // REALM attribute header
0x65, 0x78, 0x61, 0x6d, // Realm value (11 bytes) and padding (1 byte)
0x70, 0x6c, 0x65, 0x2e, //
0x6f, 0x72, 0x67, 0x00, //
0x00, 0x1d, 0x00, 0x04, // PASSWORD-ALGORITHM attribute header
0x00, 0x02, 0x00, 0x00, // PASSWORD-ALGORITHM value (4 bytes)
0x00, 0x1c, 0x00, 0x20, // MESSAGE-INTEGRITY-SHA256 attribute header
0xb5, 0xc7, 0xbf, 0x00, // HMAC-SHA256 value
0x5b, 0x6c, 0x52, 0xa2, //
0x1c, 0x51, 0xc5, 0xe8, //
0x92, 0xf8, 0x19, 0x24, //
0x13, 0x62, 0x96, 0xcb, //
0x92, 0x7c, 0x43, 0x14, //
0x93, 0x09, 0x27, 0x8c, //
0xc6, 0x51, 0x8e, 0x65, //
};
memset(&msg, 0, sizeof(msg));
if (_juice_stun_read(message2, sizeof(message2), &msg) <= 0)
return -1;
if(msg.msg_class != STUN_CLASS_REQUEST || msg.msg_method != STUN_METHOD_BINDING)
return -1;
if (memcmp(msg.transaction_id, message2 + 8, 12) != 0)
return -1;
if (!msg.credentials.enable_userhash)
return -1;
if (memcmp(msg.credentials.userhash, message2 + 24, 32) != 0)
return -1;
if (strcmp(msg.credentials.realm, "example.org") != 0)
return -1;
if (strcmp(msg.credentials.nonce, "obMatJos2AAACf//499k954d6OL34oL9FSTvy64sA") != 0)
return -1;
if (!msg.has_integrity)
return -1;
// Username is "<U+30DE><U+30C8><U+30EA><U+30C3><U+30AF><U+30B9>" or "マトリックス"
// aka "The Matrix" in Japanese
strcpy(msg.credentials.username, "マトリックス");
if (!_juice_stun_check_integrity(message2, sizeof(message2), &msg, "TheMatrIX"))
return -1;
if(msg.error_code != STUN_ERROR_INTERNAL_VALIDATION_FAILED)
return -1;
return 0;
}

220
thirdparty/libjuice/test/thread.c vendored Normal file
View File

@@ -0,0 +1,220 @@
/**
* Copyright (c) 2022 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // 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_thread() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Agent 1: Create agent in thread concurrency mode
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
config1.concurrency_mode = JUICE_CONCURRENCY_MODE_THREAD;
config1.stun_server_host = "stun.l.google.com";
config1.stun_server_port = 19302;
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;
agent1 = juice_create(&config1);
// Agent 2: Create agent in thread concurrency mode
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
config2.concurrency_mode = JUICE_CONCURRENCY_MODE_THREAD;
config2.stun_server_host = "stun.l.google.com";
config2.stun_server_port = 19302;
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;
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;
}
}
// 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);
}

241
thirdparty/libjuice/test/turn.c vendored Normal file
View File

@@ -0,0 +1,241 @@
/**
* 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
static void sleep(unsigned int secs) { Sleep(secs * 1000); }
#else
#include <unistd.h> // 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_turn() {
juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);
// Agent 1: Create agent
juice_config_t config1;
memset(&config1, 0, sizeof(config1));
// STUN server example (use your own server in production)
config1.stun_server_host = "openrelay.metered.ca";
config1.stun_server_port = 80;
// TURN server example (use your own server in production)
juice_turn_server_t turn_server;
memset(&turn_server, 0, sizeof(turn_server));
turn_server.host = "openrelay.metered.ca";
turn_server.port = 80;
turn_server.username = "openrelayproject";
turn_server.password = "openrelayproject";
config1.turn_servers = &turn_server;
config1.turn_servers_count = 1;
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;
agent1 = juice_create(&config1);
// Agent 2: Create agent
juice_config_t config2;
memset(&config2, 0, sizeof(config2));
// STUN server example (use your own server in production)
config2.stun_server_host = "openrelay.metered.ca";
config2.stun_server_port = 80;
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;
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 || state1 == JUICE_STATE_CONNECTED) &&
(state2 == JUICE_STATE_CONNECTED || 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);
success &= (strstr(local, "relay") != NULL);
}
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);
success &= (strstr(remote, "relay") != NULL);
}
// 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;
}
}
// 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) {
// Filter relayed candidates
if (!strstr(sdp, "relay"))
return;
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) {
// Filter server reflexive candidates
if (!strstr(sdp, "srflx"))
return;
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);
}