2 * InspIRCd -- Internet Relay Chat Daemon
4 * Copyright (C) 2009-2010 Daniel De Graaf <danieldg@inspircd.org>
5 * Copyright (C) 2007-2009 Robin Burchell <robin+git@viroteck.net>
6 * Copyright (C) 2006-2009 Dennis Friis <peavey@inspircd.org>
7 * Copyright (C) 2006-2008 Craig Edwards <craigedwards@brainbox.cc>
8 * Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
9 * Copyright (C) 2006 Oliver Lupton <oliverlupton@gmail.com>
11 * This file is part of InspIRCd. InspIRCd is free software: you can
12 * redistribute it and/or modify it under the terms of the GNU General Public
13 * License as published by the Free Software Foundation, version 2.
15 * This program is distributed in the hope that it will be useful, but WITHOUT
16 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include "exitcodes.h"
29 #include "configparser.h"
32 ServerLimits::ServerLimits(ConfigTag* tag)
33 : NickMax(tag->getUInt("maxnick", 30))
34 , ChanMax(tag->getUInt("maxchan", 64))
35 , MaxModes(tag->getUInt("maxmodes", 20))
36 , IdentMax(tag->getUInt("maxident", 10))
37 , MaxQuit(tag->getUInt("maxquit", 255))
38 , MaxTopic(tag->getUInt("maxtopic", 307))
39 , MaxKick(tag->getUInt("maxkick", 255))
40 , MaxReal(tag->getUInt("maxreal", tag->getUInt("maxgecos", 128)))
41 , MaxAway(tag->getUInt("maxaway", 200))
42 , MaxLine(tag->getUInt("maxline", 512))
43 , MaxHost(tag->getUInt("maxhost", 64))
47 ServerConfig::ServerPaths::ServerPaths(ConfigTag* tag)
48 : Config(tag->getString("configdir", INSPIRCD_CONFIG_PATH))
49 , Data(tag->getString("datadir", INSPIRCD_DATA_PATH))
50 , Log(tag->getString("logdir", INSPIRCD_LOG_PATH))
51 , Module(tag->getString("moduledir", INSPIRCD_MODULE_PATH))
55 static ConfigTag* CreateEmptyTag()
58 return ConfigTag::create("empty", "<auto>", 0, items);
61 ServerConfig::ServerConfig()
62 : EmptyTag(CreateEmptyTag())
66 , NoSnoticeStack(false)
70 ServerConfig::~ServerConfig()
75 static void ReadXLine(ServerConfig* conf, const std::string& tag, const std::string& key, XLineFactory* make)
77 insp::flat_set<std::string> configlines;
79 ConfigTagList tags = conf->ConfTags(tag);
80 for(ConfigIter i = tags.first; i != tags.second; ++i)
82 ConfigTag* ctag = i->second;
84 const std::string mask = ctag->getString(key);
86 throw CoreException("<" + tag + ":" + key + "> missing at " + ctag->getTagLocation());
88 const std::string reason = ctag->getString("reason");
90 throw CoreException("<" + tag + ":reason> missing at " + ctag->getTagLocation());
92 XLine* xl = make->Generate(ServerInstance->Time(), 0, ServerInstance->Config->ServerName, reason, mask);
93 xl->from_config = true;
94 configlines.insert(xl->Displayable());
95 if (!ServerInstance->XLines->AddLine(xl, NULL))
99 ServerInstance->XLines->ExpireRemovedConfigLines(make->GetType(), configlines);
102 typedef std::map<std::string, ConfigTag*> LocalIndex;
103 void ServerConfig::CrossCheckOperClassType()
105 LocalIndex operclass;
106 ConfigTagList tags = ConfTags("class");
107 for(ConfigIter i = tags.first; i != tags.second; ++i)
109 ConfigTag* tag = i->second;
110 std::string name = tag->getString("name");
112 throw CoreException("<class:name> missing from tag at " + tag->getTagLocation());
113 if (operclass.find(name) != operclass.end())
114 throw CoreException("Duplicate class block with name " + name + " at " + tag->getTagLocation());
115 operclass[name] = tag;
117 tags = ConfTags("type");
118 for(ConfigIter i = tags.first; i != tags.second; ++i)
120 ConfigTag* tag = i->second;
121 std::string name = tag->getString("name");
123 throw CoreException("<type:name> is missing from tag at " + tag->getTagLocation());
124 if (OperTypes.find(name) != OperTypes.end())
125 throw CoreException("Duplicate type block with name " + name + " at " + tag->getTagLocation());
127 OperInfo* ifo = new OperInfo(name);
128 OperTypes[name] = ifo;
129 ifo->type_block = tag;
131 std::string classname;
132 irc::spacesepstream str(tag->getString("classes"));
133 while (str.GetToken(classname))
135 LocalIndex::iterator cls = operclass.find(classname);
136 if (cls == operclass.end())
137 throw CoreException("Oper type " + name + " has missing class " + classname);
138 ifo->class_blocks.push_back(cls->second);
142 tags = ConfTags("oper");
143 for(ConfigIter i = tags.first; i != tags.second; ++i)
145 ConfigTag* tag = i->second;
147 std::string name = tag->getString("name");
149 throw CoreException("<oper:name> missing from tag at " + tag->getTagLocation());
151 std::string type = tag->getString("type");
152 OperIndex::iterator tblk = OperTypes.find(type);
153 if (tblk == OperTypes.end())
154 throw CoreException("Oper block " + name + " has missing type " + type);
155 if (oper_blocks.find(name) != oper_blocks.end())
156 throw CoreException("Duplicate oper block with name " + name + " at " + tag->getTagLocation());
158 OperInfo* ifo = new OperInfo(type);
159 ifo->oper_block = tag;
160 ifo->type_block = tblk->second->type_block;
161 ifo->class_blocks.assign(tblk->second->class_blocks.begin(), tblk->second->class_blocks.end());
162 oper_blocks[name] = ifo;
166 void ServerConfig::CrossCheckConnectBlocks(ServerConfig* current)
168 typedef std::map<std::string, ConnectClass*> ClassMap;
169 ClassMap oldBlocksByMask;
172 for(ClassVector::iterator i = current->Classes.begin(); i != current->Classes.end(); ++i)
174 ConnectClass* c = *i;
175 if (c->name.compare(0, 8, "unnamed-", 8))
177 oldBlocksByMask["n" + c->name] = c;
179 else if (c->type == CC_ALLOW || c->type == CC_DENY)
181 std::string typeMask = (c->type == CC_ALLOW) ? "a" : "d";
183 oldBlocksByMask[typeMask] = c;
188 size_t blk_count = config_data.count("connect");
191 // No connect blocks found; make a trivial default block
193 ConfigTag* tag = ConfigTag::create("connect", "<auto>", 0, items);
194 (*items)["allow"] = "*";
195 config_data.insert(std::make_pair("connect", tag));
199 Classes.resize(blk_count);
200 std::map<std::string, size_t> names;
202 bool try_again = true;
203 for(size_t tries = 0; try_again; tries++)
206 ConfigTagList tags = ConfTags("connect");
208 for(ConfigIter it = tags.first; it != tags.second; ++it, ++i)
210 ConfigTag* tag = it->second;
214 ConnectClass* parent = NULL;
215 std::string parentName = tag->getString("parent");
216 if (!parentName.empty())
218 std::map<std::string, size_t>::const_iterator parentIter = names.find(parentName);
219 if (parentIter == names.end())
222 // couldn't find parent this time. If it's the last time, we'll never find it.
223 if (tries >= blk_count)
224 throw CoreException("Could not find parent connect class \"" + parentName + "\" for connect block at " + tag->getTagLocation());
227 parent = Classes[parentIter->second];
230 std::string name = tag->getString("name");
231 std::string mask, typeMask;
234 if (tag->readString("allow", mask, false))
237 typeMask = 'a' + mask;
239 else if (tag->readString("deny", mask, false))
242 typeMask = 'd' + mask;
244 else if (!name.empty())
248 typeMask = 'n' + mask;
252 throw CoreException("Connect class must have allow, deny, or name specified at " + tag->getTagLocation());
257 name = "unnamed-" + ConvToStr(i);
261 typeMask = 'n' + name;
264 if (names.find(name) != names.end())
265 throw CoreException("Two connect classes with name \"" + name + "\" defined!");
268 ConnectClass* me = parent ?
269 new ConnectClass(tag, type, mask, *parent) :
270 new ConnectClass(tag, type, mask);
274 me->registration_timeout = tag->getDuration("timeout", me->registration_timeout);
275 me->pingtime = tag->getDuration("pingfreq", me->pingtime);
277 if (tag->readString("sendq", sendq))
279 // attempt to guess a good hard/soft sendq from a single value
280 unsigned long value = strtoul(sendq.c_str(), NULL, 10);
282 me->softsendqmax = value / 16;
284 me->softsendqmax = value;
285 me->hardsendqmax = value * 8;
287 me->softsendqmax = tag->getUInt("softsendq", me->softsendqmax);
288 me->hardsendqmax = tag->getUInt("hardsendq", me->hardsendqmax);
289 me->recvqmax = tag->getUInt("recvq", me->recvqmax);
290 me->penaltythreshold = tag->getUInt("threshold", me->penaltythreshold);
291 me->commandrate = tag->getUInt("commandrate", me->commandrate);
292 me->fakelag = tag->getBool("fakelag", me->fakelag);
293 me->maxlocal = tag->getUInt("localmax", me->maxlocal);
294 me->maxglobal = tag->getUInt("globalmax", me->maxglobal);
295 me->maxchans = tag->getUInt("maxchans", me->maxchans);
296 me->maxconnwarn = tag->getBool("maxconnwarn", me->maxconnwarn);
297 me->limit = tag->getUInt("limit", me->limit);
298 me->resolvehostnames = tag->getBool("resolvehostnames", me->resolvehostnames);
300 std::string ports = tag->getString("port");
303 irc::portparser portrange(ports, false);
304 while (int port = portrange.GetToken())
305 me->ports.insert(port);
308 ClassMap::iterator oldMask = oldBlocksByMask.find(typeMask);
309 if (oldMask != oldBlocksByMask.end())
311 ConnectClass* old = oldMask->second;
312 oldBlocksByMask.erase(oldMask);
322 static std::string GetServerName()
326 if (gethostname(hostname, sizeof(hostname)) == 0)
328 std::string name(hostname);
329 if (name.find('.') == std::string::npos)
332 if (name.length() <= ServerInstance->Config->Limits.MaxHost && InspIRCd::IsHost(name))
336 return "irc.example.com";
339 void ServerConfig::Fill()
341 ConfigTag* options = ConfValue("options");
342 ConfigTag* security = ConfValue("security");
343 ConfigTag* server = ConfValue("server");
346 ServerName = server->getString("name", GetServerName(), InspIRCd::IsHost);
348 sid = server->getString("id");
349 if (!sid.empty() && !InspIRCd::IsSID(sid))
350 throw CoreException(sid + " is not a valid server ID. A server ID must be 3 characters long, with the first character a digit and the next two characters a digit or letter.");
352 CaseMapping = options->getString("casemapping", "rfc1459");
353 if (CaseMapping == "ascii")
354 national_case_insensitive_map = ascii_case_insensitive_map;
355 else if (CaseMapping == "rfc1459")
356 national_case_insensitive_map = rfc_case_insensitive_map;
358 throw CoreException("<options:casemapping> must be set to 'ascii', or 'rfc1459'");
362 std::string name = server->getString("name");
363 if (!name.empty() && name != ServerName)
364 throw CoreException("You must restart to change the server name");
366 std::string nsid = server->getString("id");
367 if (!nsid.empty() && nsid != sid)
368 throw CoreException("You must restart to change the server id");
370 std::string casemapping = options->getString("casemapping");
371 // Ignore this value if CaseMapping is set to something the core doesn't provide (i.e., m_nationalchars).
372 if (!casemapping.empty() && casemapping != CaseMapping && (CaseMapping == "ascii" || CaseMapping == "rfc1459"))
373 throw CoreException("You must restart to change the server casemapping");
376 SoftLimit = ConfValue("performance")->getUInt("softlimit", (SocketEngine::GetMaxFds() > 0 ? SocketEngine::GetMaxFds() : LONG_MAX), 10);
377 CCOnConnect = ConfValue("performance")->getBool("clonesonconnect", true);
378 MaxConn = ConfValue("performance")->getUInt("somaxconn", SOMAXCONN);
379 TimeSkipWarn = ConfValue("performance")->getDuration("timeskipwarn", 2, 0, 30);
380 XLineMessage = options->getString("xlinemessage", options->getString("moronbanner", "You're banned!"));
381 ServerDesc = server->getString("description", "Configure Me");
382 Network = server->getString("network", "Network");
383 NetBufferSize = ConfValue("performance")->getInt("netbuffersize", 10240, 1024, 65534);
384 CustomVersion = security->getString("customversion");
385 HideBans = security->getBool("hidebans");
386 HideServer = security->getString("hideserver", security->getString("hidewhois"));
387 SyntaxHints = options->getBool("syntaxhints");
388 FullHostInTopic = options->getBool("hostintopic");
389 MaxTargets = security->getUInt("maxtargets", 20, 1, 31);
390 DefaultModes = options->getString("defaultmodes", "not");
391 PID = ConfValue("pid")->getString("file");
392 MaxChans = ConfValue("channels")->getUInt("users", 20);
393 OperMaxChans = ConfValue("channels")->getUInt("opers", 0);
394 c_ipv4_range = ConfValue("cidr")->getUInt("ipv4clone", 32, 1, 32);
395 c_ipv6_range = ConfValue("cidr")->getUInt("ipv6clone", 128, 1, 128);
396 Limits = ServerLimits(ConfValue("limits"));
397 Paths = ServerPaths(ConfValue("path"));
398 NoSnoticeStack = options->getBool("nosnoticestack", false);
400 std::string defbind = options->getString("defaultbind");
401 if (stdalgo::string::equalsci(defbind, "ipv4"))
403 WildcardIPv6 = false;
405 else if (stdalgo::string::equalsci(defbind, "ipv6"))
412 int socktest = socket(AF_INET6, SOCK_STREAM, 0);
414 WildcardIPv6 = false;
416 SocketEngine::Close(socktest);
419 ReadXLine(this, "badip", "ipmask", ServerInstance->XLines->GetFactory("Z"));
420 ReadXLine(this, "badnick", "nick", ServerInstance->XLines->GetFactory("Q"));
421 ReadXLine(this, "badhost", "host", ServerInstance->XLines->GetFactory("K"));
422 ReadXLine(this, "exception", "host", ServerInstance->XLines->GetFactory("E"));
424 const std::string restrictbannedusers = options->getString("restrictbannedusers", "yes");
425 if (stdalgo::string::equalsci(restrictbannedusers, "no"))
426 RestrictBannedUsers = ServerConfig::BUT_NORMAL;
427 else if (stdalgo::string::equalsci(restrictbannedusers, "silent"))
428 RestrictBannedUsers = ServerConfig::BUT_RESTRICT_SILENT;
429 else if (stdalgo::string::equalsci(restrictbannedusers, "yes"))
430 RestrictBannedUsers = ServerConfig::BUT_RESTRICT_NOTIFY;
432 throw CoreException(restrictbannedusers + " is an invalid <options:restrictbannedusers> value, at " + options->getTagLocation());
435 // WARNING: it is not safe to use most of the codebase in this function, as it
436 // will run in the config reader thread
437 void ServerConfig::Read()
439 /* Load and parse the config file, if there are any errors then explode */
441 ParseStack stack(this);
444 valid = stack.ParseFile(ServerInstance->ConfigFileName, 0);
446 catch (CoreException& err)
449 errstr << err.GetReason() << std::endl;
453 void ServerConfig::Apply(ServerConfig* old, const std::string &useruid)
459 * These values can only be set on boot. Keep their old values. Do it before we send messages so we actually have a servername.
461 this->CaseMapping = old->CaseMapping;
462 this->ServerName = old->ServerName;
463 this->sid = old->sid;
464 this->cmdline = old->cmdline;
467 /* The stuff in here may throw CoreException, be sure we're in a position to catch it. */
470 // Ensure the user has actually edited ther config.
471 ConfigTagList dietags = ConfTags("die");
472 if (dietags.first != dietags.second)
474 errstr << "Your configuration has not been edited correctly!" << std::endl;
475 for (ConfigIter iter = dietags.first; iter != dietags.second; ++iter)
477 ConfigTag* tag = iter->second;
478 const std::string reason = tag->getString("reason", "You left a <die> tag in your config", 1);
479 errstr << reason << " (at " << tag->getTagLocation() << ")" << std::endl;
485 // Handle special items
486 CrossCheckOperClassType();
487 CrossCheckConnectBlocks(old);
489 catch (CoreException &ce)
491 errstr << ce.GetReason() << std::endl;
494 // Check errors before dealing with failed binds, since continuing on failed bind is wanted in some circumstances.
495 valid = errstr.str().empty();
497 // write once here, to try it out and make sure its ok
499 ServerInstance->WritePID(this->PID, !old);
501 ConfigTagList binds = ConfTags("bind");
502 if (binds.first == binds.second)
503 errstr << "Possible configuration error: you have not defined any <bind> blocks." << std::endl
504 << "You will need to do this if you want clients to be able to connect!" << std::endl;
508 // On first run, ports are bound later on
510 ServerInstance->BindPorts(pl);
513 std::cout << "Warning! Some of your listener" << (pl.size() == 1 ? "s" : "") << " failed to bind:" << std::endl;
514 for (FailedPortList::const_iterator iter = pl.begin(); iter != pl.end(); ++iter)
516 const FailedPort& fp = *iter;
517 errstr << " " << fp.sa.str() << ": " << strerror(fp.error) << std::endl
518 << " " << "Created from <bind> tag at " << fp.tag->getTagLocation() << std::endl;
523 User* user = useruid.empty() ? NULL : ServerInstance->FindUUID(useruid);
527 ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "There were errors in your configuration file:");
531 while (errstr.good())
534 getline(errstr, line, '\n');
537 // On startup, print out to console (still attached at this point)
539 std::cout << line << std::endl;
540 // If a user is rehashing, tell them directly
542 user->WriteRemoteNotice(InspIRCd::Format("*** %s", line.c_str()));
544 ServerInstance->SNO->WriteGlobalSno('a', line);
548 errstr.str(std::string());
550 /* No old configuration -> initial boot, nothing more to do here */
555 ServerInstance->Exit(EXIT_STATUS_CONFIG);
562 // If there were errors processing configuration, don't touch modules.
569 user->WriteRemoteNotice("*** Successfully rehashed server.");
570 ServerInstance->SNO->WriteGlobalSno('a', "*** Successfully rehashed server.");
573 void ServerConfig::ApplyModules(User* user)
575 std::vector<std::string> added_modules;
576 ModuleManager::ModuleMap removed_modules = ServerInstance->Modules->GetModules();
578 ConfigTagList tags = ConfTags("module");
579 for(ConfigIter i = tags.first; i != tags.second; ++i)
581 ConfigTag* tag = i->second;
583 if (tag->readString("name", name))
585 name = ModuleManager::ExpandModName(name);
586 // if this module is already loaded, the erase will succeed, so we need do nothing
587 // otherwise, we need to add the module (which will be done later)
588 if (removed_modules.erase(name) == 0)
589 added_modules.push_back(name);
593 for (ModuleManager::ModuleMap::iterator i = removed_modules.begin(); i != removed_modules.end(); ++i)
595 const std::string& modname = i->first;
596 // Don't remove core_*.so, just remove m_*.so
597 if (InspIRCd::Match(modname, "core_*.so", ascii_case_insensitive_map))
599 if (ServerInstance->Modules->Unload(i->second))
601 ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH UNLOADED MODULE: %s", modname.c_str());
604 user->WriteNumeric(RPL_UNLOADEDMODULE, modname, InspIRCd::Format("Module %s successfully unloaded.", modname.c_str()));
606 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully unloaded.", modname.c_str());
611 user->WriteNumeric(ERR_CANTUNLOADMODULE, modname, InspIRCd::Format("Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str()));
613 ServerInstance->SNO->WriteGlobalSno('a', "Failed to unload module %s: %s", modname.c_str(), ServerInstance->Modules->LastError().c_str());
617 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
619 // Skip modules which are already loaded.
620 if (ServerInstance->Modules->Find(*adding))
623 if (ServerInstance->Modules->Load(*adding))
625 ServerInstance->SNO->WriteGlobalSno('a', "*** REHASH LOADED MODULE: %s",adding->c_str());
627 user->WriteNumeric(RPL_LOADEDMODULE, *adding, InspIRCd::Format("Module %s successfully loaded.", adding->c_str()));
629 ServerInstance->SNO->WriteGlobalSno('a', "Module %s successfully loaded.", adding->c_str());
634 user->WriteNumeric(ERR_CANTLOADMODULE, *adding, InspIRCd::Format("Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str()));
636 ServerInstance->SNO->WriteGlobalSno('a', "Failed to load module %s: %s", adding->c_str(), ServerInstance->Modules->LastError().c_str());
641 ConfigTag* ServerConfig::ConfValue(const std::string &tag)
643 ConfigTagList found = config_data.equal_range(tag);
644 if (found.first == found.second)
646 ConfigTag* rv = found.first->second;
648 if (found.first != found.second)
649 ServerInstance->Logs->Log("CONFIG", LOG_DEFAULT, "Multiple <" + tag + "> tags found; only first will be used "
650 "(first at " + rv->getTagLocation() + "; second at " + found.first->second->getTagLocation() + ")");
654 ConfigTagList ServerConfig::ConfTags(const std::string& tag)
656 return config_data.equal_range(tag);
659 std::string ServerConfig::Escape(const std::string& str, bool xml)
662 for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
667 escaped += xml ? """ : "\"";
670 escaped += xml ? "&" : "&";
673 escaped += xml ? "\\" : "\\\\";
683 void ConfigReaderThread::Run()
689 void ConfigReaderThread::Finish()
691 ServerConfig* old = ServerInstance->Config;
692 ServerInstance->Logs->Log("CONFIG", LOG_DEBUG, "Switching to new configuration...");
693 ServerInstance->Config = this->Config;
694 Config->Apply(old, TheUserUID);
699 * Apply the changed configuration from the rehash.
701 * XXX: The order of these is IMPORTANT, do not reorder them without testing
704 ServerInstance->Users.RehashCloneCounts();
705 ServerInstance->XLines->CheckELines();
706 ServerInstance->XLines->ApplyLines();
707 User* user = ServerInstance->FindUUID(TheUserUID);
709 ConfigStatus status(user);
710 const ModuleManager::ModuleMap& mods = ServerInstance->Modules->GetModules();
711 for (ModuleManager::ModuleMap::const_iterator i = mods.begin(); i != mods.end(); ++i)
715 ServerInstance->Logs->Log("MODULE", LOG_DEBUG, "Rehashing " + i->first);
716 i->second->ReadConfig(status);
718 catch (CoreException& modex)
720 ServerInstance->Logs->Log("MODULE", LOG_DEFAULT, "Exception caught: " + modex.GetReason());
722 user->WriteNotice(i->first + ": " + modex.GetReason());
726 // The description of this server may have changed - update it for WHOIS etc.
727 ServerInstance->FakeClient->server->description = Config->ServerDesc;
729 ServerInstance->ISupport.Build();
731 ServerInstance->Logs->CloseLogs();
732 ServerInstance->Logs->OpenFileLogs();
734 if (Config->RawLog && !old->RawLog)
735 ServerInstance->Users->ServerNoticeAll("*** Raw I/O logging is enabled on this server. All messages, passwords, and commands are being recorded.");
742 ServerInstance->Config = old;