Example #1
0
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);
	}
}
Example #2
0
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));
}
Example #3
0
	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;
		}
	}
Example #4
0
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;
}
Example #5
0
	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;
		}
	}
Example #6
0
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);
	}
}
Example #7
0
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;
}
Example #8
0
	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);
	}
Example #9
0
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;
}
Example #10
0
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");
}
Example #11
0
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);
}
Example #12
0
	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;
	}
Example #13
0
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));
}