void Notifier::EmitWithBinary( uint32_t targetId, const std::string& event, const uint8_t* binaryData, size_t binaryLen) { MS_TRACE(); static const Json::StaticString JsonStringTargetId{ "targetId" }; static const Json::StaticString JsonStringEvent{ "event" }; static const Json::StaticString JsonStringBinary{ "binary" }; Json::Value json(Json::objectValue); json[JsonStringTargetId] = Json::UInt{ targetId }; json[JsonStringEvent] = event; json[JsonStringBinary] = true; this->channel->Send(json); this->channel->SendBinary(binaryData, binaryLen); }
void init() { MS_TRACE(); ignoreSignals(); DepLibUV::PrintVersion(); // Initialize static stuff. DepOpenSSL::ClassInit(); DepLibSRTP::ClassInit(); DepUsrSCTP::ClassInit(); RTC::UdpSocket::ClassInit(); RTC::TcpServer::ClassInit(); RTC::DtlsTransport::ClassInit(); RTC::SrtpSession::ClassInit(); Utils::Crypto::ClassInit(); }
void Loop::Close() { MS_TRACE(); int err; sigset_t signal_mask; if (this->closed) { MS_ERROR("already closed"); return; } this->closed = true; // First block all the signals to not be interrupted while closing. sigfillset(&signal_mask); err = pthread_sigmask(SIG_BLOCK, &signal_mask, nullptr); if (err) MS_ERROR("pthread_sigmask() failed: %s", std::strerror(errno)); // Close the SignalsHandler. if (this->signalsHandler) this->signalsHandler->Close(); // Close all the Rooms. // NOTE: Upon Room closure the onRoomClosed() method is called which // removes it from the map, so this is the safe way to iterate the map // and remove elements. for (auto it = this->rooms.begin(); it != this->rooms.end();) { RTC::Room* room = it->second; it = this->rooms.erase(it); room->Close(); } // Close the Notifier. this->notifier->Close(); // Close the Channel socket. if (this->channel) this->channel->Close(); }
Request::Request(Channel::UnixStreamSocket* channel, Json::Value& json) : channel(channel) { MS_TRACE(); static const Json::StaticString k_id("id"); static const Json::StaticString k_method("method"); static const Json::StaticString k_internal("internal"); static const Json::StaticString k_data("data"); if (json[k_id].isUInt()) this->id = json[k_id].asUInt(); else MS_THROW_ERROR("json has no numeric .id field"); if (json[k_method].isString()) this->method = json[k_method].asString(); else MS_THROW_ERROR("json has no string .method field"); auto it = Request::string2MethodId.find(this->method); if (it != Request::string2MethodId.end()) { this->methodId = it->second; } else { Reject("method not allowed"); MS_THROW_ERROR("unknown .method '%s'", this->method.c_str()); } if (json[k_internal].isObject()) this->internal = json[k_internal]; else this->internal = Json::Value(Json::objectValue); if (json[k_data].isObject()) this->data = json[k_data]; else this->data = Json::Value(Json::objectValue); }
inline bool DtlsTransport::SetTimeout() { MS_TRACE(); long int ret; struct timeval dtls_timeout; uint64_t timeout_ms; // NOTE: If ret == 0 then ignore the value in dtls_timeout. // NOTE: No DTLSv_1_2_get_timeout() or DTLS_get_timeout() in OpenSSL 1.1.0-dev. ret = DTLSv1_get_timeout(this->ssl, (void*)&dtls_timeout); if (ret == 0) return true; timeout_ms = (dtls_timeout.tv_sec * (uint64_t)1000) + (dtls_timeout.tv_usec / 1000); if (timeout_ms == 0) { return true; } else if (timeout_ms < 30000) { MS_DEBUG("DTLS timer set in %" PRIu64 "ms", timeout_ms); this->timer->Start(timeout_ms); return true; } // NOTE: Don't start the timer again if the timeout is greater than 30 seconds. else { MS_WARN("DTLS timeout too high (%" PRIu64 "ms), resetting DLTS", timeout_ms); Reset(); // Set state and notify the listener. this->state = DtlsState::FAILED; this->listener->onDtlsFailed(this); return false; } }
void Loop::onSignal(SignalsHandler* signalsHandler, int signum) { MS_TRACE(); switch (signum) { case SIGINT: MS_DEBUG("signal INT received, exiting"); Close(); break; case SIGTERM: MS_DEBUG("signal TERM received, exiting"); Close(); break; default: MS_WARN("received a signal (with signum %d) for which there is no handling code", signum); } }
void Settings::SetDtlsCertificateAndPrivateKeyFiles(std::string& dtlsCertificateFile, std::string& dtlsPrivateKeyFile) { MS_TRACE(); try { Utils::File::CheckFile(dtlsCertificateFile.c_str()); } catch (const MediaSoupError &error) { MS_THROW_ERROR("RTC.dtlsCertificateFile: %s", error.what()); } try { Utils::File::CheckFile(dtlsPrivateKeyFile.c_str()); } catch (const MediaSoupError &error) { MS_THROW_ERROR("RTC.dtlsPrivateKeyFile: %s", error.what()); } Settings::configuration.RTC.dtlsCertificateFile = dtlsCertificateFile; Settings::configuration.RTC.dtlsPrivateKeyFile = dtlsPrivateKeyFile; }
bool TcpConnection::SetPeerAddress() { MS_TRACE(); int err; int len = sizeof(this->peerAddr); err = uv_tcp_getpeername(this->uvHandle, (struct sockaddr*)&this->peerAddr, &len); if (err) { MS_ERROR("uv_tcp_getpeername() failed: %s", uv_strerror(err)); return false; } int family; Utils::IP::GetAddressInfo((const struct sockaddr*)&this->peerAddr, &family, this->peerIP, &this->peerPort); return true; }
void Utils::File::CheckFile(const char* file) { MS_TRACE(); struct stat file_stat; int err; // Ensure the given file exists. err = stat(file, &file_stat); if (err) MS_THROW_ERROR("cannot read file '%s': %s", file, std::strerror(errno)); // Ensure it is a regular file. if (! S_ISREG(file_stat.st_mode)) MS_THROW_ERROR("'%s' is not a regular file", file); // Ensure it is readable. err = access(file, R_OK); if (err) MS_THROW_ERROR("cannot read file '%s': %s", file, std::strerror(errno)); }
inline void TcpConnection::onUvRead(ssize_t nread, const uv_buf_t* buf) { MS_TRACE(); if (this->isClosing) return; if (nread == 0) return; // Data received. if (nread > 0) { // Update the buffer data length. this->bufferDataLen += (size_t)nread; // Notify the subclass. userOnTcpConnectionRead(); } // Client disconneted. else if (nread == UV_EOF || nread == UV_ECONNRESET) { MS_DEBUG("connection closed by peer, closing server side"); this->isClosedByPeer = true; // Close server side of the connection. Close(); } // Some error. else { MS_DEBUG("read error, closing the connection: %s", uv_strerror(nread)); this->hasError = true; // Close server side of the connection. Close(); } }
/** * Reject the Request. * @param reason Description string. */ void Request::Reject(const char* reason) { MS_TRACE(); static const Json::StaticString k_id("id"); static const Json::StaticString k_rejected("rejected"); static const Json::StaticString k_reason("reason"); MS_ASSERT(!this->replied, "Request already replied"); this->replied = true; Json::Value json(Json::objectValue); json[k_id] = (Json::UInt)this->id; json[k_rejected] = true; if (reason) json[k_reason] = reason; this->channel->Send(json); }
size_t FeedbackPsVbcmItem::Serialize(uint8_t* buffer) { MS_TRACE(); // Add minimum header. std::memcpy(buffer, this->header, 8); // Copy the content. std::memcpy(buffer + 8, this->header->value, GetLength()); size_t offset = 8 + GetLength(); // 32 bits padding. size_t padding = (-offset) & 3; for (size_t i{ 0 }; i < padding; ++i) { buffer[offset + i] = 0; } return offset + padding; }
inline void TcpConnection::onUvWriteError(int error) { MS_TRACE(); if (this->isClosing) return; if (error == UV_EPIPE || error == UV_ENOTCONN) { MS_DEBUG("write error, closing the connection: %s", uv_strerror(error)); } else { MS_DEBUG("write error, closing the connection: %s", uv_strerror(error)); this->hasError = true; } Close(); }
Loop::Loop(Channel::UnixStreamSocket* channel) : channel(channel) { MS_TRACE(); // Set us as Channel's listener. this->channel->SetListener(this); // Create the Notifier instance. this->notifier = new Channel::Notifier(this->channel); // Set the signals handler. this->signalsHandler = new SignalsHandler(this); // Add signals to handle. this->signalsHandler->AddSignal(SIGINT, "INT"); this->signalsHandler->AddSignal(SIGTERM, "TERM"); MS_DEBUG("starting libuv loop"); DepLibUV::RunLoop(); MS_DEBUG("libuv loop ended"); }
void RtpSender::Send(RTC::RtpParameters* rtpParameters) { MS_TRACE(); bool updated = false; // Free the previous rtpParameters. if (this->rtpParameters) { delete this->rtpParameters; updated = true; } this->rtpParameters = rtpParameters; if (updated) { Json::Value event_data = this->rtpParameters->toJson(); this->notifier->Emit(this->rtpSenderId, "updateparameters", event_data); } }
void TcpServer::ClassInit() { MS_TRACE(); int err; if (!Settings::configuration.rtcIPv4.empty()) { err = uv_ip4_addr( Settings::configuration.rtcIPv4.c_str(), 0, reinterpret_cast<struct sockaddr_in*>(&RTC::TcpServer::sockaddrStorageIPv4)); if (err != 0) MS_THROW_ERROR("uv_ipv4_addr() failed: %s", uv_strerror(err)); } if (!Settings::configuration.rtcIPv6.empty()) { err = uv_ip6_addr( Settings::configuration.rtcIPv6.c_str(), 0, reinterpret_cast<struct sockaddr_in6*>(&RTC::TcpServer::sockaddrStorageIPv6)); if (err != 0) MS_THROW_ERROR("uv_ipv6_addr() failed: %s", uv_strerror(err)); } TcpServer::minPort = Settings::configuration.rtcMinPort; TcpServer::maxPort = Settings::configuration.rtcMaxPort; uint16_t i = RTC::TcpServer::minPort; do { RTC::TcpServer::availableIPv4Ports[i] = true; RTC::TcpServer::availableIPv6Ports[i] = true; } while (i++ != RTC::TcpServer::maxPort); }
bool Settings::ReloadConfigurationFile() { MS_TRACE(); if (Settings::arguments.configFile.empty()) { MS_ERROR("no configuration file was given in command line options"); return false; } libconfig::Config* config; try { config = ParseConfigFile(); } catch (const MediaSoupError &error) { MS_ERROR("%s", error.what()); return false; } std::string str_value; // Just some configuration settings can be reloaded. try { /* First level settings. */ if (config->lookupValue("logLevel", str_value)) SetLogLevel(str_value); else Settings::configuration.logLevel = LOG_DEBUG; } catch (const MediaSoupError &error) { MS_ERROR("error in configuration file: %s", error.what()); delete config; return false; } delete config; return true; }
void Settings::SetRTCports(MS_PORT minPort, MS_PORT maxPort) { MS_TRACE(); if (minPort < 1024) MS_THROW_ERROR("RTC.minPort must be greater or equal than 1024"); if (maxPort == 0) MS_THROW_ERROR("RTC.maxPort can not be 0"); // Make minPort even. minPort &= ~1; // Make maxPort odd. if (maxPort % 2 == 0) maxPort--; if ((maxPort - minPort) < 99) MS_THROW_ERROR("RTC.maxPort must be at least 99 ports higher than RTC.minPort"); Settings::configuration.RTC.minPort = minPort; Settings::configuration.RTC.maxPort = maxPort; }
void DtlsTransport::ReadCertificateAndPrivateKeyFromFiles() { MS_TRACE(); FILE* file = nullptr; file = fopen(Settings::configuration.dtlsCertificateFile.c_str(), "r"); if (!file) { MS_ERROR("error reading DTLS certificate file: %s", std::strerror(errno)); goto error; } DtlsTransport::certificate = PEM_read_X509(file, nullptr, nullptr, nullptr); if (!DtlsTransport::certificate) { LOG_OPENSSL_ERROR("PEM_read_X509() failed"); goto error; } fclose(file); file = fopen(Settings::configuration.dtlsPrivateKeyFile.c_str(), "r"); if (!file) { MS_ERROR("error reading DTLS private key file: %s", std::strerror(errno)); goto error; } DtlsTransport::privateKey = PEM_read_PrivateKey(file, nullptr, nullptr, nullptr); if (!DtlsTransport::privateKey) { LOG_OPENSSL_ERROR("PEM_read_PrivateKey() failed"); goto error; } fclose(file); return; error: MS_THROW_ERROR("error reading DTLS certificate and private key PEM files"); }
void OpenSSL::ClassDestroy() { MS_TRACE(); MS_DEBUG("unloading openssl"); // FAQ: https://www.openssl.org/support/faq.html#PROG13 // Thread-local cleanup functions. ERR_remove_thread_state(nullptr); // Application-global cleanup functions that are aware of usage (and // therefore thread-safe). ENGINE_cleanup(); // "Brutal" (thread-unsafe) Application-global cleanup functions. ERR_free_strings(); EVP_cleanup(); // Removes all ciphers and digests. CRYPTO_cleanup_all_ex_data(); // https://bugs.launchpad.net/percona-server/+bug/1341067. sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); // Free mutexes. for (int i=0; i<OpenSSL::numMutexes; i++) { int err = pthread_mutex_destroy(&OpenSSL::mutexes[i]); if (err) MS_ERROR("pthread_mutex_destroy() failed with return code %d\n", err); } if (OpenSSL::mutexes) delete[] OpenSSL::mutexes; // Reset callbacks. CRYPTO_THREADID_set_callback(nullptr); CRYPTO_set_locking_callback(nullptr); CRYPTO_set_dynlock_create_callback(nullptr); CRYPTO_set_dynlock_lock_callback(nullptr); CRYPTO_set_dynlock_destroy_callback(nullptr); }
inline void TcpConnection::onUvReadAlloc(size_t suggested_size, uv_buf_t* buf) { MS_TRACE(); // If this is the first call to onUvReadAlloc() then allocate the receiving buffer now. if (!this->buffer) this->buffer = new uint8_t[this->bufferSize]; // Tell UV to write after the last data byte in the buffer. buf->base = (char *)(this->buffer + this->bufferDataLen); // Give UV all the remaining space in the buffer. if (this->bufferSize > this->bufferDataLen) { buf->len = this->bufferSize - this->bufferDataLen; } else { buf->len = 0; MS_WARN("no available space in the buffer"); } }
void TcpConnection::Setup(Listener* listener, struct sockaddr_storage* localAddr, const std::string &localIP, uint16_t localPort) { MS_TRACE(); int err; // Set the UV handle. err = uv_tcp_init(DepLibUV::GetLoop(), this->uvHandle); if (err) { delete this->uvHandle; this->uvHandle = nullptr; MS_THROW_ERROR("uv_tcp_init() failed: %s", uv_strerror(err)); } // Set the listener. this->listener = listener; // Set the local address. this->localAddr = localAddr; this->localIP = localIP; this->localPort = localPort; }
void RtpSender::HandleRequest(Channel::Request* request) { MS_TRACE(); switch (request->methodId) { case Channel::Request::MethodId::rtpSender_dump: { Json::Value json = toJson(); request->Accept(json); break; } default: { MS_ERROR("unknown method"); request->Reject("unknown method"); } } }
void SignalsHandler::AddSignal(int signum, std::string name) { MS_TRACE(); int err; uv_signal_t* uvHandle = new uv_signal_t; uvHandle->data = (void*)this; err = uv_signal_init(DepLibUV::GetLoop(), uvHandle); if (err) { delete uvHandle; MS_THROW_ERROR("uv_signal_init() failed for signal %s: %s", name.c_str(), uv_strerror(err)); } err = uv_signal_start(uvHandle, (uv_signal_cb)on_signal, signum); if (err) MS_THROW_ERROR("uv_signal_start() failed for signal %s: %s", name.c_str(), uv_strerror(err)); // Enter the UV handle into the vector. this->uvHandles.push_back(uvHandle); }
void Settings::PrintHelp(bool error) { MS_TRACE(); std::string help; help.append("\nUsage: "); help.append(Version::command); help.append(" [options]\n"); help.append("Options:\n"); help.append(" -c, --configfile FILE Path to the configuration file\n"); help.append(" -d, --daemonize Run in daemon mode\n"); help.append(" -p, --pidfile FILE Create a PID file (requires daemon mode)\n"); help.append(" -u, --user USER Run with the given system user\n"); help.append(" -g, --group GROUP Run with the given system group\n"); help.append(" -v, --version Show version\n"); help.append(" -h, --help Show this message\n"); if (error) std::fprintf(stderr, "%s", help.c_str()); else std::fprintf(stdout, "%s", help.c_str()); }
void Request::Accept(Json::Value &data) { MS_TRACE(); static Json::Value empty_data(Json::objectValue); static const Json::StaticString k_id("id"); static const Json::StaticString k_accepted("accepted"); static const Json::StaticString k_data("data"); MS_ASSERT(!this->replied, "Request already replied"); this->replied = true; Json::Value json(Json::objectValue); json[k_id] = (Json::UInt)this->id; json[k_accepted] = true; if (data.isObject()) json[k_data] = data; else json[k_data] = empty_data; this->channel->Send(json); }
inline void DtlsTransport::SendPendingOutgoingDtlsData() { MS_TRACE(); if (BIO_eof(this->sslBioToNetwork)) return; long read; char* data = nullptr; read = BIO_get_mem_data(this->sslBioToNetwork, &data); if (read <= 0) return; MS_DEBUG("%ld bytes of DTLS data ready to sent to the peer", read); // Notify the listener. this->listener->onOutgoingDtlsData(this, (uint8_t*)data, (size_t)read); // Clear the BIO buffer. // NOTE: the (void) avoids the -Wunused-value warning. (void)BIO_reset(this->sslBioToNetwork); }
void DtlsTransport::SendApplicationData(const uint8_t* data, size_t len) { MS_TRACE(); // We cannot send data to the peer if its remote fingerprint is not validated. if (this->state != DtlsState::CONNECTED) { MS_ERROR("cannot send application data while DTLS is not fully connected"); return; } if (len == 0) { MS_DEBUG("ignoring 0 length data"); return; } int written; written = SSL_write(this->ssl, (const void*)data, (int)len); if (written < 0) { LOG_OPENSSL_ERROR("SSL_write() failed"); CheckStatus(written); } else if (written != (int)len) { MS_WARN("OpenSSL SSL_write() wrote less (%d bytes) than given data (%zu bytes)", written, len); } // Send data. SendPendingOutgoingDtlsData(); }
uv_tcp_t* TcpServer::GetRandomPort(int addressFamily) { MS_TRACE(); if (addressFamily == AF_INET && !Settings::configuration.hasIPv4) MS_THROW_ERROR("IPv4 family not available for RTC"); else if (addressFamily == AF_INET6 && !Settings::configuration.hasIPv6) MS_THROW_ERROR("IPv6 family not available for RTC"); int err; uv_tcp_t* uvHandle{ nullptr }; struct sockaddr_storage bindAddr { }; const char* listenIp; uint16_t initialPort; uint16_t iteratingPort; uint16_t attempt{ 0 }; uint16_t bindAttempt{ 0 }; int flags{ 0 }; std::unordered_map<uint16_t, bool>* availablePorts; switch (addressFamily) { case AF_INET: availablePorts = &RTC::TcpServer::availableIPv4Ports; bindAddr = RTC::TcpServer::sockaddrStorageIPv4; listenIp = Settings::configuration.rtcIPv4.c_str(); break; case AF_INET6: availablePorts = &RTC::TcpServer::availableIPv6Ports; bindAddr = RTC::TcpServer::sockaddrStorageIPv6; listenIp = Settings::configuration.rtcIPv6.c_str(); // Don't also bind into IPv4 when listening in IPv6. flags |= UV_TCP_IPV6ONLY; break; default: MS_THROW_ERROR("invalid address family given"); break; } // Choose a random first port to start from. initialPort = static_cast<uint16_t>(Utils::Crypto::GetRandomUInt( static_cast<uint32_t>(RTC::TcpServer::minPort), static_cast<uint32_t>(RTC::TcpServer::maxPort))); iteratingPort = initialPort; // Iterate the RTC TCP ports until getting one available. // Fail also after bind() fails N times in theorically available ports. while (true) { ++attempt; // Increase the iterate port) within the range of RTC TCP ports. if (iteratingPort < RTC::TcpServer::maxPort) iteratingPort += 1; else iteratingPort = RTC::TcpServer::minPort; // Check whether the chosen port is available. if (!(*availablePorts)[iteratingPort]) { MS_DEBUG_DEV( "port in use, trying again [port:%" PRIu16 ", attempt:%" PRIu16 "]", iteratingPort, attempt); // If we have tried all the ports in the range raise an error. if (iteratingPort == initialPort) MS_THROW_ERROR("no more available ports for IP '%s'", listenIp); continue; } // Here we already have a theorically available port. // Now let's check whether no other process is listening into it. // Set the chosen port into the sockaddr struct(s). switch (addressFamily) { case AF_INET: (reinterpret_cast<struct sockaddr_in*>(&bindAddr))->sin_port = htons(iteratingPort); break; case AF_INET6: (reinterpret_cast<struct sockaddr_in6*>(&bindAddr))->sin6_port = htons(iteratingPort); break; } // Try to bind on it. ++bindAttempt; uvHandle = new uv_tcp_t(); err = uv_tcp_init(DepLibUV::GetLoop(), uvHandle); if (err != 0) { delete uvHandle; MS_THROW_ERROR("uv_tcp_init() failed: %s", uv_strerror(err)); } err = uv_tcp_bind(uvHandle, reinterpret_cast<const struct sockaddr*>(&bindAddr), flags); if (err != 0) { MS_WARN_DEV( "uv_tcp_bind() failed [port:%" PRIu16 ", attempt:%" PRIu16 "]: %s", attempt, iteratingPort, uv_strerror(err)); uv_close(reinterpret_cast<uv_handle_t*>(uvHandle), static_cast<uv_close_cb>(onErrorClose)); // If bind() fails due to "too many open files" stop here. if (err == UV_EMFILE) MS_THROW_ERROR("uv_tcp_bind() fails due to many open files"); // If bind() fails for more that MaxBindAttempts then raise an error. if (bindAttempt > MaxBindAttempts) MS_THROW_ERROR( "uv_tcp_bind() fails more than %" PRIu16 " times for IP '%s'", MaxBindAttempts, listenIp); // If we have tried all the ports in the range raise an error. if (iteratingPort == initialPort) MS_THROW_ERROR("no more available ports for IP '%s'", listenIp); continue; } // Set the port as unavailable. (*availablePorts)[iteratingPort] = false; MS_DEBUG_DEV( "bind success [ip:%s, port:%" PRIu16 ", attempt:%" PRIu16 "]", listenIp, iteratingPort, attempt); return uvHandle; }; }
void Stream::Close() { MS_TRACE(); delete this; }