/** Because most of the I/O gubbins are encapsulated within * BufferedSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. */ TreeSocket::TreeSocket(SpanningTreeUtilities* Util, Link* link, Autoconnect* myac, const std::string& ipaddr) : Utils(Util) { age = ServerInstance->Time(); linkID = assign(link->Name); capab = new CapabData; capab->link = link; capab->ac = myac; capab->capab_phase = 0; MyRoot = NULL; proto_version = 0; ConnectionFailureShown = false; LinkState = CONNECTING; if (!link->Hook.empty()) { ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook); if (!prov) { SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID); return; } AddIOHook(prov->creator); } DoConnect(ipaddr, link->Port, link->Timeout, link->Bind); Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout); SendCapabilities(1); }
/** Constructor for incoming connections */ 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) , burstsent(false), age(ServerInstance->Time()) { capab = new CapabData; capab->capab_phase = 0; for (ListenSocket::IOHookProvList::iterator i = via->iohookprovs.begin(); i != via->iohookprovs.end(); ++i) { ListenSocket::IOHookProvRef& iohookprovref = *i; if (!iohookprovref) continue; iohookprovref->OnAccept(this, client, server); // IOHook could have encountered a fatal error, e.g. if the TLS ClientHello was already in the queue and there was no common TLS version if (!getError().empty()) { TreeSocket::OnError(I_ERR_OTHER); return; } } SendCapabilities(1); Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, 30); }
/** Because most of the I/O gubbins are encapsulated within * BufferedSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. */ TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr) : linkID(assign(link->Name)), LinkState(CONNECTING), MyRoot(NULL), proto_version(0), ConnectionFailureShown(false) , age(ServerInstance->Time()) { capab = new CapabData; capab->link = link; capab->ac = myac; capab->capab_phase = 0; DoConnect(ipaddr, link->Port, link->Timeout, link->Bind); Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout); SendCapabilities(1); }
/** 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; if (via->iohookprov) via->iohookprov->OnAccept(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); }
/** Because most of the I/O gubbins are encapsulated within * BufferedSocket, we just call the superclass constructor for * most of the action, and append a few of our own values * to it. */ TreeSocket::TreeSocket(Link* link, Autoconnect* myac, const std::string& ipaddr) : linkID(assign(link->Name)), LinkState(CONNECTING), MyRoot(NULL), proto_version(0), ConnectionFailureShown(false) , age(ServerInstance->Time()) { capab = new CapabData; capab->link = link; capab->ac = myac; capab->capab_phase = 0; if (!link->Hook.empty()) { ServiceProvider* prov = ServerInstance->Modules->FindService(SERVICE_IOHOOK, link->Hook); if (!prov) { SetError("Could not find hook '" + link->Hook + "' for connection to " + linkID); return; } AddIOHook(static_cast<IOHook*>(prov)); } DoConnect(ipaddr, link->Port, link->Timeout, link->Bind); Utils->timeoutlist[this] = std::pair<std::string, int>(linkID, link->Timeout); SendCapabilities(1); }
bool TreeSocket::Capab(const parameterlist ¶ms) { if (params.size() < 1) { this->SendError("Invalid number of parameters for CAPAB - Mismatched version"); return false; } if (params[0] == "START") { capab->ModuleList.clear(); capab->OptModuleList.clear(); capab->CapKeys.clear(); if (params.size() > 1) proto_version = atoi(params[1].c_str()); SendCapabilities(2); } else if (params[0] == "END") { std::string reason; /* Compare ModuleList and check CapKeys */ if ((this->capab->ModuleList != this->MyModules(VF_COMMON)) && (this->capab->ModuleList.length())) { std::string diffIneed, diffUneed; ListDifference(this->capab->ModuleList, this->MyModules(VF_COMMON), ' ', diffIneed, diffUneed); if (diffIneed.length() || diffUneed.length()) { reason = "Modules incorrectly matched on these servers."; if (diffIneed.length()) reason += " Not loaded here:" + diffIneed; if (diffUneed.length()) reason += " Not loaded there:" + diffUneed; this->SendError("CAPAB negotiation failed: "+reason); return false; } } if (this->capab->OptModuleList != this->MyModules(VF_OPTCOMMON) && this->capab->OptModuleList.length()) { std::string diffIneed, diffUneed; ListDifference(this->capab->OptModuleList, this->MyModules(VF_OPTCOMMON), ' ', diffIneed, diffUneed); if (diffIneed.length() || diffUneed.length()) { if (Utils->AllowOptCommon) { ServerInstance->SNO->WriteToSnoMask('l', "Optional module lists do not match, some commands may not work globally.%s%s%s%s", diffIneed.length() ? " Not loaded here:" : "", diffIneed.c_str(), diffUneed.length() ? " Not loaded there:" : "", diffUneed.c_str()); } else { reason = "Optional modules incorrectly matched on these servers, and options::allowmismatch not set."; if (diffIneed.length()) reason += " Not loaded here:" + diffIneed; if (diffUneed.length()) reason += " Not loaded there:" + diffUneed; this->SendError("CAPAB negotiation failed: "+reason); return false; } } } if (this->capab->CapKeys.find("PROTOCOL") == this->capab->CapKeys.end()) { reason = "Protocol version not specified"; } else { proto_version = atoi(capab->CapKeys.find("PROTOCOL")->second.c_str()); if (proto_version < MinCompatProtocol) { reason = "Server is using protocol version " + ConvToStr(proto_version) + " which is too old to link with this server (version " + ConvToStr(ProtocolVersion) + (ProtocolVersion != MinCompatProtocol ? ", links with " + ConvToStr(MinCompatProtocol) + " and above)" : ")"); } } if(this->capab->CapKeys.find("PREFIX") != this->capab->CapKeys.end() && this->capab->CapKeys.find("PREFIX")->second != ServerInstance->Modes->BuildPrefixes()) reason = "One or more of the prefixes on the remote server are invalid on this server."; if (!capab->ChanModes.empty()) { if (capab->ChanModes != BuildModeList(MODETYPE_CHANNEL)) { std::string diffIneed, diffUneed; ListDifference(capab->ChanModes, BuildModeList(MODETYPE_CHANNEL), ' ', diffIneed, diffUneed); if (diffIneed.length() || diffUneed.length()) { reason = "Channel modes not matched on these servers."; if (diffIneed.length()) reason += " Not loaded here:" + diffIneed; if (diffUneed.length()) reason += " Not loaded there:" + diffUneed; } } } else if (this->capab->CapKeys.find("CHANMODES") != this->capab->CapKeys.end()) { if (this->capab->CapKeys.find("CHANMODES")->second != ServerInstance->Modes->GiveModeList(MASK_CHANNEL)) reason = "One or more of the channel modes on the remote server are invalid on this server."; } if (!capab->UserModes.empty()) { if (capab->UserModes != BuildModeList(MODETYPE_USER)) { std::string diffIneed, diffUneed; ListDifference(capab->UserModes, BuildModeList(MODETYPE_USER), ' ', diffIneed, diffUneed); if (diffIneed.length() || diffUneed.length()) { reason = "User modes not matched on these servers."; if (diffIneed.length()) reason += " Not loaded here:" + diffIneed; if (diffUneed.length()) reason += " Not loaded there:" + diffUneed; } } } else if (this->capab->CapKeys.find("USERMODES") != this->capab->CapKeys.end()) { if (this->capab->CapKeys.find("USERMODES")->second != ServerInstance->Modes->GiveModeList(MASK_USER)) reason = "One or more of the user modes on the remote server are invalid on this server."; } /* Challenge response, store their challenge for our password */ std::map<std::string,std::string>::iterator n = this->capab->CapKeys.find("CHALLENGE"); if (Utils->ChallengeResponse && (n != this->capab->CapKeys.end()) && (ServerInstance->Modules->Find("m_sha256.so"))) { /* Challenge-response is on now */ this->SetTheirChallenge(n->second); if (!this->GetTheirChallenge().empty() && (this->LinkState == CONNECTING)) { this->SendCapabilities(2); this->WriteLine("SERVER "+ServerInstance->Config->ServerName+" "+this->MakePass(capab->link->SendPass, capab->theirchallenge)+" 0 "+ServerInstance->Config->GetSID()+" :"+ServerInstance->Config->ServerDesc); } } else { /* They didnt specify a challenge or we don't have m_sha256.so, we use plaintext */ if (this->LinkState == CONNECTING) { this->SendCapabilities(2); this->WriteLine("SERVER "+ServerInstance->Config->ServerName+" "+capab->link->SendPass+" 0 "+ServerInstance->Config->GetSID()+" :"+ServerInstance->Config->ServerDesc); } } if (reason.length()) { this->SendError("CAPAB negotiation failed: "+reason); return false; } } else if ((params[0] == "MODULES") && (params.size() == 2)) { if (!capab->ModuleList.length()) { capab->ModuleList = params[1]; } else { capab->ModuleList.push_back(' '); capab->ModuleList.append(params[1]); } } else if ((params[0] == "MODSUPPORT") && (params.size() == 2)) { if (!capab->OptModuleList.length()) { capab->OptModuleList = params[1]; } else { capab->OptModuleList.push_back(' '); capab->OptModuleList.append(params[1]); } } else if ((params[0] == "CHANMODES") && (params.size() == 2)) { capab->ChanModes = params[1]; } else if ((params[0] == "USERMODES") && (params.size() == 2)) { capab->UserModes = params[1]; } else if ((params[0] == "CAPABILITIES") && (params.size() == 2)) { irc::tokenstream capabs(params[1]); std::string item; while (capabs.GetToken(item)) { /* Process each key/value pair */ std::string::size_type equals = item.find('='); if (equals != std::string::npos) { std::string var = item.substr(0, equals); std::string value = item.substr(equals+1, item.length()); capab->CapKeys[var] = value; } } } return true; }