Beispiel #1
0
	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);
	}
Beispiel #2
0
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();
}
Beispiel #3
0
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();
}
Beispiel #4
0
	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);
	}
Beispiel #5
0
	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;
		}
	}
Beispiel #6
0
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);
	}
}
Beispiel #7
0
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;
}
Beispiel #9
0
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();
	}
}
Beispiel #11
0
	/**
	 * 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);
	}
Beispiel #12
0
		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();
}
Beispiel #14
0
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");
}
Beispiel #15
0
	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);
		}
	}
Beispiel #16
0
	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);
	}
Beispiel #17
0
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;
}
Beispiel #18
0
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;
}
Beispiel #19
0
	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");
	}
Beispiel #20
0
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;
}
Beispiel #23
0
	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);
}
Beispiel #25
0
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());
}
Beispiel #26
0
	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);
	}
Beispiel #27
0
	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);
	}
Beispiel #28
0
	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();
	}
Beispiel #29
0
	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;
		};
	}
Beispiel #30
0
void Stream::Close() {
	MS_TRACE();

	delete this;
}