diff --git a/src/server.c b/src/server.c index 8ad2ab1..ea18f2d 100644 --- a/src/server.c +++ b/src/server.c @@ -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 @@ -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)); @@ -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; } diff --git a/src/turn.c b/src/turn.c index 5f4abdb..985300d 100644 --- a/src/turn.c +++ b/src/turn.c @@ -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) @@ -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); } @@ -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;