From 8a4e7fabafd8d42b0b2cbdbd766bea49a62bfae1 Mon Sep 17 00:00:00 2001 From: Chris Hiszpanski Date: Sun, 22 May 2022 01:36:30 -0700 Subject: Added tcp host candidate --- example/index.js | 6 ++- example/main.c | 35 +++++++++++--- tinyrtc.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- tinyrtc.h | 46 ++++++++++++++++-- 4 files changed, 215 insertions(+), 15 deletions(-) diff --git a/example/index.js b/example/index.js index a161a10..6eae9b3 100644 --- a/example/index.js +++ b/example/index.js @@ -1,4 +1,5 @@ window.onload = function demo() { + // create peer connection let pc = new RTCPeerConnection({ iceCandidatePoolSize: 4 @@ -27,10 +28,13 @@ window.onload = function demo() { if ('candidate' in d) { if (d.candidate) { console.log("remote candidate:\n%c%s", "color: blue;", d.candidate); + pc.addIceCandidate(d).catch((e) => { + console.log("addIceCandidate:\n%c%s", "color: red;", e); + }); } } else if ('sdp' in d) { pc.setRemoteDescription(d).catch((e) => { - console.log("local error:\n%c%s", "color: red;", e); + console.log("setRemoteDescription:\n%c%s", "color: red;", e); }); console.log("remote answer:\n%c%s", "color: blue;", d.sdp); } diff --git a/example/main.c b/example/main.c index 33ef4bb..e850a40 100644 --- a/example/main.c +++ b/example/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "jsmn.h" @@ -60,7 +61,7 @@ struct trtc_ice_candidate_t parse_ice_candidate(const char *s) } // (callback) Called for each discovered local ICE candidate -int on_ice_candidate(const struct trtc_ice_candidate_t c, void *arg) { +void on_ice_candidate(const struct trtc_ice_candidate_t c, void *arg) { // send ice candidate over signaling channel printf("{" "\"candidate\":\"%s\"," @@ -68,15 +69,18 @@ int on_ice_candidate(const struct trtc_ice_candidate_t c, void *arg) { "\"sdpMid\":\"%s\"," "\"usernameFragment\":null" "}\n", c.candidate, c.sdp_mline_index, c.sdp_mid); - - return 0; } int main(int argc, char **argv) { + openlog(NULL, LOG_PERROR, LOG_USER); + // disable stdout buffering to ensure signaling messages send immediately setvbuf(stdout, NULL, _IONBF, 0); + // initialize tinyrtc library + trtc_init(); + // create peer connection struct trtc_config_t cfg = { .ice_servers = { @@ -88,6 +92,12 @@ int main(int argc, char **argv) } }; struct trtc_peerconn_t* pc = trtc_peer_connection(cfg); + if (!pc) { + syslog(LOG_ERR, "trtc_peer_connection() failed"); + return -1; + } + + trtc_set_on_ice_candidate(pc, on_ice_candidate, NULL); while (true) { int n; @@ -96,21 +106,32 @@ int main(int argc, char **argv) if (n = getline(&msg, &msglen, stdin), -1 == n) { + syslog(LOG_ERR, "%m"); return -1; } + // case: sdp offer if (strstr(msg, "\"sdp\"")) { - // got offer. send answer. - printf("{\"sdp\":\"%s\",\"type\":\"answer\"}\n", trtc_create_answer(pc)); + // XXX trtc_set_remote_description(pc, offer); + const char *answer = trtc_create_answer(pc); + + /* send SDP answer via stdout signaling channel */ + printf("{\"sdp\":\"%s\",\"type\":\"answer\"}\n", answer); + + trtc_set_local_description(pc, answer); + + // case: ice candidate } else if (strstr(msg, "\"candidate\"")) { struct trtc_ice_candidate_t c = parse_ice_candidate(msg); - on_ice_candidate(c, NULL); trtc_add_ice_candidate(pc, c); + + // case: error } else { printf("unrecognized signaling message\n"); } - } + + syslog(LOG_ERR, "terminating"); return 0; } diff --git a/tinyrtc.c b/tinyrtc.c index 2092224..2b74890 100644 --- a/tinyrtc.c +++ b/tinyrtc.c @@ -26,9 +26,16 @@ * OF SUCH DAMAGE. */ +#include // freeifaddrs, getifaddrs #include #include // snprintf #include // memset +#include // close + +#include +#include +#include +#include #include "tinyrtc.h" @@ -41,8 +48,21 @@ struct trtc_peerconn_t { char offer[TRTC_MAX_SDP_SIZE]; char answer[TRTC_MAX_SDP_SIZE]; + /* local description */ + const char* ldesc; + + /* remote description */ + const char* rdesc; + char l_ice_pwd[TRTC_MAX_ICE_PWD_SIZE]; char l_ice_ufrag[TRTC_MAX_ICE_UFRAG_SIZE]; + + /* onIceCandidate callback */ + trtc_on_ice_candidate_t* on_ice_candidate; + void* on_ice_candidate_arg; + + /* tcp host candidate socket */ + int host_tcp_fd; }; @@ -53,20 +73,131 @@ static struct trtc_peerconn_t peer_connection_pool[TRTC_MAX_PEER_CONNECTIONS]; /// RTC PEER CONNECTION API //////////////////////////////////////////////// -void rtc_init() { +void trtc_init() { memset(peer_connection_pool, 0, sizeof(peer_connection_pool)); } +/** + * Get host candidates + * + * \return 0 on success, -1 on error. + */ +static int get_host_candidates(struct trtc_peerconn_t* pc) { + struct ifaddrs *ifaddr, *ifa; + socklen_t laddrlen; + + /* sanity check */ + if (!pc) return -1; + + /* create tcp socket */ + pc->host_tcp_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (-1 == pc->host_tcp_fd) { + return -1; + } + + /* bind to arbitrary port */ + struct sockaddr_in laddr = { + .sin_family = AF_INET, + .sin_addr.s_addr = INADDR_ANY, + .sin_port = 0, + }; + if (-1 == bind(pc->host_tcp_fd, (struct sockaddr*)&laddr, sizeof(laddr))) { + return -1; + } + laddrlen = sizeof(struct sockaddr_in); + if (-1 == getsockname(pc->host_tcp_fd, (struct sockaddr *)&laddr, &laddrlen)) { + return -1; + } + + /* listen on socket */ + if (-1 == listen(pc->host_tcp_fd, 0)) { + return -1; + } + + /* get network interface addresses */ + if (-1 == getifaddrs(&ifaddr)) { + return -1; + } + + /* iterate over addresses */ + for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) { + if (!ifa->ifa_addr) continue; + + /* skip loopback interface addresses */ + if (IFF_LOOPBACK & ifa->ifa_flags) continue; + + if (AF_INET == ifa->ifa_addr->sa_family) { + struct sockaddr_in *sa = (struct sockaddr_in*)ifa->ifa_addr; + + /* host tcp candidate */ + struct trtc_ice_candidate_t c = { + .sdp_mid = "0", + .sdp_mline_index = 0 + }; + snprintf(c.candidate, TRTC_MAX_ICE_CANDIDATE_SIZE, + "candidate:2894319779 1 tcp 212260223 %s %d typ host tcptype passive", + inet_ntoa(sa->sin_addr), + laddr.sin_port); // XXXX + + /* callback */ + if (pc->on_ice_candidate) { + pc->on_ice_candidate(c, pc->on_ice_candidate_arg); + } + } + } + + freeifaddrs(ifaddr); + + return 0; +} + +/** + * Peer connection thread + * + * Each peer connection uses one background thread for stepping the ICE agent, + * handling timeouts, and sending/receiving packets. + * + * \param arg[in] User-specified argument. + * + * \return NULL. + */ +static void* peer_connection_thread(void *arg) { + return NULL; +} + struct trtc_peerconn_t* trtc_peer_connection(struct trtc_config_t cfg) { + struct trtc_peerconn_t* pc = NULL; + for (int i = 0; i < TRTC_MAX_PEER_CONNECTIONS; i++) { if (!peer_connection_pool[i].active) { memset(&peer_connection_pool[i], 0, sizeof(struct trtc_peerconn_t)); - peer_connection_pool[i].active = true; - return &peer_connection_pool[i]; + pc = &peer_connection_pool[i]; + break; } } - return NULL; + return pc; +} + +void trtc_peer_connection_destroy(struct trtc_peerconn_t* pc) { + if (!pc) return; + + close(pc->host_tcp_fd); + + // XXX mark as available +} + +int trtc_set_on_ice_candidate( + struct trtc_peerconn_t* pc, + trtc_on_ice_candidate_t* cb, + void* arg +) { + if (!pc || !cb) return -1; + + pc->on_ice_candidate = cb; + pc->on_ice_candidate_arg = arg; + + return 0; } int trtc_add_ice_candidate(struct trtc_peerconn_t *pc, const struct trtc_ice_candidate_t c) { @@ -83,6 +214,7 @@ const char * trtc_create_answer(struct trtc_peerconn_t *pc) { "v=0\\r\\n" "o=- 2210401696197537454 2 IN IP4 127.0.0.1\\r\\n" // XXX "s=-\\r\\n" + "u=https://tinyrtc.org/\\r\\n" "t=0 0\\r\\n" "a=group:BUNDLE 0\\r\\n" "a=msid-semantic: WMS\\r\\n" @@ -118,9 +250,12 @@ const char * trtc_create_answer(struct trtc_peerconn_t *pc) { }; int trtc_set_local_description(struct trtc_peerconn_t *pc, const char *sdp) { + pc->ldesc = sdp; + get_host_candidates(pc); return -1; }; int trtc_set_remote_description(struct trtc_peerconn_t *pc, const char *sdp) { + pc->rdesc = sdp; return -1; }; diff --git a/tinyrtc.h b/tinyrtc.h index 205ae1b..ff29e15 100644 --- a/tinyrtc.h +++ b/tinyrtc.h @@ -70,21 +70,61 @@ struct trtc_ice_candidate_t { /// CALLBACKS ////////////////////////////////////////////////////////////// -typedef int (trtc_on_ice_candidate_t)(const struct trtc_ice_candidate_t c, void *arg); +typedef void (trtc_on_ice_candidate_t)(const struct trtc_ice_candidate_t c, void *arg); /// RTC PEER CONNECTION API //////////////////////////////////////////////// +/** + * Initialize library. Must call once before calling other functions. + */ +void trtc_init(); + +/** + * Create new peer connection + * + * \param cfg[in] Configuration. + * + * \return Peer connection. NULL if all peer connections are already in use. + */ struct trtc_peerconn_t* trtc_peer_connection(struct trtc_config_t cfg); +void trtc_peer_connection_destroy(struct trtc_peerconn_t* pc); -int trtc_add_ice_candidate(struct trtc_peerconn_t *pc, const struct trtc_ice_candidate_t c); +/** + * Adds remote ICE candidate to local ICE agent + * + * \param pc[in] Peer connection. + * \param c[in] ICE candidate from remote peer. + * + * \return 0 on success. -1 on error. + */ +int trtc_add_ice_candidate( + struct trtc_peerconn_t *pc, const struct trtc_ice_candidate_t c); int trtc_add_track(struct trtc_peerconn_t *pc); +/** + * Create SDP answer in response to a received SDP offer. + * + * \param pc[in] Peer connection. + * + * \return SDP answer. NULL on error. + */ const char * trtc_create_answer(struct trtc_peerconn_t *pc); -int trtc_set_local_description(struct trtc_peerconn_t *pc, const char *sdp); +/** + * Sets callback to call for each discovered local ICE candidate + * + * \param pc[in] Peer connection. + * \param cb[in] Callback function. + * \param arg[in] User-specified argument to pass to callback function. + * + * \return 0 on success. -1 on error. + */ +int trtc_set_on_ice_candidate( + struct trtc_peerconn_t* pc, trtc_on_ice_candidate_t* cb, void* arg); +int trtc_set_local_description(struct trtc_peerconn_t *pc, const char *sdp); int trtc_set_remote_description(struct trtc_peerconn_t *pc, const char *sdp); #endif -- cgit v1.2.3