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; } }
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); }
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); } } }
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(); }
/** 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); }
/** 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); }
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(); }
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; }
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); } } }