]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/inspircd_io.cpp
Change error message so it's not 'out of range' when a tag doesn't exist
[user/henk/code/inspircd.git] / src / inspircd_io.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 using namespace std;
18
19 #include "inspircd_config.h"
20 #include <sys/time.h>
21 #include <sys/resource.h>
22 #include <sys/types.h>
23 #include <string>
24 #include <unistd.h>
25 #include <sstream>
26 #include <iostream>
27 #include <fstream>
28 #include "message.h"
29 #include "inspircd.h"
30 #include "inspircd_io.h"
31 #include "inspstring.h"
32 #include "helperfuncs.h"
33 #include "userprocess.h"
34 #include "xline.h"
35
36 extern ServerConfig *Config;
37 extern InspIRCd* ServerInstance;
38 extern int openSockfd[MAX_DESCRIPTORS];
39 extern time_t TIME;
40
41 extern int MODCOUNT;
42 extern std::vector<Module*> modules;
43 extern std::vector<ircd_module*> factory;
44
45 std::vector<std::string> old_module_names, new_module_names, added_modules, removed_modules;
46
47 ServerConfig::ServerConfig()
48 {
49         this->ClearStack();
50         *TempDir = *ServerName = *Network = *ServerDesc = *AdminName = '\0';
51         *HideWhoisServer = *AdminEmail = *AdminNick = *diepass = *restartpass = '\0';
52         *CustomVersion = *motd = *rules = *PrefixQuit = *DieValue = *DNSServer = '\0';
53         *OperOnlyStats = *ModPath = *MyExecutable = *DisabledCommands = *PID = '\0';
54         log_file = NULL;
55         OperSpyWhois = nofork = HideBans = HideSplits = false;
56         AllowHalfop = true;
57         dns_timeout = DieDelay = 5;
58         MaxTargets = 20;
59         NetBufferSize = 10240;
60         SoftLimit = MAXCLIENTS;
61         MaxConn = SOMAXCONN;
62         MaxWhoResults = 100;
63         debugging = 0;
64         LogLevel = DEFAULT;
65         maxbans.clear();
66 }
67
68 void ServerConfig::ClearStack()
69 {
70         include_stack.clear();
71 }
72
73 Module* ServerConfig::GetIOHook(int port)
74 {
75         std::map<int,Module*>::iterator x = IOHookModule.find(port);
76         return (x != IOHookModule.end() ? x->second : NULL);
77 }
78
79 bool ServerConfig::AddIOHook(int port, Module* iomod)
80 {
81         if (!GetIOHook(port))
82         {
83                 IOHookModule[port] = iomod;
84                 return true;
85         }
86         else
87         {
88                 ModuleException err("Port already hooked by another module");
89                 throw(err);
90                 return false;
91         }
92 }
93
94 bool ServerConfig::DelIOHook(int port)
95 {
96         std::map<int,Module*>::iterator x = IOHookModule.find(port);
97         if (x != IOHookModule.end())
98         {
99                 IOHookModule.erase(x);
100                 return true;
101         }
102         return false;
103 }
104
105 bool ServerConfig::CheckOnce(char* tag, bool bail, userrec* user)
106 {
107         int count = ConfValueEnum(Config->config_data, tag);
108         
109         if (count > 1)
110         {
111                 if (bail)
112                 {
113                         printf("There were errors in your configuration:\nYou have more than one <%s> tag, this is not permitted.\n",tag);
114                         Exit(0);
115                 }
116                 else
117                 {
118                         if (user)
119                         {
120                                 WriteServ(user->fd,"There were errors in your configuration:");
121                                 WriteServ(user->fd,"You have more than one <%s> tag, this is not permitted.\n",tag);
122                         }
123                         else
124                         {
125                                 WriteOpers("There were errors in the configuration file:");
126                                 WriteOpers("You have more than one <%s> tag, this is not permitted.\n",tag);
127                         }
128                 }
129                 return false;
130         }
131         if (count < 1)
132         {
133                 if (bail)
134                 {
135                         printf("There were errors in your configuration:\nYou have not defined a <%s> tag, this is required.\n",tag);
136                         Exit(0);
137                 }
138                 else
139                 {
140                         if (user)
141                         {
142                                 WriteServ(user->fd,"There were errors in your configuration:");
143                                 WriteServ(user->fd,"You have not defined a <%s> tag, this is required.",tag);
144                         }
145                         else
146                         {
147                                 WriteOpers("There were errors in the configuration file:");
148                                 WriteOpers("You have not defined a <%s> tag, this is required.",tag);
149                         }
150                 }
151                 return false;
152         }
153         return true;
154 }
155
156 bool NoValidation(const char* tag, const char* value, void* data)
157 {
158         log(DEBUG,"No validation for <%s:%s>",tag,value);
159         return true;
160 }
161
162 bool ValidateTempDir(const char* tag, const char* value, void* data)
163 {
164         char* x = (char*)data;
165         if (!*x)
166                 strlcpy(x,"/tmp",1024);
167         return true;
168 }
169  
170 bool ValidateMaxTargets(const char* tag, const char* value, void* data)
171 {
172         int* x = (int*)data;
173         if ((*x < 0) || (*x > 31))
174         {
175                 log(DEFAULT,"WARNING: <options:maxtargets> value is greater than 31 or less than 0, set to 20.");
176                 *x = 20;
177         }
178         return true;
179 }
180
181 bool ValidateSoftLimit(const char* tag, const char* value, void* data)
182 {
183         int* x = (int*)data;    
184         if ((*x < 1) || (*x > MAXCLIENTS))
185         {
186                 log(DEFAULT,"WARNING: <options:softlimit> value is greater than %d or less than 0, set to %d.",MAXCLIENTS,MAXCLIENTS);
187                 *x = MAXCLIENTS;
188         }
189         return true;
190 }
191
192 bool ValidateMaxConn(const char* tag, const char* value, void* data)
193 {
194         int* x = (int*)data;    
195         if (*x > SOMAXCONN)
196                 log(DEFAULT,"WARNING: <options:somaxconn> value may be higher than the system-defined SOMAXCONN value!");
197         if (!*x)
198                 *x = SOMAXCONN;
199         return true;
200 }
201
202 bool ValidateDnsTimeout(const char* tag, const char* value, void* data)
203 {
204         int* x = (int*)data;
205         if (!*x)
206                 *x = 5;
207         return true;
208 }
209
210 bool ValidateDnsServer(const char* tag, const char* value, void* data)
211 {
212         char* x = (char*)data;
213         if (!*x)
214         {
215                 // attempt to look up their nameserver from /etc/resolv.conf
216                 log(DEFAULT,"WARNING: <dns:server> not defined, attempting to find working server in /etc/resolv.conf...");
217                 ifstream resolv("/etc/resolv.conf");
218                 std::string nameserver;
219                 bool found_server = false;
220
221                 if (resolv.is_open())
222                 {
223                         while (resolv >> nameserver)
224                         {
225                                 if ((nameserver == "nameserver") && (!found_server))
226                                 {
227                                         resolv >> nameserver;
228                                         strlcpy(x,nameserver.c_str(),MAXBUF);
229                                         found_server = true;
230                                         log(DEFAULT,"<dns:server> set to '%s' as first resolver in /etc/resolv.conf.",nameserver.c_str());
231                                 }
232                         }
233
234                         if (!found_server)
235                         {
236                                 log(DEFAULT,"/etc/resolv.conf contains no viable nameserver entries! Defaulting to nameserver '127.0.0.1'!");
237                                 strlcpy(x,"127.0.0.1",MAXBUF);
238                         }
239                 }
240                 else
241                 {
242                         log(DEFAULT,"/etc/resolv.conf can't be opened! Defaulting to nameserver '127.0.0.1'!");
243                         strlcpy(x,"127.0.0.1",MAXBUF);
244                 }
245         }
246         return true;
247 }
248
249 bool ValidateModPath(const char* tag, const char* value, void* data)
250 {
251         char* x = (char*)data;  
252         if (!*x)
253                 strlcpy(x,MOD_PATH,MAXBUF);
254         return true;
255 }
256
257
258 bool ValidateServerName(const char* tag, const char* value, void* data)
259 {
260         char* x = (char*)data;
261         if (!strchr(x,'.'))
262         {
263                 log(DEFAULT,"WARNING: <server:name> '%s' is not a fully-qualified domain name. Changed to '%s%c'",x,x,'.');
264                 charlcat(x,'.',MAXBUF);
265         }
266         //strlower(x);
267         return true;
268 }
269
270 bool ValidateNetBufferSize(const char* tag, const char* value, void* data)
271 {
272         if ((!Config->NetBufferSize) || (Config->NetBufferSize > 65535) || (Config->NetBufferSize < 1024))
273         {
274                 log(DEFAULT,"No NetBufferSize specified or size out of range, setting to default of 10240.");
275                 Config->NetBufferSize = 10240;
276         }
277         return true;
278 }
279
280 bool ValidateMaxWho(const char* tag, const char* value, void* data)
281 {
282         if ((!Config->MaxWhoResults) || (Config->MaxWhoResults > 65535) || (Config->MaxWhoResults < 1))
283         {
284                 log(DEFAULT,"No MaxWhoResults specified or size out of range, setting to default of 128.");
285                 Config->MaxWhoResults = 128;
286         }
287         return true;
288 }
289
290 bool ValidateLogLevel(const char* tag, const char* value, void* data)
291 {
292         const char* dbg = (const char*)data;
293         Config->LogLevel = DEFAULT;
294         if (!strcmp(dbg,"debug"))
295         {
296                 Config->LogLevel = DEBUG;
297                 Config->debugging = 1;
298         }
299         else if (!strcmp(dbg,"verbose"))
300                 Config->LogLevel = VERBOSE;
301         else if (!strcmp(dbg,"default"))
302                 Config->LogLevel = DEFAULT;
303         else if (!strcmp(dbg,"sparse"))
304                 Config->LogLevel = SPARSE;
305         else if (!strcmp(dbg,"none"))
306                 Config->LogLevel = NONE;
307         return true;
308 }
309
310 bool ValidateMotd(const char* tag, const char* value, void* data)
311 {
312         readfile(Config->MOTD,Config->motd);
313         return true;
314 }
315
316 bool ValidateRules(const char* tag, const char* value, void* data)
317 {
318         readfile(Config->RULES,Config->rules);
319         return true;
320 }
321
322 /* Callback called before processing the first <connect> tag
323  */
324 bool InitConnect(const char* tag)
325 {
326         log(DEFAULT,"Reading connect classes...");
327         Config->Classes.clear();
328         return true;
329 }
330
331 /* Callback called to process a single <connect> tag
332  */
333 bool DoConnect(const char* tag, char** entries, void** values, int* types)
334 {
335         ConnectClass c;
336         char* allow = (char*)values[0]; /* Yeah, there are a lot of values. Live with it. */
337         char* deny = (char*)values[1];
338         char* password = (char*)values[2];
339         int* timeout = (int*)values[3];
340         int* pingfreq = (int*)values[4];
341         int* flood = (int*)values[5];
342         int* threshold = (int*)values[6];
343         int* sendq = (int*)values[7];
344         int* recvq = (int*)values[8];
345         int* localmax = (int*)values[9];
346         int* globalmax = (int*)values[10];
347
348         if (*allow)
349         {
350                 c.host = allow;
351                 c.type = CC_ALLOW;
352                 c.pass = password;
353                 c.registration_timeout = *timeout;
354                 c.pingtime = *pingfreq;
355                 c.flood = *flood;
356                 c.threshold = *threshold;
357                 c.sendqmax = *sendq;
358                 c.recvqmax = *recvq;
359                 c.maxlocal = *localmax;
360                 c.maxglobal = *globalmax;
361
362
363                 if (c.maxlocal == 0)
364                         c.maxlocal = 3;
365                 if (c.maxglobal == 0)
366                         c.maxglobal = 3;
367                 if (c.threshold == 0)
368                 {
369                         c.threshold = 1;
370                         c.flood = 999;
371                         log(DEFAULT,"Warning: Connect allow line '%s' has no flood/threshold settings. Setting this tag to 999 lines in 1 second.",c.host.c_str());
372                 }
373                 if (c.sendqmax == 0)
374                         c.sendqmax = 262114;
375                 if (c.recvqmax == 0)
376                         c.recvqmax = 4096;
377                 if (c.registration_timeout == 0)
378                         c.registration_timeout = 90;
379                 if (c.pingtime == 0)
380                         c.pingtime = 120;
381                 Config->Classes.push_back(c);
382         }
383         else
384         {
385                 c.host = deny;
386                 c.type = CC_DENY;
387                 Config->Classes.push_back(c);
388                 log(DEBUG,"Read connect class type DENY, host=%s",deny);
389         }
390
391         return true;
392 }
393
394 /* Callback called when there are no more <connect> tags
395  */
396 bool DoneConnect(const char* tag)
397 {
398         log(DEBUG,"DoneConnect called for tag: %s",tag);
399         return true;
400 }
401
402 /* Callback called before processing the first <uline> tag
403  */
404 bool InitULine(const char* tag)
405 {
406         Config->ulines.clear();
407         return true;
408 }
409
410 /* Callback called to process a single <uline> tag
411  */
412 bool DoULine(const char* tag, char** entries, void** values, int* types)
413 {
414         char* server = (char*)values[0];
415         log(DEBUG,"Read ULINE '%s'",server);
416         Config->ulines.push_back(server);
417         return true;
418 }
419
420 /* Callback called when there are no more <uline> tags
421  */
422 bool DoneULine(const char* tag)
423 {
424         return true;
425 }
426
427 /* Callback called before processing the first <module> tag
428  */
429 bool InitModule(const char* tag)
430 {
431         old_module_names.clear();
432         new_module_names.clear();
433         added_modules.clear();
434         removed_modules.clear();
435         for (std::vector<std::string>::iterator t = Config->module_names.begin(); t != Config->module_names.end(); t++)
436         {
437                 old_module_names.push_back(*t);
438         }
439         return true;
440 }
441
442 /* Callback called to process a single <module> tag
443  */
444 bool DoModule(const char* tag, char** entries, void** values, int* types)
445 {
446         char* modname = (char*)values[0];
447         new_module_names.push_back(modname);
448         return true;
449 }
450
451 /* Callback called when there are no more <module> tags
452  */
453 bool DoneModule(const char* tag)
454 {
455         // now create a list of new modules that are due to be loaded
456         // and a seperate list of modules which are due to be unloaded
457         for (std::vector<std::string>::iterator _new = new_module_names.begin(); _new != new_module_names.end(); _new++)
458         {
459                 bool added = true;
460
461                 for (std::vector<std::string>::iterator old = old_module_names.begin(); old != old_module_names.end(); old++)
462                 {
463                         if (*old == *_new)
464                                 added = false;
465                 }
466
467                 if (added)
468                         added_modules.push_back(*_new);
469         }
470
471         for (std::vector<std::string>::iterator oldm = old_module_names.begin(); oldm != old_module_names.end(); oldm++)
472         {
473                 bool removed = true;
474                 for (std::vector<std::string>::iterator newm = new_module_names.begin(); newm != new_module_names.end(); newm++)
475                 {
476                         if (*newm == *oldm)
477                                 removed = false;
478                 }
479
480                 if (removed)
481                         removed_modules.push_back(*oldm);
482         }
483         return true;
484 }
485
486 /* Callback called before processing the first <banlist> tag
487  */
488 bool InitMaxBans(const char* tag)
489 {
490         Config->maxbans.clear();
491         return true;
492 }
493
494 /* Callback called to process a single <banlist> tag
495  */
496 bool DoMaxBans(const char* tag, char** entries, void** values, int* types)
497 {
498         char* channel = (char*)values[0];
499         int* limit = (int*)values[1];
500         Config->maxbans[channel] = *limit;
501         return true;
502 }
503
504 /* Callback called when there are no more <banlist> tags.
505  */
506 bool DoneMaxBans(const char* tag)
507 {
508         return true;
509 }
510
511 void ServerConfig::Read(bool bail, userrec* user)
512 {
513         char debug[MAXBUF];             /* Temporary buffer for debugging value */
514         char* data[12];                 /* Temporary buffers for reading multiple occurance tags into */
515         void* ptr[12];                  /* Temporary pointers for passing to callbacks */
516         int r_i[12];                    /* Temporary array for casting */
517         int rem = 0, add = 0;           /* Number of modules added, number of modules removed */
518         std::ostringstream errstr;      /* String stream containing the error output */
519
520         /* These tags MUST occur and must ONLY occur once in the config file */
521         static char* Once[] = { "server", "admin", "files", "power", "options", "pid", NULL };
522
523         /* These tags can occur ONCE or not at all */
524         static InitialConfig Values[] = {
525                 {"options",             "softlimit",                    &this->SoftLimit,               DT_INTEGER, ValidateSoftLimit},
526                 {"options",             "somaxconn",                    &this->MaxConn,                 DT_INTEGER, ValidateMaxConn},
527                 {"server",              "name",                         &this->ServerName,              DT_CHARPTR, ValidateServerName},
528                 {"server",              "description",                  &this->ServerDesc,              DT_CHARPTR, NoValidation},
529                 {"server",              "network",                      &this->Network,                 DT_CHARPTR, NoValidation},
530                 {"admin",               "name",                         &this->AdminName,               DT_CHARPTR, NoValidation},
531                 {"admin",               "email",                        &this->AdminEmail,              DT_CHARPTR, NoValidation},
532                 {"admin",               "nick",                         &this->AdminNick,               DT_CHARPTR, NoValidation},
533                 {"files",               "motd",                         &this->motd,                    DT_CHARPTR, ValidateMotd},
534                 {"files",               "rules",                        &this->rules,                   DT_CHARPTR, ValidateRules},
535                 {"power",               "diepass",                      &this->diepass,                 DT_CHARPTR, NoValidation},      
536                 {"power",               "pauseval",                     &this->DieDelay,                DT_INTEGER, NoValidation},
537                 {"power",               "restartpass",                  &this->restartpass,             DT_CHARPTR, NoValidation},
538                 {"options",             "prefixquit",                   &this->PrefixQuit,              DT_CHARPTR, NoValidation},
539                 {"die",                 "value",                        &this->DieValue,                DT_CHARPTR, NoValidation},
540                 {"options",             "loglevel",                     &debug,                         DT_CHARPTR, ValidateLogLevel},
541                 {"options",             "netbuffersize",                &this->NetBufferSize,           DT_INTEGER, ValidateNetBufferSize},
542                 {"options",             "maxwho",                       &this->MaxWhoResults,           DT_INTEGER, ValidateMaxWho},
543                 {"options",             "allowhalfop",                  &this->AllowHalfop,             DT_BOOLEAN, NoValidation},
544                 {"dns",                 "server",                       &this->DNSServer,               DT_CHARPTR, ValidateDnsServer},
545                 {"dns",                 "timeout",                      &this->dns_timeout,             DT_INTEGER, ValidateDnsTimeout},
546                 {"options",             "moduledir",                    &this->ModPath,                 DT_CHARPTR, ValidateModPath},
547                 {"disabled",            "commands",                     &this->DisabledCommands,        DT_CHARPTR, NoValidation},
548                 {"options",             "operonlystats",                &this->OperOnlyStats,           DT_CHARPTR, NoValidation},
549                 {"options",             "customversion",                &this->CustomVersion,           DT_CHARPTR, NoValidation},
550                 {"options",             "hidesplits",                   &this->HideSplits,              DT_BOOLEAN, NoValidation},
551                 {"options",             "hidebans",                     &this->HideBans,                DT_BOOLEAN, NoValidation},
552                 {"options",             "hidewhois",                    &this->HideWhoisServer,         DT_CHARPTR, NoValidation},
553                 {"options",             "operspywhois",                 &this->OperSpyWhois,            DT_BOOLEAN, NoValidation},
554                 {"options",             "tempdir",                      &this->TempDir,                 DT_CHARPTR, ValidateTempDir},
555                 {"pid",                 "file",                         &this->PID,                     DT_CHARPTR, NoValidation},
556                 {NULL}
557         };
558
559         /* These tags can occur multiple times, and therefore they have special code to read them
560          * which is different to the code for reading the singular tags listed above.
561          */
562         static MultiConfig MultiValues[] = {
563
564                 {"connect",
565                                 {"allow",       "deny",         "password",     "timeout",      "pingfreq",     "flood",
566                                 "threshold",    "sendq",        "recvq",        "localmax",     "globalmax",    NULL},
567                                 {DT_CHARPTR,    DT_CHARPTR,     DT_CHARPTR,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER,
568                                  DT_INTEGER,    DT_INTEGER,     DT_INTEGER,     DT_INTEGER,     DT_INTEGER},
569                                 InitConnect, DoConnect, DoneConnect},
570
571                 {"uline",
572                                 {"server",      NULL},
573                                 {DT_CHARPTR},
574                                 InitULine,DoULine,DoneULine},
575
576                 {"banlist",
577                                 {"chan",        "limit",        NULL},
578                                 {DT_CHARPTR,    DT_INTEGER},
579                                 InitMaxBans, DoMaxBans, DoneMaxBans},
580
581                 {"module",
582                                 {"name",        NULL},
583                                 {DT_CHARPTR},
584                                 InitModule, DoModule, DoneModule},
585
586                 {"badip",
587                                 {"reason",      "ipmask",       NULL},
588                                 {DT_CHARPTR,    DT_CHARPTR},
589                                 InitXLine, DoZLine, DoneXLine},
590
591                 {"badnick",
592                                 {"reason",      "nick",         NULL},
593                                 {DT_CHARPTR,    DT_CHARPTR},
594                                 InitXLine, DoQLine, DoneXLine},
595
596                 {"badhost",
597                                 {"reason",      "host",         NULL},
598                                 {DT_CHARPTR,    DT_CHARPTR},
599                                 InitXLine, DoKLine, DoneXLine},
600
601                 {"exception",
602                                 {"reason",      "host",         NULL},
603                                 {DT_CHARPTR,    DT_CHARPTR},
604                                 InitXLine, DoELine, DoneXLine},
605
606                 {"type",
607                                 {"name",        "classes",      NULL},
608                                 {DT_CHARPTR,    DT_CHARPTR},
609                                 InitTypes, DoType, DoneClassesAndTypes},
610
611                 {"class",
612                                 {"name",        "commands",     NULL},
613                                 {DT_CHARPTR,    DT_CHARPTR},
614                                 InitClasses, DoClass, DoneClassesAndTypes},
615
616                 {NULL}
617         };
618
619         include_stack.clear();
620
621         /* Load and parse the config file, if there are any errors then explode */
622         
623         /* Make a copy here so if it fails then we can carry on running with an unaffected config */
624         ConfigDataHash newconfig;
625         
626         if (this->LoadConf(newconfig, CONFIG_FILE, errstr))
627         {
628                 /* If we succeeded, set the ircd config to the new one */
629                 Config->config_data = newconfig;
630                 
631 /*              int c = 1;
632                 std::string last;
633                 
634                 for(ConfigDataHash::const_iterator i = this->config_data.begin(); i != this->config_data.end(); i++)
635                 {
636                         c = (i->first != last) ? 1 : c+1;
637                         last = i->first;
638                         
639                         std::cout << "[" << i->first << " " << c << "/" << this->config_data.count(i->first) << "]" << std::endl;
640                         
641                         for(KeyValList::const_iterator j = i->second.begin(); j != i->second.end(); j++)
642                                 std::cout << "\t" << j->first << " = " << j->second << std::endl;
643                         
644                         std::cout << "[/" << i->first << " " << c << "/" << this->config_data.count(i->first) << "]" << std::endl;
645                 }
646  */     }
647         else
648         {
649                 log(DEFAULT, "There were errors in your configuration:\n%s", errstr.str().c_str());
650
651                 if (bail)
652                 {
653                         /* Unneeded because of the log() aboive? */
654                         printf("There were errors in your configuration:\n%s",errstr.str().c_str());
655                         Exit(0);
656                 }
657                 else
658                 {
659                         std::string errors = errstr.str();
660                         std::string::size_type start;
661                         unsigned int prefixlen;
662                         
663                         start = 0;
664                         /* ":Config->ServerName NOTICE user->nick :" */
665                         prefixlen = strlen(Config->ServerName) + strlen(user->nick) + 11;
666         
667                         if (user)
668                         {
669                                 WriteServ(user->fd,"NOTICE %s :There were errors in the configuration file:",user->nick);
670                                 
671                                 while(start < errors.length())
672                                 {
673                                         WriteServ(user->fd, "NOTICE %s :%s",user->nick, errors.substr(start, 510 - prefixlen).c_str());
674                                         start += 510 - prefixlen;
675                                 }
676                         }
677                         else
678                         {
679                                 WriteOpers("There were errors in the configuration file:");
680                                 
681                                 while(start < errors.length())
682                                 {
683                                         WriteOpers(errors.substr(start, 360).c_str());
684                                         start += 360;
685                                 }
686                         }
687
688                         return;
689                 }
690         }
691
692         /* Check we dont have more than one of singular tags, or any of them missing
693          */
694         for (int Index = 0; Once[Index]; Index++)
695                 if (!CheckOnce(Once[Index],bail,user))
696                         return;
697
698         /* Read the values of all the tags which occur once or not at all, and call their callbacks.
699          */
700         for (int Index = 0; Values[Index].tag; Index++)
701         {
702                 int* val_i = (int*) Values[Index].val;
703                 char* val_c = (char*) Values[Index].val;
704
705                 switch (Values[Index].datatype)
706                 {
707                         case DT_CHARPTR:
708                                 /* Assuming MAXBUF here, potentially unsafe */
709                                 ConfValue(this->config_data, Values[Index].tag, Values[Index].value, 0, val_c, MAXBUF);
710                         break;
711
712                         case DT_INTEGER:
713                                 ConfValueInteger(this->config_data, Values[Index].tag, Values[Index].value, 0, *val_i);
714                         break;
715
716                         case DT_BOOLEAN:
717                                 *val_i = ConfValueBool(this->config_data, Values[Index].tag, Values[Index].value, 0);
718                         break;
719
720                         case DT_NOTHING:
721                         break;
722                 }
723
724                 Values[Index].validation_function(Values[Index].tag, Values[Index].value, Values[Index].val);
725         }
726
727         /* Claim memory for use when reading multiple tags
728          */
729         for (int n = 0; n < 12; n++)
730                 data[n] = new char[MAXBUF];
731
732         /* Read the multiple-tag items (class tags, connect tags, etc)
733          * and call the callbacks associated with them. We have three
734          * callbacks for these, a 'start', 'item' and 'end' callback.
735          */
736         
737         /* XXX - Make this use ConfValueInteger and so on */
738         for (int Index = 0; MultiValues[Index].tag; Index++)
739         {
740                 MultiValues[Index].init_function(MultiValues[Index].tag);
741
742                 int number_of_tags = ConfValueEnum(this->config_data, MultiValues[Index].tag);
743
744                 for (int tagnum = 0; tagnum < number_of_tags; tagnum++)
745                 {
746                         for (int valuenum = 0; MultiValues[Index].items[valuenum]; valuenum++)
747                         {
748                                 ConfValue(this->config_data, MultiValues[Index].tag, MultiValues[Index].items[valuenum], tagnum, data[valuenum], MAXBUF);
749
750                                 switch (MultiValues[Index].datatype[valuenum])
751                                 {
752                                         case DT_CHARPTR:
753                                                 ptr[valuenum] = data[valuenum];
754                                         break;
755                                         case DT_INTEGER:
756                                                 r_i[valuenum] = atoi(data[valuenum]);
757                                                 ptr[valuenum] = &r_i[valuenum];
758                                         break;
759                                         case DT_BOOLEAN:
760                                                 r_i[valuenum] = ((*data[valuenum] == tolower('y')) || (*data[valuenum] == tolower('t')) || (*data[valuenum] == '1'));
761                                                 ptr[valuenum] = &r_i[valuenum];
762                                         break;
763                                         default:
764                                         break;
765                                 }
766                         }
767                         MultiValues[Index].validation_function(MultiValues[Index].tag, (char**)MultiValues[Index].items, ptr, MultiValues[Index].datatype);
768                 }
769
770                 MultiValues[Index].finish_function(MultiValues[Index].tag);
771         }
772
773         /* Free any memory we claimed
774          */
775         for (int n = 0; n < 12; n++)
776                 delete[] data[n];
777
778         // write once here, to try it out and make sure its ok
779         WritePID(Config->PID);
780
781         log(DEFAULT,"Done reading configuration file, InspIRCd is now starting.");
782
783         /* If we're rehashing, let's load any new modules, and unload old ones
784          */
785         if (!bail)
786         {
787                 ServerInstance->stats->BoundPortCount = BindPorts(false);
788
789                 if (!removed_modules.empty())
790                         for (std::vector<std::string>::iterator removing = removed_modules.begin(); removing != removed_modules.end(); removing++)
791                         {
792                                 if (ServerInstance->UnloadModule(removing->c_str()))
793                                 {
794                                         WriteOpers("*** REHASH UNLOADED MODULE: %s",removing->c_str());
795
796                                         if (user)
797                                                 WriteServ(user->fd,"973 %s %s :Module %s successfully unloaded.",user->nick, removing->c_str(), removing->c_str());
798
799                                         rem++;
800                                 }
801                                 else
802                                 {
803                                         if (user)
804                                                 WriteServ(user->fd,"972 %s %s :Failed to unload module %s: %s",user->nick, removing->c_str(), removing->c_str(), ServerInstance->ModuleError());
805                                 }
806                         }
807
808                 if (!added_modules.empty())
809                 for (std::vector<std::string>::iterator adding = added_modules.begin(); adding != added_modules.end(); adding++)
810                 {
811                         if (ServerInstance->LoadModule(adding->c_str()))
812                         {
813                                 WriteOpers("*** REHASH LOADED MODULE: %s",adding->c_str());
814
815                                 if (user)
816                                         WriteServ(user->fd,"975 %s %s :Module %s successfully loaded.",user->nick, adding->c_str(), adding->c_str());
817
818                                 add++;
819                         }
820                         else
821                         {
822                                 if (user)
823                                         WriteServ(user->fd,"974 %s %s :Failed to load module %s: %s",user->nick, adding->c_str(), adding->c_str(), ServerInstance->ModuleError());
824                         }
825                 }
826
827                 log(DEFAULT,"Successfully unloaded %lu of %lu modules and loaded %lu of %lu modules.",(unsigned long)rem,(unsigned long)removed_modules.size(),(unsigned long)add,(unsigned long)added_modules.size());
828         }
829 }
830
831 void Exit(int status)
832 {
833         if (Config->log_file)
834                 fclose(Config->log_file);
835         send_error("Server shutdown.");
836         exit (status);
837 }
838
839 void Killed(int status)
840 {
841         if (Config->log_file)
842                 fclose(Config->log_file);
843         send_error("Server terminated.");
844         exit(status);
845 }
846
847 char* CleanFilename(char* name)
848 {
849         char* p = name + strlen(name);
850         while ((p != name) && (*p != '/')) p--;
851         return (p != name ? ++p : p);
852 }
853
854
855 void Rehash(int status)
856 {
857         WriteOpers("Rehashing config file %s due to SIGHUP",CleanFilename(CONFIG_FILE));
858         fclose(Config->log_file);
859         OpenLog(NULL,0);
860         Config->Read(false,NULL);
861         FOREACH_MOD(I_OnRehash,OnRehash(""));
862 }
863
864
865
866 void Start()
867 {
868         printf("\033[1;32mInspire Internet Relay Chat Server, compiled %s at %s\n",__DATE__,__TIME__);
869         printf("(C) ChatSpike Development team.\033[0m\n\n");
870         printf("Developers:\t\t\033[1;32mBrain, FrostyCoolSlug, w00t, Om\033[0m\n");
871         printf("Others:\t\t\t\033[1;32mSee /INFO Output\033[0m\n");
872         printf("Name concept:\t\t\033[1;32mLord_Zathras\033[0m\n\n");
873 }
874
875 void WritePID(const std::string &filename)
876 {
877         ofstream outfile(filename.c_str());
878         if (outfile.is_open())
879         {
880                 outfile << getpid();
881                 outfile.close();
882         }
883         else
884         {
885                 printf("Failed to write PID-file '%s', exiting.\n",filename.c_str());
886                 log(DEFAULT,"Failed to write PID-file '%s', exiting.",filename.c_str());
887                 Exit(0);
888         }
889 }
890
891 void SetSignals()
892 {
893         signal (SIGALRM, SIG_IGN);
894         signal (SIGHUP, Rehash);
895         signal (SIGPIPE, SIG_IGN);
896         signal (SIGTERM, Exit);
897         signal (SIGSEGV, Error);
898 }
899
900
901 bool DaemonSeed()
902 {
903         int childpid;
904         if ((childpid = fork ()) < 0)
905                 return (ERROR);
906         else if (childpid > 0)
907         {
908                 /* We wait a few seconds here, so that the shell prompt doesnt come back over the output */
909                 sleep(6);
910                 exit (0);
911         }
912         setsid ();
913         umask (007);
914         printf("InspIRCd Process ID: \033[1;32m%lu\033[0m\n",(unsigned long)getpid());
915
916         rlimit rl;
917         if (getrlimit(RLIMIT_CORE, &rl) == -1)
918         {
919                 log(DEFAULT,"Failed to getrlimit()!");
920                 return false;
921         }
922         else
923         {
924                 rl.rlim_cur = rl.rlim_max;
925                 if (setrlimit(RLIMIT_CORE, &rl) == -1)
926                         log(DEFAULT,"setrlimit() failed, cannot increase coredump size.");
927         }
928   
929         return true;
930 }
931
932
933 /* Make Sure Modules Are Avaliable!
934  * (BugFix By Craig.. See? I do work! :p)
935  * Modified by brain, requires const char*
936  * to work with other API functions
937  */
938
939 /* XXX - Needed? */
940 bool FileExists (const char* file)
941 {
942         FILE *input;
943         if ((input = fopen (file, "r")) == NULL)
944         {
945                 return(false);
946         }
947         else
948         {
949                 fclose (input);
950                 return(true);
951         }
952 }
953
954 bool ServerConfig::LoadConf(ConfigDataHash &target, const char* filename, std::ostringstream &errorstream)
955 {
956         std::ifstream conf(filename);
957         std::string line;
958         char ch;
959         long linenumber;
960         bool in_tag;
961         bool in_quote;
962         bool in_comment;
963         
964         linenumber = 1;
965         in_tag = false;
966         in_quote = false;
967         in_comment = false;
968         
969         /* Check if the file open failed first */
970         if (!conf)
971         {
972                 errorstream << "LoadConf: Couldn't open config file: " << filename << std::endl;
973                 return false;
974         }
975         
976         /* Fix the chmod of the file to restrict it to the current user and group */
977         chmod(filename,0600);
978         
979         for (unsigned int t = 0; t < include_stack.size(); t++)
980         {
981                 if (std::string(filename) == include_stack[t])
982                 {
983                         errorstream << "File " << filename << " is included recursively (looped inclusion)." << std::endl;
984                         return false;
985                 }
986         }
987         
988         /* It's not already included, add it to the list of files we've loaded */
989         include_stack.push_back(filename);
990         
991         /* Start reading characters... */       
992         while(conf.get(ch))
993         {
994                 /*
995                  * Here we try and get individual tags on separate lines,
996                  * this would be so easy if we just made people format
997                  * their config files like that, but they don't so...
998                  * We check for a '<' and then know the line is over when
999                  * we get a '>' not inside quotes. If we find two '<' and
1000                  * no '>' then die with an error.
1001                  */
1002                 
1003                 if((ch == '#') && !in_quote)
1004                         in_comment = true;
1005                 
1006                 if(((ch == '\n') || (ch == '\r')) && in_quote)
1007                 {
1008                         errorstream << "Got a newline within a quoted section, this is probably a typo: " << filename << ":" << linenumber << std::endl;
1009                         return false;
1010                 }
1011                 
1012                 switch(ch)
1013                 {
1014                         case '\n':
1015                                 linenumber++;
1016                         case '\r':
1017                                 in_comment = false;
1018                         case '\0':
1019                                 continue;
1020                         case '\t':
1021                                 ch = ' ';
1022                 }
1023                 
1024                 if(in_comment)
1025                         continue;
1026                 
1027                 line += ch;
1028                 
1029                 if(ch == '<')
1030                 {
1031                         if(in_tag)
1032                         {
1033                                 if(!in_quote)
1034                                 {
1035                                         errorstream << "Got another opening < when the first one wasn't closed: " << filename << ":" << linenumber << std::endl;
1036                                         return false;
1037                                 }
1038                         }
1039                         else
1040                         {
1041                                 if(in_quote)
1042                                 {
1043                                         errorstream << "We're in a quote but outside a tag, interesting. " << filename << ":" << linenumber << std::endl;
1044                                         return false;
1045                                 }
1046                                 else
1047                                 {
1048                                         // errorstream << "Opening new config tag on line " << linenumber << std::endl;
1049                                         in_tag = true;
1050                                 }
1051                         }
1052                 }
1053                 else if(ch == '"')
1054                 {
1055                         if(in_tag)
1056                         {
1057                                 if(in_quote)
1058                                 {
1059                                         // errorstream << "Closing quote in config tag on line " << linenumber << std::endl;
1060                                         in_quote = false;
1061                                 }
1062                                 else
1063                                 {
1064                                         // errorstream << "Opening quote in config tag on line " << linenumber << std::endl;
1065                                         in_quote = true;
1066                                 }
1067                         }
1068                         else
1069                         {
1070                                 if(in_quote)
1071                                 {
1072                                         errorstream << "Found a (closing) \" outside a tag: " << filename << ":" << linenumber << std::endl;
1073                                 }
1074                                 else
1075                                 {
1076                                         errorstream << "Found a (opening) \" outside a tag: " << filename << ":" << linenumber << std::endl;
1077                                 }
1078                         }
1079                 }
1080                 else if(ch == '>')
1081                 {
1082                         if(!in_quote)
1083                         {
1084                                 if(in_tag)
1085                                 {
1086                                         // errorstream << "Closing config tag on line " << linenumber << std::endl;
1087                                         in_tag = false;
1088
1089                                         /*
1090                                          * If this finds an <include> then ParseLine can simply call
1091                                          * LoadConf() and load the included config into the same ConfigDataHash
1092                                          */
1093                                         
1094                                         if(!this->ParseLine(target, line, linenumber, errorstream))
1095                                                 return false;
1096                                         
1097                                         line.clear();
1098                                 }
1099                                 else
1100                                 {
1101                                         errorstream << "Got a closing > when we weren't inside a tag: " << filename << ":" << linenumber << std::endl;
1102                                         return false;
1103                                 }
1104                         }
1105                 }
1106         }
1107         
1108         return true;
1109 }
1110
1111 bool ServerConfig::LoadConf(ConfigDataHash &target, const std::string &filename, std::ostringstream &errorstream)
1112 {
1113         return this->LoadConf(target, filename.c_str(), errorstream);
1114 }
1115
1116 bool ServerConfig::ParseLine(ConfigDataHash &target, std::string &line, long linenumber, std::ostringstream &errorstream)
1117 {
1118         std::string tagname;
1119         std::string current_key;
1120         std::string current_value;
1121         KeyValList results;
1122         bool got_name;
1123         bool got_key;
1124         bool in_quote;
1125         
1126         got_name = got_key = in_quote = false;
1127         
1128         // std::cout << "ParseLine(data, '" << line << "', " << linenumber << ", stream)" << std::endl;
1129         
1130         for(std::string::iterator c = line.begin(); c != line.end(); c++)
1131         {
1132                 if(!got_name)
1133                 {
1134                         /* We don't know the tag name yet. */
1135                         
1136                         if(*c != ' ')
1137                         {
1138                                 if(*c != '<')
1139                                 {
1140                                         tagname += *c;
1141                                 }
1142                         }
1143                         else
1144                         {
1145                                 /* We got to a space, we should have the tagname now. */
1146                                 if(tagname.length())
1147                                 {
1148                                         got_name = true;
1149                                 }
1150                         }
1151                 }
1152                 else
1153                 {
1154                         /* We have the tag name */
1155                         if(!got_key)
1156                         {
1157                                 /* We're still reading the key name */
1158                                 if(*c != '=')
1159                                 {
1160                                         if(*c != ' ')
1161                                         {
1162                                                 current_key += *c;
1163                                         }
1164                                 }
1165                                 else
1166                                 {
1167                                         /* We got an '=', end of the key name. */
1168                                         got_key = true;
1169                                 }
1170                         }
1171                         else
1172                         {
1173                                 /* We have the key name, now we're looking for quotes and the value */
1174                                 if(*c == '"')
1175                                 {
1176                                         if(!in_quote)
1177                                         {
1178                                                 /* We're not already in a quote. */
1179                                                 in_quote = true;
1180                                         }
1181                                         else
1182                                         {
1183                                                 /* Leaving quotes, we have the value */
1184                                                 results.push_back(KeyVal(current_key, current_value));
1185                                                 
1186                                                 // std::cout << "<" << tagname << ":" << current_key << "> " << current_value << std::endl;
1187                                                 
1188                                                 in_quote = false;
1189                                                 got_key = false;
1190                                                 
1191                                                 if((tagname == "include") && (current_key == "file"))
1192                                                 {
1193                                                         if(!this->DoInclude(target, current_value, errorstream))
1194                                                                 return false;
1195                                                 }
1196                                                 
1197                                                 current_key.clear();
1198                                                 current_value.clear();
1199                                         }
1200                                 }
1201                                 else
1202                                 {
1203                                         if(in_quote)
1204                                         {
1205                                                 current_value += *c;
1206                                         }
1207                                 }
1208                         }
1209                 }
1210         }
1211         
1212         /* Finished parsing the tag, add it to the config hash */
1213         target.insert(std::pair<std::string, KeyValList > (tagname, results));
1214         
1215         return true;
1216 }
1217
1218 bool ServerConfig::DoInclude(ConfigDataHash &target, const std::string &file, std::ostringstream &errorstream)
1219 {
1220         std::string confpath;
1221         std::string newfile;
1222         std::string::size_type pos;
1223         
1224         confpath = CONFIG_FILE;
1225         newfile = file;
1226         
1227         for (std::string::iterator c = newfile.begin(); c != newfile.end(); c++)
1228         {
1229                 if (*c == '\\')
1230                 {
1231                         *c = '/';
1232                 }
1233         }
1234
1235         if (file[0] != '/')
1236         {
1237                 if((pos = confpath.find("/inspircd.conf")) != std::string::npos)
1238                 {
1239                         /* Leaves us with just the path */
1240                         newfile = confpath.substr(0, pos) + std::string("/") + newfile;
1241                 }
1242                 else
1243                 {
1244                         errorstream << "Couldn't get config path from: " << confpath << std::endl;
1245                         return false;
1246                 }
1247         }
1248         
1249         return LoadConf(target, newfile, errorstream);
1250 }
1251
1252 bool ServerConfig::ConfValue(ConfigDataHash &target, const char* tag, const char* var, int index, char* result, int length)
1253 {
1254         std::string value;
1255         bool r = ConfValue(target, std::string(tag), std::string(var), index, value);
1256         strlcpy(result, value.c_str(), length);
1257         return r;
1258 }
1259
1260 bool ServerConfig::ConfValue(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, std::string &result)
1261 {
1262         ConfigDataHash::size_type pos = index;
1263         if((pos >= 0) && (pos < target.count(tag)))
1264         {
1265                 ConfigDataHash::const_iterator iter = target.find(tag);
1266                 
1267                 for(int i = 0; i < index; i++)
1268                         iter++;
1269                 
1270                 for(KeyValList::const_iterator j = iter->second.begin(); j != iter->second.end(); j++)
1271                 {
1272                         if(j->first == var)
1273                         {
1274                                 result = j->second;
1275                                 return true;
1276                         }
1277                 }
1278         }
1279         else if(pos == 0)
1280         {
1281                 log(DEBUG, "No <%s> tags in config file.", tag.c_str());
1282         }
1283         else
1284         {
1285                 log(DEBUG, "ConfValue got an out-of-range index %d, there are only %d occurences of %s", pos, target.count(tag), tag.c_str());
1286         }
1287         
1288         return false;
1289 }
1290         
1291 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const char* tag, const char* var, int index, int &result)
1292 {
1293         return ConfValueInteger(target, std::string(tag), std::string(var), index, result);
1294 }
1295
1296 bool ServerConfig::ConfValueInteger(ConfigDataHash &target, const std::string &tag, const std::string &var, int index, int &result)
1297 {
1298         std::string value;
1299         std::istringstream stream;
1300         bool r = ConfValue(target, tag, var, index, value);
1301         stream.str(value);
1302         if(!(stream >> result))
1303                 return false;
1304         return r;
1305 }
1306         
1307 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const char* tag, const char* var, int index)
1308 {
1309         return ConfValueBool(target, std::string(tag), std::string(var), index);
1310 }
1311
1312 bool ServerConfig::ConfValueBool(ConfigDataHash &target, const std::string &tag, const std::string &var, int index)
1313 {
1314         std::string result;
1315         if(!ConfValue(target, tag, var, index, result))
1316                 return false;
1317         
1318         return ((result == "yes") || (result == "true") || (result == "1"));
1319 }
1320         
1321 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const char* tag)
1322 {
1323         return ConfValueEnum(target, std::string(tag));
1324 }
1325
1326 int ServerConfig::ConfValueEnum(ConfigDataHash &target, const std::string &tag)
1327 {
1328         return target.count(tag);
1329 }
1330         
1331 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const char* tag, int index)
1332 {
1333         return 1;
1334 }
1335
1336 int ServerConfig::ConfVarEnum(ConfigDataHash &target, const std::string &tag, int index)
1337 {
1338         ConfigDataHash::size_type pos = index;
1339         
1340         if((pos >= 0) && (pos < target.count(tag)))
1341         {
1342                 ConfigDataHash::const_iterator iter = target.find(tag);
1343                 
1344                 for(int i = 0; i < index; i++)
1345                         iter++;
1346                 
1347                 return iter->second.size();
1348         }
1349         else
1350         {
1351                 log(DEBUG, "ConfVarEnum got an out-of-range index %d", pos);
1352         }
1353         
1354         return 0;
1355 }
1356
1357 /** This will bind a socket to a port. It works for UDP/TCP.
1358  * If a hostname is given to bind to, the function will first
1359  * attempt to resolve the hostname, then bind to the IP the 
1360  * hostname resolves to. This is a blocking lookup blocking for
1361  * a maximum of one second before it times out, using the DNS
1362  * server specified in the configuration file.
1363  */ 
1364 bool BindSocket(int sockfd, struct sockaddr_in client, struct sockaddr_in server, int port, char* addr)
1365 {
1366         memset(&server,0,sizeof(server));
1367         struct in_addr addy;
1368         bool resolved = false;
1369         char resolved_addr[128];
1370
1371         if (*addr == '*')
1372                 *addr = 0;
1373
1374         if (*addr && !inet_aton(addr,&addy))
1375         {
1376                 /* If they gave a hostname, bind to the IP it resolves to */
1377                 if (CleanAndResolve(resolved_addr, addr, true))
1378                 {
1379                         inet_aton(resolved_addr,&addy);
1380                         log(DEFAULT,"Resolved binding '%s' -> '%s'",addr,resolved_addr);
1381                         server.sin_addr = addy;
1382                         resolved = true;
1383                 }
1384                 else
1385                 {
1386                         log(DEFAULT,"WARNING: Could not resolve '%s' to an IP for binding to on port %d",addr,port);
1387                         return false;
1388                 }
1389         }
1390         server.sin_family = AF_INET;
1391         if (!resolved)
1392         {
1393                 if (!*addr)
1394                 {
1395                         server.sin_addr.s_addr = htonl(INADDR_ANY);
1396                 }
1397                 else
1398                 {
1399                         server.sin_addr = addy;
1400                 }
1401         }
1402         server.sin_port = htons(port);
1403         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server)) < 0)
1404         {
1405                 return false;
1406         }
1407         else
1408         {
1409                 log(DEBUG,"Bound port %s:%d",*addr ? addr : "*",port);
1410                 if (listen(sockfd, Config->MaxConn) == -1)
1411                 {
1412                         log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
1413                         return false;
1414                 }
1415                 else
1416                 {
1417                         NonBlocking(sockfd);
1418                         return true;
1419                 }
1420         }
1421 }
1422
1423
1424 // Open a TCP Socket
1425 int OpenTCPSocket()
1426 {
1427         int sockfd;
1428         int on = 1;
1429         struct linger linger = { 0 };
1430   
1431         if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
1432         {
1433                 log(DEFAULT,"Error creating TCP socket: %s",strerror(errno));
1434                 return (ERROR);
1435         }
1436         else
1437         {
1438                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
1439                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
1440                 linger.l_onoff = 1;
1441                 linger.l_linger = 1;
1442                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &linger,sizeof(linger));
1443                 return (sockfd);
1444         }
1445 }
1446
1447 bool HasPort(int port, char* addr)
1448 {
1449         for (int count = 0; count < ServerInstance->stats->BoundPortCount; count++)
1450         {
1451                 if ((port == Config->ports[count]) && (!strcasecmp(Config->addrs[count],addr)))
1452                 {
1453                         return true;
1454                 }
1455         }
1456         return false;
1457 }
1458
1459 int BindPorts(bool bail)
1460 {
1461         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
1462         sockaddr_in client,server;
1463         int clientportcount = 0;
1464         int BoundPortCount = 0;
1465
1466         if (!bail)
1467         {
1468                 int InitialPortCount = ServerInstance->stats->BoundPortCount;
1469                 log(DEBUG,"Initial port count: %d",InitialPortCount);
1470
1471                 for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
1472                 {
1473                         Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
1474                         Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
1475                         Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
1476
1477                         if (((!*Type) || (!strcmp(Type,"clients"))) && (!HasPort(atoi(configToken),Addr)))
1478                         {
1479                                 // modules handle server bind types now
1480                                 Config->ports[clientportcount+InitialPortCount] = atoi(configToken);
1481                                 if (*Addr == '*')
1482                                         *Addr = 0;
1483
1484                                 strlcpy(Config->addrs[clientportcount+InitialPortCount],Addr,256);
1485                                 clientportcount++;
1486                                 log(DEBUG,"NEW binding %s:%s [%s] from config",Addr,configToken, Type);
1487                         }
1488                 }
1489                 int PortCount = clientportcount;
1490                 if (PortCount)
1491                 {
1492                         for (int count = InitialPortCount; count < InitialPortCount + PortCount; count++)
1493                         {
1494                                 if ((openSockfd[count] = OpenTCPSocket()) == ERROR)
1495                                 {
1496                                         log(DEBUG,"Bad fd %d binding port [%s:%d]",openSockfd[count],Config->addrs[count],Config->ports[count]);
1497                                         return ERROR;
1498                                 }
1499                                 if (!BindSocket(openSockfd[count],client,server,Config->ports[count],Config->addrs[count]))
1500                                 {
1501                                         log(DEFAULT,"Failed to bind port [%s:%d]: %s",Config->addrs[count],Config->ports[count],strerror(errno));
1502                                 }
1503                                 else
1504                                 {
1505                                         /* Associate the new open port with a slot in the socket engine */
1506                                         ServerInstance->SE->AddFd(openSockfd[count],true,X_LISTEN);
1507                                         BoundPortCount++;
1508                                 }
1509                         }
1510                         return InitialPortCount + BoundPortCount;
1511                 }
1512                 else
1513                 {
1514                         log(DEBUG,"There is nothing new to bind!");
1515                 }
1516                 return InitialPortCount;
1517         }
1518
1519         for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
1520         {
1521                 Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
1522                 Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
1523                 Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
1524
1525                 if ((!*Type) || (!strcmp(Type,"clients")))
1526                 {
1527                         // modules handle server bind types now
1528                         Config->ports[clientportcount] = atoi(configToken);
1529
1530                         // If the client put bind "*", this is an unrealism.
1531                         // We don't actually support this as documented, but
1532                         // i got fed up of people trying it, so now it converts
1533                         // it to an empty string meaning the same 'bind to all'.
1534                         if (*Addr == '*')
1535                                 *Addr = 0;
1536
1537                         strlcpy(Config->addrs[clientportcount],Addr,256);
1538                         clientportcount++;
1539                         log(DEBUG,"Binding %s:%s [%s] from config",Addr,configToken, Type);
1540                 }
1541         }
1542
1543         int PortCount = clientportcount;
1544
1545         for (int count = 0; count < PortCount; count++)
1546         {
1547                 if ((openSockfd[BoundPortCount] = OpenTCPSocket()) == ERROR)
1548                 {
1549                         log(DEBUG,"Bad fd %d binding port [%s:%d]",openSockfd[BoundPortCount],Config->addrs[count],Config->ports[count]);
1550                         return ERROR;
1551                 }
1552
1553                 if (!BindSocket(openSockfd[BoundPortCount],client,server,Config->ports[count],Config->addrs[count]))
1554                 {
1555                         log(DEFAULT,"Failed to bind port [%s:%d]: %s",Config->addrs[count],Config->ports[count],strerror(errno));
1556                 }
1557                 else
1558                 {
1559                         /* well we at least bound to one socket so we'll continue */
1560                         BoundPortCount++;
1561                 }
1562         }
1563
1564         /* if we didn't bind to anything then abort */
1565         if (!BoundPortCount)
1566         {
1567                 log(DEFAULT,"No ports bound, bailing!");
1568                 printf("\nERROR: Could not bind any of %d ports! Please check your configuration.\n\n", PortCount);
1569                 return ERROR;
1570         }
1571
1572         return BoundPortCount;
1573 }