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(); }
void DtlsTransport::GenerateFingerprints() { MS_TRACE(); for (auto it = DtlsTransport::string2FingerprintAlgorithm.begin(); it != DtlsTransport::string2FingerprintAlgorithm.end(); ++it) { std::string algorithm_str = it->first; FingerprintAlgorithm algorithm = it->second; uint8_t binary_fingerprint[EVP_MAX_MD_SIZE]; unsigned int size = 0; char hex_fingerprint[(EVP_MAX_MD_SIZE * 2) + 1]; const EVP_MD* hash_function; int ret; switch (algorithm) { case FingerprintAlgorithm::SHA1: hash_function = EVP_sha1(); break; case FingerprintAlgorithm::SHA224: hash_function = EVP_sha224(); break; case FingerprintAlgorithm::SHA256: hash_function = EVP_sha256(); break; case FingerprintAlgorithm::SHA384: hash_function = EVP_sha384(); break; case FingerprintAlgorithm::SHA512: hash_function = EVP_sha512(); break; default: MS_ABORT("unknown algorithm"); } ret = X509_digest(DtlsTransport::certificate, hash_function, binary_fingerprint, &size); if (ret == 0) { MS_ERROR("X509_digest() failed"); MS_THROW_ERROR("Fingerprints generation failed"); } // Convert to hexadecimal format in lowecase without colons. for (unsigned int i = 0; i < size; i++) { std::sprintf(hex_fingerprint + (i * 2), "%.2x", binary_fingerprint[i]); } hex_fingerprint[size * 2] = '\0'; MS_DEBUG("%-7s fingerprint: %s", algorithm_str.c_str(), hex_fingerprint); // Store in the JSON. DtlsTransport::localFingerprints[algorithm_str] = hex_fingerprint; } }
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 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 DtlsTransport::Run(Role localRole) { MS_TRACE(); MS_ASSERT(localRole == Role::CLIENT || localRole == Role::SERVER, "local DTLS role must be 'client' or 'server'"); Role previousLocalRole = this->localRole; if (localRole == previousLocalRole) { MS_ERROR("same local DTLS role provided, doing nothing"); return; } // If the previous local DTLS role was 'client' or 'server' do reset. if (previousLocalRole == Role::CLIENT || previousLocalRole == Role::SERVER) { MS_DEBUG("resetting DTLS due to local role change"); Reset(); } // Update local role. this->localRole = localRole; // Set state and notify the listener. this->state = DtlsState::CONNECTING; this->listener->onDtlsConnecting(this); switch (this->localRole) { case Role::CLIENT: MS_DEBUG("running [role:client]"); SSL_set_connect_state(this->ssl); SSL_do_handshake(this->ssl); SendPendingOutgoingDtlsData(); SetTimeout(); break; case Role::SERVER: MS_DEBUG("running [role:server]"); SSL_set_accept_state(this->ssl); SSL_do_handshake(this->ssl); break; default: MS_ABORT("invalid local DTLS role"); break; } }
void Room::onRTPPacket(RTC::Peer* peer, RTC::RTPPacket* packet) { MS_TRACE(); int peer_id = *(int*)peer->GetUserData(); MS_BYTE payload_type = packet->GetPayloadType(); MS_4BYTES original_ssrc = packet->GetSSRC(); MS_DEBUG("valid RTP packet received from Peer %d [ssrc: %llu | payload: %hu]", peer_id, (unsigned long long)packet->GetSSRC(), (unsigned short)packet->GetPayloadType()); switch(payload_type) { case PAYLOAD_TYPE_AUDIO: case PAYLOAD_TYPE_VIDEO: break; default: MS_ERROR("payload is not OPUS %d nor VP8 %d, packet ignored", PAYLOAD_TYPE_AUDIO, PAYLOAD_TYPE_VIDEO); return; } // Deliver to all the peers. // TODO: but this one? yes (TODO) for (auto dst_peer : this->peers) { // if (dst_peer == peer) // continue; int dst_peer_id = *(int*)dst_peer->GetUserData(); switch(payload_type) { case PAYLOAD_TYPE_AUDIO: packet->SetSSRC((MS_4BYTES)(SSRC_AUDIO_BASE + (peer_id * 10) + dst_peer_id)); break; case PAYLOAD_TYPE_VIDEO: packet->SetSSRC((MS_4BYTES)(SSRC_VIDEO_BASE + (peer_id * 10) + dst_peer_id)); break; default: MS_ABORT("no puede ser!!!"); return; } MS_DEBUG("sending RTP packet to Peer %d [ssrc: %llu | payload: %hu | size: %zu]", dst_peer_id, (unsigned long long)packet->GetSSRC(), (unsigned short)packet->GetPayloadType(), packet->GetLength()); dst_peer->SendRTPPacket(packet); // NOTE: recover the previous SSRC since other peers are going to // send this same RTPPacket! packet->SetSSRC(original_ssrc); } }
void DtlsTransport::ProcessDtlsData(const uint8_t* data, size_t len) { MS_TRACE(); int written; int read; if (!IsRunning()) { MS_ERROR("cannot process data while not running"); return; } // Write the received DTLS data into the sslBioFromNetwork. written = BIO_write(this->sslBioFromNetwork, (const void*)data, (int)len); if (written != (int)len) MS_WARN("OpenSSL BIO_write() wrote less (%zu bytes) than given data (%zu bytes)", (size_t)written, len); // Must call SSL_read() to process received DTLS data. read = SSL_read(this->ssl, (void*)DtlsTransport::sslReadBuffer, MS_SSL_READ_BUFFER_SIZE); // Send data if it's ready. SendPendingOutgoingDtlsData(); // Check SSL status and return if it is bad/closed. if (!CheckStatus(read)) return; // Set/update the DTLS timeout. if (!SetTimeout()) return; // Application data received. Notify to the listener. if (read > 0) { // It is allowed to receive DTLS data even before validating remote fingerprint. if (!this->handshakeDone) { MS_DEBUG("ignoring application data received while DTLS handshake not done"); return; } // Notify the listener. this->listener->onDtlsApplicationData(this, (uint8_t*)DtlsTransport::sslReadBuffer, (size_t)read); } }
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 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); }
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 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(); }
int main(int argc, char* argv[]) { // Ensure we are called by our Node library. if (argc == 1 || !std::getenv("MEDIASOUP_CHANNEL_FD")) { std::cerr << "ERROR: you don't seem to be my real father" << std::endl; std::_Exit(EXIT_FAILURE); } std::string id = std::string(argv[1]); int channelFd = std::stoi(std::getenv("MEDIASOUP_CHANNEL_FD")); // Initialize libuv stuff (we need it for the Channel). DepLibUV::ClassInit(); // Set the Channel socket (this will be handled and deleted by the Loop). Channel::UnixStreamSocket* channel = new Channel::UnixStreamSocket(channelFd); // Initialize the Logger. Logger::Init(id, channel); // Setup the configuration. try { Settings::SetConfiguration(argc, argv); } catch (const MediaSoupError &error) { MS_ERROR("configuration error: %s", error.what()); exitWithError(); } // Print the effective configuration. Settings::PrintConfiguration(); MS_DEBUG("starting " MS_PROCESS_NAME " [pid:%ld]", (long)getpid()); #if defined(MS_LITTLE_ENDIAN) MS_DEBUG("detected Little-Endian CPU"); #elif defined(MS_BIG_ENDIAN) MS_DEBUG("detected Big-Endian CPU"); #endif #if defined(INTPTR_MAX) && defined(INT32_MAX) && (INTPTR_MAX == INT32_MAX) MS_DEBUG("detected 32 bits architecture"); #elif defined(INTPTR_MAX) && defined(INT64_MAX) && (INTPTR_MAX == INT64_MAX) MS_DEBUG("detected 64 bits architecture"); #else MS_WARN("cannot determine whether the architecture is 32 or 64 bits"); #endif try { init(); // Run the Loop. Loop loop(channel); destroy(); MS_DEBUG_STD("success exit"); exitSuccess(); } catch (const MediaSoupError &error) { destroy(); MS_ERROR_STD("failure exit: %s", error.what()); exitWithError(); } }
void Settings::ReadArguments(int argc, char* argv[]) { MS_TRACE(); /* Variables for getopt. */ int c; int option_index = 0; extern char *optarg; extern int optind, opterr, optopt; // For getopt_long(). // Begin with ":" so we get ':' error in case a valid option requires argument. std::string short_options = ":c:dp:u:g:vh"; // For getopt_long(). struct option long_options[] = { { "configfile", required_argument, nullptr, 'c' }, { "daemonize", no_argument, nullptr, 'd' }, { "pidfile", required_argument, nullptr, 'p' }, { "user", required_argument, nullptr, 'u' }, { "group", required_argument, nullptr, 'g' }, { "version", no_argument, nullptr, 'v' }, { "help", no_argument, nullptr, 'h' }, { 0, 0, 0, 0 } }; // A map for associating short and long option names. std::map<char, std::string> long_option_names = { { 'c', "configfile" }, { 'd', "daemonize" }, { 'p', "pidfile" }, { 'u', "user" }, { 'g', "group" }, { 'v', "version" }, { 'h', "help" } }; /* Parse command line options. */ opterr = 0; // Don't allow getopt to print error messages. while ((c = getopt_long(argc, argv, short_options.c_str(), long_options, &option_index)) != -1) { switch(c) { case 'c': Settings::arguments.configFile = optarg; break; case 'd': Settings::arguments.daemonize = true; break; case 'p': Settings::arguments.pidFile = optarg; break; case 'u': Settings::arguments.user = optarg; break; case 'g': Settings::arguments.group = optarg; break; case 'v': Settings::PrintVersion(); std::_Exit(EXIT_SUCCESS); break; case 'h': Settings::PrintVersion(); Settings::PrintHelp(false); std::_Exit(EXIT_SUCCESS); break; // Invalid option. case '?': if (isprint(optopt)) { MS_ERROR("invalid option '-%c'", (char)optopt); Settings::PrintHelp(true); std::_Exit(EXIT_FAILURE); } else { MS_ERROR("unknown long option given as argument"); Settings::PrintHelp(true); std::_Exit(EXIT_FAILURE); } // Valid option, but it requires and argument that is not given. case ':': MS_ERROR("option '-%c' or '--%s' requires an argument", (char)optopt, long_option_names[(char)optopt].c_str()); Settings::PrintHelp(true); std::_Exit(EXIT_FAILURE); break; // This should never happen. default: MS_ABORT("'default' should never happen"); } } // Ensure there are no more command line arguments after parsed options. if (optind != argc) MS_EXIT_FAILURE("there are remaining arguments after parsing command line options"); // Ensure that PID file is not given when in foreground mode. if (! Settings::arguments.pidFile.empty() && ! Settings::arguments.daemonize) MS_EXIT_FAILURE("PID file option requires daemon mode"); }
inline bool DtlsTransport::CheckRemoteFingerprint() { MS_TRACE(); MS_ASSERT(this->remoteFingerprint.algorithm != FingerprintAlgorithm::NONE, "remote fingerprint not set"); X509* certificate; uint8_t binary_fingerprint[EVP_MAX_MD_SIZE]; unsigned int size = 0; char hex_fingerprint[(EVP_MAX_MD_SIZE * 2) + 1]; const EVP_MD* hash_function; int ret; certificate = SSL_get_peer_certificate(this->ssl); if (!certificate) { MS_WARN("no certificate was provided by the peer"); return false; } switch (this->remoteFingerprint.algorithm) { case FingerprintAlgorithm::SHA1: hash_function = EVP_sha1(); break; case FingerprintAlgorithm::SHA224: hash_function = EVP_sha224(); break; case FingerprintAlgorithm::SHA256: hash_function = EVP_sha256(); break; case FingerprintAlgorithm::SHA384: hash_function = EVP_sha384(); break; case FingerprintAlgorithm::SHA512: hash_function = EVP_sha512(); break; default: MS_ABORT("unknown algorithm"); } ret = X509_digest(certificate, hash_function, binary_fingerprint, &size); X509_free(certificate); if (ret == 0) { MS_ERROR("X509_digest() failed"); return false; } // Convert to hexadecimal format in lowecase without colons. for (unsigned int i = 0; i < size; i++) { std::sprintf(hex_fingerprint + (i * 2), "%.2x", binary_fingerprint[i]); } hex_fingerprint[size * 2] = '\0'; if (this->remoteFingerprint.value.compare(hex_fingerprint) != 0) { MS_WARN("fingerprint in the remote certificate (%s) does not match the announced one (%s)", hex_fingerprint, this->remoteFingerprint.value.c_str()); return false; } MS_DEBUG("valid remote fingerprint"); return true; }
void DtlsTransport::CreateSSL_CTX() { MS_TRACE(); std::string ssl_srtp_profiles; EC_KEY* ecdh = nullptr; int ret; /* Set the global DTLS context. */ // - Both DTLS 1.0 and 1.2 (requires OpenSSL >= 1.1.0). #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) DtlsTransport::sslCtx = SSL_CTX_new(DTLS_method()); // - Just DTLS 1.0 (requires OpenSSL >= 1.0.1). #elif (OPENSSL_VERSION_NUMBER >= 0x10001000L) DtlsTransport::sslCtx = SSL_CTX_new(DTLSv1_method()); #else #error "too old OpenSSL version" #endif if (!DtlsTransport::sslCtx) { LOG_OPENSSL_ERROR("SSL_CTX_new() failed"); goto error; } ret = SSL_CTX_use_certificate(DtlsTransport::sslCtx, DtlsTransport::certificate); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_use_certificate() failed"); goto error; } ret = SSL_CTX_use_PrivateKey(DtlsTransport::sslCtx, DtlsTransport::privateKey); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_use_PrivateKey() failed"); goto error; } ret = SSL_CTX_check_private_key(DtlsTransport::sslCtx); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_check_private_key() failed"); goto error; } // Set options. SSL_CTX_set_options(DtlsTransport::sslCtx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_TICKET | SSL_OP_SINGLE_ECDH_USE); // Don't use sessions cache. SSL_CTX_set_session_cache_mode(DtlsTransport::sslCtx, SSL_SESS_CACHE_OFF); // Read always as much into the buffer as possible. // NOTE: This is the default for DTLS, but a bug in non latest OpenSSL // versions makes this call required. SSL_CTX_set_read_ahead(DtlsTransport::sslCtx, 1); SSL_CTX_set_verify_depth(DtlsTransport::sslCtx, 4); // Require certificate from peer. SSL_CTX_set_verify(DtlsTransport::sslCtx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, on_ssl_certificate_verify); // Set SSL info callback. SSL_CTX_set_info_callback(DtlsTransport::sslCtx, on_ssl_info); // Set ciphers. ret = SSL_CTX_set_cipher_list(DtlsTransport::sslCtx, "ALL:!ADH:!LOW:!EXP:!MD5:!aNULL:!eNULL:@STRENGTH"); if (ret == 0) { LOG_OPENSSL_ERROR("SSL_CTX_set_cipher_list() failed"); goto error; } // Enable ECDH ciphers. // DOC: http://en.wikibooks.org/wiki/OpenSSL/Diffie-Hellman_parameters // NOTE: https://code.google.com/p/chromium/issues/detail?id=406458 // For OpenSSL >= 1.0.2: #if (OPENSSL_VERSION_NUMBER >= 0x10002000L) SSL_CTX_set_ecdh_auto(DtlsTransport::sslCtx, 1); #else ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); if (!ecdh) { LOG_OPENSSL_ERROR("EC_KEY_new_by_curve_name() failed"); goto error; } if (SSL_CTX_set_tmp_ecdh(DtlsTransport::sslCtx, ecdh) != 1) { LOG_OPENSSL_ERROR("SSL_CTX_set_tmp_ecdh() failed"); goto error; } EC_KEY_free(ecdh); ecdh = nullptr; #endif // Set the "use_srtp" DTLS extension. for (auto it = DtlsTransport::srtpProfiles.begin(); it != DtlsTransport::srtpProfiles.end(); ++it) { if (it != DtlsTransport::srtpProfiles.begin()) ssl_srtp_profiles += ":"; SrtpProfileMapEntry* profile_entry = &(*it); ssl_srtp_profiles += profile_entry->name; } MS_DEBUG("setting SRTP profiles for DTLS: %s", ssl_srtp_profiles.c_str()); // NOTE: This function returns 0 on success. ret = SSL_CTX_set_tlsext_use_srtp(DtlsTransport::sslCtx, ssl_srtp_profiles.c_str()); if (ret != 0) { MS_ERROR("SSL_CTX_set_tlsext_use_srtp() failed when entering '%s'", ssl_srtp_profiles.c_str()); LOG_OPENSSL_ERROR("SSL_CTX_set_tlsext_use_srtp() failed"); goto error; } return; error: if (DtlsTransport::sslCtx) { SSL_CTX_free(DtlsTransport::sslCtx); DtlsTransport::sslCtx = nullptr; } if (ecdh) EC_KEY_free(ecdh); MS_THROW_ERROR("SSL context creation failed"); }
void Loop::onChannelRequest(Channel::UnixStreamSocket* channel, Channel::Request* request) { MS_TRACE(); MS_DEBUG("'%s' request", request->method.c_str()); switch (request->methodId) { case Channel::Request::MethodId::worker_dump: { static const Json::StaticString k_workerId("workerId"); static const Json::StaticString k_rooms("rooms"); Json::Value json(Json::objectValue); Json::Value json_rooms(Json::arrayValue); json[k_workerId] = Logger::id; for (auto& kv : this->rooms) { auto room = kv.second; json_rooms.append(room->toJson()); } json[k_rooms] = json_rooms; request->Accept(json); break; } case Channel::Request::MethodId::worker_updateSettings: { Settings::HandleRequest(request); break; } case Channel::Request::MethodId::worker_createRoom: { RTC::Room* room; uint32_t roomId; try { room = GetRoomFromRequest(request, &roomId); } catch (const MediaSoupError &error) { request->Reject(error.what()); return; } if (room) { request->Reject("Room already exists"); return; } try { room = new RTC::Room(this, this->notifier, roomId); } catch (const MediaSoupError &error) { request->Reject(error.what()); return; } this->rooms[roomId] = room; MS_DEBUG("Room created [roomId:%" PRIu32 "]", roomId); request->Accept(); break; } case Channel::Request::MethodId::room_close: case Channel::Request::MethodId::room_dump: case Channel::Request::MethodId::room_createPeer: case Channel::Request::MethodId::room_getCapabilities: case Channel::Request::MethodId::peer_close: case Channel::Request::MethodId::peer_dump: case Channel::Request::MethodId::peer_createTransport: case Channel::Request::MethodId::peer_createRtpReceiver: case Channel::Request::MethodId::transport_close: case Channel::Request::MethodId::transport_dump: case Channel::Request::MethodId::transport_setRemoteDtlsParameters: case Channel::Request::MethodId::rtpReceiver_close: case Channel::Request::MethodId::rtpReceiver_dump: case Channel::Request::MethodId::rtpReceiver_receive: case Channel::Request::MethodId::rtpReceiver_setRtpRawEvent: case Channel::Request::MethodId::rtpReceiver_setRtpObjectEvent: case Channel::Request::MethodId::rtpSender_dump: case Channel::Request::MethodId::rtpSender_setTransport: { RTC::Room* room; try { room = GetRoomFromRequest(request); } catch (const MediaSoupError &error) { request->Reject(error.what()); return; } if (!room) { request->Reject("Room does not exist"); return; } room->HandleRequest(request); break; } default: { MS_ERROR("unknown method"); request->Reject("unknown method"); } } }