Git Product home page Git Product logo

libjuice's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libjuice's Issues

TURN refresh interval

The TURN refresh interval is defined in agent.h as 9 minutes, while RFC 8445 8.3.11 recommends 15 seconds.
I noticed that with certain firewalls, the lack of TURN refresh messages causes the NAT binding to be dropped, which in turn causes the TURN server to reject the data packets.
In my tests, a value of 15 seconds fixes the problem.

ice_sort_candidates function why don't start with first Condidate ?

void ice_sort_candidates(ice_description_t *description) {
// In-place insertion sort
ice_candidate_t *begin = description->candidates;
ice_candidate_t *end = begin + description->candidates_count;
ice_candidate_t *cur = begin;
while (++cur < end) {
uint32_t priority = cur->priority;
ice_candidate_t *prev = cur;
ice_candidate_t tmp = *prev;
while (--prev >= begin && prev->priority < priority) {
*(prev + 1) = *prev;
}
if (prev + 1 != cur)
*(prev + 1) = tmp;
}
}

while (++cur < end) ; "++cur" start with second candidate,not first condidate

Sharing a socket across multiple agents

Hi Paul,
Would it be possible to make libjuice be able to share a socket across multiple agents? I am specifically looking for the ability to allow an application to allow many peers to connect on the same port.

Have questions about remote priority calculation

int ice_update_candidate_pair(ice_candidate_pair_t *pair, bool is_controlling) {
	// Compute pair priority according to RFC 8445, extended to support generic pairs missing local
	// or remote See https://tools.ietf.org/html/rfc8445#section-6.1.2.3
	if (!pair->local && !pair->remote)
		return 0;
	uint64_t local_priority =
	    pair->local
	        ? pair->local->priority
	        : ice_compute_priority(ICE_CANDIDATE_TYPE_HOST, pair->remote->resolved.addr.ss_family,
	                               pair->remote->component);
	uint64_t remote_priority =
	    pair->remote
	        ? pair->remote->priority
	        : ice_compute_priority(ICE_CANDIDATE_TYPE_HOST, pair->local->resolved.addr.ss_family,
	                               pair->local->component);
	uint64_t g = is_controlling ? local_priority : remote_priority;
	uint64_t d = is_controlling ? remote_priority : local_priority;
	uint64_t min = g < d ? g : d;
	uint64_t max = g > d ? g : d;
	pair->priority = (min << 32) + (max << 1) + (g > d ? 1 : 0);
	return 0;
}

remote_priority calculate use ICE_CANDIDATE_TYPE_HOST as candidate type , is right?

agent_arm_transmission

hello , Paul
I've been reading your code for almost a month, i like your code very much. now i meet two problem ,
1、I don't know the function of agent_arm_transmission function
2、what is meaning for agent->entries , I thought it was to record the address of the stun server, but it seems not, is has a relationship with the candidate pair

thanks for reading

Fuzz testing

The STUN protocol parsing implementation would benefit from fuzz testing.

CMake cannot install juice.h

Hi, it looks like in the cmake install rule you've set a wrong path for the include file: include/juice.h instead of include/juice/juice.h.

calling juice_send ,the memory leak is very fast

Calling juice_send, on linux such as Ubuntu on the X86 platform, there is no problem, there is no memory leak.
But when calling juice_send on an embedded platform like arm, the memory leak is very fast,Increase memory by 264kb every 1S to 3S.
At present, I want to fix this problem, can you please give some directions for exclusion?

If the network does not go to relay mode in the end, then I looked at the code, mainly sendto, but sendto causes leakage, I think it is unlikely, whether it is necessary to release the incoming data additionally

stun_read print format error

before:

if (size < sizeof(struct stun_header) + length) {
    JLOG_ERROR("Invalid STUN message length", length, size - sizeof(struct stun_header));
    return -1;
}

after:

if (size < sizeof(struct stun_header) + length) {
    JLOG_ERROR("Invalid STUN message length:%z, real length:%z", length, size - sizeof(struct stun_header));
    return -1;
}

ICE Negotiation is really slow

I recently brought libdatachannel up-to-date with master and ICE negotiation is super slow now, on some connections. It can take up to ten seconds to negotiate a pair, whereas before it would be near-instant. I am always in an environment where both endpoints are NATted. This is happening on both Firefox and Chrome. The weird thing is that it is only happening on my internet connection, and only with this latest version. Changing MAX_STUN_RETRANSMISSION_COUNT to 1 helps, but I have no idea the consequence of such a change 😅.

Reflexive candidate

I have no idea why this got removed for reflexive candidates

if (agent->config.cb_candidate)
	agent->config.cb_candidate(agent, buffer, agent->config.user_ptr);

But this prevents me from setting up a connection from a public ip windows machine

htonll(x) implementation issue

before:
#define htonll(x)
((uint64_t)htonl(((uint64_t)(x)&0xFFFFFFFF) << 32) | (uint64_t)htonl((uint64_t)(x) >> 32))

after:
#define htonll(x)
((uint64_t)(((uint64_t)htonl(x)) << 32 ) | (uint64_t)htonl((x) >> 32))

Not reverting to IPv4 if IPv6 fails

Hi and thanks for the great library. I think I noticed a small logic error that appears when running on a device where IPv6 is disabled.

In /src/udp.c:70:

// Prefer IPv6
struct addrinfo *ai;
if ((ai = find_family(ai_list, AF_INET6)) == NULL &&
    (ai = find_family(ai_list, AF_INET)) == NULL) {
	JLOG_ERROR("getaddrinfo for binding address failed: no suitable "
	           "address family");
	goto error;
}

Shouldn't the && really be a || so that the second statement gets evaluated if the first fails?

I would make a PR but I couldn't really figure out how to do it 😅

[Windows] Question and possible issue ?

Hi,

Are you sure that

libjuice/src/agent.c

Lines 57 to 63 in 33612d1

#ifdef _WIN32
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData)) {
JLOG_FATAL("WSAStartup failed");
return NULL;
}
#endif

and

libjuice/src/agent.c

Lines 106 to 108 in 33612d1

#ifdef _WIN32
WSACleanup();
#endif

are a good idea ?

I didn't test but my feeling is that it will completly start/shutdown network stack for the current app on Windows. Meaning that if someone use ICE in just a part in their app and then shutdown the agent, it will shutdown the whole network stack ?

TURN: Received 438 error after a long run

438

test code below

#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 main(int argc, char *argv[]) {
	juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);

	// Agent 1: Create agent
	juice_config_t config1;
	memset(&config1, 0, sizeof(config1));

	// TURN server
	// Please do not use outside of libjuice tests
	juice_turn_server_t turn_server;
	memset(&turn_server, 0, sizeof(turn_server));
	turn_server.host = "stun.ageneau.net";
	turn_server.port = 3478;
	turn_server.username = "juice_test";
	turn_server.password = "28245150316902";
	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;

	// Agent 2: Create agent
	juice_config_t config2;
	memset(&config2, 0, sizeof(config2));
	juice_turn_server_t turn_server2;
	memcpy(&turn_server2, &turn_server, sizeof(juice_turn_server_t));

	// Use the same TURN server
	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;

	agent1 = juice_create(&config1);
	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);

	// Agent 2: Gather candidates (and send them to agent 1)
	juice_gather_candidates(agent2);

	sleep(10);

	// -- 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);
	}

	for (;;) {
		const char *message = "Hello from 1";
		juice_send(agent1, message, strlen(message));
		sleep(1);
	}

	sleep(300);


	// Agent 1: destroy
	juice_destroy(agent1);

	// Agent 2: destroy
	juice_destroy(agent2);

	// Sleep so we can check destruction went well
	sleep(2);

	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 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);

	// 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);
}

// 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);
}

undefined reference to `getrandom'

compile error as follow:

gcc -pthread -o tests test/connectivity.o test/crc32.o test/main.o test/notrickle.o test/server.o test/stun.o libjuice.a
libjuice.a(random.o): In function juice_random': random.c:(.text+0x21): undefined reference to getrandom'
collect2: error: ld returned 1 exit status

ubuntu 16.04
Linux ubuntu 4.4.0-189-generic #219-Ubuntu SMP Tue Aug 11 12:26:50 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

UDP Port Range

Quick question: would it be possible to specify a port range when making the first connection to the STUN server ?

[Windows] Secure random generator

vcpkg comment: microsoft/vcpkg#13703 (comment)

Missing library for

libjuice/src/random.c

Lines 49 to 62 in 5d3e8b3

static int random_bytes(void *buf, size_t size) {
HCRYPTPROV prov;
if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) {
JLOG_WARN("Win32: CryptAcquireContext failed");
return -1;
}
BOOL success = CryptGenRandom(prov, (DWORD)size, (BYTE *)buf);
if (!success) {
JLOG_WARN("Win32: CryptGenRandom failed");
}
CryptReleaseContext(prov, 0);
return success ? 0 : -1;
}

From the vcpkg auto-build:

LNK4075: ignoring '/INCREMENTAL' due to '/OPT:ICF' specification [D:\buildtrees\libjuice\x64-uwp-dbg\juice.vcxproj]
            Creating library D:/buildtrees/libjuice/x64-uwp-dbg/Debug/juice.lib and object D:/buildtrees/libjuice/x64-uwp-dbg/Debug/juice.exp
     4>random.obj : error LNK2019: unresolved external symbol CryptAcquireContext referenced in function random_bytes [D:\buildtrees\libjuice\x64-uwp-dbg\juice.vcxproj]
     4>D:\buildtrees\libjuice\x64-uwp-dbg\Debug\juice.dll : fatal error LNK1120: 1 unresolved externals [D:\buildtrees\libjuice\x64-uwp-dbg\juice.vcxproj]
     4>Done Building Project "D:\buildtrees\libjuice\x64-uwp-dbg\juice.vcxproj" (default targets) -- FAILED.
     3>Done Building Project "D:\buildtrees\libjuice\x64-uwp-dbg\ALL_BUILD.vcxproj" (default targets) -- FAILED.
     1>Done Building Project "D:\buildtrees\libjuice\x64-uwp-dbg\install.vcxproj" (default targets) -- FAILED.

Do you want to support WinXP ? If not, you could switch to

#include <bcrypt.h>
#pragma comment(lib, bcrypt)
BCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)size, BCRYPT_USE_SYSTEM_PREFERRED_RNG);

The OpenSSL library seems to use it for its random number generator: https://github.com/openssl/openssl/blob/f64f26220442db3c6913188e6014e5bc5bc34653/crypto/rand/rand_win.c#L70

Connecting to web_webrtc takes 3 times the time in stun mode and in turn mode

Currently using the library to connect to the web side, in the same LAN mode, the stun mode takes hundreds of milliseconds, and in the turn mode of different LANs, the time consumption can be close to 3s. In the same code environment, using libnice, the turn mode takes less time. It is more than 100 milliseconds than the stun mode. Through the current phenomenon, it is suspected that the program has not done a good job in dealing with the interactive process of turn!
look forward to your reply!

TURN: Received 438 error after a long run

438

test code below

#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 main(int argc, char *argv[]) {
	juice_set_log_level(JUICE_LOG_LEVEL_DEBUG);

	// Agent 1: Create agent
	juice_config_t config1;
	memset(&config1, 0, sizeof(config1));

	// TURN server
	// Please do not use outside of libjuice tests
	juice_turn_server_t turn_server;
	memset(&turn_server, 0, sizeof(turn_server));
	turn_server.host = "stun.ageneau.net";
	turn_server.port = 3478;
	turn_server.username = "juice_test";
	turn_server.password = "28245150316902";
	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;

	// Agent 2: Create agent
	juice_config_t config2;
	memset(&config2, 0, sizeof(config2));
	juice_turn_server_t turn_server2;
	memcpy(&turn_server2, &turn_server, sizeof(juice_turn_server_t));

	// Use the same TURN server
	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;

	agent1 = juice_create(&config1);
	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);

	// Agent 2: Gather candidates (and send them to agent 1)
	juice_gather_candidates(agent2);

	sleep(10);

	// -- Connection should be finished --
	bool success = true;

	// 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);
	}

	for (;;) {
		const char *message = "Hello from 1";
		juice_send(agent1, message, strlen(message));
		sleep(1);
	}

	sleep(300);


	// Agent 1: destroy
	juice_destroy(agent1);

	// Agent 2: destroy
	juice_destroy(agent2);

	// Sleep so we can check destruction went well
	sleep(2);

	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 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);

	// 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);
}

// 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);
}

compile error in ubuntu 16.0.4

Latest code
gcc version 5.4.0
make error as follows in ubuntu 16.0.4

root@ubuntu:libjuice#  make USE_NETTLE=1
Package nettle was not found in the pkg-config search path.
Perhaps you should add the directory containing `nettle.pc'
to the PKG_CONFIG_PATH environment variable
No package 'nettle' found
gcc -O2 -pthread -fPIC -Wno-address-of-packed-member -DUSE_NETTLE=1 -Iinclude/juice  -MMD -MP -o src/addr.o -c src/addr.c
Package nettle was not found in the pkg-config search path.
Perhaps you should add the directory containing `nettle.pc'
to the PKG_CONFIG_PATH environment variable
No package 'nettle' found
gcc -O2 -pthread -fPIC -Wno-address-of-packed-member -DUSE_NETTLE=1 -Iinclude/juice  -MMD -MP -o src/agent.o -c src/agent.c
Package nettle was not found in the pkg-config search path.
Perhaps you should add the directory containing `nettle.pc'
to the PKG_CONFIG_PATH environment variable
No package 'nettle' found
gcc -O2 -pthread -fPIC -Wno-address-of-packed-member -DUSE_NETTLE=1 -Iinclude/juice  -MMD -MP -o src/crc32.o -c src/crc32.c
Package nettle was not found in the pkg-config search path.
Perhaps you should add the directory containing `nettle.pc'
to the PKG_CONFIG_PATH environment variable
No package 'nettle' found
gcc -O2 -pthread -fPIC -Wno-address-of-packed-member -DUSE_NETTLE=1 -Iinclude/juice  -MMD -MP -o src/hmac.o -c src/hmac.c
src/hmac.c:22:25: fatal error: nettle/hmac.h: No such file or directory
compilation terminated.
Makefile:40: recipe for target 'src/hmac.o' failed
make: *** [src/hmac.o] Error 1

Candidates gathering

I am unable to generate candidates that firefox can connect to using libjuice.
Candidates gathered using webrtc standard implementation (libnice):

candidate:390988917 1 udp 2122260223 192.168.122.127 52942 typ host generation 0 ufrag hCZ1 network-id 1
candidate:1506670725 1 tcp 1518280447 192.168.122.127 62560 typ host tcptype passive generation 0 ufrag hCZ1 network-id 1
candidate:2122599348 1 udp 1686052607 [PUBLIC IP] 52942 typ srflx raddr 192.168.122.127 rport 52942 generation 0 ufrag hCZ1 network-id 1

Candidates gathered using libjuice:

a=candidate:1 1 UDP 2122317823 192.168.122.127 58201 typ host
a=candidate:2 1 UDP 1686110207 [PUBLIC IP] 58201 typ srflx

Relayed connection is used although direct connection is possible

Hi,
i have two clients in different networks (both using libjuice).
They are behind EIM-NATs and establishing a direct connection with libjuice always works with the help of a STUN server.
However; If I add a TURN server they will sometimes use the relayed connection instead of the direct connection.
I'm not an ICE expert but I think they should use the direct connection right?

Log from one of the clients when direct connection is used:
direct.log

Log from one of the clients when turn connection is used:
turn.log

Unfortunately I can't tell from the log why the relayed connection is chosen over the direct connection.

Spinning thread problem in agent_bookkeeping

We’re seeing intermittent high CPU usage in our production environment caused by agent_run/agent_bookkeeping spinning with most of the CPU time spent in clock_gettime. We have not been able to reproduce this issue in our staging environment (which is almost identical) or locally on our development machines.

We noticed you have since replaced the use of select with poll. Our understanding is that this is unlikely to fix our problem. Our best guess is that the next_timestamp returned by agent_bookkeeping is less than or equal to current_timestamp(), which causes select/poll to return immediately.

We would like to help, but we’re not sure how. Our debugging options in the production environment are limited. We managed to grab a few stack traces with gdb at the cost of a degraded user experience (see below).

The problem occurs randomly and can last a few minutes or hours, then it often fixes itself (stops spinning). We’ve had it for a few months. It comes and goes, and does not seem to affect our service too much. We modified our container-based production environment so that we could log in and attach to the process with gdb. After that, the problem took over a month to reappear.

# top -H -p 34

top - 08:51:46 up 6 days, 22:19,  0 users,  load average: 1.22, 1.19, 1.17
Threads:  51 total,   1 running,  50 sleeping,   0 stopped,   0 zombie
%Cpu(s):  4.2 us,  1.7 sy,  0.0 ni, 93.7 id,  0.0 wa,  0.0 hi,  0.2 si,  0.2 st
KiB Mem :  8166304 total,  5484796 free,  1067852 used,  1613656 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  6932204 avail Mem

  PID PR  NI    VIRT    RES    SHR S %CPU %MEM     TIME+ COMMAND
 5996 20   0 1886676 762396  44088 R 99.9  9.3 101:25.08 NIO-ELT-#0
   34 20   0 1886676 762396  44088 S  0.0  9.3   0:00.03 Run
   35 20   0 1886676 762396  44088 S  0.0  9.3   0:00.00 Run
   36 20   0 1886676 762396  44088 S  0.0  9.3   0:00.00 Run
   37 20   0 1886676 762396  44088 S  0.0  9.3   0:00.08 NIO-ELT-#0

GDB traces from different points in time after the above top result:

Thread 25 (Thread 0x7f9abd7fb700 (LWP 5996)):
#0  0x00007ffca8f88b52 in clock_gettime ()
#1  0x00007f9b0344cd36 in __GI___clock_gettime (clock_id=clock_id@entry=0, tp=tp@entry=0x7f9abd7fa910) at ../sysdeps/unix/clock_gettime.c:115
#2  0x000055efedd66601 in current_timestamp () at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/timestamp.c:32
#3  0x000055efedd54cd1 in agent_run (agent=<optimized out>) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:624
#4  0x000055efedd543e6 in agent_thread_entry (arg=0x0) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:201
#5  0x00007f9b060b56db in start_thread (arg=0x7f9abd7fb700) at pthread_create.c:463
#6  0x00007f9b0343d71f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
Thread 29 (Thread 0x7f9abd7fb700 (LWP 5996)):
#0  0x00007f9b03432e1f in __GI___select (nfds=nfds@entry=1340, readfds=readfds@entry=0x7f9abd7fa970, writefds=writefds@entry=0x0, exceptfds=exceptfds@entry=0x0, timeout=timeout@entry=0x7f9abd7fa948)
    at ../sysdeps/unix/sysv/linux/select.c:41
#1  0x000055efedd54d9c in agent_run (agent=<optimized out>) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:640
#2  0x000055efedd543e6 in agent_thread_entry (arg=0x53c) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:201
#3  0x00007f9b060b56db in start_thread (arg=0x7f9abd7fb700) at pthread_create.c:463
#4  0x00007f9b0343d71f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
Thread 29 (Thread 0x7f9abd7fb700 (LWP 5996)):
#0  0x00007ffca8f88b52 in clock_gettime ()
#1  0x00007f9b0344cd36 in __GI___clock_gettime (clock_id=clock_id@entry=0, tp=tp@entry=0x7f9abd7fa860) at ../sysdeps/unix/clock_gettime.c:115
#2  0x000055efedd66601 in current_timestamp () at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/timestamp.c:32
#3  0x000055efedd5663b in agent_bookkeeping (agent=agent@entry=0x7f9ac27f9520, next_timestamp=next_timestamp@entry=0x7f9abd7fa968) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:790
#4  0x000055efedd54cc5 in agent_run (agent=<optimized out>) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:623
#5  0x000055efedd543e6 in agent_thread_entry (arg=0x0) at Vendor/libdatachannel/libdatachannel/deps/libjuice/src/agent.c:201
#6  0x00007f9b060b56db in start_thread (arg=0x7f9abd7fb700) at pthread_create.c:463
#7  0x00007f9b0343d71f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95

We have a few more like the above taken while in clock_gettime.

Any help would be greatly appreciated.

No pair is selected if remote candidates arrive faster than the local one from STUN

Hello!

I write the issue here because even if I talk about the examples from libdatachannel, I guess this might be a problem related to libjuice. If not, I can rewrite the issue in libdatachannel.

I've compiled the client example application from libdatachannel. I also run the python signaling server on a public server. Then I run both clients on machines that are on different networks and try to create a connection between them by providing one with the id of the other. Both receive a srflx candidate from the STUN server with the public IP address, which I will call from now "remote candidate" and "local candidate".

The problem is that it won't find any valid pairs, even after the remote candidates have been added IF the local candidate coming from the STUN server arrived faster than the remote one. On the other hand, if the local one arrives after the remote candidate, everything works. Now, the entire application is asynchronous, meaning the local candidate and remote candidate can arrive in any order... and usually, the local candidate arrives faster, so most of the time the connection won't be opened.

From what I investigated, a valid pair is selected in agent_bookkeeping based on entries that have their state set to ICE_CANDIDATE_PAIR_STATE_SUCCEEDED, and this is set in agent_process_stun_binding based on entries that have their type set to AGENT_STUN_ENTRY_TYPE_CHECK. Now, these entries are added by setRemoteCandidate from libdatachannel and the function agent_process_stun_binding is called only when the agent receives data from the STUN server. Based on this, if the function setRemoteCandidate is called after the STUN server sent the candidate, the function agent_process_stun_binding will never be called again (because neither agent_recv will be called again since there's no more data from the STUN server) thus leading to not selecting any pair.

In agent_add_remote_candidate, I saw that there is a function agent_interrupt. Is this supposed to make sure the function agent_process_stun_binding is called after the candidate is added? Because if the answer is yes, it doesn't do its job.

Any idea on what should be done? Am I missing something?

poll failed, errno=22

Hi,

I'm trying to run violet server on (x86) and (Raspberry PI armv71) Debian 10 but it is facing following error on both platforms.

$ ./violet --credentials=test:123
2021-09-22 22:56:23 INFO    server.c:214: Created server on port 3478
2021-09-22 22:56:23 FATAL   server.c:387: poll failed, errno=22

IP Differentiated Services on Windows

IP Differentiated Services by setting the IPv4 ToS field or the IPv6 Traffic Class field are not supported on Microsoft Windows. Microsoft has a proprietary API called qWave for QoS, it should be investigated to establish whether the DSCP can be set without administrator flow configuration.

// TODO: Investigate if DSCP can be still set directly without administrator flow configuration.

Clarification on setting local description with setLocalDescription()

Hello Paul-Louis,

I have referred to peerconnection.hpp APIs and example programs like copy-paste. In this we do not have way to send the negotiated offer description as local description with setLocalDescription () sort of API. This setLocalDescription () does not take any input and it sets the description decided by the libdatachannel internally (some fields are hardcoded like sctp port). how to set the application decided (through negotiation with SIP) description like SCTP port, UDP port, fingerprint etc. Can you please help answering this.

0.7.3 has warnings when built with NO_SERVER

Probably not serious, but thought this would be useful to know.

Toolchain is attached.
toolchain.txt

Running on Ubuntu 16.04.

mkdir build
cd build
cmake -DNO_SERVER=1 ..
make

-- The C compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Looking for pthread.h
-- Looking for pthread.h - found
-- Looking for pthread_create
-- Looking for pthread_create - not found
-- Check if compiler accepts -pthread
-- Check if compiler accepts -pthread - yes
-- Found Threads: TRUE
-- Configuring done
-- Generating done
-- Build files have been written to: /home/datav/DataV/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/build
Scanning dependencies of target juice
[ 3%] Building C object CMakeFiles/juice.dir/src/addr.c.o
[ 7%] Building C object CMakeFiles/juice.dir/src/agent.c.o
[ 10%] Building C object CMakeFiles/juice.dir/src/crc32.c.o
[ 14%] Building C object CMakeFiles/juice.dir/src/const_time.c.o
[ 17%] Building C object CMakeFiles/juice.dir/src/base64.c.o
[ 21%] Building C object CMakeFiles/juice.dir/src/hash.c.o
[ 25%] Building C object CMakeFiles/juice.dir/src/hmac.c.o
[ 28%] Building C object CMakeFiles/juice.dir/src/ice.c.o
[ 32%] Building C object CMakeFiles/juice.dir/src/juice.c.o
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c: In function ‘juice_server_create’:
/home/
tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c:170:79: warning: unused parameter ‘config’ [-Wunused-parameter]
PORT juice_server_t *juice_server_create(const juice_server_config_t *config) {
^
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c: In function ‘juice_server_destroy’:
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c:181:56: warning: unused parameter ‘server’ [-Wunused-parameter]
JUICE_EXPORT void juice_server_destroy(juice_server_t *server) {
^
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c: In function ‘juice_server_get_port’:
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c:188:61: warning: unused parameter ‘server’ [-Wunused-parameter]
JUICE_EXPORT uint16_t juice_server_get_port(juice_server_t *server) {
^
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c: In function ‘juice_server_create’:
/home/tool-chains/external-libraries/build.x86_64/libjuice-0.7.3/src/juice.c:179:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
[ 35%] Building C object CMakeFiles/juice.dir/src/log.c.o
[ 39%] Building C object CMakeFiles/juice.dir/src/random.c.o
[ 42%] Building C object CMakeFiles/juice.dir/src/server.c.o
[ 46%] Building C object CMakeFiles/juice.dir/src/stun.c.o
[ 50%] Building C object CMakeFiles/juice.dir/src/timestamp.c.o
[ 53%] Building C object CMakeFiles/juice.dir/src/turn.c.o
[ 57%] Building C object CMakeFiles/juice.dir/src/udp.c.o
[ 60%] Linking C shared library libjuice.so
[ 60%] Built target juice
Scanning dependencies of target juice-tests
[ 64%] Building C object CMakeFiles/juice-tests.dir/test/main.c.o
[ 67%] Building C object CMakeFiles/juice-tests.dir/test/crc32.c.o
[ 71%] Building C object CMakeFiles/juice-tests.dir/test/base64.c.o
[ 75%] Building C object CMakeFiles/juice-tests.dir/test/stun.c.o
[ 78%] Building C object CMakeFiles/juice-tests.dir/test/gathering.c.o
[ 82%] Building C object CMakeFiles/juice-tests.dir/test/connectivity.c.o
[ 85%] Building C object CMakeFiles/juice-tests.dir/test/notrickle.c.o
[ 89%] Building C object CMakeFiles/juice-tests.dir/test/turn.c.o
[ 92%] Building C object CMakeFiles/juice-tests.dir/test/server.c.o
[ 96%] Building C object CMakeFiles/juice-tests.dir/test/conflict.c.o
[100%] Linking C executable tests
[100%] Built target juice-tests
[ 60%] Built target juice
[100%] Built target juice-tests

have question with udp_get_addrs

if ((sa->sa_family == AF_INET || sa->sa_family == AF_INET6) && !addr_is_local(sa) &&
!(has_temp_inet6 && sa->sa_family == AF_INET6 && !addr_is_temp_inet6(sa))) {

!(has_temp_inet6 && sa->sa_family == AF_INET6 && !addr_is_temp_inet6(sa)))
The first "!" does not need to be added, this means that non-temporary ipv6 addresses can be added

getaddrinfo failed with error 11001

On Windows 10, when using google's stun servers, the getaddrinfo fails inside addr_resolve with an ultimate "STUN address resolution failed" and multiple "STUN message send failed" afterwards.

Should a nslookup work on these addresses because they also fail ?

Symmetric NATs

Hi - will this navigate through more complex NAT setups - such as symmetric NATs ?

Install error: implicit declaration of function 'clock_gettime' is invalid in C99

I don't really know any C but am trying to install node-datachannel, and it uses libdatachannel and this, libjuice, as a dependency. This is on an older macOS, 10.10 I'm using as a test server.
I'm using npm run build and the error stops the build and the exact error is:

[ 2%] Building C object _deps/libdatachannel-build/deps/libjuice/CMakeFiles/juice-static.dir/src/random.c.o
/Users/testserver/Desktop/node-datachannel/build/_deps/libdatachannel-src/deps/libjuice/src/random.c:75:6: warning: implicit declaration of function 'clock_gettime' is invalid in C99
[-Wimplicit-function-declaration]
if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
^
/Users/testserver/Desktop/node-datachannel/build/_deps/libdatachannel-src/deps/libjuice/src/random.c:75:20: error: use of undeclared identifier 'CLOCK_REALTIME'
if (clock_gettime(CLOCK_REALTIME, &ts) == 0)
^
1 warning and 1 error generated.
make[2]: *** [_deps/libdatachannel-build/deps/libjuice/CMakeFiles/juice-static.dir/src/random.c.o] Error 1
make[1]: *** [_deps/libdatachannel-build/deps/libjuice/CMakeFiles/juice-static.dir/all] Error 2
make: *** [all] Error 2
ERR! OMG Process terminated: 2

Am I doing something wrong? Any help or a workaround would be a life saver right now as I have no idea what to do.

Visual Studio 2019

When using visual studio 2017, everything is fine, working like a charm. But i recently tried to build on a machine who is running visual studio 2019, and i get an error trying to send stun messages:

STUN message send failed, errno=10049

Do you have any idea how to fix this?

agent.c:777: Failed to interrupt thread by triggering socket, errno=99

Hey!

I wanted to use libjuice in a personal project but noticed some error messages; I'm getting agent.c:777: Failed to interrupt thread by triggering socket, errno=99 on every call of
juice_add_remote_candidate() and juice_destroy(). The connection establishment seems to work fine despite the warnings; however, trying to destroy the agent hangs my program (OTOH your client seems to not suffer from that problem, so that might be a bug on my end)

The problem seems to also exist in the tests executable, built on Linux (I tried NixOS 20.09 and Ubuntu 20.04).

How to reproduce:

  • Freshly clone repository
  • run make
  • run ./tests

Does " connectivity.c " test work only in local network ??

I want to create simple P2P. I create a STUN/TURN server using violet library.
And now i want to divide the connectivity test into 2 clients and to make a simple P2P.
But i get confused if i can make P2P when clients behind different NATs.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.