Annotation of ircnowd/src/ngircd/conf.c, Revision 1.1.1.1
1.1 tomglok 1: /*
2: * ngIRCd -- The Next Generation IRC Daemon
3: * Copyright (c)2001-2019 Alexander Barton (alex@barton.de) and Contributors.
4: *
5: * This program is free software; you can redistribute it and/or modify
6: * it under the terms of the GNU General Public License as published by
7: * the Free Software Foundation; either version 2 of the License, or
8: * (at your option) any later version.
9: * Please read the file COPYING, README and AUTHORS for more information.
10: */
11:
12: #include "portab.h"
13:
14: /**
15: * @file
16: * Configuration management (reading, parsing & validation)
17: */
18:
19: #include <assert.h>
20: #include <errno.h>
21: #ifdef PROTOTYPES
22: # include <stdarg.h>
23: #else
24: # include <varargs.h>
25: #endif
26: #include <stdio.h>
27: #include <stdlib.h>
28: #include <string.h>
29: #include <strings.h>
30: #include <time.h>
31: #include <unistd.h>
32: #include <pwd.h>
33: #include <grp.h>
34: #include <sys/types.h>
35: #include <dirent.h>
36:
37: #include "ngircd.h"
38: #include "conn.h"
39: #include "channel.h"
40: #include "log.h"
41: #include "match.h"
42:
43: #include "conf.h"
44:
45:
46: static bool Use_Log = true, Using_MotdFile = true;
47: static CONF_SERVER New_Server;
48: static int New_Server_Idx;
49:
50: static char Conf_MotdFile[FNAME_LEN];
51: static char Conf_HelpFile[FNAME_LEN];
52: static char Conf_IncludeDir[FNAME_LEN];
53:
54: static void Set_Defaults PARAMS(( bool InitServers ));
55: static bool Read_Config PARAMS(( bool TestOnly, bool IsStarting ));
56: static void Read_Config_File PARAMS(( const char *File, FILE *fd ));
57: static bool Validate_Config PARAMS(( bool TestOnly, bool Rehash ));
58:
59: static void Handle_GLOBAL PARAMS((const char *File, int Line,
60: char *Var, char *Arg ));
61: static void Handle_LIMITS PARAMS((const char *File, int Line,
62: char *Var, char *Arg ));
63: static void Handle_OPTIONS PARAMS((const char *File, int Line,
64: char *Var, char *Arg ));
65: static void Handle_OPERATOR PARAMS((const char *File, int Line,
66: char *Var, char *Arg ));
67: static void Handle_SERVER PARAMS((const char *File, int Line,
68: char *Var, char *Arg ));
69: static void Handle_CHANNEL PARAMS((const char *File, int Line,
70: char *Var, char *Arg ));
71:
72: static void Config_Error PARAMS((const int Level, const char *Format, ...));
73:
74: static void Config_Error_NaN PARAMS((const char *File, const int LINE,
75: const char *Value));
76: static void Config_Error_Section PARAMS((const char *File, const int Line,
77: const char *Item, const char *Section));
78: static void Config_Error_TooLong PARAMS((const char *File, const int LINE,
79: const char *Value));
80:
81: static void Init_Server_Struct PARAMS(( CONF_SERVER *Server ));
82:
83:
84: #ifdef WANT_IPV6
85: #define DEFAULT_LISTEN_ADDRSTR "::,0.0.0.0"
86: #else
87: #define DEFAULT_LISTEN_ADDRSTR "0.0.0.0"
88: #endif
89:
90: #ifdef HAVE_LIBSSL
91: #define DEFAULT_CIPHERS "HIGH:!aNULL:@STRENGTH:!SSLv3"
92: #endif
93: #ifdef HAVE_LIBGNUTLS
94: #define DEFAULT_CIPHERS "SECURE128:-VERS-SSL3.0"
95: #endif
96:
97: #ifdef SSL_SUPPORT
98:
99: static void Handle_SSL PARAMS((const char *File, int Line, char *Var, char *Ark));
100:
101: struct SSLOptions Conf_SSLOptions;
102:
103: /**
104: * Initialize SSL configuration.
105: */
106: static void
107: ConfSSL_Init(void)
108: {
109: free(Conf_SSLOptions.KeyFile);
110: Conf_SSLOptions.KeyFile = NULL;
111:
112: free(Conf_SSLOptions.CertFile);
113: Conf_SSLOptions.CertFile = NULL;
114:
115: free(Conf_SSLOptions.DHFile);
116: Conf_SSLOptions.DHFile = NULL;
117: array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
118:
119: array_free(&Conf_SSLOptions.ListenPorts);
120:
121: free(Conf_SSLOptions.CipherList);
122: Conf_SSLOptions.CipherList = NULL;
123: }
124:
125: /**
126: * Check if the current configuration uses/requires SSL.
127: *
128: * @returns true if SSL is used and should be initialized.
129: */
130: GLOBAL bool
131: Conf_SSLInUse(void)
132: {
133: int i;
134:
135: /* SSL listen ports configured? */
136: if (array_bytes(&Conf_SSLOptions.ListenPorts))
137: return true;
138:
139: for (i = 0; i < MAX_SERVERS; i++) {
140: if (Conf_Server[i].port > 0
141: && Conf_Server[i].SSLConnect)
142: return true;
143: }
144: return false;
145: }
146:
147: /**
148: * Make sure that a configured file is readable.
149: *
150: * Currently, this function is only used for SSL-related options ...
151: *
152: * @param Var Configuration variable
153: * @param Filename Configured filename
154: */
155: static void
156: CheckFileReadable(const char *Var, const char *Filename)
157: {
158: FILE *fp;
159:
160: if (!Filename)
161: return;
162:
163: fp = fopen(Filename, "r");
164: if (fp)
165: fclose(fp);
166: else
167: Config_Error(LOG_ERR, "Can't read \"%s\" (\"%s\"): %s",
168: Filename, Var, strerror(errno));
169: }
170:
171: #endif
172:
173:
174: /**
175: * Duplicate string and warn on errors.
176: *
177: * @returns Pointer to string on success, NULL otherwise.
178: */
179: static char *
180: strdup_warn(const char *str)
181: {
182: char *ptr = strdup(str);
183: if (!ptr)
184: Config_Error(LOG_ERR,
185: "Could not allocate memory for string: %s", str);
186: return ptr;
187: }
188:
189: /**
190: * Output a comma separated list of ports (integer values).
191: */
192: static void
193: ports_puts(array *a)
194: {
195: size_t len;
196: UINT16 *ports;
197: len = array_length(a, sizeof(UINT16));
198: if (len--) {
199: ports = (UINT16*) array_start(a);
200: printf("%u", (unsigned int) *ports);
201: while (len--) {
202: ports++;
203: printf(", %u", (unsigned int) *ports);
204: }
205: }
206: putc('\n', stdout);
207: }
208:
209: /**
210: * Parse a comma separated string into an array of port numbers (integers).
211: */
212: static void
213: ports_parse(array *a, const char *File, int Line, char *Arg)
214: {
215: char *ptr;
216: int port;
217: UINT16 port16;
218:
219: array_trunc(a);
220:
221: ptr = strtok( Arg, "," );
222: while (ptr) {
223: ngt_TrimStr(ptr);
224: port = atoi(ptr);
225: if (port > 0 && port < 0xFFFF) {
226: port16 = (UINT16) port;
227: if (!array_catb(a, (char*)&port16, sizeof port16))
228: Config_Error(LOG_ERR, "%s, line %d Could not add port number %ld: %s",
229: File, Line, port, strerror(errno));
230: } else {
231: Config_Error( LOG_ERR, "%s, line %d (section \"Global\"): Illegal port number %ld!",
232: File, Line, port );
233: }
234:
235: ptr = strtok( NULL, "," );
236: }
237: }
238:
239: /**
240: * Initialize configuration module.
241: */
242: GLOBAL void
243: Conf_Init( void )
244: {
245: Read_Config(false, true);
246: Validate_Config(false, false);
247: }
248:
249: /**
250: * "Rehash" (reload) server configuration.
251: *
252: * @returns true if configuration has been re-read, false on errors.
253: */
254: GLOBAL bool
255: Conf_Rehash( void )
256: {
257: if (!Read_Config(false, false))
258: return false;
259: Validate_Config(false, true);
260:
261: /* Update CLIENT structure of local server */
262: Client_SetInfo(Client_ThisServer(), Conf_ServerInfo);
263: return true;
264: }
265:
266: /**
267: * Output a boolean value as "yes/no" string.
268: */
269: static const char*
270: yesno_to_str(int boolean_value)
271: {
272: if (boolean_value)
273: return "yes";
274: return "no";
275: }
276:
277: /**
278: * Free all IRC operator configuration structures.
279: */
280: static void
281: opers_free(void)
282: {
283: struct Conf_Oper *op;
284: size_t len;
285:
286: len = array_length(&Conf_Opers, sizeof(*op));
287: op = array_start(&Conf_Opers);
288: while (len--) {
289: free(op->mask);
290: op++;
291: }
292: array_free(&Conf_Opers);
293: }
294:
295: /**
296: * Output all IRC operator configuration structures.
297: */
298: static void
299: opers_puts(void)
300: {
301: struct Conf_Oper *op;
302: size_t count, i;
303:
304: count = array_length(&Conf_Opers, sizeof(*op));
305: op = array_start(&Conf_Opers);
306: for (i = 0; i < count; i++, op++) {
307: if (!op->name[0])
308: continue;
309:
310: puts("[OPERATOR]");
311: printf(" Name = %s\n", op->name);
312: printf(" Password = %s\n", op->pwd);
313: printf(" Mask = %s\n\n", op->mask ? op->mask : "");
314: }
315: }
316:
317: /**
318: * Read configuration, validate and output it.
319: *
320: * This function waits for a keypress of the user when stdin/stdout are valid
321: * tty's ("you can read our nice message and we can read in your keypress").
322: *
323: * @return 0 on success, 1 on failure(s); therefore the result code can
324: * directly be used by exit() when running "ngircd --configtest".
325: */
326: GLOBAL int
327: Conf_Test( void )
328: {
329: struct passwd *pwd;
330: struct group *grp;
331: unsigned int i, j;
332: bool config_valid;
333: size_t predef_channel_count;
334: struct Conf_Channel *predef_chan;
335:
336: Use_Log = false;
337:
338: if (!Read_Config(true, true))
339: return 1;
340:
341: config_valid = Validate_Config(true, false);
342:
343: /* Valid tty? */
344: if(isatty(fileno(stdin)) && isatty(fileno(stdout))) {
345: puts("OK, press enter to see a dump of your server configuration ...");
346: getchar();
347: } else
348: puts("Ok, dump of your server configuration follows:\n");
349:
350: puts("[GLOBAL]");
351: printf(" Name = %s\n", Conf_ServerName);
352: printf(" AdminInfo1 = %s\n", Conf_ServerAdmin1);
353: printf(" AdminInfo2 = %s\n", Conf_ServerAdmin2);
354: printf(" AdminEMail = %s\n", Conf_ServerAdminMail);
355: printf(" HelpFile = %s\n", Conf_HelpFile);
356: printf(" Info = %s\n", Conf_ServerInfo);
357: printf(" Listen = %s\n", Conf_ListenAddress);
358: if (Using_MotdFile) {
359: printf(" MotdFile = %s\n", Conf_MotdFile);
360: printf(" MotdPhrase =\n");
361: } else {
362: printf(" MotdFile = \n");
363: printf(" MotdPhrase = %s\n", array_bytes(&Conf_Motd)
364: ? (const char*) array_start(&Conf_Motd) : "");
365: }
366: printf(" Network = %s\n", Conf_Network);
367: if (!Conf_PAM)
368: printf(" Password = %s\n", Conf_ServerPwd);
369: printf(" PidFile = %s\n", Conf_PidFile);
370: printf(" Ports = ");
371: ports_puts(&Conf_ListenPorts);
372: grp = getgrgid(Conf_GID);
373: if (grp)
374: printf(" ServerGID = %s\n", grp->gr_name);
375: else
376: printf(" ServerGID = %ld\n", (long)Conf_GID);
377: pwd = getpwuid(Conf_UID);
378: if (pwd)
379: printf(" ServerUID = %s\n", pwd->pw_name);
380: else
381: printf(" ServerUID = %ld\n", (long)Conf_UID);
382: puts("");
383:
384: puts("[LIMITS]");
385: printf(" ConnectRetry = %d\n", Conf_ConnectRetry);
386: printf(" IdleTimeout = %d\n", Conf_IdleTimeout);
387: printf(" MaxConnections = %d\n", Conf_MaxConnections);
388: printf(" MaxConnectionsIP = %d\n", Conf_MaxConnectionsIP);
389: printf(" MaxJoins = %d\n", Conf_MaxJoins > 0 ? Conf_MaxJoins : -1);
390: printf(" MaxNickLength = %u\n", Conf_MaxNickLength - 1);
391: printf(" MaxPenaltyTime = %ld\n", (long)Conf_MaxPenaltyTime);
392: printf(" MaxListSize = %d\n", Conf_MaxListSize);
393: printf(" PingTimeout = %d\n", Conf_PingTimeout);
394: printf(" PongTimeout = %d\n", Conf_PongTimeout);
395: puts("");
396:
397: puts("[OPTIONS]");
398: printf(" AllowedChannelTypes = %s\n", Conf_AllowedChannelTypes);
399: printf(" AllowRemoteOper = %s\n", yesno_to_str(Conf_AllowRemoteOper));
400: printf(" ChrootDir = %s\n", Conf_Chroot);
401: printf(" CloakHost = %s\n", Conf_CloakHost);
402: printf(" CloakHostModeX = %s\n", Conf_CloakHostModeX);
403: printf(" CloakHostSalt = %s\n", Conf_CloakHostSalt);
404: printf(" CloakUserToNick = %s\n", yesno_to_str(Conf_CloakUserToNick));
405: #ifdef WANT_IPV6
406: printf(" ConnectIPv4 = %s\n", yesno_to_str(Conf_ConnectIPv6));
407: printf(" ConnectIPv6 = %s\n", yesno_to_str(Conf_ConnectIPv4));
408: #endif
409: printf(" DefaultUserModes = %s\n", Conf_DefaultUserModes);
410: printf(" DNS = %s\n", yesno_to_str(Conf_DNS));
411: #ifdef IDENT
412: printf(" Ident = %s\n", yesno_to_str(Conf_Ident));
413: #endif
414: printf(" IncludeDir = %s\n", Conf_IncludeDir);
415: printf(" MorePrivacy = %s\n", yesno_to_str(Conf_MorePrivacy));
416: printf(" NoticeBeforeRegistration = %s\n", yesno_to_str(Conf_NoticeBeforeRegistration));
417: printf(" OperCanUseMode = %s\n", yesno_to_str(Conf_OperCanMode));
418: printf(" OperChanPAutoOp = %s\n", yesno_to_str(Conf_OperChanPAutoOp));
419: printf(" OperServerMode = %s\n", yesno_to_str(Conf_OperServerMode));
420: #ifdef PAM
421: printf(" PAM = %s\n", yesno_to_str(Conf_PAM));
422: printf(" PAMIsOptional = %s\n", yesno_to_str(Conf_PAMIsOptional));
423: printf(" PAMServiceName = %s\n", Conf_PAMServiceName);
424: #endif
425: #ifndef STRICT_RFC
426: printf(" RequireAuthPing = %s\n", yesno_to_str(Conf_AuthPing));
427: #endif
428: printf(" ScrubCTCP = %s\n", yesno_to_str(Conf_ScrubCTCP));
429: #ifdef SYSLOG
430: printf(" SyslogFacility = %s\n",
431: ngt_SyslogFacilityName(Conf_SyslogFacility));
432: #endif
433: printf(" WebircPassword = %s\n", Conf_WebircPwd);
434: puts("");
435:
436: #ifdef SSL_SUPPORT
437: puts("[SSL]");
438: printf(" CertFile = %s\n", Conf_SSLOptions.CertFile
439: ? Conf_SSLOptions.CertFile : "");
440: printf(" CipherList = %s\n", Conf_SSLOptions.CipherList ?
441: Conf_SSLOptions.CipherList : DEFAULT_CIPHERS);
442: printf(" DHFile = %s\n", Conf_SSLOptions.DHFile
443: ? Conf_SSLOptions.DHFile : "");
444: printf(" KeyFile = %s\n", Conf_SSLOptions.KeyFile
445: ? Conf_SSLOptions.KeyFile : "");
446: if (array_bytes(&Conf_SSLOptions.KeyFilePassword))
447: puts(" KeyFilePassword = <secret>");
448: else
449: puts(" KeyFilePassword = ");
450: array_free_wipe(&Conf_SSLOptions.KeyFilePassword);
451: printf(" Ports = ");
452: ports_puts(&Conf_SSLOptions.ListenPorts);
453: puts("");
454: #endif
455:
456: opers_puts();
457:
458: for( i = 0; i < MAX_SERVERS; i++ ) {
459: if( ! Conf_Server[i].name[0] ) continue;
460:
461: /* Valid "Server" section */
462: puts( "[SERVER]" );
463: printf( " Name = %s\n", Conf_Server[i].name );
464: printf( " Host = %s\n", Conf_Server[i].host );
465: printf( " Port = %u\n", (unsigned int)Conf_Server[i].port );
466: #ifdef SSL_SUPPORT
467: printf( " SSLConnect = %s\n", Conf_Server[i].SSLConnect?"yes":"no");
468: #endif
469: printf( " MyPassword = %s\n", Conf_Server[i].pwd_in );
470: printf( " PeerPassword = %s\n", Conf_Server[i].pwd_out );
471: printf( " ServiceMask = %s\n", Conf_Server[i].svs_mask);
472: printf( " Group = %d\n", Conf_Server[i].group );
473: printf( " Passive = %s\n\n", Conf_Server[i].flags & CONF_SFLAG_DISABLED ? "yes" : "no");
474: }
475:
476: predef_channel_count = array_length(&Conf_Channels, sizeof(*predef_chan));
477: predef_chan = array_start(&Conf_Channels);
478:
479: for (i = 0; i < predef_channel_count; i++, predef_chan++) {
480: if (!predef_chan->name[0])
481: continue;
482:
483: /* Valid "Channel" section */
484: puts( "[CHANNEL]" );
485: printf(" Name = %s\n", predef_chan->name);
486: for(j = 0; j < predef_chan->modes_num; j++)
487: printf(" Modes = %s\n", predef_chan->modes[j]);
488: printf(" Key = %s\n", predef_chan->key);
489: printf(" MaxUsers = %lu\n", predef_chan->maxusers);
490: printf(" Topic = %s\n", predef_chan->topic);
491: printf(" KeyFile = %s\n\n", predef_chan->keyfile);
492: }
493:
494: return (config_valid ? 0 : 1);
495: }
496:
497: /**
498: * Remove connection information from configured server.
499: *
500: * If the server is set as "once", delete it from our configuration;
501: * otherwise set the time for the next connection attempt.
502: *
503: * Non-server connections will be silently ignored.
504: */
505: GLOBAL void
506: Conf_UnsetServer( CONN_ID Idx )
507: {
508: int i;
509: time_t t;
510:
511: /* Check all our configured servers */
512: for( i = 0; i < MAX_SERVERS; i++ ) {
513: if( Conf_Server[i].conn_id != Idx ) continue;
514:
515: /* Gotcha! Mark server configuration as "unused": */
516: Conf_Server[i].conn_id = NONE;
517:
518: if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
519: /* Delete configuration here */
520: Init_Server_Struct( &Conf_Server[i] );
521: } else {
522: /* Set time for next connect attempt */
523: t = time(NULL);
524: if (Conf_Server[i].lasttry < t - Conf_ConnectRetry) {
525: /* The connection has been "long", so we don't
526: * require the next attempt to be delayed. */
527: Conf_Server[i].lasttry =
528: t - Conf_ConnectRetry + RECONNECT_DELAY;
529: } else {
530: /* "Short" connection, enforce "ConnectRetry"
531: * but randomize it a little bit: 15 seconds. */
532: Conf_Server[i].lasttry =
533: #ifdef HAVE_ARC4RANDOM
534: t + (arc4random() % 15);
535: #else
536: t + rand() / (RAND_MAX / 15);
537: #endif
538: }
539: }
540: }
541: }
542:
543: /**
544: * Set connection information for specified configured server.
545: */
546: GLOBAL bool
547: Conf_SetServer( int ConfServer, CONN_ID Idx )
548: {
549: assert( ConfServer > NONE );
550: assert( Idx > NONE );
551:
552: if (Conf_Server[ConfServer].conn_id > NONE &&
553: Conf_Server[ConfServer].conn_id != Idx) {
554: Log(LOG_ERR,
555: "Connection %d: Server configuration of \"%s\" already in use by connection %d!",
556: Idx, Conf_Server[ConfServer].name,
557: Conf_Server[ConfServer].conn_id);
558: Conn_Close(Idx, NULL, "Server configuration already in use", true);
559: return false;
560: }
561: Conf_Server[ConfServer].conn_id = Idx;
562: return true;
563: }
564:
565: /**
566: * Get index of server in configuration structure.
567: */
568: GLOBAL int
569: Conf_GetServer( CONN_ID Idx )
570: {
571: int i = 0;
572:
573: assert( Idx > NONE );
574:
575: for( i = 0; i < MAX_SERVERS; i++ ) {
576: if( Conf_Server[i].conn_id == Idx ) return i;
577: }
578: return NONE;
579: }
580:
581: /**
582: * Enable a server by name and adjust its port number.
583: *
584: * @returns true if a server has been enabled and now has a valid port
585: * number and host name for outgoing connections.
586: */
587: GLOBAL bool
588: Conf_EnableServer( const char *Name, UINT16 Port )
589: {
590: int i;
591:
592: assert( Name != NULL );
593: for( i = 0; i < MAX_SERVERS; i++ ) {
594: if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
595: /* Gotcha! Set port and enable server: */
596: Conf_Server[i].port = Port;
597: Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
598: return (Conf_Server[i].port && Conf_Server[i].host[0]);
599: }
600: }
601: return false;
602: }
603:
604: /**
605: * Enable a server by name.
606: *
607: * The server is only usable as outgoing server, if it has set a valid port
608: * number for outgoing connections!
609: * If not, you have to use Conf_EnableServer() function to make it available.
610: *
611: * @returns true if a server has been enabled; false otherwise.
612: */
613: GLOBAL bool
614: Conf_EnablePassiveServer(const char *Name)
615: {
616: int i;
617:
618: assert( Name != NULL );
619: for (i = 0; i < MAX_SERVERS; i++) {
620: if ((strcasecmp( Conf_Server[i].name, Name ) == 0)
621: && (Conf_Server[i].port > 0)) {
622: /* BINGO! Enable server */
623: Conf_Server[i].flags &= ~CONF_SFLAG_DISABLED;
624: Conf_Server[i].lasttry = 0;
625: return true;
626: }
627: }
628: return false;
629: }
630:
631: /**
632: * Disable a server by name.
633: * An already established connection will be disconnected.
634: *
635: * @returns true if a server was found and has been disabled.
636: */
637: GLOBAL bool
638: Conf_DisableServer( const char *Name )
639: {
640: int i;
641:
642: assert( Name != NULL );
643: for( i = 0; i < MAX_SERVERS; i++ ) {
644: if( strcasecmp( Conf_Server[i].name, Name ) == 0 ) {
645: /* Gotcha! Disable and disconnect server: */
646: Conf_Server[i].flags |= CONF_SFLAG_DISABLED;
647: if( Conf_Server[i].conn_id > NONE )
648: Conn_Close(Conf_Server[i].conn_id, NULL,
649: "Server link terminated on operator request",
650: true);
651: return true;
652: }
653: }
654: return false;
655: }
656:
657: /**
658: * Add a new remote server to our configuration.
659: *
660: * @param Name Name of the new server.
661: * @param Port Port number to connect to or 0 for incoming connections.
662: * @param Host Host name to connect to.
663: * @param MyPwd Password that will be sent to the peer.
664: * @param PeerPwd Password that must be received from the peer.
665: * @returns true if the new server has been added; false otherwise.
666: */
667: GLOBAL bool
668: Conf_AddServer(const char *Name, UINT16 Port, const char *Host,
669: const char *MyPwd, const char *PeerPwd)
670: {
671: int i;
672:
673: assert( Name != NULL );
674: assert( Host != NULL );
675: assert( MyPwd != NULL );
676: assert( PeerPwd != NULL );
677:
678: /* Search unused item in server configuration structure */
679: for( i = 0; i < MAX_SERVERS; i++ ) {
680: /* Is this item used? */
681: if( ! Conf_Server[i].name[0] ) break;
682: }
683: if( i >= MAX_SERVERS ) return false;
684:
685: Init_Server_Struct( &Conf_Server[i] );
686: strlcpy( Conf_Server[i].name, Name, sizeof( Conf_Server[i].name ));
687: strlcpy( Conf_Server[i].host, Host, sizeof( Conf_Server[i].host ));
688: strlcpy( Conf_Server[i].pwd_out, MyPwd, sizeof( Conf_Server[i].pwd_out ));
689: strlcpy( Conf_Server[i].pwd_in, PeerPwd, sizeof( Conf_Server[i].pwd_in ));
690: Conf_Server[i].port = Port;
691: Conf_Server[i].flags = CONF_SFLAG_ONCE;
692:
693: return true;
694: }
695:
696: /**
697: * Check if the given nickname is reserved for services on a particular server.
698: *
699: * @param ConfServer The server index to check.
700: * @param Nick The nickname to check.
701: * @returns true if the given nickname belongs to an "IRC service".
702: */
703: GLOBAL bool
704: Conf_NickIsService(int ConfServer, const char *Nick)
705: {
706: assert (ConfServer >= 0);
707: assert (ConfServer < MAX_SERVERS);
708:
709: return MatchCaseInsensitiveList(Conf_Server[ConfServer].svs_mask,
710: Nick, ",");
711: }
712:
713: /**
714: * Check if the given nickname is blocked for "normal client" use.
715: *
716: * @param Nick The nickname to check.
717: * @returns true if the given nickname belongs to an "IRC service".
718: */
719: GLOBAL bool
720: Conf_NickIsBlocked(const char *Nick)
721: {
722: int i;
723:
724: for(i = 0; i < MAX_SERVERS; i++) {
725: if (!Conf_Server[i].name[0])
726: continue;
727: if (Conf_NickIsService(i, Nick))
728: return true;
729: }
730: return false;
731: }
732:
733: /**
734: * Initialize configuration settings with their default values.
735: */
736: static void
737: Set_Defaults(bool InitServers)
738: {
739: int i;
740: char random[RANDOM_SALT_LEN + 1];
741:
742: /* Global */
743: strcpy(Conf_ServerName, "");
744: strcpy(Conf_ServerAdmin1, "");
745: strcpy(Conf_ServerAdmin2, "");
746: strcpy(Conf_ServerAdminMail, "");
747: snprintf(Conf_ServerInfo, sizeof Conf_ServerInfo, "%s %s",
748: PACKAGE_NAME, PACKAGE_VERSION);
749: strcpy(Conf_Network, "");
750: free(Conf_ListenAddress);
751: Conf_ListenAddress = NULL;
752: array_free(&Conf_ListenPorts);
753: array_free(&Conf_Motd);
754: array_free(&Conf_Helptext);
755: strlcpy(Conf_MotdFile, SYSCONFDIR, sizeof(Conf_MotdFile));
756: strlcat(Conf_MotdFile, MOTD_FILE, sizeof(Conf_MotdFile));
757: strlcpy(Conf_HelpFile, DOCDIR, sizeof(Conf_HelpFile));
758: strlcat(Conf_HelpFile, HELP_FILE, sizeof(Conf_HelpFile));
759: strcpy(Conf_ServerPwd, "");
760: strlcpy(Conf_PidFile, PID_FILE, sizeof(Conf_PidFile));
761: Conf_UID = Conf_GID = 0;
762:
763: /* Limits */
764: Conf_ConnectRetry = 60;
765: Conf_IdleTimeout = 0;
766: Conf_MaxConnections = 0;
767: Conf_MaxConnectionsIP = 5;
768: Conf_MaxJoins = 10;
769: Conf_MaxNickLength = CLIENT_NICK_LEN_DEFAULT;
770: Conf_MaxPenaltyTime = -1;
771: Conf_MaxListSize = 100;
772: Conf_PingTimeout = 120;
773: Conf_PongTimeout = 20;
774:
775: /* Options */
776: strlcpy(Conf_AllowedChannelTypes, CHANTYPES,
777: sizeof(Conf_AllowedChannelTypes));
778: Conf_AllowRemoteOper = false;
779: #ifndef STRICT_RFC
780: Conf_AuthPing = false;
781: #endif
782: strlcpy(Conf_Chroot, CHROOT_DIR, sizeof(Conf_Chroot));
783: strcpy(Conf_CloakHost, "");
784: strcpy(Conf_CloakHostModeX, "");
785: strlcpy(Conf_CloakHostSalt, ngt_RandomStr(random, RANDOM_SALT_LEN),
786: sizeof(Conf_CloakHostSalt));
787: Conf_CloakUserToNick = false;
788: Conf_ConnectIPv4 = true;
789: #ifdef WANT_IPV6
790: Conf_ConnectIPv6 = true;
791: #else
792: Conf_ConnectIPv6 = false;
793: #endif
794: strcpy(Conf_DefaultUserModes, "");
795: Conf_DNS = true;
796: #ifdef IDENTAUTH
797: Conf_Ident = true;
798: #else
799: Conf_Ident = false;
800: #endif
801: strcpy(Conf_IncludeDir, "");
802: Conf_MorePrivacy = false;
803: Conf_NoticeBeforeRegistration = false;
804: Conf_OperCanMode = false;
805: Conf_OperChanPAutoOp = true;
806: Conf_OperServerMode = false;
807: #ifdef PAM
808: Conf_PAM = true;
809: #else
810: Conf_PAM = false;
811: #endif
812: Conf_PAMIsOptional = false;
813: strcpy(Conf_PAMServiceName, "ngircd");
814: Conf_ScrubCTCP = false;
815: #ifdef SYSLOG
816: #ifdef LOG_LOCAL5
817: Conf_SyslogFacility = LOG_LOCAL5;
818: #else
819: Conf_SyslogFacility = 0;
820: #endif
821: #endif
822:
823: /* Initialize server configuration structures */
824: if (InitServers) {
825: for (i = 0; i < MAX_SERVERS;
826: Init_Server_Struct(&Conf_Server[i++]));
827: }
828: }
829:
830: /**
831: * Get number of configured listening ports.
832: *
833: * @returns The number of ports (IPv4+IPv6) on which the server should listen.
834: */
835: static bool
836: no_listenports(void)
837: {
838: size_t cnt = array_bytes(&Conf_ListenPorts);
839: #ifdef SSL_SUPPORT
840: cnt += array_bytes(&Conf_SSLOptions.ListenPorts);
841: #endif
842: return cnt == 0;
843: }
844:
845: /**
846: * Read contents of a text file into an array.
847: *
848: * This function is used to read the MOTD and help text file, for example.
849: *
850: * @param Filename Name of the file to read.
851: * @return true, when the file has been read in.
852: */
853: static bool
854: Read_TextFile(const char *Filename, const char *Name, array *Destination)
855: {
856: char line[COMMAND_LEN];
857: FILE *fp;
858: int line_no = 1;
859:
860: if (*Filename == '\0')
861: return false;
862:
863: fp = fopen(Filename, "r");
864: if (!fp) {
865: Config_Error(LOG_ERR, "Can't read %s file \"%s\": %s",
866: Name, Filename, strerror(errno));
867: return false;
868: }
869:
870: array_free(Destination);
871: while (fgets(line, (int)sizeof line, fp)) {
872: ngt_TrimLastChr(line, '\n');
873:
874: /* add text including \0 */
875: if (!array_catb(Destination, line, strlen(line) + 1)) {
876: Log(LOG_ERR, "Cannot read/add \"%s\", line %d: %s",
877: Filename, line_no, strerror(errno));
878: break;
879: }
880: line_no++;
881: }
882: fclose(fp);
883: return true;
884: }
885:
886: /**
887: * Read ngIRCd configuration file.
888: *
889: * Please note that this function uses exit(1) on fatal errors and therefore
890: * can result in ngIRCd terminating!
891: *
892: * @param IsStarting Flag indicating if ngIRCd is starting or not.
893: * @returns true when the configuration file has been read
894: * successfully; false otherwise.
895: */
896: static bool
897: Read_Config(bool TestOnly, bool IsStarting)
898: {
899: const UINT16 defaultport = 6667;
900: char *ptr, file[FNAME_LEN];
901: struct dirent *entry;
902: int i, n;
903: FILE *fd;
904: DIR *dh;
905:
906: Config_Error(LOG_INFO, "Using configuration file \"%s\" ...", NGIRCd_ConfFile);
907:
908: /* Open configuration file */
909: fd = fopen( NGIRCd_ConfFile, "r" );
910: if( ! fd ) {
911: /* No configuration file found! */
912: Config_Error( LOG_ALERT, "Can't read configuration \"%s\": %s",
913: NGIRCd_ConfFile, strerror( errno ));
914: if (!IsStarting)
915: return false;
916: Config_Error( LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME );
917: exit( 1 );
918: }
919:
920: opers_free();
921: Set_Defaults(IsStarting);
922:
923: if (TestOnly)
924: Config_Error(LOG_INFO,
925: "Reading configuration from \"%s\" ...",
926: NGIRCd_ConfFile );
927:
928: /* Clean up server configuration structure: mark all already
929: * configured servers as "once" so that they are deleted
930: * after the next disconnect and delete all unused servers.
931: * And delete all servers which are "duplicates" of servers
932: * that are already marked as "once" (such servers have been
933: * created by the last rehash but are now useless). */
934: for( i = 0; i < MAX_SERVERS; i++ ) {
935: if( Conf_Server[i].conn_id == NONE ) Init_Server_Struct( &Conf_Server[i] );
936: else {
937: /* This structure is in use ... */
938: if( Conf_Server[i].flags & CONF_SFLAG_ONCE ) {
939: /* Check for duplicates */
940: for( n = 0; n < MAX_SERVERS; n++ ) {
941: if( n == i ) continue;
942:
943: if( Conf_Server[i].conn_id == Conf_Server[n].conn_id ) {
944: Init_Server_Struct( &Conf_Server[n] );
945: #ifdef DEBUG
946: Log(LOG_DEBUG,"Deleted unused duplicate server %d (kept %d).",
947: n, i );
948: #endif
949: }
950: }
951: } else {
952: /* Mark server as "once" */
953: Conf_Server[i].flags |= CONF_SFLAG_ONCE;
954: Log( LOG_DEBUG, "Marked server %d as \"once\"", i );
955: }
956: }
957: }
958:
959: /* Initialize variables */
960: Init_Server_Struct( &New_Server );
961: New_Server_Idx = NONE;
962: #ifdef SSL_SUPPORT
963: ConfSSL_Init();
964: #endif
965:
966: Read_Config_File(NGIRCd_ConfFile, fd);
967: fclose(fd);
968:
969: if (Conf_IncludeDir[0]) {
970: dh = opendir(Conf_IncludeDir);
971: if (!dh)
972: Config_Error(LOG_ALERT,
973: "Can't open include directory \"%s\": %s",
974: Conf_IncludeDir, strerror(errno));
975: } else {
976: strlcpy(Conf_IncludeDir, SYSCONFDIR, sizeof(Conf_IncludeDir));
977: strlcat(Conf_IncludeDir, CONFIG_DIR, sizeof(Conf_IncludeDir));
978: dh = opendir(Conf_IncludeDir);
979: }
980:
981: /* Include further configuration files, if IncludeDir is available */
982: if (dh) {
983: while ((entry = readdir(dh)) != NULL) {
984: ptr = strrchr(entry->d_name, '.');
985: if (!ptr || strcasecmp(ptr, ".conf") != 0)
986: continue;
987: snprintf(file, sizeof(file), "%s/%s",
988: Conf_IncludeDir, entry->d_name);
989: if (TestOnly)
990: Config_Error(LOG_INFO,
991: "Reading configuration from \"%s\" ...",
992: file);
993: fd = fopen(file, "r");
994: if (fd) {
995: Read_Config_File(file, fd);
996: fclose(fd);
997: } else
998: Config_Error(LOG_ALERT,
999: "Can't read configuration \"%s\": %s",
1000: file, strerror(errno));
1001: }
1002: closedir(dh);
1003: }
1004:
1005: /* Check if there is still a server to add */
1006: if( New_Server.name[0] ) {
1007: /* Copy data to "real" server structure */
1008: assert( New_Server_Idx > NONE );
1009: Conf_Server[New_Server_Idx] = New_Server;
1010: }
1011:
1012: /* not a single listening port? Add default. */
1013: if (no_listenports() &&
1014: !array_copyb(&Conf_ListenPorts, (char*) &defaultport, sizeof defaultport))
1015: {
1016: Config_Error(LOG_ALERT, "Could not add default listening Port %u: %s",
1017: (unsigned int) defaultport, strerror(errno));
1018:
1019: exit(1);
1020: }
1021:
1022: if (!Conf_ListenAddress)
1023: Conf_ListenAddress = strdup_warn(DEFAULT_LISTEN_ADDRSTR);
1024:
1025: if (!Conf_ListenAddress) {
1026: Config_Error(LOG_ALERT, "%s exiting due to fatal errors!", PACKAGE_NAME);
1027: exit(1);
1028: }
1029:
1030: /* No MOTD phrase configured? (re)try motd file. */
1031: if (array_bytes(&Conf_Motd) == 0) {
1032: if (Read_TextFile(Conf_MotdFile, "MOTD", &Conf_Motd))
1033: Using_MotdFile = true;
1034: }
1035:
1036: /* Try to read ngIRCd help text file. */
1037: (void)Read_TextFile(Conf_HelpFile, "help text", &Conf_Helptext);
1038: if (!array_bytes(&Conf_Helptext))
1039: Config_Error(LOG_WARNING,
1040: "No help text available, HELP command will be of limited use.");
1041:
1042: #ifdef SSL_SUPPORT
1043: /* Make sure that all SSL-related files are readable */
1044: CheckFileReadable("CertFile", Conf_SSLOptions.CertFile);
1045: CheckFileReadable("DHFile", Conf_SSLOptions.DHFile);
1046: CheckFileReadable("KeyFile", Conf_SSLOptions.KeyFile);
1047:
1048: /* Set the default ciphers if none were configured */
1049: if (!Conf_SSLOptions.CipherList)
1050: Conf_SSLOptions.CipherList = strdup_warn(DEFAULT_CIPHERS);
1051: #endif
1052:
1053: return true;
1054: }
1055:
1056: /**
1057: * Read in and handle a configuration file.
1058: *
1059: * @param File Name of the configuration file.
1060: * @param fd File descriptor already opened for reading.
1061: */
1062: static void
1063: Read_Config_File(const char *File, FILE *fd)
1064: {
1065: char section[LINE_LEN], str[LINE_LEN], *var, *arg, *ptr;
1066: int i, line = 0;
1067: size_t count;
1068:
1069: /* Read configuration file */
1070: section[0] = '\0';
1071: while (true) {
1072: if (!fgets(str, sizeof(str), fd))
1073: break;
1074: ngt_TrimStr(str);
1075: line++;
1076:
1077: /* Skip comments and empty lines */
1078: if (str[0] == ';' || str[0] == '#' || str[0] == '\0')
1079: continue;
1080:
1081: if (strlen(str) >= sizeof(str) - 1) {
1082: Config_Error(LOG_WARNING, "%s, line %d too long!",
1083: File, line);
1084: continue;
1085: }
1086:
1087: /* Is this the beginning of a new section? */
1088: if ((str[0] == '[') && (str[strlen(str) - 1] == ']')) {
1089: strlcpy(section, str, sizeof(section));
1090: if (strcasecmp(section, "[GLOBAL]") == 0
1091: || strcasecmp(section, "[LIMITS]") == 0
1092: || strcasecmp(section, "[OPTIONS]") == 0
1093: #ifdef SSL_SUPPORT
1094: || strcasecmp(section, "[SSL]") == 0
1095: #endif
1096: )
1097: continue;
1098:
1099: if (strcasecmp(section, "[SERVER]") == 0) {
1100: /* Check if there is already a server to add */
1101: if (New_Server.name[0]) {
1102: /* Copy data to "real" server structure */
1103: assert(New_Server_Idx > NONE);
1104: Conf_Server[New_Server_Idx] =
1105: New_Server;
1106: }
1107:
1108: /* Re-init structure for new server */
1109: Init_Server_Struct(&New_Server);
1110:
1111: /* Search unused item in server configuration structure */
1112: for (i = 0; i < MAX_SERVERS; i++) {
1113: /* Is this item used? */
1114: if (!Conf_Server[i].name[0])
1115: break;
1116: }
1117: if (i >= MAX_SERVERS) {
1118: /* Oops, no free item found! */
1119: Config_Error(LOG_ERR,
1120: "Too many servers configured.");
1121: New_Server_Idx = NONE;
1122: } else
1123: New_Server_Idx = i;
1124: continue;
1125: }
1126:
1127: if (strcasecmp(section, "[CHANNEL]") == 0) {
1128: count = array_length(&Conf_Channels,
1129: sizeof(struct
1130: Conf_Channel));
1131: if (!array_alloc
1132: (&Conf_Channels,
1133: sizeof(struct Conf_Channel), count)) {
1134: Config_Error(LOG_ERR,
1135: "Could not allocate memory for new operator (line %d)",
1136: line);
1137: }
1138: continue;
1139: }
1140:
1141: if (strcasecmp(section, "[OPERATOR]") == 0) {
1142: count = array_length(&Conf_Opers,
1143: sizeof(struct Conf_Oper));
1144: if (!array_alloc(&Conf_Opers,
1145: sizeof(struct Conf_Oper),
1146: count)) {
1147: Config_Error(LOG_ERR,
1148: "Could not allocate memory for new channel (line &d)",
1149: line);
1150: }
1151: continue;
1152: }
1153:
1154: Config_Error(LOG_ERR,
1155: "%s, line %d: Unknown section \"%s\"!",
1156: File, line, section);
1157: section[0] = 0x1;
1158: }
1159: if (section[0] == 0x1)
1160: continue;
1161:
1162: /* Split line into variable name and parameters */
1163: ptr = strchr(str, '=');
1164: if (!ptr) {
1165: Config_Error(LOG_ERR, "%s, line %d: Syntax error!",
1166: File, line);
1167: continue;
1168: }
1169: *ptr = '\0';
1170: var = str;
1171: ngt_TrimStr(var);
1172: arg = ptr + 1;
1173: ngt_TrimStr(arg);
1174:
1175: if (strcasecmp(section, "[GLOBAL]") == 0)
1176: Handle_GLOBAL(File, line, var, arg);
1177: else if (strcasecmp(section, "[LIMITS]") == 0)
1178: Handle_LIMITS(File, line, var, arg);
1179: else if (strcasecmp(section, "[OPTIONS]") == 0)
1180: Handle_OPTIONS(File, line, var, arg);
1181: #ifdef SSL_SUPPORT
1182: else if (strcasecmp(section, "[SSL]") == 0)
1183: Handle_SSL(File, line, var, arg);
1184: #endif
1185: else if (strcasecmp(section, "[OPERATOR]") == 0)
1186: Handle_OPERATOR(File, line, var, arg);
1187: else if (strcasecmp(section, "[SERVER]") == 0)
1188: Handle_SERVER(File, line, var, arg);
1189: else if (strcasecmp(section, "[CHANNEL]") == 0)
1190: Handle_CHANNEL(File, line, var, arg);
1191: else
1192: Config_Error(LOG_ERR,
1193: "%s, line %d: Variable \"%s\" outside section!",
1194: File, line, var);
1195: }
1196: }
1197:
1198: /**
1199: * Check whether a string argument is "true" or "false".
1200: *
1201: * @param Arg Input string.
1202: * @returns true if the input string has been parsed as "yes", "true"
1203: * (case insensitive) or a non-zero integer value.
1204: */
1205: static bool
1206: Check_ArgIsTrue(const char *Arg)
1207: {
1208: if (strcasecmp(Arg, "yes") == 0)
1209: return true;
1210: if (strcasecmp(Arg, "true") == 0)
1211: return true;
1212: if (atoi(Arg) != 0)
1213: return true;
1214:
1215: return false;
1216: }
1217:
1218: /**
1219: * Handle setting of "MaxNickLength".
1220: *
1221: * @param Line Line number in configuration file.
1222: * @raram Arg Input string.
1223: * @returns New configured maximum nickname length.
1224: */
1225: static unsigned int
1226: Handle_MaxNickLength(const char *File, int Line, const char *Arg)
1227: {
1228: unsigned new;
1229:
1230: new = (unsigned) atoi(Arg) + 1;
1231: if (new > CLIENT_NICK_LEN) {
1232: Config_Error(LOG_WARNING,
1233: "%s, line %d: Value of \"MaxNickLength\" exceeds %u!",
1234: File, Line, CLIENT_NICK_LEN - 1);
1235: return CLIENT_NICK_LEN;
1236: }
1237: if (new < 2) {
1238: Config_Error(LOG_WARNING,
1239: "%s, line %d: Value of \"MaxNickLength\" must be at least 1!",
1240: File, Line);
1241: return 2;
1242: }
1243: return new;
1244: }
1245:
1246: /**
1247: * Output a warning messages if IDENT is configured but not compiled in.
1248: */
1249: static void
1250: WarnIdent(const char UNUSED *File, int UNUSED Line)
1251: {
1252: #ifndef IDENTAUTH
1253: if (Conf_Ident) {
1254: /* user has enabled ident lookups explicitly, but ... */
1255: Config_Error(LOG_WARNING,
1256: "%s: line %d: \"Ident = yes\", but ngircd was built without IDENT support!",
1257: File, Line);
1258: }
1259: #endif
1260: }
1261:
1262: /**
1263: * Output a warning messages if IPv6 is configured but not compiled in.
1264: */
1265: static void
1266: WarnIPv6(const char UNUSED *File, int UNUSED Line)
1267: {
1268: #ifndef WANT_IPV6
1269: if (Conf_ConnectIPv6) {
1270: /* user has enabled IPv6 explicitly, but ... */
1271: Config_Error(LOG_WARNING,
1272: "%s: line %d: \"ConnectIPv6 = yes\", but ngircd was built without IPv6 support!",
1273: File, Line);
1274: }
1275: #endif
1276: }
1277:
1278: /**
1279: * Output a warning messages if PAM is configured but not compiled in.
1280: */
1281: static void
1282: WarnPAM(const char UNUSED *File, int UNUSED Line)
1283: {
1284: #ifndef PAM
1285: if (Conf_PAM) {
1286: Config_Error(LOG_WARNING,
1287: "%s: line %d: \"PAM = yes\", but ngircd was built without PAM support!",
1288: File, Line);
1289: }
1290: #endif
1291: }
1292:
1293:
1294: /**
1295: * Handle variable in [Global] configuration section.
1296: *
1297: * @param Line Line numer in configuration file.
1298: * @param Var Variable name.
1299: * @param Arg Variable argument.
1300: */
1301: static void
1302: Handle_GLOBAL(const char *File, int Line, char *Var, char *Arg )
1303: {
1304: struct passwd *pwd;
1305: struct group *grp;
1306: size_t len;
1307: char *ptr;
1308:
1309: assert(File != NULL);
1310: assert(Line > 0);
1311: assert(Var != NULL);
1312: assert(Arg != NULL);
1313:
1314: if (strcasecmp(Var, "Name") == 0) {
1315: len = strlcpy(Conf_ServerName, Arg, sizeof(Conf_ServerName));
1316: if (len >= sizeof(Conf_ServerName))
1317: Config_Error_TooLong(File, Line, Var);
1318: return;
1319: }
1320: if (strcasecmp(Var, "AdminInfo1") == 0) {
1321: len = strlcpy(Conf_ServerAdmin1, Arg, sizeof(Conf_ServerAdmin1));
1322: if (len >= sizeof(Conf_ServerAdmin1))
1323: Config_Error_TooLong(File, Line, Var);
1324: return;
1325: }
1326: if (strcasecmp(Var, "AdminInfo2") == 0) {
1327: len = strlcpy(Conf_ServerAdmin2, Arg, sizeof(Conf_ServerAdmin2));
1328: if (len >= sizeof(Conf_ServerAdmin2))
1329: Config_Error_TooLong(File, Line, Var);
1330: return;
1331: }
1332: if (strcasecmp(Var, "AdminEMail") == 0) {
1333: len = strlcpy(Conf_ServerAdminMail, Arg,
1334: sizeof(Conf_ServerAdminMail));
1335: if (len >= sizeof(Conf_ServerAdminMail))
1336: Config_Error_TooLong(File, Line, Var);
1337: return;
1338: }
1339: if (strcasecmp(Var, "Info") == 0) {
1340: len = strlcpy(Conf_ServerInfo, Arg, sizeof(Conf_ServerInfo));
1341: if (len >= sizeof(Conf_ServerInfo))
1342: Config_Error_TooLong(File, Line, Var);
1343: return;
1344: }
1345: if (strcasecmp(Var, "HelpFile") == 0) {
1346: len = strlcpy(Conf_HelpFile, Arg, sizeof(Conf_HelpFile));
1347: if (len >= sizeof(Conf_HelpFile))
1348: Config_Error_TooLong(File, Line, Var);
1349: return;
1350: }
1351: if (strcasecmp(Var, "Listen") == 0) {
1352: if (Conf_ListenAddress) {
1353: Config_Error(LOG_ERR,
1354: "Multiple Listen= options, ignoring: %s",
1355: Arg);
1356: return;
1357: }
1358: Conf_ListenAddress = strdup_warn(Arg);
1359: /* If allocation fails, we're in trouble: we cannot ignore the
1360: * error -- otherwise ngircd would listen on all interfaces. */
1361: if (!Conf_ListenAddress) {
1362: Config_Error(LOG_ALERT,
1363: "%s exiting due to fatal errors!",
1364: PACKAGE_NAME);
1365: exit(1);
1366: }
1367: return;
1368: }
1369: if (strcasecmp(Var, "MotdFile") == 0) {
1370: len = strlcpy(Conf_MotdFile, Arg, sizeof(Conf_MotdFile));
1371: if (len >= sizeof(Conf_MotdFile))
1372: Config_Error_TooLong(File, Line, Var);
1373: return;
1374: }
1375: if (strcasecmp(Var, "MotdPhrase") == 0) {
1376: len = strlen(Arg);
1377: if (len == 0)
1378: return;
1379: if (len >= 127) {
1380: Config_Error_TooLong(File, Line, Var);
1381: return;
1382: }
1383: if (!array_copyb(&Conf_Motd, Arg, len + 1))
1384: Config_Error(LOG_WARNING,
1385: "%s, line %d: Could not append MotdPhrase: %s",
1386: File, Line, strerror(errno));
1387: Using_MotdFile = false;
1388: return;
1389: }
1390: if (strcasecmp(Var, "Network") == 0) {
1391: len = strlcpy(Conf_Network, Arg, sizeof(Conf_Network));
1392: if (len >= sizeof(Conf_Network))
1393: Config_Error_TooLong(File, Line, Var);
1394: ptr = strchr(Conf_Network, ' ');
1395: if (ptr) {
1396: Config_Error(LOG_WARNING,
1397: "%s, line %d: \"Network\" can't contain spaces!",
1398: File, Line);
1399: *ptr = '\0';
1400: }
1401: return;
1402: }
1403: if(strcasecmp(Var, "Password") == 0) {
1404: len = strlcpy(Conf_ServerPwd, Arg, sizeof(Conf_ServerPwd));
1405: if (len >= sizeof(Conf_ServerPwd))
1406: Config_Error_TooLong(File, Line, Var);
1407: return;
1408: }
1409: if (strcasecmp(Var, "PidFile") == 0) {
1410: len = strlcpy(Conf_PidFile, Arg, sizeof(Conf_PidFile));
1411: if (len >= sizeof(Conf_PidFile))
1412: Config_Error_TooLong(File, Line, Var);
1413: return;
1414: }
1415: if (strcasecmp(Var, "Ports") == 0) {
1416: ports_parse(&Conf_ListenPorts, File, Line, Arg);
1417: return;
1418: }
1419: if (strcasecmp(Var, "ServerGID") == 0) {
1420: grp = getgrnam(Arg);
1421: if (grp)
1422: Conf_GID = grp->gr_gid;
1423: else {
1424: Conf_GID = (unsigned int)atoi(Arg);
1425: if (!Conf_GID && strcmp(Arg, "0"))
1426: Config_Error(LOG_WARNING,
1427: "%s, line %d: Value of \"%s\" is not a valid group name or ID!",
1428: File, Line, Var);
1429: }
1430: return;
1431: }
1432: if (strcasecmp(Var, "ServerUID") == 0) {
1433: pwd = getpwnam(Arg);
1434: if (pwd)
1435: Conf_UID = pwd->pw_uid;
1436: else {
1437: Conf_UID = (unsigned int)atoi(Arg);
1438: if (!Conf_UID && strcmp(Arg, "0"))
1439: Config_Error(LOG_WARNING,
1440: "%s, line %d: Value of \"%s\" is not a valid user name or ID!",
1441: File, Line, Var);
1442: }
1443: return;
1444: }
1445:
1446: Config_Error_Section(File, Line, Var, "Global");
1447: }
1448:
1449: /**
1450: * Handle variable in [Limits] configuration section.
1451: *
1452: * @param Line Line numer in configuration file.
1453: * @param Var Variable name.
1454: * @param Arg Variable argument.
1455: */
1456: static void
1457: Handle_LIMITS(const char *File, int Line, char *Var, char *Arg)
1458: {
1459: assert(File != NULL);
1460: assert(Line > 0);
1461: assert(Var != NULL);
1462: assert(Arg != NULL);
1463:
1464: if (strcasecmp(Var, "ConnectRetry") == 0) {
1465: Conf_ConnectRetry = atoi(Arg);
1466: if (Conf_ConnectRetry < 5) {
1467: Config_Error(LOG_WARNING,
1468: "%s, line %d: Value of \"ConnectRetry\" too low!",
1469: File, Line);
1470: Conf_ConnectRetry = 5;
1471: }
1472: return;
1473: }
1474: if (strcasecmp(Var, "IdleTimeout") == 0) {
1475: Conf_IdleTimeout = atoi(Arg);
1476: if (!Conf_IdleTimeout && strcmp(Arg, "0"))
1477: Config_Error_NaN(File, Line, Var);
1478: return;
1479: }
1480: if (strcasecmp(Var, "MaxConnections") == 0) {
1481: Conf_MaxConnections = atoi(Arg);
1482: if (!Conf_MaxConnections && strcmp(Arg, "0"))
1483: Config_Error_NaN(File, Line, Var);
1484: return;
1485: }
1486: if (strcasecmp(Var, "MaxConnectionsIP") == 0) {
1487: Conf_MaxConnectionsIP = atoi(Arg);
1488: if (!Conf_MaxConnectionsIP && strcmp(Arg, "0"))
1489: Config_Error_NaN(File, Line, Var);
1490: return;
1491: }
1492: if (strcasecmp(Var, "MaxJoins") == 0) {
1493: Conf_MaxJoins = atoi(Arg);
1494: if (!Conf_MaxJoins && strcmp(Arg, "0"))
1495: Config_Error_NaN(File, Line, Var);
1496: return;
1497: }
1498: if (strcasecmp(Var, "MaxNickLength") == 0) {
1499: Conf_MaxNickLength = Handle_MaxNickLength(File, Line, Arg);
1500: return;
1501: }
1502: if (strcasecmp(Var, "MaxListSize") == 0) {
1503: Conf_MaxListSize = atoi(Arg);
1504: if (!Conf_MaxListSize && strcmp(Arg, "0"))
1505: Config_Error_NaN(File, Line, Var);
1506: return;
1507: }
1508: if (strcasecmp(Var, "MaxPenaltyTime") == 0) {
1509: Conf_MaxPenaltyTime = atol(Arg);
1510: if (Conf_MaxPenaltyTime < -1)
1511: Conf_MaxPenaltyTime = -1; /* "unlimited" */
1512: return;
1513: }
1514: if (strcasecmp(Var, "PingTimeout") == 0) {
1515: Conf_PingTimeout = atoi(Arg);
1516: if (Conf_PingTimeout < 5) {
1517: Config_Error(LOG_WARNING,
1518: "%s, line %d: Value of \"PingTimeout\" too low!",
1519: File, Line);
1520: Conf_PingTimeout = 5;
1521: }
1522: return;
1523: }
1524: if (strcasecmp(Var, "PongTimeout") == 0) {
1525: Conf_PongTimeout = atoi(Arg);
1526: if (Conf_PongTimeout < 5) {
1527: Config_Error(LOG_WARNING,
1528: "%s, line %d: Value of \"PongTimeout\" too low!",
1529: File, Line);
1530: Conf_PongTimeout = 5;
1531: }
1532: return;
1533: }
1534:
1535: Config_Error_Section(File, Line, Var, "Limits");
1536: }
1537:
1538: /**
1539: * Handle variable in [Options] configuration section.
1540: *
1541: * @param Line Line numer in configuration file.
1542: * @param Var Variable name.
1543: * @param Arg Variable argument.
1544: */
1545: static void
1546: Handle_OPTIONS(const char *File, int Line, char *Var, char *Arg)
1547: {
1548: size_t len;
1549: char *p;
1550:
1551: assert(File != NULL);
1552: assert(Line > 0);
1553: assert(Var != NULL);
1554: assert(Arg != NULL);
1555:
1556: if (strcasecmp(Var, "AllowedChannelTypes") == 0) {
1557: p = Arg;
1558: Conf_AllowedChannelTypes[0] = '\0';
1559: while (*p) {
1560: if (strchr(Conf_AllowedChannelTypes, *p)) {
1561: /* Prefix is already included; ignore it */
1562: p++;
1563: continue;
1564: }
1565:
1566: if (strchr(CHANTYPES, *p)) {
1567: len = strlen(Conf_AllowedChannelTypes) + 1;
1568: assert(len < sizeof(Conf_AllowedChannelTypes));
1569: Conf_AllowedChannelTypes[len - 1] = *p;
1570: Conf_AllowedChannelTypes[len] = '\0';
1571: } else {
1572: Config_Error(LOG_WARNING,
1573: "%s, line %d: Unknown channel prefix \"%c\" in \"AllowedChannelTypes\"!",
1574: File, Line, *p);
1575: }
1576: p++;
1577: }
1578: return;
1579: }
1580: if (strcasecmp(Var, "AllowRemoteOper") == 0) {
1581: Conf_AllowRemoteOper = Check_ArgIsTrue(Arg);
1582: return;
1583: }
1584: if (strcasecmp(Var, "ChrootDir") == 0) {
1585: len = strlcpy(Conf_Chroot, Arg, sizeof(Conf_Chroot));
1586: if (len >= sizeof(Conf_Chroot))
1587: Config_Error_TooLong(File, Line, Var);
1588: return;
1589: }
1590: if (strcasecmp(Var, "CloakHost") == 0) {
1591: len = strlcpy(Conf_CloakHost, Arg, sizeof(Conf_CloakHost));
1592: if (len >= sizeof(Conf_CloakHost))
1593: Config_Error_TooLong(File, Line, Var);
1594: return;
1595: }
1596: if (strcasecmp(Var, "CloakHostModeX") == 0) {
1597: len = strlcpy(Conf_CloakHostModeX, Arg, sizeof(Conf_CloakHostModeX));
1598: if (len >= sizeof(Conf_CloakHostModeX))
1599: Config_Error_TooLong(File, Line, Var);
1600: return;
1601: }
1602: if (strcasecmp(Var, "CloakHostSalt") == 0) {
1603: len = strlcpy(Conf_CloakHostSalt, Arg, sizeof(Conf_CloakHostSalt));
1604: if (len >= sizeof(Conf_CloakHostSalt))
1605: Config_Error_TooLong(File, Line, Var);
1606: return;
1607: }
1608: if (strcasecmp(Var, "CloakUserToNick") == 0) {
1609: Conf_CloakUserToNick = Check_ArgIsTrue(Arg);
1610: return;
1611: }
1612: if (strcasecmp(Var, "ConnectIPv6") == 0) {
1613: Conf_ConnectIPv6 = Check_ArgIsTrue(Arg);
1614: WarnIPv6(File, Line);
1615: return;
1616: }
1617: if (strcasecmp(Var, "ConnectIPv4") == 0) {
1618: Conf_ConnectIPv4 = Check_ArgIsTrue(Arg);
1619: return;
1620: }
1621: if (strcasecmp(Var, "DefaultUserModes") == 0) {
1622: p = Arg;
1623: Conf_DefaultUserModes[0] = '\0';
1624: while (*p) {
1625: if (strchr(Conf_DefaultUserModes, *p)) {
1626: /* Mode is already included; ignore it */
1627: p++;
1628: continue;
1629: }
1630:
1631: if (strchr(USERMODES, *p)) {
1632: len = strlen(Conf_DefaultUserModes) + 1;
1633: assert(len < sizeof(Conf_DefaultUserModes));
1634: Conf_DefaultUserModes[len - 1] = *p;
1635: Conf_DefaultUserModes[len] = '\0';
1636: } else {
1637: Config_Error(LOG_WARNING,
1638: "%s, line %d: Unknown user mode \"%c\" in \"DefaultUserModes\"!",
1639: File, Line, *p);
1640: }
1641: p++;
1642: }
1643: return;
1644: }
1645: if (strcasecmp(Var, "DNS") == 0) {
1646: Conf_DNS = Check_ArgIsTrue(Arg);
1647: return;
1648: }
1649: if (strcasecmp(Var, "Ident") == 0) {
1650: Conf_Ident = Check_ArgIsTrue(Arg);
1651: WarnIdent(File, Line);
1652: return;
1653: }
1654: if (strcasecmp(Var, "IncludeDir") == 0) {
1655: if (Conf_IncludeDir[0]) {
1656: Config_Error(LOG_ERR,
1657: "%s, line %d: Can't overwrite value of \"IncludeDir\" variable!",
1658: File, Line);
1659: return;
1660: }
1661: len = strlcpy(Conf_IncludeDir, Arg, sizeof(Conf_IncludeDir));
1662: if (len >= sizeof(Conf_IncludeDir))
1663: Config_Error_TooLong(File, Line, Var);
1664: return;
1665: }
1666: if (strcasecmp(Var, "MorePrivacy") == 0) {
1667: Conf_MorePrivacy = Check_ArgIsTrue(Arg);
1668: return;
1669: }
1670: if (strcasecmp(Var, "NoticeBeforeRegistration") == 0) {
1671: Conf_NoticeBeforeRegistration = Check_ArgIsTrue(Arg);
1672: return;
1673: }
1674: if (strcasecmp(Var, "OperCanUseMode") == 0) {
1675: Conf_OperCanMode = Check_ArgIsTrue(Arg);
1676: return;
1677: }
1678: if (strcasecmp(Var, "OperChanPAutoOp") == 0) {
1679: Conf_OperChanPAutoOp = Check_ArgIsTrue(Arg);
1680: return;
1681: }
1682: if (strcasecmp(Var, "OperServerMode") == 0) {
1683: Conf_OperServerMode = Check_ArgIsTrue(Arg);
1684: return;
1685: }
1686: if (strcasecmp(Var, "PAM") == 0) {
1687: Conf_PAM = Check_ArgIsTrue(Arg);
1688: WarnPAM(File, Line);
1689: return;
1690: }
1691: if (strcasecmp(Var, "PAMIsOptional") == 0 ) {
1692: Conf_PAMIsOptional = Check_ArgIsTrue(Arg);
1693: return;
1694: }
1695: if (strcasecmp(Var, "PAMServiceName") == 0) {
1696: len = strlcpy(Conf_PAMServiceName, Arg, sizeof(Conf_PAMServiceName));
1697: if (len >= sizeof(Conf_PAMServiceName))
1698: Config_Error_TooLong(File, Line, Var);
1699: return;
1700: }
1701: #ifndef STRICT_RFC
1702: if (strcasecmp(Var, "RequireAuthPing") == 0) {
1703: Conf_AuthPing = Check_ArgIsTrue(Arg);
1704: return;
1705: }
1706: #endif
1707: if (strcasecmp(Var, "ScrubCTCP") == 0) {
1708: Conf_ScrubCTCP = Check_ArgIsTrue(Arg);
1709: return;
1710: }
1711: #ifdef SYSLOG
1712: if (strcasecmp(Var, "SyslogFacility") == 0) {
1713: Conf_SyslogFacility = ngt_SyslogFacilityID(Arg,
1714: Conf_SyslogFacility);
1715: return;
1716: }
1717: #endif
1718: if (strcasecmp(Var, "WebircPassword") == 0) {
1719: len = strlcpy(Conf_WebircPwd, Arg, sizeof(Conf_WebircPwd));
1720: if (len >= sizeof(Conf_WebircPwd))
1721: Config_Error_TooLong(File, Line, Var);
1722: return;
1723: }
1724:
1725: Config_Error_Section(File, Line, Var, "Options");
1726: }
1727:
1728: #ifdef SSL_SUPPORT
1729:
1730: /**
1731: * Handle variable in [SSL] configuration section.
1732: *
1733: * @param Line Line numer in configuration file.
1734: * @param Var Variable name.
1735: * @param Arg Variable argument.
1736: */
1737: static void
1738: Handle_SSL(const char *File, int Line, char *Var, char *Arg)
1739: {
1740: assert(File != NULL);
1741: assert(Line > 0);
1742: assert(Var != NULL);
1743: assert(Arg != NULL);
1744:
1745: if (strcasecmp(Var, "CertFile") == 0) {
1746: assert(Conf_SSLOptions.CertFile == NULL);
1747: Conf_SSLOptions.CertFile = strdup_warn(Arg);
1748: return;
1749: }
1750: if (strcasecmp(Var, "DHFile") == 0) {
1751: assert(Conf_SSLOptions.DHFile == NULL);
1752: Conf_SSLOptions.DHFile = strdup_warn(Arg);
1753: return;
1754: }
1755: if (strcasecmp(Var, "KeyFile") == 0) {
1756: assert(Conf_SSLOptions.KeyFile == NULL);
1757: Conf_SSLOptions.KeyFile = strdup_warn(Arg);
1758: return;
1759: }
1760: if (strcasecmp(Var, "KeyFilePassword") == 0) {
1761: assert(array_bytes(&Conf_SSLOptions.KeyFilePassword) == 0);
1762: if (!array_copys(&Conf_SSLOptions.KeyFilePassword, Arg))
1763: Config_Error(LOG_ERR,
1764: "%s, line %d (section \"SSL\"): Could not copy %s: %s!",
1765: File, Line, Var, strerror(errno));
1766: return;
1767: }
1768: if (strcasecmp(Var, "Ports") == 0) {
1769: ports_parse(&Conf_SSLOptions.ListenPorts, File, Line, Arg);
1770: return;
1771: }
1772: if (strcasecmp(Var, "CipherList") == 0) {
1773: assert(Conf_SSLOptions.CipherList == NULL);
1774: Conf_SSLOptions.CipherList = strdup_warn(Arg);
1775: return;
1776: }
1777:
1778: Config_Error_Section(File, Line, Var, "SSL");
1779: }
1780:
1781: #endif
1782:
1783: /**
1784: * Handle variable in [Operator] configuration section.
1785: *
1786: * @param Line Line numer in configuration file.
1787: * @param Var Variable name.
1788: * @param Arg Variable argument.
1789: */
1790: static void
1791: Handle_OPERATOR(const char *File, int Line, char *Var, char *Arg )
1792: {
1793: size_t len;
1794: struct Conf_Oper *op;
1795:
1796: assert( File != NULL );
1797: assert( Line > 0 );
1798: assert( Var != NULL );
1799: assert( Arg != NULL );
1800:
1801: op = array_get(&Conf_Opers, sizeof(*op),
1802: array_length(&Conf_Opers, sizeof(*op)) - 1);
1803: if (!op)
1804: return;
1805:
1806: if (strcasecmp(Var, "Name") == 0) {
1807: /* Name of IRC operator */
1808: len = strlcpy(op->name, Arg, sizeof(op->name));
1809: if (len >= sizeof(op->name))
1810: Config_Error_TooLong(File, Line, Var);
1811: return;
1812: }
1813: if (strcasecmp(Var, "Password") == 0) {
1814: /* Password of IRC operator */
1815: len = strlcpy(op->pwd, Arg, sizeof(op->pwd));
1816: if (len >= sizeof(op->pwd))
1817: Config_Error_TooLong(File, Line, Var);
1818: return;
1819: }
1820: if (strcasecmp(Var, "Mask") == 0) {
1821: if (op->mask)
1822: return; /* Hostname already configured */
1823: op->mask = strdup_warn( Arg );
1824: return;
1825: }
1826:
1827: Config_Error_Section(File, Line, Var, "Operator");
1828: }
1829:
1830: /**
1831: * Handle variable in [Server] configuration section.
1832: *
1833: * @param Line Line numer in configuration file.
1834: * @param Var Variable name.
1835: * @param Arg Variable argument.
1836: */
1837: static void
1838: Handle_SERVER(const char *File, int Line, char *Var, char *Arg )
1839: {
1840: long port;
1841: size_t len;
1842:
1843: assert( File != NULL );
1844: assert( Line > 0 );
1845: assert( Var != NULL );
1846: assert( Arg != NULL );
1847:
1848: /* Ignore server block if no space is left in server configuration structure */
1849: if( New_Server_Idx <= NONE ) return;
1850:
1851: if( strcasecmp( Var, "Host" ) == 0 ) {
1852: /* Hostname of the server */
1853: len = strlcpy( New_Server.host, Arg, sizeof( New_Server.host ));
1854: if (len >= sizeof( New_Server.host ))
1855: Config_Error_TooLong(File, Line, Var);
1856: return;
1857: }
1858: if( strcasecmp( Var, "Name" ) == 0 ) {
1859: /* Name of the server ("Nick"/"ID") */
1860: len = strlcpy( New_Server.name, Arg, sizeof( New_Server.name ));
1861: if (len >= sizeof( New_Server.name ))
1862: Config_Error_TooLong(File, Line, Var);
1863: return;
1864: }
1865: if (strcasecmp(Var, "Bind") == 0) {
1866: if (ng_ipaddr_init(&New_Server.bind_addr, Arg, 0))
1867: return;
1868:
1869: Config_Error(LOG_ERR, "%s, line %d (section \"Server\"): Can't parse IP address \"%s\"",
1870: File, Line, Arg);
1871: return;
1872: }
1873: if( strcasecmp( Var, "MyPassword" ) == 0 ) {
1874: /* Password of this server which is sent to the peer */
1875: if (*Arg == ':') {
1876: Config_Error(LOG_ERR,
1877: "%s, line %d (section \"Server\"): MyPassword must not start with ':'!",
1878: File, Line);
1879: }
1880: len = strlcpy( New_Server.pwd_in, Arg, sizeof( New_Server.pwd_in ));
1881: if (len >= sizeof( New_Server.pwd_in ))
1882: Config_Error_TooLong(File, Line, Var);
1883: return;
1884: }
1885: if( strcasecmp( Var, "PeerPassword" ) == 0 ) {
1886: /* Passwort of the peer which must be received */
1887: len = strlcpy( New_Server.pwd_out, Arg, sizeof( New_Server.pwd_out ));
1888: if (len >= sizeof( New_Server.pwd_out ))
1889: Config_Error_TooLong(File, Line, Var);
1890: return;
1891: }
1892: if( strcasecmp( Var, "Port" ) == 0 ) {
1893: /* Port to which this server should connect */
1894: port = atol( Arg );
1895: if (port >= 0 && port < 0xFFFF)
1896: New_Server.port = (UINT16)port;
1897: else
1898: Config_Error(LOG_ERR,
1899: "%s, line %d (section \"Server\"): Illegal port number %ld!",
1900: File, Line, port );
1901: return;
1902: }
1903: #ifdef SSL_SUPPORT
1904: if( strcasecmp( Var, "SSLConnect" ) == 0 ) {
1905: New_Server.SSLConnect = Check_ArgIsTrue(Arg);
1906: return;
1907: }
1908: #endif
1909: if( strcasecmp( Var, "Group" ) == 0 ) {
1910: /* Server group */
1911: New_Server.group = atoi( Arg );
1912: if (!New_Server.group && strcmp(Arg, "0"))
1913: Config_Error_NaN(File, Line, Var);
1914: return;
1915: }
1916: if( strcasecmp( Var, "Passive" ) == 0 ) {
1917: if (Check_ArgIsTrue(Arg))
1918: New_Server.flags |= CONF_SFLAG_DISABLED;
1919: return;
1920: }
1921: if (strcasecmp(Var, "ServiceMask") == 0) {
1922: len = strlcpy(New_Server.svs_mask, ngt_LowerStr(Arg),
1923: sizeof(New_Server.svs_mask));
1924: if (len >= sizeof(New_Server.svs_mask))
1925: Config_Error_TooLong(File, Line, Var);
1926: return;
1927: }
1928:
1929: Config_Error_Section(File, Line, Var, "Server");
1930: }
1931:
1932: /**
1933: * Copy channel name into channel structure.
1934: *
1935: * If the channel name is not valid because of a missing prefix ('#', '&'),
1936: * a default prefix of '#' will be added.
1937: *
1938: * @param new_chan New already allocated channel structure.
1939: * @param name Name of the new channel.
1940: * @returns true on success, false otherwise.
1941: */
1942: static bool
1943: Handle_Channelname(struct Conf_Channel *new_chan, const char *name)
1944: {
1945: size_t size = sizeof(new_chan->name);
1946: char *dest = new_chan->name;
1947:
1948: if (!Channel_IsValidName(name)) {
1949: /*
1950: * maybe user forgot to add a '#'.
1951: * This is only here for user convenience.
1952: */
1953: *dest = '#';
1954: --size;
1955: ++dest;
1956: }
1957: return size > strlcpy(dest, name, size);
1958: }
1959:
1960: /**
1961: * Handle variable in [Channel] configuration section.
1962: *
1963: * @param Line Line numer in configuration file.
1964: * @param Var Variable name.
1965: * @param Arg Variable argument.
1966: */
1967: static void
1968: Handle_CHANNEL(const char *File, int Line, char *Var, char *Arg)
1969: {
1970: size_t len;
1971: struct Conf_Channel *chan;
1972:
1973: assert( File != NULL );
1974: assert( Line > 0 );
1975: assert( Var != NULL );
1976: assert( Arg != NULL );
1977:
1978: chan = array_get(&Conf_Channels, sizeof(*chan),
1979: array_length(&Conf_Channels, sizeof(*chan)) - 1);
1980: if (!chan)
1981: return;
1982:
1983: if (strcasecmp(Var, "Name") == 0) {
1984: if (!Handle_Channelname(chan, Arg))
1985: Config_Error_TooLong(File, Line, Var);
1986: return;
1987: }
1988: if (strcasecmp(Var, "Modes") == 0) {
1989: /* Initial modes */
1990: if(chan->modes_num >= sizeof(chan->modes)) {
1991: Config_Error(LOG_ERR, "Too many Modes, option ignored.");
1992: return;
1993: }
1994: chan->modes[chan->modes_num++] = strndup(Arg, COMMAND_LEN);
1995: if(strlen(Arg) >= COMMAND_LEN)
1996: Config_Error_TooLong(File, Line, Var);
1997: return;
1998: }
1999: if( strcasecmp( Var, "Topic" ) == 0 ) {
2000: /* Initial topic */
2001: len = strlcpy(chan->topic, Arg, sizeof(chan->topic));
2002: if (len >= sizeof(chan->topic))
2003: Config_Error_TooLong(File, Line, Var);
2004: return;
2005: }
2006: if( strcasecmp( Var, "Key" ) == 0 ) {
2007: /* Initial Channel Key (mode k) */
2008: len = strlcpy(chan->key, Arg, sizeof(chan->key));
2009: if (len >= sizeof(chan->key))
2010: Config_Error_TooLong(File, Line, Var);
2011: Config_Error(LOG_WARNING,
2012: "%s, line %d (section \"Channel\"): \"%s\" is deprecated here, use \"Modes = +k <key>\"!",
2013: File, Line, Var);
2014: return;
2015: }
2016: if( strcasecmp( Var, "MaxUsers" ) == 0 ) {
2017: /* maximum user limit, mode l */
2018: chan->maxusers = (unsigned long) atol(Arg);
2019: if (!chan->maxusers && strcmp(Arg, "0"))
2020: Config_Error_NaN(File, Line, Var);
2021: Config_Error(LOG_WARNING,
2022: "%s, line %d (section \"Channel\"): \"%s\" is deprecated here, use \"Modes = +l <limit>\"!",
2023: File, Line, Var);
2024: return;
2025: }
2026: if (strcasecmp(Var, "KeyFile") == 0) {
2027: /* channel keys */
2028: len = strlcpy(chan->keyfile, Arg, sizeof(chan->keyfile));
2029: if (len >= sizeof(chan->keyfile))
2030: Config_Error_TooLong(File, Line, Var);
2031: return;
2032: }
2033:
2034: Config_Error_Section(File, Line, Var, "Channel");
2035: }
2036:
2037: /**
2038: * Validate server configuration.
2039: *
2040: * Please note that this function uses exit(1) on fatal errors and therefore
2041: * can result in ngIRCd terminating!
2042: *
2043: * @param Configtest true if the daemon has been called with "--configtest".
2044: * @param Rehash true if re-reading configuration on runtime.
2045: * @returns true if configuration is valid.
2046: */
2047: static bool
2048: Validate_Config(bool Configtest, bool Rehash)
2049: {
2050: /* Validate configuration settings. */
2051:
2052: #ifdef DEBUG
2053: int i, servers, servers_once;
2054: #endif
2055: bool config_valid = true;
2056: char *ptr;
2057:
2058: /* Emit a warning when the config file is not a full path name */
2059: if (NGIRCd_ConfFile[0] && NGIRCd_ConfFile[0] != '/') {
2060: Config_Error(LOG_WARNING,
2061: "Not specifying a full path name to \"%s\" can cause problems when rehashing the server!",
2062: NGIRCd_ConfFile);
2063: }
2064:
2065: /* Validate configured server name, see RFC 2812 section 2.3.1 */
2066: ptr = Conf_ServerName;
2067: do {
2068: if (*ptr >= 'a' && *ptr <= 'z') continue;
2069: if (*ptr >= 'A' && *ptr <= 'Z') continue;
2070: if (*ptr >= '0' && *ptr <= '9') continue;
2071: if (ptr > Conf_ServerName) {
2072: if (*ptr == '.' || *ptr == '-')
2073: continue;
2074: }
2075: Conf_ServerName[0] = '\0';
2076: break;
2077: } while (*(++ptr));
2078:
2079: if (!Conf_ServerName[0] || !strchr(Conf_ServerName, '.'))
2080: {
2081: /* No server name configured! */
2082: config_valid = false;
2083: Config_Error(LOG_ALERT,
2084: "No (valid) server name configured in \"%s\" (section 'Global': 'Name')!",
2085: NGIRCd_ConfFile);
2086: if (!Configtest && !Rehash) {
2087: Config_Error(LOG_ALERT,
2088: "%s exiting due to fatal errors!",
2089: PACKAGE_NAME);
2090: exit(1);
2091: }
2092: }
2093:
2094: #ifdef STRICT_RFC
2095: if (!Conf_ServerAdminMail[0]) {
2096: /* No administrative contact configured! */
2097: config_valid = false;
2098: Config_Error(LOG_ALERT,
2099: "No administrator email address configured in \"%s\" ('AdminEMail')!",
2100: NGIRCd_ConfFile);
2101: if (!Configtest) {
2102: Config_Error(LOG_ALERT,
2103: "%s exiting due to fatal errors!",
2104: PACKAGE_NAME);
2105: exit(1);
2106: }
2107: }
2108: #endif
2109:
2110: if (!Conf_ServerAdmin1[0] && !Conf_ServerAdmin2[0]
2111: && !Conf_ServerAdminMail[0]) {
2112: /* No administrative information configured! */
2113: Config_Error(LOG_WARNING,
2114: "No administrative information configured but required by RFC!");
2115: }
2116:
2117: #ifdef PAM
2118: if (Conf_PAM && Conf_ServerPwd[0])
2119: Config_Error(LOG_ERR,
2120: "This server uses PAM, \"Password\" in [Global] section will be ignored!");
2121: #endif
2122:
2123: if (Conf_MaxPenaltyTime != -1)
2124: Config_Error(LOG_WARNING,
2125: "Maximum penalty increase ('MaxPenaltyTime') is set to %ld, this is not recommended!",
2126: Conf_MaxPenaltyTime);
2127:
2128: #ifdef DEBUG
2129: servers = servers_once = 0;
2130: for (i = 0; i < MAX_SERVERS; i++) {
2131: if (Conf_Server[i].name[0]) {
2132: servers++;
2133: if (Conf_Server[i].flags & CONF_SFLAG_ONCE)
2134: servers_once++;
2135: }
2136: }
2137: Log(LOG_DEBUG,
2138: "Configuration: Operators=%ld, Servers=%d[%d], Channels=%ld",
2139: array_length(&Conf_Opers, sizeof(struct Conf_Oper)),
2140: servers, servers_once,
2141: array_length(&Conf_Channels, sizeof(struct Conf_Channel)));
2142: #endif
2143:
2144: return config_valid;
2145: }
2146:
2147: /**
2148: * Output "line too long" warning.
2149: *
2150: * @param Line Line number in configuration file.
2151: * @param Item Affected variable name.
2152: */
2153: static void
2154: Config_Error_TooLong(const char *File, const int Line, const char *Item)
2155: {
2156: Config_Error(LOG_WARNING, "%s, line %d: Value of \"%s\" too long!",
2157: File, Line, Item );
2158: }
2159:
2160: /**
2161: * Output "unknown variable" warning.
2162: *
2163: * @param Line Line number in configuration file.
2164: * @param Item Affected variable name.
2165: * @param Section Section name.
2166: */
2167: static void
2168: Config_Error_Section(const char *File, const int Line, const char *Item,
2169: const char *Section)
2170: {
2171: Config_Error(LOG_ERR, "%s, line %d (section \"%s\"): Unknown variable \"%s\"!",
2172: File, Line, Section, Item);
2173: }
2174:
2175: /**
2176: * Output "not a number" warning.
2177: *
2178: * @param Line Line number in configuration file.
2179: * @param Item Affected variable name.
2180: */
2181: static void
2182: Config_Error_NaN(const char *File, const int Line, const char *Item )
2183: {
2184: Config_Error(LOG_WARNING, "%s, line %d: Value of \"%s\" is not a number!",
2185: File, Line, Item );
2186: }
2187:
2188: /**
2189: * Output configuration error to console and/or logfile.
2190: *
2191: * On runtime, the normal log functions of the daemon are used. But when
2192: * testing the configuration ("--configtest"), all messages go directly
2193: * to the console.
2194: *
2195: * @param Level Severity level of the message.
2196: * @param Format Format string; see printf() function.
2197: */
2198: #ifdef PROTOTYPES
2199: static void Config_Error( const int Level, const char *Format, ... )
2200: #else
2201: static void Config_Error( Level, Format, va_alist )
2202: const int Level;
2203: const char *Format;
2204: va_dcl
2205: #endif
2206: {
2207: char msg[MAX_LOG_MSG_LEN];
2208: va_list ap;
2209:
2210: assert( Format != NULL );
2211:
2212: #ifdef PROTOTYPES
2213: va_start( ap, Format );
2214: #else
2215: va_start( ap );
2216: #endif
2217: vsnprintf( msg, MAX_LOG_MSG_LEN, Format, ap );
2218: va_end( ap );
2219:
2220: if (!Use_Log) {
2221: if (Level <= LOG_WARNING)
2222: printf(" - %s\n", msg);
2223: else
2224: puts(msg);
2225: } else
2226: Log(Level, "%s", msg);
2227: }
2228:
2229: #ifdef DEBUG
2230:
2231: /**
2232: * Dump internal state of the "configuration module".
2233: */
2234: GLOBAL void
2235: Conf_DebugDump(void)
2236: {
2237: int i;
2238:
2239: Log(LOG_DEBUG, "Configured servers:");
2240: for (i = 0; i < MAX_SERVERS; i++) {
2241: if (! Conf_Server[i].name[0])
2242: continue;
2243: Log(LOG_DEBUG,
2244: " - %s: %s:%d, last=%ld, group=%d, flags=%d, conn=%d",
2245: Conf_Server[i].name, Conf_Server[i].host,
2246: Conf_Server[i].port, Conf_Server[i].lasttry,
2247: Conf_Server[i].group, Conf_Server[i].flags,
2248: Conf_Server[i].conn_id);
2249: }
2250: }
2251:
2252: #endif
2253:
2254: /**
2255: * Initialize server configuration structure to default values.
2256: *
2257: * @param Server Pointer to server structure to initialize.
2258: */
2259: static void
2260: Init_Server_Struct( CONF_SERVER *Server )
2261: {
2262: assert( Server != NULL );
2263:
2264: memset( Server, 0, sizeof (CONF_SERVER) );
2265:
2266: Server->group = NONE;
2267: Server->lasttry = time( NULL ) - Conf_ConnectRetry + STARTUP_DELAY;
2268:
2269: if( NGIRCd_Passive ) Server->flags = CONF_SFLAG_DISABLED;
2270:
2271: Proc_InitStruct(&Server->res_stat);
2272: Server->conn_id = NONE;
2273: memset(&Server->bind_addr, 0, sizeof(Server->bind_addr));
2274: }
2275:
2276: /* -eof- */
CVSweb