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