Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,21 @@ int server_forward(juice_server_t *server, server_turn_alloc_t *alloc) {
}
addr_unmap_inet6_v4mapped((struct sockaddr *)&record.addr, &record.len);

// RFC 5766 8. Permissions:
// When a UDP datagram arrives at the relayed transport address for the allocation, the
// server extracts the source IP address from the IP header. The server then compares this
// address with the IP address associated with each permission in the list of permissions
// for the allocation. If no match is found, relaying is not permitted, and the server
// silently discards the UDP datagram.
if (!turn_has_permission(&alloc->map, &record)) {
if (JLOG_DEBUG_ENABLED) {
char record_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(&record, record_str, ADDR_MAX_STRING_LEN);
JLOG_DEBUG("No permission for remote address %s, discarding", record_str);
}
return -1;
}

uint16_t channel;
if (turn_get_bound_channel(&alloc->map, &record, &channel)) {
// Use ChannelData
Expand Down Expand Up @@ -1063,12 +1078,21 @@ int server_process_turn_channel_bind(juice_server_t *server, const stun_message_
credentials);
}

// RFC 5766 11.3. Receiving a ChannelBind Response
// When the client receives a ChannelBind success response, it updates its data structures to
// record that the channel binding is now active. It also updates its data structures to record
// that the corresponding permission has been installed or refreshed.
const addr_record_t *peer = msg->peers;
if (!turn_bind_channel(&alloc->map, peer, msg->transaction_id, channel, BIND_LIFETIME)) {
server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500,
credentials);
return -1;
}
if (!turn_set_permission(&alloc->map, msg->transaction_id, peer, PERMISSION_LIFETIME)) {
server_answer_stun_error(server, msg->transaction_id, src, msg->msg_method, 500,
credentials);
return -1;
}

stun_message_t ans;
memset(&ans, 0, sizeof(ans));
Expand Down Expand Up @@ -1105,7 +1129,11 @@ int server_process_turn_send(juice_server_t *server, const stun_message_t *msg,

const addr_record_t *peer = msg->peers;
if (!turn_has_permission(&alloc->map, peer)) {
JLOG_WARN("No permission for peer address");
if (JLOG_WARN_ENABLED) {
char peer_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(peer, peer_str, ADDR_MAX_STRING_LEN);
JLOG_WARN("No permission for peer address %s", peer_str);
}
return -1;
}

Expand Down
22 changes: 20 additions & 2 deletions src/turn.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,14 @@ static void delete_entry(turn_map_t *map, turn_entry_t *entry) {
*/
static turn_entry_t *find_entry(turn_map_t *map, const addr_record_t *record,
turn_entry_type_t type, bool allow_deleted) {
unsigned long key = (addr_record_hash(record, false) + (int)type) % map->map_size;
// RFC 5766: only addresses are compared and port numbers are not considered.
unsigned long key = (addr_record_hash(record, false /*no port*/) + (int)type) % map->map_size;
unsigned long pos = key;
while (true) {
turn_entry_t *entry = map->map + pos;
if (entry->type == TURN_ENTRY_TYPE_EMPTY ||
(entry->type == type && addr_record_is_equal(&entry->record, record, false)))
(entry->type == type &&
addr_record_is_equal(&entry->record, record, false /*no port*/)))
break;

if (allow_deleted && entry->type == TURN_ENTRY_TYPE_DELETED)
Expand Down Expand Up @@ -241,6 +243,13 @@ void turn_destroy_map(turn_map_t *map) {

bool turn_set_permission(turn_map_t *map, const uint8_t *transaction_id,
const addr_record_t *record, timediff_t duration) {
if (record) {
if (JLOG_DEBUG_ENABLED) {
char record_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(record, record_str, ADDR_MAX_STRING_LEN);
JLOG_DEBUG("Updating TURN permission for address %s", record_str);
}
}
return update_timestamp(map, TURN_ENTRY_TYPE_PERMISSION, transaction_id, record, duration);
}

Expand All @@ -254,6 +263,15 @@ bool turn_has_permission(turn_map_t *map, const addr_record_t *record) {

bool turn_bind_channel(turn_map_t *map, const addr_record_t *record, const uint8_t *transaction_id,
uint16_t channel, timediff_t duration) {
if(!record)
return false;

if (JLOG_DEBUG_ENABLED) {
char record_str[ADDR_MAX_STRING_LEN];
addr_record_to_string(record, record_str, ADDR_MAX_STRING_LEN);
JLOG_DEBUG("Binding TURN channel %hu to address %s", channel, record_str);
}

if (!is_valid_channel(channel)) {
JLOG_ERROR("Invalid channel number: 0x%hX", channel);
return false;
Expand Down
Loading