void TcpConnection::Close() { MS_TRACE(); if (this->isClosing) return; int err; this->isClosing = true; // Don't read more. err = uv_read_stop((uv_stream_t*)this->uvHandle); if (err) MS_ABORT("uv_read_stop() failed: %s", uv_strerror(err)); // If there is no error and the peer didn't close its connection side then close gracefully. if (!this->hasError && !this->isClosedByPeer) { // Use uv_shutdown() so pending data to be written will be sent to the peer // before closing. uv_shutdown_t* req = new uv_shutdown_t; req->data = (void*)this; err = uv_shutdown(req, (uv_stream_t*)this->uvHandle, (uv_shutdown_cb)on_shutdown); if (err) MS_ABORT("uv_shutdown() failed: %s", uv_strerror(err)); } // Otherwise directly close the socket. else { uv_close((uv_handle_t*)this->uvHandle, (uv_close_cb)on_close); } }
void TcpConnection::Write(const uint8_t* data, size_t len) { MS_TRACE(); if (this->isClosing) return; if (len == 0) return; uv_buf_t buffer; int written; int err; // First try uv_try_write(). In case it can not directly write all the given // data then build a uv_req_t and use uv_write(). buffer = uv_buf_init((char*)data, len); written = uv_try_write((uv_stream_t*)this->uvHandle, &buffer, 1); // All the data was written. Done. if (written == (int)len) { return; } // Cannot write any data at first time. Use uv_write(). else if (written == UV_EAGAIN || written == UV_ENOSYS) { // Set written to 0 so pending_len can be properly calculated. written = 0; } // Error. Should not happen. else if (written < 0) { MS_WARN("uv_try_write() failed, closing the connection: %s", uv_strerror(written)); Close(); return; } // MS_DEBUG("could just write %zu bytes (%zu given) at first time, using uv_write() now", (size_t)written, len); size_t pending_len = len - written; // Allocate a special UvWriteData struct pointer. UvWriteData* write_data = (UvWriteData*)std::malloc(sizeof(UvWriteData) + pending_len); write_data->connection = this; std::memcpy(write_data->store, data + written, pending_len); write_data->req.data = (void*)write_data; buffer = uv_buf_init((char*)write_data->store, pending_len); err = uv_write(&write_data->req, (uv_stream_t*)this->uvHandle, &buffer, 1, (uv_write_cb)on_write); if (err) MS_ABORT("uv_write() failed: %s", uv_strerror(err)); }
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; } }
CRYPTO_dynlock_value* OpenSSL::DynCreateFunction(const char* file, int line) { // MS_TRACE(); CRYPTO_dynlock_value* value = new CRYPTO_dynlock_value; if (! value) { MS_ABORT("new CRYPTO_dynlock_value failed"); return nullptr; } pthread_mutex_init(&value->mutex, nullptr); return value; }
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 Settings::SetDefaultNumWorkers() { MS_TRACE(); int err; uv_cpu_info_t* cpus; int num_cpus; err = uv_cpu_info(&cpus, &num_cpus); if (err) MS_ABORT("uv_cpu_info() failed: %s", uv_strerror(err)); uv_free_cpu_info(cpus, num_cpus); MS_DEBUG("auto-detected value for numWorkers: %d", num_cpus); Settings::configuration.numWorkers = num_cpus; }
inline void DtlsTransport::ExtractSrtpKeys(RTC::SrtpSession::Profile srtp_profile) { MS_TRACE(); uint8_t srtp_material[MS_SRTP_MASTER_LENGTH * 2]; uint8_t* srtp_local_key; uint8_t* srtp_local_salt; uint8_t* srtp_remote_key; uint8_t* srtp_remote_salt; uint8_t srtp_local_master_key[MS_SRTP_MASTER_LENGTH]; uint8_t srtp_remote_master_key[MS_SRTP_MASTER_LENGTH]; int ret; ret = SSL_export_keying_material(this->ssl, srtp_material, MS_SRTP_MASTER_LENGTH * 2, "EXTRACTOR-dtls_srtp", 19, nullptr, 0, 0); MS_ASSERT(ret != 0, "SSL_export_keying_material() failed"); switch (this->localRole) { case Role::SERVER: srtp_remote_key = srtp_material; srtp_local_key = srtp_remote_key + MS_SRTP_MASTER_KEY_LENGTH; srtp_remote_salt = srtp_local_key + MS_SRTP_MASTER_KEY_LENGTH; srtp_local_salt = srtp_remote_salt + MS_SRTP_MASTER_SALT_LENGTH; break; case Role::CLIENT: srtp_local_key = srtp_material; srtp_remote_key = srtp_local_key + MS_SRTP_MASTER_KEY_LENGTH; srtp_local_salt = srtp_remote_key + MS_SRTP_MASTER_KEY_LENGTH; srtp_remote_salt = srtp_local_salt + MS_SRTP_MASTER_SALT_LENGTH; break; default: MS_ABORT("no DTLS role set"); break; } // Create the SRTP local master key. std::memcpy(srtp_local_master_key, srtp_local_key, MS_SRTP_MASTER_KEY_LENGTH); std::memcpy(srtp_local_master_key + MS_SRTP_MASTER_KEY_LENGTH, srtp_local_salt, MS_SRTP_MASTER_SALT_LENGTH); // Create the SRTP remote master key. std::memcpy(srtp_remote_master_key, srtp_remote_key, MS_SRTP_MASTER_KEY_LENGTH); std::memcpy(srtp_remote_master_key + MS_SRTP_MASTER_KEY_LENGTH, srtp_remote_salt, MS_SRTP_MASTER_SALT_LENGTH); // Set state and notify the listener. this->state = DtlsState::CONNECTED; this->listener->onDtlsConnected(this, srtp_profile, srtp_local_master_key, MS_SRTP_MASTER_LENGTH, srtp_remote_master_key, MS_SRTP_MASTER_LENGTH); }
bool Settings::IsBindableIP(const std::string &ip, int family, int* _bind_err) { MS_TRACE(); struct sockaddr_storage bind_addr; int bind_socket; int err = 0; bool bind_ok; switch (family) { case AF_INET: err = uv_ip4_addr(ip.c_str(), 0, (struct sockaddr_in*)&bind_addr); if (err) MS_ABORT("uv_ipv4_addr() failed: %s", uv_strerror(err)); bind_socket = socket(AF_INET, SOCK_DGRAM, 0); if (bind_socket == -1) MS_ABORT("socket() failed: %s", std::strerror(errno)); err = bind(bind_socket, (const struct sockaddr*)&bind_addr, sizeof(struct sockaddr_in)); break; case AF_INET6: uv_ip6_addr(ip.c_str(), 0, (struct sockaddr_in6*)&bind_addr); if (err) MS_ABORT("uv_ipv6_addr() failed: %s", uv_strerror(err)); bind_socket = socket(AF_INET6, SOCK_DGRAM, 0); if (bind_socket == -1) MS_ABORT("socket() failed: %s", std::strerror(errno)); err = bind(bind_socket, (const struct sockaddr*)&bind_addr, sizeof(struct sockaddr_in6)); break; default: MS_ABORT("unknown family"); } if (err == 0) { bind_ok = true; } else { bind_ok = false; *_bind_err = errno; } err = close(bind_socket); if (err) MS_ABORT("close() failed: %s", std::strerror(errno)); return bind_ok; }
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"); }
void Settings::SetDefaultRTClistenIP(int requested_family) { MS_TRACE(); int err; uv_interface_address_t* addresses; int num_addresses; std::string ipv4; std::string ipv6; int _bind_err; err = uv_interface_addresses(&addresses, &num_addresses); if (err) MS_ABORT("uv_interface_addresses() failed: %s", uv_strerror(err)); for (int i=0; i<num_addresses; i++) { uv_interface_address_t address = addresses[i]; // Ignore internal addresses. if (address.is_internal) continue; int family; MS_PORT port; std::string ip; Utils::IP::GetAddressInfo((struct sockaddr*)(&address.address.address4), &family, ip, &port); if (family != requested_family) continue; switch(family) { case AF_INET: // Ignore if already got an IPv4. if (! ipv4.empty()) continue; // Check if it is bindable. if (! IsBindableIP(ip, AF_INET, &_bind_err)) { MS_DEBUG("ignoring '%s' for RTC.listenIPv4: %s", ip.c_str(), std::strerror(errno)); continue; } MS_DEBUG("auto-discovered '%s' for RTC.listenIPv4", ip.c_str()); ipv4 = ip; break; case AF_INET6: // Ignore if already got an IPv6. if (! ipv6.empty()) continue; // Check if it is bindable. if (! IsBindableIP(ip, AF_INET6, &_bind_err)) { MS_DEBUG("ignoring '%s' for RTC.listenIPv6: %s", ip.c_str(), std::strerror(errno)); continue; } MS_DEBUG("auto-discovered '%s' for RTC.listenIPv6", ip.c_str()); ipv6 = ip; break; } } if (! ipv4.empty()) { Settings::configuration.RTC.listenIPv4 = ipv4; Settings::configuration.RTC.hasIPv4 = true; } if (! ipv6.empty()) { Settings::configuration.RTC.listenIPv6 = ipv6; Settings::configuration.RTC.hasIPv6 = true; } uv_free_interface_addresses(addresses, num_addresses); }
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 TcpConnection::Write(const uint8_t* data1, size_t len1, const uint8_t* data2, size_t len2) { MS_TRACE(); if (this->isClosing) return; if (len1 == 0 && len2 == 0) return; size_t total_len = len1 + len2; uv_buf_t buffers[2]; int written; int err; // First try uv_try_write(). In case it can not directly write all the given // data then build a uv_req_t and use uv_write(). buffers[0] = uv_buf_init((char*)data1, len1); buffers[1] = uv_buf_init((char*)data2, len2); written = uv_try_write((uv_stream_t*)this->uvHandle, buffers, 2); // All the data was written. Done. if (written == (int)total_len) { return; } // Cannot write any data at first time. Use uv_write(). else if (written == UV_EAGAIN || written == UV_ENOSYS) { // Set written to 0 so pending_len can be properly calculated. written = 0; } // Error. Should not happen. else if (written < 0) { MS_WARN("uv_try_write() failed, closing the connection: %s", uv_strerror(written)); Close(); return; } // MS_DEBUG("could just write %zu bytes (%zu given) at first time, using uv_write() now", (size_t)written, total_len); size_t pending_len = total_len - written; // Allocate a special UvWriteData struct pointer. UvWriteData* write_data = (UvWriteData*)std::malloc(sizeof(UvWriteData) + pending_len); write_data->connection = this; // If the first buffer was not entirely written then splice it. if ((size_t)written < len1) { std::memcpy(write_data->store, data1 + (size_t)written, len1 - (size_t)written); std::memcpy(write_data->store + (len1 - (size_t)written), data2, len2); } // Otherwise just take the pending data in the second buffer. else { std::memcpy(write_data->store, data2 + ((size_t)written - len1), len2 - ((size_t)written - len1)); } write_data->req.data = (void*)write_data; uv_buf_t buffer = uv_buf_init((char*)write_data->store, pending_len); err = uv_write(&write_data->req, (uv_stream_t*)this->uvHandle, &buffer, 1, (uv_write_cb)on_write); if (err) MS_ABORT("uv_write() failed: %s", uv_strerror(err)); }