Beispiel #1
0
void StreamSocket::Close()
{
	if (this->fd > -1)
	{
		// final chance, dump as much of the sendq as we can
		DoWrite();
		if (GetIOHook())
		{
			try
			{
				GetIOHook()->OnStreamSocketClose(this);
			}
			catch (CoreException& modexcept)
			{
				ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
					modexcept.GetSource(), modexcept.GetReason());
			}
			DelIOHook();
		}
		ServerInstance->SE->Shutdown(this, 2);
		ServerInstance->SE->DelFd(this);
		ServerInstance->SE->Close(this);
		fd = -1;
	}
}
Beispiel #2
0
	HttpServerSocket(int newfd, const std::string& IP, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
		: BufferedSocket(newfd), ip(IP), postsize(0)
	{
		InternalState = HTTP_SERVE_WAIT_REQUEST;

		FOREACH_MOD(OnHookIO, (this, via));
		if (GetIOHook())
			GetIOHook()->OnStreamSocketAccept(this, client, server);
	}
Beispiel #3
0
void StreamSocket::DoRead()
{
	if (GetIOHook())
	{
		int rv = -1;
		try
		{
			rv = GetIOHook()->OnStreamSocketRead(this, recvq);
		}
		catch (CoreException& modexcept)
		{
			ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "%s threw an exception: %s",
				modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
			return;
		}
		if (rv > 0)
			OnDataReady();
		if (rv < 0)
			SetError("Read Error"); // will not overwrite a better error message
	}
	else
	{
		char* ReadBuffer = ServerInstance->GetReadBuffer();
		int n = SocketEngine::Recv(this, ReadBuffer, ServerInstance->Config->NetBufferSize, 0);
		if (n == ServerInstance->Config->NetBufferSize)
		{
			SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
			recvq.append(ReadBuffer, n);
			OnDataReady();
		}
		else if (n > 0)
		{
			SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ);
			recvq.append(ReadBuffer, n);
			OnDataReady();
		}
		else if (n == 0)
		{
			error = "Connection closed";
			SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
		}
		else if (SocketEngine::IgnoreError())
		{
			SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_READ_WILL_BLOCK);
		}
		else if (errno == EINTR)
		{
			SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_ADD_TRIAL_READ);
		}
		else
		{
			error = SocketEngine::LastError();
			SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
		}
	}
}
Beispiel #4
0
void BufferedSocket::DoWrite()
{
	if (state == I_CONNECTING)
	{
		state = I_CONNECTED;
		this->OnConnected();
		if (GetIOHook())
			GetIOHook()->OnStreamSocketConnect(this);
		else
			ServerInstance->SE->ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
	}
	this->StreamSocket::DoWrite();
}
Beispiel #5
0
/** When a listening socket gives us a new file descriptor,
 * we must associate it with a socket without creating a new
 * connection. This constructor is used for this purpose.
 */
TreeSocket::TreeSocket(int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
	: BufferedSocket(newfd)
	, linkID("inbound from " + client->addr()), LinkState(WAIT_AUTH_1), MyRoot(NULL), proto_version(0)
	, ConnectionFailureShown(false), age(ServerInstance->Time())
{
	capab = new CapabData;
	capab->capab_phase = 0;

	FOREACH_MOD(OnHookIO, (this, via));
	if (GetIOHook())
		GetIOHook()->OnStreamSocketAccept(this, client, server);
	SendCapabilities(1);

	Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
}
Beispiel #6
0
/** When a listening socket gives us a new file descriptor,
 * we must associate it with a socket without creating a new
 * connection. This constructor is used for this purpose.
 */
TreeSocket::TreeSocket(SpanningTreeUtilities* Util, int newfd, ListenSocket* via, irc::sockets::sockaddrs* client, irc::sockets::sockaddrs* server)
	: BufferedSocket(newfd), Utils(Util)
{
	capab = new CapabData;
	capab->capab_phase = 0;
	MyRoot = NULL;
	age = ServerInstance->Time();
	LinkState = WAIT_AUTH_1;
	proto_version = 0;
	ConnectionFailureShown = false;
	linkID = "inbound from " + client->addr();

	FOREACH_MOD(I_OnHookIO, OnHookIO(this, via));
	if (GetIOHook())
		GetIOHook()->OnStreamSocketAccept(this, client, server);
	SendCapabilities(1);

	Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30);
}
Beispiel #7
0
void BufferedSocket::DoWrite()
{
	if (state == I_CONNECTING)
	{
		state = I_CONNECTED;
		this->OnConnected();
		if (!GetIOHook())
			SocketEngine::ChangeEventMask(this, FD_WANT_FAST_READ | FD_WANT_EDGE_WRITE);
	}
	this->StreamSocket::DoWrite();
}
Beispiel #8
0
bool TreeSocket::ComparePass(const Link& link, const std::string &theirs)
{
	capab->auth_fingerprint = !link.Fingerprint.empty();
	capab->auth_challenge = !capab->ourchallenge.empty() && !capab->theirchallenge.empty();

	std::string fp;
	if (GetIOHook())
	{
		SocketCertificateRequest req(this, Utils->Creator);
		if (req.cert)
		{
			fp = req.cert->GetFingerprint();
		}
	}

	if (capab->auth_challenge)
	{
		std::string our_hmac = MakePass(link.RecvPass, capab->ourchallenge);

		/* Straight string compare of hashes */
		if (our_hmac != theirs)
			return false;
	}
	else
	{
		/* Straight string compare of plaintext */
		if (link.RecvPass != theirs)
			return false;
	}

	if (capab->auth_fingerprint)
	{
		/* Require fingerprint to exist and match */
		if (link.Fingerprint != fp)
		{
			ServerInstance->SNO->WriteToSnoMask('l',"Invalid SSL fingerprint on link %s: need \"%s\" got \"%s\"",
				link.Name.c_str(), link.Fingerprint.c_str(), fp.c_str());
			SendError("Provided invalid SSL fingerprint " + fp + " - expected " + link.Fingerprint);
			return false;
		}
	}
	else if (!fp.empty())
	{
		ServerInstance->SNO->WriteToSnoMask('l', "SSL fingerprint for link %s is \"%s\". "
			"You can improve security by specifying this in <link:fingerprint>.", link.Name.c_str(), fp.c_str());
	}
	return true;
}
Beispiel #9
0
void StreamSocket::DoWrite()
{
	if (sendq.empty())
		return;
	if (!error.empty() || fd < 0)
	{
		ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "DoWrite on errored or closed socket");
		return;
	}

	if (GetIOHook())
	{
		int rv = -1;
		try
		{
			while (error.empty() && !sendq.empty())
			{
				if (sendq.size() > 1 && sendq[0].length() < 1024)
				{
					// Avoid multiple repeated SSL encryption invocations
					// This adds a single copy of the queue, but avoids
					// much more overhead in terms of system calls invoked
					// by the IOHook.
					//
					// The length limit of 1024 is to prevent merging strings
					// more than once when writes begin to block.
					std::string tmp;
					tmp.reserve(1280);
					while (!sendq.empty() && tmp.length() < 1024)
					{
						tmp.append(sendq.front());
						sendq.pop_front();
					}
					sendq.push_front(tmp);
				}
				std::string& front = sendq.front();
				int itemlen = front.length();

				{
					rv = GetIOHook()->OnStreamSocketWrite(this, front);
					if (rv > 0)
					{
						// consumed the entire string, and is ready for more
						sendq_len -= itemlen;
						sendq.pop_front();
					}
					else if (rv == 0)
					{
						// socket has blocked. Stop trying to send data.
						// IOHook has requested unblock notification from the socketengine

						// Since it is possible that a partial write took place, adjust sendq_len
						sendq_len = sendq_len - itemlen + front.length();
						return;
					}
					else
					{
						SetError("Write Error"); // will not overwrite a better error message
						return;
					}
				}
			}
		}
		catch (CoreException& modexcept)
		{
			ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "%s threw an exception: %s",
				modexcept.GetSource().c_str(), modexcept.GetReason().c_str());
		}
	}
	else
	{
		// don't even try if we are known to be blocking
		if (GetEventMask() & FD_WRITE_WILL_BLOCK)
			return;
		// start out optimistic - we won't need to write any more
		int eventChange = FD_WANT_EDGE_WRITE;
		while (error.empty() && sendq_len && eventChange == FD_WANT_EDGE_WRITE)
		{
			// Prepare a writev() call to write all buffers efficiently
			int bufcount = sendq.size();

			// cap the number of buffers at MYIOV_MAX
			if (bufcount > MYIOV_MAX)
			{
				bufcount = MYIOV_MAX;
			}

			int rv_max = 0;
			int rv;
			{
				SocketEngine::IOVector iovecs[MYIOV_MAX];
				for (int i = 0; i < bufcount; i++)
				{
					iovecs[i].iov_base = const_cast<char*>(sendq[i].data());
					iovecs[i].iov_len = sendq[i].length();
					rv_max += sendq[i].length();
				}
				rv = SocketEngine::WriteV(this, iovecs, bufcount);
			}

			if (rv == (int)sendq_len)
			{
				// it's our lucky day, everything got written out. Fast cleanup.
				// This won't ever happen if the number of buffers got capped.
				sendq_len = 0;
				sendq.clear();
			}
			else if (rv > 0)
			{
				// Partial write. Clean out strings from the sendq
				if (rv < rv_max)
				{
					// it's going to block now
					eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
				}
				sendq_len -= rv;
				while (rv > 0 && !sendq.empty())
				{
					std::string& front = sendq.front();
					if (front.length() <= (size_t)rv)
					{
						// this string got fully written out
						rv -= front.length();
						sendq.pop_front();
					}
					else
					{
						// stopped in the middle of this string
						front.erase(0, rv);
						rv = 0;
					}
				}
			}
			else if (rv == 0)
			{
				error = "Connection closed";
			}
			else if (SocketEngine::IgnoreError())
			{
				eventChange = FD_WANT_FAST_WRITE | FD_WRITE_WILL_BLOCK;
			}
			else if (errno == EINTR)
			{
				// restart interrupted syscall
				errno = 0;
			}
			else
			{
				error = SocketEngine::LastError();
			}
		}
		if (!error.empty())
		{
			// error - kill all events
			SocketEngine::ChangeEventMask(this, FD_WANT_NO_READ | FD_WANT_NO_WRITE);
		}
		else
		{
			SocketEngine::ChangeEventMask(this, eventChange);
		}
	}
}