paullouisageneau / libjuice Goto Github PK
View Code? Open in Web Editor NEWJUICE is a UDP Interactive Connectivity Establishment library
License: Mozilla Public License 2.0
JUICE is a UDP Interactive Connectivity Establishment library
License: Mozilla Public License 2.0
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.
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
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.
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?
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
The STUN protocol parsing implementation would benefit from fuzz testing.
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, 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
Hi,
I feel like importing the whole Nettle or OpenSSL lib just for HMAC computation is a bit overkill. Can we use something lighter like https://github.com/kazuho/picohash ?
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;
}
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 😅.
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
Implement proper ice-options
support, in particular ice2
as described in RFC 8445.
On Windows 10, WSAIoctl fails when using a socket AF_INET6.
I'm trying to create P2P connection using libjuice. I need something like connectivity.c but agents in separated files.
STUN error code 300 and ALTERNATE-SERVER attribute should be implemented as specified by RFC 8489.
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))
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 😅
ICE restart support (see RFC 8445 2.4.) should be implemented.
Hi,
Are you sure that
Lines 57 to 63 in 33612d1
Lines 106 to 108 in 33612d1
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 ?
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);
}
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
Quick question: would it be possible to specify a port range when making the first connection to the STUN server ?
vcpkg comment: microsoft/vcpkg#13703 (comment)
Missing library for
Lines 49 to 62 in 5d3e8b3
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
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!
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);
}
Some network environments block UDP and only TCP can passed. I am trying to implement RFC 6062, Is it a good idea?
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
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
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.
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.
I got the error after run the libjuice after 20 minute,
cloud u pls resolve it?
by the way, i use mqtt_c (for signaling client)/mosquitto(for signaling server).
thanks!
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?
no problem
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 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.
Line 149 in 11c8961
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.
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
ICE and DTLS restart support should be implemented.
Requires #130
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
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 ?
Hi - will this navigate through more complex NAT setups - such as symmetric NATs ?
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.
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?
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:
make
./tests
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.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.