/** * WebRTC implementation for tiny devices * * Copyright (c) 2022 Chris Hiszpanski. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY * OF SUCH DAMAGE. */ #include // freeifaddrs, getifaddrs #include #include // snprintf #include // memset #include // close #include #include #include #include #include "tinyrtc.h" /// OPAQUE TYPE DEFINITIONS //////////////////////////////////////////////// struct rtc_peer_connection { bool active; char offer[TINYRTC_MAX_SDP_SIZE]; char answer[TINYRTC_MAX_SDP_SIZE]; /* local description */ const char* ldesc; /* remote description */ const char* rdesc; char l_ice_pwd[TINYRTC_MAX_ICE_PWD_SIZE]; char l_ice_ufrag[TINYRTC_MAX_ICE_UFRAG_SIZE]; /* onIceCandidate callback */ rtc_on_ice_candidate* on_ice_candidate; void* on_ice_candidate_arg; /* tcp host candidate socket */ int host_tcp_fd; }; /// STATIC ALLOCATIONS ///////////////////////////////////////////////////// static struct rtc_peer_connection peer_connection_pool[TINYRTC_MAX_PEER_CONNECTIONS]; /// RTC PEER CONNECTION API //////////////////////////////////////////////// void rtc_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 rtc_peer_connection* 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 rtc_ice_candidate c = { .sdp_mid = "0", .sdp_mline_index = 0 }; snprintf(c.candidate, TINYRTC_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 rtc_peer_connection* rtc_peer_connection_create(struct rtc_configuration cfg) { struct rtc_peer_connection* pc = NULL; for (int i = 0; i < TINYRTC_MAX_PEER_CONNECTIONS; i++) { if (!peer_connection_pool[i].active) { memset(&peer_connection_pool[i], 0, sizeof(struct rtc_peer_connection)); pc = &peer_connection_pool[i]; break; } } return pc; } void rtc_peer_connection_destroy(struct rtc_peer_connection* pc) { if (!pc) return; close(pc->host_tcp_fd); // XXX mark as available } int rtc_set_on_ice_candidate(struct rtc_peer_connection* pc, rtc_on_ice_candidate* cb, void* arg) { if (!pc || !cb) return -1; pc->on_ice_candidate = cb; pc->on_ice_candidate_arg = arg; return 0; } int rtc_add_ice_candidate(struct rtc_peer_connection* pc, const struct rtc_ice_candidate c) { return -1; }; const char* rtc_create_answer(struct rtc_peer_connection* pc) { unsigned char fp[32]; snprintf(pc->l_ice_ufrag, TINYRTC_MAX_ICE_UFRAG_SIZE, "xxxx"); // XXX snprintf(pc->l_ice_pwd, TINYRTC_MAX_ICE_PWD_SIZE, "xxxxxxxxxxxxxxxxxxxxxx"); // XXX snprintf(pc->answer, TINYRTC_MAX_SDP_SIZE, "v=0\\r\\n" "o=- 2210401696197537454 2 IN IP4 127.0.0.1\\r\\n" // XXX "s=-\\r\\n" "u=https://liburtc.org/\\r\\n" "t=0 0\\r\\n" "a=group:BUNDLE 0\\r\\n" "a=msid-semantic: WMS\\r\\n" "m=video 9 UDP/TLS/RTP/SAVPF 98\\r\\n" // XXX "c=IN IP4 0.0.0.0\\r\\n" "a=rtcp:9 IN IP4 0.0.0.0\\r\\n" "a=ice-ufrag:%s\\r\\n" "a=ice-pwd:%s\\r\\n" "a=ice-options:trickle\\r\\n" "a=fingerprint:sha-256 " "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:" "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:" "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:" "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX\\r\\n" "a=setup:passive\\r\\n" "a=mid:0\\r\\n" "a=sendonly\\r\\n" "a=rtcp-mux\\r\\n" "a=rtcp-rsize\\r\\n" "a=rtpmap:98 H264/90000\\r\\n" "a=rtcp-fb:98 nack\\r\\n" "a=rtcp-fb:98 nack pli\\r\\n" "a=fmtp:98 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\\r\\n", pc->l_ice_ufrag, pc->l_ice_pwd, fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7], fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15], fp[16], fp[17], fp[18], fp[19], fp[20], fp[21], fp[22], fp[23], fp[24], fp[25], fp[26], fp[27], fp[28], fp[29], fp[30], fp[31] ); return pc->answer; }; int rtc_set_local_description(struct rtc_peer_connection* pc, const char* sdp) { pc->ldesc = sdp; get_host_candidates(pc); return -1; }; int rtc_set_remote_description(struct rtc_peer_connection* pc, const char* sdp) { pc->rdesc = sdp; return -1; }; /* vim: set et ts=4 sw=4 : */