From 78a30e212efd72a7386d73ec0bffa0b43b29dc8b Mon Sep 17 00:00:00 2001 From: Thulinma Date: Mon, 8 Apr 2024 13:11:59 +0200 Subject: [PATCH] Added RTMPS listening port support --- lib/socket.cpp | 45 ++++++++++++++++++ lib/socket.h | 3 ++ src/output/output_rtmp.cpp | 96 ++++++++++++++++++++++++++++++++++++++ src/output/output_rtmp.h | 12 +++++ 4 files changed, 156 insertions(+) diff --git a/lib/socket.cpp b/lib/socket.cpp index ad152ec1..f1df95d7 100644 --- a/lib/socket.cpp +++ b/lib/socket.cpp @@ -807,6 +807,51 @@ static void my_debug(void *ctx, int level, const char *file, int line, const cha fprintf((FILE *)ctx, "%s:%04d: %s", file, line, str); fflush((FILE *)ctx); } + +/// Takes a just-accepted socket and SSL-ifies it. +bool Socket::Connection::sslAccept(mbedtls_ssl_config * sslConf, mbedtls_ctr_drbg_context * dbgCtx){ + int ret; + server_fd = new mbedtls_net_context; + mbedtls_net_init(server_fd); + server_fd->fd = getSocket(); + + ssl = new mbedtls_ssl_context; + mbedtls_ssl_init(ssl); + if ((ret = mbedtls_ctr_drbg_reseed(dbgCtx, (const unsigned char *)"child", 5)) != 0){ + FAIL_MSG("Could not reseed"); + close(); + return false; + } + + // Set up the SSL connection + if ((ret = mbedtls_ssl_setup(ssl, sslConf)) != 0){ + FAIL_MSG("Could not set up SSL connection"); + close(); + return false; + } + + // Inform mbedtls how we'd like to use the connection (uses default bio handlers) + // We tell it to use non-blocking IO here + mbedtls_net_set_nonblock(server_fd); + Blocking = false; + mbedtls_ssl_set_bio(ssl, server_fd, mbedtls_net_send, mbedtls_net_recv, NULL); + // do the SSL handshake + while ((ret = mbedtls_ssl_handshake(ssl)) != 0){ + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){ + char error_buf[200]; + mbedtls_strerror(ret, error_buf, 200); + WARN_MSG("Could not handshake, SSL error: %s (%d)", error_buf, ret); + close(); + return false; + }else{ + Util::sleep(20); + } + } + sslConnected = true; + HIGH_MSG("Started SSL connection handler"); + return true; +} + #endif /// Create a new TCP Socket. This socket will (try to) connect to the given host/port right away. diff --git a/lib/socket.h b/lib/socket.h index bd9758e7..97785765 100644 --- a/lib/socket.h +++ b/lib/socket.h @@ -144,6 +144,9 @@ namespace Socket{ void open(std::string hostname, int port, bool nonblock, bool with_ssl = false); // Open TCP connection. void open(std::string adres, bool nonblock = false); // Open Unix connection. void open(int write, int read); // Open from two existing file descriptors. +#ifdef SSL + bool sslAccept(mbedtls_ssl_config * sslConf, mbedtls_ctr_drbg_context * dbgCtx); +#endif void close(); ///< Close connection. void drop(); ///< Close connection without shutdown. void setBlocking(bool blocking); ///< Set this socket to be blocking (true) or nonblocking (false). diff --git a/src/output/output_rtmp.cpp b/src/output/output_rtmp.cpp index 4967ae25..1f6301f5 100644 --- a/src/output/output_rtmp.cpp +++ b/src/output/output_rtmp.cpp @@ -21,7 +21,19 @@ const char * trackType(char ID){ namespace Mist{ +#ifdef SSL + bool sslEnabled = false; + mbedtls_entropy_context OutRTMP::entropy; + mbedtls_ctr_drbg_context OutRTMP::ctr_drbg; + mbedtls_ssl_config OutRTMP::sslConf; + mbedtls_x509_crt OutRTMP::srvcert; + mbedtls_pk_context OutRTMP::pkey; +#endif + OutRTMP::OutRTMP(Socket::Connection &conn) : Output(conn){ +#ifdef SSL + if (sslEnabled){myConn.sslAccept(&sslConf, &ctr_drbg);} +#endif lastSilence = 0; hasSilence = false; lastAudioInserted = 0; @@ -90,6 +102,74 @@ namespace Mist{ } } +#ifdef SSL + /// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket + void OutRTMP::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){ + // No cert or key? Non-SSL mode. + if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){ + INFO_MSG("No cert or key set, regular RTMP mode"); + Output::listener(conf, callback); + return; + } + + INFO_MSG("Cert and key set, RTMPS mode"); + sslEnabled = true; + + // Declare and set up all required mbedtls structures + int ret; + mbedtls_ssl_config_init(&sslConf); + mbedtls_entropy_init(&entropy); + mbedtls_pk_init(&pkey); + mbedtls_x509_crt_init(&srvcert); + mbedtls_ctr_drbg_init(&ctr_drbg); + + // seed the rng + if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, + (const unsigned char *)APPNAME, strlen(APPNAME))) != 0){ + FAIL_MSG("Could not seed the random number generator!"); + } + + // Read certificate chain(s) from cmdline option(s) + JSON::Value certs = config->getOption("cert", true); + jsonForEach(certs, it){ + if (it->asStringRef().size()){// Ignore empty entries (default is empty) + ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str()); + if (ret != 0){ + WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str()); + } + } + } + + // Read key from cmdline option + ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0); + if (ret != 0){ + FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str()); + return; + } + + if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0){ + FAIL_MSG("SSL config defaults failed"); + return; + } + mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg); + mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL); + if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){ + FAIL_MSG("SSL config own certificate failed"); + return; + } + + Output::listener(conf, callback); + + // Free all the mbedtls structures + mbedtls_x509_crt_free(&srvcert); + mbedtls_pk_free(&pkey); + mbedtls_ssl_config_free(&sslConf); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + } +#endif + void OutRTMP::startPushOut(const char *args){ myConn.close(); @@ -277,6 +357,22 @@ namespace Mist{ capa["optional"]["maxkbps"]["short"] = "K"; capa["optional"]["maxkbps"]["default"] = 0; capa["optional"]["maxkbps"]["type"] = "uint"; + +#ifdef SSL + capa["optional"]["cert"]["name"] = "Certificate"; + capa["optional"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain"; + capa["optional"]["cert"]["option"] = "--cert"; + capa["optional"]["cert"]["short"] = "C"; + capa["optional"]["cert"]["default"] = ""; + capa["optional"]["cert"]["type"] = "str"; + capa["optional"]["key"]["name"] = "Key"; + capa["optional"]["key"]["help"] = "Private key for SSL"; + capa["optional"]["key"]["option"] = "--key"; + capa["optional"]["key"]["short"] = "k"; + capa["optional"]["key"]["default"] = ""; + capa["optional"]["key"]["type"] = "str"; +#endif + cfg->addConnectorOptions(1935, capa); config = cfg; config->addStandardPushCapabilities(capa); diff --git a/src/output/output_rtmp.h b/src/output/output_rtmp.h index bcef783d..a5beae34 100644 --- a/src/output/output_rtmp.h +++ b/src/output/output_rtmp.h @@ -19,6 +19,9 @@ namespace Mist{ static bool listenMode(); void requestHandler(); bool onFinish(); +#ifdef SSL + static void listener(Util::Config &conf, int (*callback)(Socket::Connection &S)); +#endif protected: std::string streamOut; ///< When pushing out, the output stream name @@ -52,6 +55,15 @@ namespace Mist{ void sendLoopedAudio(uint64_t untilTimestamp); // Gets the next ADTS frame in AAC file. Loops if EOF reached void calcNextFrameInfo(); + +#ifdef SSL + // TLS-related + static mbedtls_entropy_context entropy; + static mbedtls_ctr_drbg_context ctr_drbg; + static mbedtls_ssl_config sslConf; + static mbedtls_x509_crt srvcert; + static mbedtls_pk_context pkey; +#endif }; }// namespace Mist