Commit e84dc4d0 authored by Ghislain MARY's avatar Ghislain MARY

Add a TURN RTP endpoint to handle (un)packing of TURN data indications.

parent dbdb7dec
......@@ -21,7 +21,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define MS_STUN_H
#include "mediastreamer2/mscommon.h"
#include <ortp/rtpsession.h>
#include <mediastreamer2/mscommon.h>
#define MS_STUN_MAX_MESSAGE_SIZE 2048
......@@ -120,6 +121,7 @@ typedef struct {
uint16_t method;
uint16_t length;
UInt96 tr_id;
uint8_t *data;
char *username;
char *password;
char *ha1;
......@@ -138,6 +140,7 @@ typedef struct {
uint64_t ice_controlling;
uint64_t ice_controlled;
uint32_t lifetime;
uint16_t data_length;
uint8_t requested_transport;
bool_t include_username_attribute;
bool_t has_error_code;
......@@ -164,7 +167,13 @@ typedef enum {
MS_TURN_CONTEXT_STATE_RUNNING
} MSTurnContextState;
typedef enum {
MS_TURN_CONTEXT_TYPE_RTP,
MS_TURN_CONTEXT_TYPE_RTCP
} MSTurnContextType;
typedef struct {
RtpSession *rtp_session;
char *realm;
char *nonce;
char *username;
......@@ -172,6 +181,7 @@ typedef struct {
char *ha1;
uint32_t lifetime;
MSTurnContextState state;
MSTurnContextType type;
} MSTurnContext;
......@@ -190,7 +200,7 @@ MS2_PUBLIC char * ms_stun_calculate_integrity_long_term_from_ha1(const char *buf
MS2_PUBLIC uint32_t ms_stun_calculate_fingerprint(const char *buf, size_t bufsize);
MS2_PUBLIC MSStunMessage * ms_stun_message_create(uint16_t type, uint16_t method);
MS2_PUBLIC MSStunMessage * ms_stun_message_create_from_buffer_parsing(const char *buf, size_t bufsize);
MS2_PUBLIC MSStunMessage * ms_stun_message_create_from_buffer_parsing(const uint8_t *buf, size_t bufsize);
MS2_PUBLIC MSStunMessage * ms_stun_binding_request_create(void);
MS2_PUBLIC MSStunMessage * ms_stun_binding_success_response_create(void);
MS2_PUBLIC MSStunMessage * ms_stun_binding_error_response_create(void);
......@@ -260,8 +270,11 @@ MS2_PUBLIC uint8_t ms_stun_message_get_requested_transport(const MSStunMessage *
MS2_PUBLIC bool_t ms_stun_message_has_lifetime(const MSStunMessage *msg);
MS2_PUBLIC uint32_t ms_stun_message_get_lifetime(const MSStunMessage *msg);
MS2_PUBLIC void ms_stun_message_set_lifetime(MSStunMessage *msg, uint32_t lifetime);
MS2_PUBLIC uint8_t * ms_stun_message_get_data(const MSStunMessage *msg);
MS2_PUBLIC uint16_t ms_stun_message_get_data_length(const MSStunMessage *msg);
MS2_PUBLIC void ms_stun_message_set_data(MSStunMessage *msg, uint8_t *data, uint16_t length);
MS2_PUBLIC MSTurnContext * ms_turn_context_create(void);
MS2_PUBLIC MSTurnContext * ms_turn_context_new(MSTurnContextType type, RtpSession *rtp_session);
MS2_PUBLIC void ms_turn_context_destroy(MSTurnContext *context);
MS2_PUBLIC MSTurnContextState ms_turn_context_get_state(const MSTurnContext *context);
MS2_PUBLIC void ms_turn_context_set_state(MSTurnContext *context, MSTurnContextState state);
......@@ -277,6 +290,7 @@ MS2_PUBLIC const char * ms_turn_context_get_ha1(const MSTurnContext *context);
MS2_PUBLIC void ms_turn_context_set_ha1(MSTurnContext *context, const char *ha1);
MS2_PUBLIC uint32_t ms_turn_context_get_lifetime(const MSTurnContext *context);
MS2_PUBLIC void ms_turn_context_set_lifetime(MSTurnContext *context, uint32_t lifetime);
MS2_PUBLIC RtpTransport * ms_turn_context_create_endpoint(MSTurnContext *context);
#ifdef __cplusplus
}
......
......@@ -937,6 +937,10 @@ static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si
cl->gathering_start_time = curtime;
rtp_session_get_transports(cl->rtp_session,&rtptp,NULL);
if (rtptp) {
if (cl->session->turn_enabled) {
/* Define the RTP endpoint that will perform STUN encapsulation/decapsulation for TURN data */
meta_rtp_transport_set_endpoint(rtptp, ms_turn_context_create_endpoint(cl->rtp_turn_context));
}
request = ice_stun_server_request_new(cl, cl->rtp_turn_context, rtptp, rtp_session_get_local_port(cl->rtp_session),
cl->session->turn_enabled ? MS_TURN_METHOD_ALLOCATE : MS_STUN_METHOD_BINDING);
if (si->index == 0) {
......@@ -955,6 +959,10 @@ static void ice_check_list_gather_candidates(IceCheckList *cl, Session_Index *si
rtptp=NULL;
rtp_session_get_transports(cl->rtp_session,NULL,&rtptp);
if (rtptp) {
if (cl->session->turn_enabled) {
/* Define the RTP endpoint that will perform STUN encapsulation/decapsulation for TURN data */
meta_rtp_transport_set_endpoint(rtptp, ms_turn_context_create_endpoint(cl->rtcp_turn_context));
}
request = ice_stun_server_request_new(cl, cl->rtcp_turn_context, rtptp, rtp_session_get_local_rtcp_port(cl->rtp_session),
cl->session->turn_enabled ? MS_TURN_METHOD_ALLOCATE : MS_STUN_METHOD_BINDING);
request->next_transmission_time = ice_add_ms(curtime, 2 * si->index * ICE_DEFAULT_TA_DURATION + ICE_DEFAULT_TA_DURATION);
......@@ -1060,8 +1068,8 @@ void ice_session_enable_forced_relay(IceSession *session, bool_t enable)
static void ice_check_list_create_turn_contexts(IceCheckList *cl)
{
cl->rtp_turn_context = ms_turn_context_create();
cl->rtcp_turn_context = ms_turn_context_create();
cl->rtp_turn_context = ms_turn_context_new(MS_TURN_CONTEXT_TYPE_RTP, cl->rtp_session);
cl->rtcp_turn_context = ms_turn_context_new(MS_TURN_CONTEXT_TYPE_RTCP, cl->rtp_session);
}
void ice_session_enable_turn(IceSession *session, bool_t enable)
......@@ -1482,15 +1490,6 @@ static int ice_get_transport_from_rtp_session_and_componentID(const RtpSession *
} else return -1;
}
static int ice_get_recv_port_from_rtp_session(const RtpSession *rtp_session, const OrtpEventData *evt_data)
{
if (evt_data->info.socket_type == OrtpRTPSocket) {
return rtp_session_get_local_port(rtp_session);
} else if (evt_data->info.socket_type == OrtpRTCPSocket) {
return rtp_session_get_local_rtcp_port(rtp_session);
} else return -1;
}
static MSTurnContext * ice_get_turn_context_from_check_list(const IceCheckList *cl, const OrtpEventData *evt_data) {
if (evt_data->info.socket_type == OrtpRTPSocket) {
return cl->rtp_turn_context;
......@@ -1514,7 +1513,6 @@ static void ice_send_binding_response(IceCheckList *cl, const RtpSession *rtp_se
char *username;
int len;
RtpTransport *rtptp = NULL;
int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
struct sockaddr_in dest_addr;
struct sockaddr_in source_addr;
MSStunAddress xor_mapped_address;
......@@ -1565,10 +1563,10 @@ static void ice_send_binding_response(IceCheckList *cl, const RtpSession *rtp_se
dest_addr.sin_family = AF_INET;
ice_inet_ntoa((struct sockaddr *)&dest_addr, sizeof(dest_addr), dest_addr_str, sizeof(dest_addr_str));
source_addr.sin_addr.s_addr = evt_data->packet->recv_addr.addr.ipi_addr.s_addr; // TODO: Handle IPv6
source_addr.sin_port = htons(recvport);
source_addr.sin_port = evt_data->packet->recv_addr.port;
source_addr.sin_family = AF_INET;
ice_inet_ntoa((struct sockaddr *)&source_addr, sizeof(source_addr), source_addr_str, sizeof(source_addr_str));
ms_message("ice: Send binding response: %s:%u --> %s:%u [%s]", source_addr_str, recvport, dest_addr_str, dest->port, tr_id_str);
ms_message("ice: Send binding response: %s:%u --> %s:%u [%s]", source_addr_str, ntohs(source_addr.sin_port), dest_addr_str, dest->port, tr_id_str);
ice_send_message_to_socket(rtptp, buf, len, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
}
if (buf != NULL) ms_free(buf);
......@@ -1581,7 +1579,6 @@ static void ice_send_error_response(const RtpSession *rtp_session, const OrtpEve
char *buf = NULL;
int len;
RtpTransport* rtptp;
int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
struct sockaddr_in dest_addr;
struct sockaddr_in source_addr;
char dest_addr_str[256];
......@@ -1610,10 +1607,10 @@ static void ice_send_error_response(const RtpSession *rtp_session, const OrtpEve
dest_addr.sin_family = AF_INET;
ice_inet_ntoa((struct sockaddr *)&dest_addr, sizeof(dest_addr), dest_addr_str, sizeof(dest_addr_str));
source_addr.sin_addr.s_addr = evt_data->packet->recv_addr.addr.ipi_addr.s_addr; // TODO: Handle IPv6
source_addr.sin_port = htons(recvport);
source_addr.sin_port = evt_data->packet->recv_addr.port;
source_addr.sin_family = AF_INET;
ice_inet_ntoa((struct sockaddr *)&source_addr, sizeof(source_addr), source_addr_str, sizeof(source_addr_str));
ms_message("ice: Send error response: %s:%u --> %s:%u [%s]", source_addr_str, recvport, dest_addr_str, dest->port, tr_id_str);
ms_message("ice: Send error response: %s:%u --> %s:%u [%s]", source_addr_str, ntohs(source_addr.sin_port), dest_addr_str, dest->port, tr_id_str);
ice_send_message_to_socket(rtptp, buf, len, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
}
if (buf != NULL) ms_free(buf);
......@@ -1829,16 +1826,13 @@ static IceCandidatePair * ice_trigger_connectivity_check_on_binding_request(IceC
IceCandidatePair *pair = NULL;
struct sockaddr_in source_addr;
char source_addr_str[256];
int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
memset(&source_addr,0,sizeof(source_addr));
if (recvport < 0) return NULL;
memset(&source_addr,0,sizeof(source_addr));
source_addr.sin_addr.s_addr = evt_data->packet->recv_addr.addr.ipi_addr.s_addr; // TODO: Handle IPv6
source_addr.sin_port = htons(recvport);
source_addr.sin_port = evt_data->packet->recv_addr.port;
source_addr.sin_family = AF_INET;
ice_inet_ntoa((struct sockaddr *)&source_addr, sizeof(source_addr), source_addr_str, sizeof(source_addr_str));
ice_fill_transport_address(&local_taddr, source_addr_str, recvport);
ice_fill_transport_address(&local_taddr, source_addr_str, ntohs(source_addr.sin_port));
elem = ms_list_find_custom(cl->local_candidates, (MSCompareFunc)ice_find_candidate_from_transport_address, &local_taddr);
if (elem == NULL) {
ms_error("ice: Local candidate %s:%u not found!", local_taddr.ip, local_taddr.port);
......@@ -1957,11 +1951,9 @@ static int ice_check_received_binding_response_addresses(const RtpSession *rtp_s
{
MSStunAddress4 dest;
MSStunAddress4 local;
int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
if (recvport < 0) return -1;
dest = ms_stun_hostname_to_stun_addr(pair->remote->taddr.ip, pair->remote->taddr.port);
local = ms_stun_hostname_to_stun_addr(pair->local->taddr.ip, recvport);
local = ms_stun_hostname_to_stun_addr(pair->local->taddr.ip, ntohs(evt_data->packet->recv_addr.port));
// TODO: Handle IPv6 for ipi_addr
if ( (remote_addr->addr != dest.addr)
|| (remote_addr->port != dest.port)
......@@ -2352,13 +2344,13 @@ void ice_handle_stun_packet(IceCheckList *cl, RtpSession *rtp_session, const Ort
const struct sockaddr_storage *aaddr;
int remote_port;
char tr_id_str[25];
int recvport = ice_get_recv_port_from_rtp_session(rtp_session, evt_data);
int recvport;
UInt96 tr_id;
if (cl->session == NULL) return;
memset(&source_addr, 0, sizeof(source_addr));
msg = ms_stun_message_create_from_buffer_parsing((char *) mp->b_rptr, (int)(mp->b_wptr - mp->b_rptr));
msg = ms_stun_message_create_from_buffer_parsing(mp->b_rptr, (int)(mp->b_wptr - mp->b_rptr));
if (msg == NULL) {
ms_warning("ice: Received invalid STUN packet");
return;
......@@ -2388,8 +2380,9 @@ void ice_handle_stun_packet(IceCheckList *cl, RtpSession *rtp_session, const Ort
tr_id = ms_stun_message_get_tr_id(msg);
transactionID2string(&tr_id, tr_id_str);
source_addr.sin_addr.s_addr = evt_data->packet->recv_addr.addr.ipi_addr.s_addr; // TODO: Handle IPv6
source_addr.sin_port = htons(recvport);
source_addr.sin_port = evt_data->packet->recv_addr.port;
source_addr.sin_family = AF_INET;
recvport = ntohs(source_addr.sin_port);
ice_inet_ntoa((struct sockaddr *)&source_addr, sizeof(source_addr), source_addr_str, sizeof(source_addr_str));
if (ms_stun_message_is_request(msg)) {
ms_message("ice: Recv binding request: %s:%u <-- %s:%u [%s]", source_addr_str, recvport, src6host, remote_port, tr_id_str);
......
......@@ -265,15 +265,15 @@ static void encode_lifetime(StunMessageEncoder *encoder, uint32_t lifetime) {
typedef struct {
const char *buffer;
const char *ptr;
const uint8_t *buffer;
const uint8_t *ptr;
size_t size;
size_t remaining;
bool_t error;
} StunMessageDecoder;
static void stun_message_decoder_init(StunMessageDecoder *decoder, const char *buf, size_t bufsize) {
static void stun_message_decoder_init(StunMessageDecoder *decoder, const uint8_t *buf, size_t bufsize) {
decoder->buffer = decoder->ptr = buf;
decoder->size = decoder->remaining = bufsize;
decoder->error = FALSE;
......@@ -466,6 +466,12 @@ static uint32_t decode_lifetime(StunMessageDecoder *decoder, uint16_t length) {
return decode32(decoder);
}
static uint8_t * decode_data(StunMessageDecoder *decoder, uint16_t length) {
uint8_t *data = ms_malloc(length);
memcpy(data, decode(decoder, length), length);
return data;
}
MSStunAddress4 ms_stun_hostname_to_stun_addr(const char *hostname, uint16_t default_port) {
char *host;
......@@ -618,7 +624,7 @@ MSStunMessage * ms_stun_message_create(uint16_t type, uint16_t method) {
return msg;
}
MSStunMessage * ms_stun_message_create_from_buffer_parsing(const char *buf, size_t bufsize) {
MSStunMessage * ms_stun_message_create_from_buffer_parsing(const uint8_t *buf, size_t bufsize) {
StunMessageDecoder decoder;
MSStunMessage *msg = NULL;
......@@ -738,6 +744,9 @@ MSStunMessage * ms_stun_message_create_from_buffer_parsing(const char *buf, size
case MS_TURN_ATTR_LIFETIME:
ms_stun_message_set_lifetime(msg, decode_lifetime(&decoder, length));
break;
case MS_TURN_ATTR_DATA:
ms_stun_message_set_data(msg, decode_data(&decoder, length), length);
break;
case MS_STUN_ATTR_REALM:
{
char *realm = decode_string(&decoder, length, STUN_MAX_REALM_LENGTH);
......@@ -814,10 +823,14 @@ bool_t ms_stun_message_is_indication(const MSStunMessage *msg) {
void ms_stun_message_destroy(MSStunMessage *msg) {
if (msg->username) ms_free(msg->username);
if (msg->password) ms_free(msg->password);
if (msg->password) {
memset(msg->password, '\0', strlen(msg->password));
ms_free(msg->password);
}
if (msg->realm) ms_free(msg->realm);
if (msg->message_integrity) ms_free(msg->message_integrity);
if (msg->software) ms_free(msg->software);
if (msg->data) ms_free(msg->data);
ms_free(msg);
}
......@@ -1146,10 +1159,29 @@ void ms_stun_message_set_lifetime(MSStunMessage *msg, uint32_t lifetime) {
msg->has_lifetime = TRUE;
}
uint8_t * ms_stun_message_get_data(const MSStunMessage *msg) {
return msg->data;
}
uint16_t ms_stun_message_get_data_length(const MSStunMessage *msg) {
return msg->data_length;
}
MSTurnContext * ms_turn_context_create(void) {
void ms_stun_message_set_data(MSStunMessage *msg, uint8_t *data, uint16_t length) {
if (msg->data != NULL) {
ms_free(msg->data);
msg->data = NULL;
}
msg->data = data;
msg->data_length = length;
}
MSTurnContext * ms_turn_context_new(MSTurnContextType type, RtpSession *rtp_session) {
MSTurnContext *context = ms_new0(MSTurnContext, 1);
context->state = MS_TURN_CONTEXT_STATE_IDLE;
context->type = type;
context->rtp_session = rtp_session;
return context;
}
......@@ -1212,3 +1244,89 @@ uint32_t ms_turn_context_get_lifetime(const MSTurnContext *context) {
void ms_turn_context_set_lifetime(MSTurnContext *context, uint32_t lifetime) {
context->lifetime = lifetime;
}
static int ms_turn_rtp_endpoint_recvfrom(RtpTransport *rtptp, mblk_t *msg, int flags, struct sockaddr *from, socklen_t *fromlen) {
MSTurnContext *context = (MSTurnContext *)rtptp->data;
int msgsize = 0;
if (context->rtp_session != NULL) {
msgsize = rtp_session_recvfrom(context->rtp_session, context->type == MS_TURN_CONTEXT_TYPE_RTP, msg, flags, from, fromlen);
if ((msgsize >= RTP_FIXED_HEADER_SIZE) && (rtp_get_version(msg) != 2)) {
/* This is not a RTP packet, try to see if it is a STUN one */
uint16_t stunlen = ntohs(*((uint16_t*)(msg->b_rptr + sizeof(uint16_t))));
if (msgsize == (stunlen + 20)) {
/* It seems to be a STUN packet */
MSStunMessage *stun_msg = ms_stun_message_create_from_buffer_parsing(msg->b_rptr, msgsize);
if (stun_msg != NULL) {
if (ms_stun_message_is_indication(stun_msg)
&& (ms_stun_message_get_data(stun_msg) != NULL) && (ms_stun_message_get_data_length(stun_msg) > 0)) {
/* This is TURN data indication */
const MSStunAddress *stun_addr = ms_stun_message_get_xor_peer_address(stun_msg);
if (stun_addr != NULL) {
// TODO: check if permissions have been set for the source address
/* Copy the data of the TURN data indication in the mblk_t so that it contains the unpacked data */
msgsize = ms_stun_message_get_data_length(stun_msg);
memcpy(msg->b_rptr, ms_stun_message_get_data(stun_msg), msgsize);
/* Overwrite the ortp_recv_addr of the mblk_t so that ICE source address is correct */
msg->recv_addr.family = from->sa_family;
if (from->sa_family == AF_INET) {
msg->recv_addr.addr.ipi_addr = ((struct sockaddr_in *)from)->sin_addr;
msg->recv_addr.port = ((struct sockaddr_in *)from)->sin_port;
} else if (from->sa_family == AF_INET6) {
msg->recv_addr.addr.ipi6_addr = ((struct sockaddr_in6 *)from)->sin6_addr;
msg->recv_addr.port = ((struct sockaddr_in6 *)from)->sin6_port;
} else {
ms_warning("turn: Unknown address family in ortp_recv_addr");
msgsize = 0;
}
/* Overwrite the source address of the packet so that it uses the peer address instead of the TURN server one */
if (stun_addr->family == MS_STUN_ADDR_FAMILY_IPV4) {
struct sockaddr_in *from_in = (struct sockaddr_in *)from;
from_in->sin_port = htons(stun_addr->ipv4.port);
from_in->sin_addr.s_addr = htonl(stun_addr->ipv4.addr);
from->sa_family = AF_INET;
} else if (stun_addr->family == MS_STUN_ADDR_FAMILY_IPV6) {
// TODO
from->sa_family = AF_INET6;
} else {
ms_warning("turn: Unknown address family in XOR peer address");
msgsize = 0;
}
}
}
ms_stun_message_destroy(stun_msg);
}
}
}
}
return msgsize;
}
static int ms_turn_rtp_endpoint_sendto(RtpTransport *rtptp, mblk_t *msg, int flags, const struct sockaddr *to, socklen_t tolen) {
MSTurnContext *context = (MSTurnContext *)rtptp->data;
if (context->rtp_session != NULL) {
return rtp_session_sendto(context->rtp_session, context->type == MS_TURN_CONTEXT_TYPE_RTP, msg, flags, to, tolen);
}
// TODO
return 0;
}
static void ms_turn_rtp_endpoint_close(RtpTransport *rtptp) {
MSTurnContext *context = (MSTurnContext *)rtptp->data;
context->rtp_session = NULL;
}
static void ms_turn_rtp_endpoint_destroy(RtpTransport *rtptp) {
ms_free(rtptp);
}
RtpTransport * ms_turn_context_create_endpoint(MSTurnContext *context) {
RtpTransport *rtptp = ms_new0(RtpTransport, 1);
rtptp->t_getsocket = NULL;
rtptp->t_recvfrom = ms_turn_rtp_endpoint_recvfrom;
rtptp->t_sendto = ms_turn_rtp_endpoint_sendto;
rtptp->t_close = ms_turn_rtp_endpoint_close;
rtptp->t_destroy = ms_turn_rtp_endpoint_destroy;
rtptp->data = context;
return rtptp;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment