From fbeb5a5633260ccdb3dca6c8f79e7781139b86dc Mon Sep 17 00:00:00 2001 From: Sean DuBois Date: Mon, 30 Dec 2024 00:01:15 -0500 Subject: [PATCH] Add peer_connection_create_datachannel Allows the user to send a DataChannel Open message. --- src/ice.c | 1 - src/peer_connection.c | 49 +++++++++++++++++++++++++++++++ src/peer_connection.h | 14 +++++++++ src/ssl_transport.c | 1 + tests/test_peer_connection.c | 57 +++++++++++++++++++++++++++++------- 5 files changed, 111 insertions(+), 11 deletions(-) diff --git a/src/ice.c b/src/ice.c index c22911f..756e961 100644 --- a/src/ice.c +++ b/src/ice.c @@ -87,7 +87,6 @@ int ice_candidate_from_description(IceCandidate* candidate, char* description, c candidate_start += strlen("a="); } candidate_start += strlen("candidate:"); - printf("candidate_start: %s\n", candidate_start); // a=candidate:448736988 1 udp 2122260223 172.17.0.1 49250 typ host generation 0 network-id 1 network-cost 50 // a=candidate:udpcandidate 1 udp 120 192.168.1.102 8000 typ host diff --git a/src/peer_connection.c b/src/peer_connection.c index 608f672..2d2f0d9 100644 --- a/src/peer_connection.c +++ b/src/peer_connection.c @@ -268,6 +268,55 @@ int peer_connection_datachannel_send_sid(PeerConnection* pc, char* message, size #endif } +int peer_connection_create_datachannel(PeerConnection* pc, DecpChannelType channel_type, uint16_t priority, uint32_t reliability_parameter, char* label, char* protocol) { + return peer_connection_create_datachannel_sid(pc, channel_type, priority, reliability_parameter, label, protocol, 0); +} + +int peer_connection_create_datachannel_sid(PeerConnection* pc, DecpChannelType channel_type, uint16_t priority, uint32_t reliability_parameter, char* label, char* protocol, uint16_t sid) { + int rtrn = -1; + + if (!sctp_is_connected(&pc->sctp)) { + LOGE("sctp not connected"); + return rtrn; + } + + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Message Type | Channel Type | Priority | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Reliability Parameter | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | Label Length | Protocol Length | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // | Label | + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | | + // | Protocol | + // | | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + int msg_size = 12 + strlen(label) + strlen(protocol); + uint16_t priority_big_endian = htons(priority); + uint32_t reliability_big_endian = ntohl(reliability_parameter); + uint16_t label_length = htons(strlen(label)); + uint16_t protocol_length = htons(strlen(protocol)); + char* msg = calloc(1, msg_size); + + msg[0] = DATA_CHANNEL_OPEN; + memcpy(msg + 2, &priority_big_endian, sizeof(uint16_t)); + memcpy(msg + 4, &reliability_big_endian, sizeof(uint32_t)); + memcpy(msg + 8, &label_length, sizeof(uint16_t)); + memcpy(msg + 10, &protocol_length, sizeof(uint16_t)); + memcpy(msg + 12, label, strlen(label)); + memcpy(msg + 12 + strlen(label), protocol, strlen(protocol)); + + rtrn = sctp_outgoing_data(&pc->sctp, msg, msg_size, PPID_CONTROL, sid); + free(msg); + return rtrn; +} + static char* peer_connection_dtls_role_setup_value(DtlsSrtpRole d) { return d == DTLS_SRTP_ROLE_SERVER ? "a=setup:passive" : "a=setup:active"; } diff --git a/src/peer_connection.h b/src/peer_connection.h index d917884..dca3271 100644 --- a/src/peer_connection.h +++ b/src/peer_connection.h @@ -32,6 +32,15 @@ typedef enum DataChannelType { } DataChannelType; +typedef enum DecpChannelType { + DATA_CHANNEL_RELIABLE = 0x00, + DATA_CHANNEL_RELIABLE_UNORDERED = 0x80, + DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT = 0x01, + DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED = 0x81, + DATA_CHANNEL_PARTIAL_RELIABLE_TIMED = 0x02, + DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED = 0x82, +} DecpChannelType; + typedef enum MediaCodec { CODEC_NONE = 0, @@ -84,6 +93,11 @@ void peer_connection_destroy(PeerConnection* pc); void peer_connection_close(PeerConnection* pc); int peer_connection_loop(PeerConnection* pc); + +int peer_connection_create_datachannel(PeerConnection* pc, DecpChannelType channel_type, uint16_t priority, uint32_t reliability_parameter, char* label, char* protocol); + +int peer_connection_create_datachannel_sid(PeerConnection* pc, DecpChannelType channel_type, uint16_t priority, uint32_t reliability_parameter, char* label, char* protocol, uint16_t sid); + /** * @brief send message to data channel * @param[in] peer connection diff --git a/src/ssl_transport.c b/src/ssl_transport.c index 909f9e9..641023c 100644 --- a/src/ssl_transport.c +++ b/src/ssl_transport.c @@ -8,6 +8,7 @@ #include "mbedtls/entropy.h" #include "mbedtls/ssl.h" +#include #include "config.h" #include "ports.h" #include "ssl_transport.h" diff --git a/tests/test_peer_connection.c b/tests/test_peer_connection.c index fa17832..598caa6 100644 --- a/tests/test_peer_connection.c +++ b/tests/test_peer_connection.c @@ -1,13 +1,20 @@ #include #include +#include #include #include "peer.h" #define MAX_CONNECTION_ATTEMPTS 25 +#define OFFER_DATACHANNEL_MESSAGE "Hello World" +#define ANSWER_DATACHANNEL_MESSAGE "Foobar" +#define DATACHANNEL_NAME "libpeer-datachannel" + +int test_complete = 0; typedef struct { PeerConnection *offer_peer_connection, *answer_peer_connection; + int onmessage_offer_called, onmessage_answer_called, test_complete; } TestUserData; static void onconnectionstatechange_offerer_peer_connection(PeerConnectionState state, void* user_data) { @@ -28,14 +35,26 @@ static void onicecandidate_answerer_peer_connection(char* description, void* use peer_connection_set_remote_description(test_user_data->offer_peer_connection, description); } +static void ondatachannel_onmessage_offerer_peer_connection(char* msg, size_t len, void* userdata, uint16_t sid) { + TestUserData* test_user_data = (TestUserData*)userdata; + + if (strcmp(msg, ANSWER_DATACHANNEL_MESSAGE) == 0) { + test_user_data->onmessage_offer_called = 1; + } +} + +static void ondatachannel_onmessage_answerer_peer_connection(char* msg, size_t len, void* userdata, uint16_t sid) { + TestUserData* test_user_data = (TestUserData*)userdata; + + if (strcmp(msg, OFFER_DATACHANNEL_MESSAGE) == 0) { + test_user_data->onmessage_answer_called = 1; + } +} + static void* peer_connection_task(void* user_data) { PeerConnection* peer_connection = (PeerConnection*)user_data; - while (1) { - if (peer_connection_get_state(peer_connection) == PEER_CONNECTION_COMPLETED) { - break; - } - + while (!test_complete) { peer_connection_loop(peer_connection); usleep(1000); } @@ -73,23 +92,41 @@ int main(int argc, char* argv[]) { peer_connection_onicecandidate(test_user_data.offer_peer_connection, onicecandidate_offerer_peer_connection); peer_connection_onicecandidate(test_user_data.answer_peer_connection, onicecandidate_answerer_peer_connection); + peer_connection_ondatachannel(test_user_data.offer_peer_connection, ondatachannel_onmessage_offerer_peer_connection, NULL, NULL); + peer_connection_ondatachannel(test_user_data.answer_peer_connection, ondatachannel_onmessage_answerer_peer_connection, NULL, NULL); + peer_connection_create_offer(test_user_data.offer_peer_connection); pthread_create(&offer_thread, NULL, peer_connection_task, test_user_data.offer_peer_connection); pthread_create(&answer_thread, NULL, peer_connection_task, test_user_data.answer_peer_connection); - int attempts = 0; - while (1) { - if (peer_connection_get_state(test_user_data.offer_peer_connection) == PEER_CONNECTION_COMPLETED && peer_connection_get_state(test_user_data.answer_peer_connection) == PEER_CONNECTION_COMPLETED) { - break; - } else if (attempts == MAX_CONNECTION_ATTEMPTS) { + int attempts = 0, datachannel_created = 0; + while (attempts < MAX_CONNECTION_ATTEMPTS) { + if (!datachannel_created && peer_connection_get_state(test_user_data.offer_peer_connection) == PEER_CONNECTION_COMPLETED) { + if (peer_connection_create_datachannel(test_user_data.offer_peer_connection, DATA_CHANNEL_RELIABLE, 0, 0, DATACHANNEL_NAME, "bar") == 18) { + datachannel_created = 1; + } + } + + if (peer_connection_get_state(test_user_data.offer_peer_connection) == PEER_CONNECTION_COMPLETED && + peer_connection_get_state(test_user_data.answer_peer_connection) == PEER_CONNECTION_COMPLETED && + test_user_data.onmessage_offer_called == 1 && + test_user_data.onmessage_answer_called == 1) { break; } + peer_connection_datachannel_send(test_user_data.offer_peer_connection, OFFER_DATACHANNEL_MESSAGE, sizeof(OFFER_DATACHANNEL_MESSAGE)); + peer_connection_datachannel_send(test_user_data.answer_peer_connection, ANSWER_DATACHANNEL_MESSAGE, sizeof(ANSWER_DATACHANNEL_MESSAGE)); + attempts++; usleep(250000); } + if (strcmp(DATACHANNEL_NAME, peer_connection_lookup_sid_label(test_user_data.answer_peer_connection, 0)) != 0) { + return 1; + } + + test_complete = 1; peer_connection_destroy(test_user_data.offer_peer_connection); peer_connection_destroy(test_user_data.answer_peer_connection);