2 * IRC - Internet Relay Chat, ircd/s_conf.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 1, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * @brief ircd configuration file driver
22 * @version $Id: s_conf.c,v 1.81.2.3 2006/02/16 03:49:54 entrope Exp $
31 #include "ircd_features.h"
36 #include "ircd_alloc.h"
37 #include "ircd_chattr.h"
39 #include "ircd_reply.h"
40 #include "ircd_snprintf.h"
41 #include "ircd_string.h"
59 /* #include <assert.h> -- Now using assert in ircd_log.h */
69 /** Global list of all ConfItem structures. */
70 struct ConfItem
*GlobalConfList
;
71 /** Count of items in #GlobalConfList. */
73 /** Global list of service mappings. */
74 struct s_map
*GlobalServiceMapList
;
75 /** Global list of channel quarantines. */
76 struct qline
*GlobalQuarantineList
;
77 /** Global list of spoofhosts. */
78 struct sline
*GlobalSList
= 0;
80 /** Current line number in scanner input. */
83 /** Configuration information for #me. */
84 struct LocalConf localConf
;
85 /** Global list of connection rules. */
86 struct CRuleConf
* cruleConfList
;
87 /** Global list of K-lines. */
88 struct DenyConf
* denyConfList
;
90 /** Tell a user that they are banned, dumping the message from a file.
91 * @param sptr Client being rejected
92 * @param filename Send this file's contents to \a sptr
94 static void killcomment(struct Client
* sptr
, const char* filename
)
101 if (NULL
== (file
= fbopen(filename
, "r"))) {
102 send_reply(sptr
, ERR_NOMOTD
);
103 send_reply(sptr
, SND_EXPLICIT
| ERR_YOUREBANNEDCREEP
,
104 ":Connection from your host is refused on this server.");
108 tm
= localtime((time_t*) &sb
.st_mtime
); /* NetBSD needs cast */
109 while (fbgets(line
, sizeof(line
) - 1, file
)) {
110 char* end
= line
+ strlen(line
);
113 if ('\n' == *end
|| '\r' == *end
)
118 send_reply(sptr
, RPL_MOTD
, line
);
120 send_reply(sptr
, SND_EXPLICIT
| ERR_YOUREBANNEDCREEP
,
121 ":Connection from your host is refused on this server.");
125 /** Allocate a new struct ConfItem and link it to #GlobalConfList.
126 * @return Newly allocated structure.
128 struct ConfItem
* make_conf(int type
)
130 struct ConfItem
* aconf
;
132 aconf
= (struct ConfItem
*) MyMalloc(sizeof(struct ConfItem
));
135 memset(aconf
, 0, sizeof(struct ConfItem
));
136 aconf
->status
= type
;
137 aconf
->next
= GlobalConfList
;
138 GlobalConfList
= aconf
;
142 /** Free a struct ConfItem and any resources it owns.
143 * @param aconf Item to free.
145 void free_conf(struct ConfItem
*aconf
)
147 Debug((DEBUG_DEBUG
, "free_conf: %s %s %d",
148 aconf
->host
? aconf
->host
: "*",
149 aconf
->name
? aconf
->name
: "*",
150 aconf
->address
.port
));
151 if (aconf
->dns_pending
)
152 delete_resolver_queries(aconf
);
153 MyFree(aconf
->username
);
155 MyFree(aconf
->origin_name
);
157 memset(aconf
->passwd
, 0, strlen(aconf
->passwd
));
158 MyFree(aconf
->passwd
);
160 MyFree(aconf
->hub_limit
);
165 /** Disassociate configuration from the client.
166 * @param cptr Client to operate on.
167 * @param aconf ConfItem to detach.
169 static void detach_conf(struct Client
* cptr
, struct ConfItem
* aconf
)
176 assert(0 < aconf
->clients
);
178 lp
= &(cli_confs(cptr
));
181 if ((*lp
)->value
.aconf
== aconf
) {
182 if (aconf
->conn_class
&& (aconf
->status
& CONF_CLIENT_MASK
) && ConfLinks(aconf
) > 0)
185 assert(0 < aconf
->clients
);
186 if (0 == --aconf
->clients
&& IsIllegal(aconf
))
197 /** Parse a user\@host mask into username and host or IP parts.
198 * If \a host contains no username part, set \a aconf->username to
199 * NULL. If the host part of \a host looks like an IP mask, set \a
200 * aconf->addrbits and \a aconf->address to match. Otherwise, set
201 * \a aconf->host, and set \a aconf->addrbits to -1.
202 * @param[in,out] aconf Configuration item to set.
203 * @param[in] host user\@host mask to parse.
205 void conf_parse_userhost(struct ConfItem
*aconf
, char *host
)
208 unsigned char addrbits
;
210 MyFree(aconf
->username
);
212 host_part
= strchr(host
, '@');
215 DupString(aconf
->username
, host
);
218 aconf
->username
= NULL
;
221 DupString(aconf
->host
, host_part
);
222 if (ipmask_parse(aconf
->host
, &aconf
->address
.addr
, &addrbits
))
223 aconf
->addrbits
= addrbits
;
225 aconf
->addrbits
= -1;
229 /** Copies a completed DNS query into its ConfItem.
230 * @param vptr Pointer to struct ConfItem for the block.
231 * @param hp DNS reply, or NULL if the lookup failed.
233 static void conf_dns_callback(void* vptr
, const struct irc_in_addr
*addr
, const char *h_name
)
235 struct ConfItem
* aconf
= (struct ConfItem
*) vptr
;
237 aconf
->dns_pending
= 0;
239 memcpy(&aconf
->address
.addr
, addr
, sizeof(aconf
->address
.addr
));
242 /** Start a nameserver lookup of the conf host. If the conf entry is
243 * currently doing a lookup, do nothing.
244 * @param aconf ConfItem for which to start a request.
246 static void conf_dns_lookup(struct ConfItem
* aconf
)
248 if (!aconf
->dns_pending
) {
249 char buf
[HOSTLEN
+ 1];
251 host_from_uh(buf
, aconf
->host
, HOSTLEN
);
252 gethost_byname(buf
, conf_dns_callback
, aconf
);
253 aconf
->dns_pending
= 1;
258 /** Start lookups of all addresses in the conf line. The origin must
259 * be a numeric IP address. If the remote host field is not an IP
260 * address, start a DNS lookup for it.
261 * @param aconf Connection to do lookups for.
264 lookup_confhost(struct ConfItem
*aconf
)
266 if (EmptyString(aconf
->host
) || EmptyString(aconf
->name
)) {
267 Debug((DEBUG_ERROR
, "Host/server name error: (%s) (%s)",
268 aconf
->host
, aconf
->name
));
271 if (aconf
->origin_name
272 && !ircd_aton(&aconf
->origin
.addr
, aconf
->origin_name
)) {
273 Debug((DEBUG_ERROR
, "Origin name error: (%s) (%s)",
274 aconf
->origin_name
, aconf
->name
));
277 * Do name lookup now on hostnames given and store the
278 * ip numbers in conf structure.
280 if (IsIP6Char(*aconf
->host
)) {
281 if (!ircd_aton(&aconf
->address
.addr
, aconf
->host
)) {
282 Debug((DEBUG_ERROR
, "Host/server name error: (%s) (%s)",
283 aconf
->host
, aconf
->name
));
287 conf_dns_lookup(aconf
);
290 /** Find a server by name or hostname.
291 * @param name Server name to find.
292 * @return Pointer to the corresponding ConfItem, or NULL if none exists.
294 struct ConfItem
* conf_find_server(const char* name
)
296 struct ConfItem
* conf
;
299 for (conf
= GlobalConfList
; conf
; conf
= conf
->next
) {
300 if (CONF_SERVER
== conf
->status
) {
302 * Check first servernames, then try hostnames.
303 * XXX - match returns 0 if there _is_ a match... guess they
304 * haven't decided what true is yet
306 if (0 == match(name
, conf
->name
))
313 /** Evaluate connection rules.
314 * @param name Name of server to check
315 * @param mask Filter for CRule types (only consider if type & \a mask != 0).
316 * @return Name of rule that forbids the connection; NULL if no prohibitions.
318 const char* conf_eval_crule(const char* name
, int mask
)
320 struct CRuleConf
* p
= cruleConfList
;
323 for ( ; p
; p
= p
->next
) {
324 if (0 != (p
->type
& mask
) && 0 == match(p
->hostmask
, name
)) {
325 if (crule_eval(p
->node
))
332 /** Remove all conf entries from the client except those which match
333 * the status field mask.
334 * @param cptr Client to operate on.
335 * @param mask ConfItem types to keep.
337 void det_confs_butmask(struct Client
* cptr
, int mask
)
343 for (link
= cli_confs(cptr
); link
; link
= next
) {
345 if ((link
->value
.aconf
->status
& mask
) == 0)
346 detach_conf(cptr
, link
->value
.aconf
);
350 /** Find the first (best) Client block to attach.
351 * @param cptr Client for whom to check rules.
352 * @return Authorization check result.
354 enum AuthorizationCheckResult
attach_iline(struct Client
* cptr
)
356 struct ConfItem
* aconf
;
360 for (aconf
= GlobalConfList
; aconf
; aconf
= aconf
->next
) {
361 if (aconf
->status
!= CONF_CLIENT
)
363 /* If you change any of this logic, please make corresponding
364 * changes in conf_debug_iline() below.
366 if (aconf
->address
.port
&& aconf
->address
.port
!= cli_listener(cptr
)->addr
.port
)
368 if (aconf
->username
&& match(aconf
->username
, cli_username(cptr
)))
370 if (aconf
->host
&& match(aconf
->host
, cli_sockhost(cptr
)))
372 if ((aconf
->addrbits
>= 0)
373 && !ipmask_check(&cli_ip(cptr
), &aconf
->address
.addr
, aconf
->addrbits
))
375 if (IPcheck_nr(cptr
) > aconf
->maximum
)
376 return ACR_TOO_MANY_FROM_IP
;
378 SetFlag(cptr
, FLAG_DOID
);
379 return attach_conf(cptr
, aconf
);
381 return ACR_NO_AUTHORIZATION
;
384 /** Interpret \a client as a client specifier and show which Client
385 * block(s) match that client.
387 * The client specifier may contain an IP address, hostname, listener
388 * port, or a combination of those separated by commas. IP addresses
389 * and hostnamese may be preceded by "username@"; the last given
390 * username will be used for the match.
392 * @param[in] client Client specifier.
393 * @return Matching Client block structure.
395 struct ConfItem
*conf_debug_iline(const char *client
)
397 struct irc_in_addr address
;
398 struct ConfItem
*aconf
;
399 struct DenyConf
*deny
;
401 unsigned short listener
;
402 char username
[USERLEN
+1], hostname
[HOSTLEN
+1], realname
[REALLEN
+1];
404 /* Initialize variables. */
406 memset(&address
, 0, sizeof(address
));
407 memset(&username
, 0, sizeof(username
));
408 memset(&hostname
, 0, sizeof(hostname
));
409 memset(&realname
, 0, sizeof(realname
));
411 /* Parse client specifier. */
413 struct irc_in_addr tmpaddr
;
416 /* Try to parse as listener port number first. */
417 tmp
= strtol(client
, &sep
, 10);
418 if (tmp
&& (*sep
== '\0' || *sep
== ',')) {
420 client
= sep
+ (*sep
!= '\0');
424 /* Maybe username@ before an IP address or hostname? */
425 tmp
= strcspn(client
, ",@");
426 if (client
[tmp
] == '@') {
429 ircd_strncpy(username
, client
, tmp
);
430 /* and fall through */
434 /* Looks like an IP address? */
435 tmp
= ircd_aton(&tmpaddr
, client
);
436 if (tmp
&& (client
[tmp
] == '\0' || client
[tmp
] == ',')) {
437 memcpy(&address
, &tmpaddr
, sizeof(address
));
438 client
+= tmp
+ (client
[tmp
] != '\0');
443 if (client
[0] == '$' && client
[1] == 'R') {
445 for (tmp
= 0; *client
!= '\0' && *client
!= ',' && tmp
< REALLEN
; ++client
, ++tmp
) {
447 realname
[tmp
] = *++client
;
449 realname
[tmp
] = *client
;
454 /* Else must be a hostname. */
455 tmp
= strcspn(client
, ",");
458 ircd_strncpy(hostname
, client
, tmp
);
459 client
+= tmp
+ (client
[tmp
] != '\0');
462 /* Walk configuration to find matching Client block. */
463 for (aconf
= GlobalConfList
; aconf
; aconf
= aconf
->next
) {
464 if (aconf
->status
!= CONF_CLIENT
)
466 if (aconf
->address
.port
&& aconf
->address
.port
!= listener
) {
467 fprintf(stdout
, "Listener port mismatch: %u != %u\n", aconf
->address
.port
, listener
);
470 if (aconf
->username
&& match(aconf
->username
, username
)) {
471 fprintf(stdout
, "Username mismatch: %s != %s\n", aconf
->username
, username
);
474 if (aconf
->host
&& match(aconf
->host
, hostname
)) {
475 fprintf(stdout
, "Hostname mismatch: %s != %s\n", aconf
->host
, hostname
);
478 if ((aconf
->addrbits
>= 0)
479 && !ipmask_check(&address
, &aconf
->address
.addr
, aconf
->addrbits
)) {
480 fprintf(stdout
, "IP address mismatch: %s != %s\n", aconf
->name
, ircd_ntoa(&address
));
483 fprintf(stdout
, "Match! username=%s host=%s ip=%s class=%s maxlinks=%u password=%s\n",
484 (aconf
->username
? aconf
->username
: "(null)"),
485 (aconf
->host
? aconf
->host
: "(null)"),
486 (aconf
->name
? aconf
->name
: "(null)"),
487 ConfClass(aconf
), aconf
->maximum
,
488 (aconf
->passwd
? aconf
->passwd
: "(null)"));
492 /* If no authorization, say so and exit. */
495 fprintf(stdout
, "No authorization found.\n");
499 /* Look for a Kill block with the user's name on it. */
500 for (deny
= denyConfList
; deny
; deny
= deny
->next
) {
501 if (deny
->usermask
&& match(deny
->usermask
, username
))
503 if (deny
->realmask
&& match(deny
->realmask
, realname
))
505 if (deny
->bits
> 0) {
506 if (!ipmask_check(&address
, &deny
->address
, deny
->bits
))
508 } else if (deny
->hostmask
&& match(deny
->hostmask
, hostname
))
511 /* Looks like a match; report it. */
512 fprintf(stdout
, "Denied! usermask=%s realmask=\"%s\" hostmask=%s (bits=%u)\n",
513 deny
->usermask
? deny
->usermask
: "(null)",
514 deny
->realmask
? deny
->realmask
: "(null)",
515 deny
->hostmask
? deny
->hostmask
: "(null)",
522 /** Check whether a particular ConfItem is already attached to a
524 * @param aconf ConfItem to search for
525 * @param cptr Client to check
526 * @return Non-zero if \a aconf is attached to \a cptr, zero if not.
528 static int is_attached(struct ConfItem
*aconf
, struct Client
*cptr
)
532 for (lp
= cli_confs(cptr
); lp
; lp
= lp
->next
) {
533 if (lp
->value
.aconf
== aconf
)
539 /** Associate a specific configuration entry to a *local* client (this
540 * is the one which used in accepting the connection). Note, that this
541 * automatically changes the attachment if there was an old one...
542 * @param cptr Client to attach \a aconf to
543 * @param aconf ConfItem to attach
544 * @return Authorization check result.
546 enum AuthorizationCheckResult
attach_conf(struct Client
*cptr
, struct ConfItem
*aconf
)
550 if (is_attached(aconf
, cptr
))
551 return ACR_ALREADY_AUTHORIZED
;
552 if (IsIllegal(aconf
))
553 return ACR_NO_AUTHORIZATION
;
554 if ((aconf
->status
& (CONF_OPERATOR
| CONF_CLIENT
)) &&
555 ConfLinks(aconf
) >= ConfMaxLinks(aconf
) && ConfMaxLinks(aconf
) > 0)
556 return ACR_TOO_MANY_IN_CLASS
; /* Use this for printing error message */
558 lp
->next
= cli_confs(cptr
);
559 lp
->value
.aconf
= aconf
;
560 cli_confs(cptr
) = lp
;
562 if (aconf
->status
& CONF_CLIENT_MASK
)
567 /** Return our LocalConf configuration structure.
568 * @return A pointer to #localConf.
570 const struct LocalConf
* conf_get_local(void)
575 /** Attach ConfItems to a client if the name passed matches that for
576 * the ConfItems or is an exact match for them.
577 * @param cptr Client getting the ConfItem attachments.
578 * @param name Filter to match ConfItem::name.
579 * @param statmask Filter to limit ConfItem::status.
580 * @return First ConfItem attached to \a cptr.
582 struct ConfItem
* attach_confs_byname(struct Client
* cptr
, const char* name
,
585 struct ConfItem
* tmp
;
586 struct ConfItem
* first
= NULL
;
590 if (HOSTLEN
< strlen(name
))
593 for (tmp
= GlobalConfList
; tmp
; tmp
= tmp
->next
) {
594 if (0 != (tmp
->status
& statmask
) && !IsIllegal(tmp
)) {
595 assert(0 != tmp
->name
);
596 if (0 == match(tmp
->name
, name
) || 0 == ircd_strcmp(tmp
->name
, name
)) {
597 if (ACR_OK
== attach_conf(cptr
, tmp
) && !first
)
605 /** Attach ConfItems to a client if the host passed matches that for
606 * the ConfItems or is an exact match for them.
607 * @param cptr Client getting the ConfItem attachments.
608 * @param host Filter to match ConfItem::host.
609 * @param statmask Filter to limit ConfItem::status.
610 * @return First ConfItem attached to \a cptr.
612 struct ConfItem
* attach_confs_byhost(struct Client
* cptr
, const char* host
,
615 struct ConfItem
* tmp
;
616 struct ConfItem
* first
= 0;
619 if (HOSTLEN
< strlen(host
))
622 for (tmp
= GlobalConfList
; tmp
; tmp
= tmp
->next
) {
623 if (0 != (tmp
->status
& statmask
) && !IsIllegal(tmp
)) {
624 assert(0 != tmp
->host
);
625 if (0 == match(tmp
->host
, host
) || 0 == ircd_strcmp(tmp
->host
, host
)) {
626 if (ACR_OK
== attach_conf(cptr
, tmp
) && !first
)
634 /** Find a ConfItem that has the same name and user+host fields as
635 * specified. Requires an exact match for \a name.
636 * @param name Name to match
637 * @param cptr Client to match against
638 * @param statmask Filter for ConfItem::status
639 * @return First found matching ConfItem.
641 struct ConfItem
* find_conf_exact(const char* name
, struct Client
*cptr
, int statmask
)
643 struct ConfItem
*tmp
;
645 for (tmp
= GlobalConfList
; tmp
; tmp
= tmp
->next
) {
646 if (!(tmp
->status
& statmask
) || !tmp
->name
|| !tmp
->host
||
647 0 != ircd_strcmp(tmp
->name
, name
))
650 && (EmptyString(cli_username(cptr
))
651 || match(tmp
->username
, cli_username(cptr
))))
653 if (tmp
->addrbits
< 0)
655 if (match(tmp
->host
, cli_sockhost(cptr
)))
658 else if (!ipmask_check(&cli_ip(cptr
), &tmp
->address
.addr
, tmp
->addrbits
))
660 if ((tmp
->status
& CONF_OPERATOR
)
661 && (tmp
->clients
>= MaxLinks(tmp
->conn_class
)))
668 /** Find a ConfItem from a list that has a name that matches \a name.
669 * @param lp List to search in.
670 * @param name Filter for ConfItem::name field; matches either exactly
672 * @param statmask Filter for ConfItem::status.
673 * @return First matching ConfItem from \a lp.
675 struct ConfItem
* find_conf_byname(struct SLink
* lp
, const char* name
,
678 struct ConfItem
* tmp
;
681 if (HOSTLEN
< strlen(name
))
684 for (; lp
; lp
= lp
->next
) {
685 tmp
= lp
->value
.aconf
;
686 if (0 != (tmp
->status
& statmask
)) {
687 assert(0 != tmp
->name
);
688 if (0 == ircd_strcmp(tmp
->name
, name
) || 0 == match(tmp
->name
, name
))
695 /** Find a ConfItem from a list that has a host that matches \a host.
696 * @param lp List to search in.
697 * @param host Filter for ConfItem::host field; matches as a glob.
698 * @param statmask Filter for ConfItem::status.
699 * @return First matching ConfItem from \a lp.
701 struct ConfItem
* find_conf_byhost(struct SLink
* lp
, const char* host
,
704 struct ConfItem
* tmp
= NULL
;
707 if (HOSTLEN
< strlen(host
))
710 for (; lp
; lp
= lp
->next
) {
711 tmp
= lp
->value
.aconf
;
712 if (0 != (tmp
->status
& statmask
)) {
713 assert(0 != tmp
->host
);
714 if (0 == match(tmp
->host
, host
))
721 /** Find a ConfItem from a list that has an address equal to \a ip.
722 * @param lp List to search in.
723 * @param ip Filter for ConfItem::address field; matches exactly.
724 * @param statmask Filter for ConfItem::status.
725 * @return First matching ConfItem from \a lp.
727 struct ConfItem
* find_conf_byip(struct SLink
* lp
, const struct irc_in_addr
* ip
,
730 struct ConfItem
* tmp
;
732 for (; lp
; lp
= lp
->next
) {
733 tmp
= lp
->value
.aconf
;
734 if (0 != (tmp
->status
& statmask
)
735 && !irc_in_addr_cmp(&tmp
->address
.addr
, ip
))
741 /** Free all CRules from #cruleConfList. */
742 void conf_erase_crule_list(void)
744 struct CRuleConf
* next
;
745 struct CRuleConf
* p
= cruleConfList
;
747 for ( ; p
; p
= next
) {
749 crule_free(&p
->node
);
757 /** Return #cruleConfList.
758 * @return #cruleConfList
760 const struct CRuleConf
* conf_get_crule_list(void)
762 return cruleConfList
;
765 /** Free all deny rules from #denyConfList. */
766 void conf_erase_deny_list(void)
768 struct DenyConf
* next
;
769 struct DenyConf
* p
= denyConfList
;
770 for ( ; p
; p
= next
) {
781 /** Return #denyConfList.
782 * @return #denyConfList
784 const struct DenyConf
* conf_get_deny_list(void)
789 /** Find any existing quarantine for the named channel.
790 * @param chname Channel name to search for.
791 * @return Reason for channel's quarantine, or NULL if none exists.
794 find_quarantine(const char *chname
)
798 for (qline
= GlobalQuarantineList
; qline
; qline
= qline
->next
)
799 if (!ircd_strcmp(qline
->chname
, chname
))
800 return qline
->reason
;
804 /** Free all qline structs from #GlobalQuarantineList. */
805 void clear_quarantines(void)
808 while ((qline
= GlobalQuarantineList
))
810 GlobalQuarantineList
= qline
->next
;
811 MyFree(qline
->reason
);
812 MyFree(qline
->chname
);
817 /** When non-zero, indicates that a configuration error has been seen in this pass. */
818 static int conf_error
;
819 /** When non-zero, indicates that the configuration file was loaded at least once. */
820 static int conf_already_read
;
822 extern void yyparse(void);
823 extern void init_lexer(void);
825 /** Read configuration file.
826 * @return Zero on failure, non-zero on success. */
827 int read_configuration_file(void)
830 feature_unmark(); /* unmark all features for resetting later */
831 /* Now just open an fd. The buffering isn't really needed... */
836 feature_mark(); /* reset unmarked features */
837 conf_already_read
= 1;
841 /** Report an error message about the configuration file.
842 * @param msg The error to report.
845 yyerror(const char *msg
)
847 sendto_opmask_butone(0, SNO_ALL
, "Config file parse error line %d: %s",
849 log_write(LS_CONFIG
, L_ERROR
, 0, "Config file parse error line %d: %s",
851 if (!conf_already_read
)
852 fprintf(stderr
, "Config file parse error line %d: %s\n", lineno
, msg
);
856 /** Attach CONF_UWORLD items to a server and everything attached to it. */
858 attach_conf_uworld(struct Client
*cptr
)
862 attach_confs_byhost(cptr
, cli_name(cptr
), CONF_UWORLD
);
863 for (lp
= cli_serv(cptr
)->down
; lp
; lp
= lp
->next
)
864 attach_conf_uworld(lp
->value
.cptr
);
867 /** Free all memory associated with service mapping \a smap.
868 * @param smap[in] The mapping to free.
870 void free_mapping(struct s_map
*smap
)
872 struct nick_host
*nh
, *next
;
873 for (nh
= smap
->services
; nh
; nh
= next
)
879 MyFree(smap
->command
);
880 MyFree(smap
->prepend
);
884 /** Unregister and free all current service mappings. */
885 static void close_mappings(void)
887 struct s_map
*map
, *next
;
889 for (map
= GlobalServiceMapList
; map
; map
= next
) {
891 unregister_mapping(map
);
894 GlobalServiceMapList
= NULL
;
897 /** Reload the configuration file.
898 * @param cptr Client that requested rehash (if a signal, &me).
899 * @param sig Type of rehash (0 = oper-requested, 1 = signal, 2 =
900 * oper-requested but do not restart resolver)
901 * @return CPTR_KILLED if any client was K/G-lined because of the
902 * rehash; otherwise 0.
904 int rehash(struct Client
*cptr
, int sig
)
906 struct ConfItem
** tmp
= &GlobalConfList
;
907 struct ConfItem
* tmp2
;
908 struct Client
* acptr
;
914 sendto_opmask_butone(0, SNO_OLDSNO
,
915 "Got signal SIGHUP, reloading ircd conf. file");
917 while ((tmp2
= *tmp
)) {
920 * Configuration entry is still in use by some
921 * local clients, cannot delete it--mark it so
922 * that it will be deleted when the last client
925 if (CONF_CLIENT
== (tmp2
->status
& CONF_CLIENT
))
931 tmp2
->status
|= CONF_ILLEGAL
;
938 conf_erase_crule_list();
939 conf_erase_deny_list();
943 * delete the juped nicks list
954 mark_listeners_closing();
958 read_configuration_file();
960 log_reopen(); /* reopen log files */
964 class_delete_marked(); /* unless it fails */
967 * Flush out deleted I and P lines although still in use.
969 for (tmp
= &GlobalConfList
; (tmp2
= *tmp
);) {
970 if (CONF_ILLEGAL
== (tmp2
->status
& CONF_ILLEGAL
)) {
980 for (i
= 0; i
<= HighestFd
; i
++) {
981 if ((acptr
= LocalClientArray
[i
])) {
982 assert(!IsMe(acptr
));
984 det_confs_butmask(acptr
, ~(CONF_UWORLD
| CONF_ILLEGAL
));
985 /* Because admin's are getting so uppity about people managing to
986 * get past K/G's etc, we'll "fix" the bug by actually explaining
989 if ((found_g
= find_kill(acptr
, 0))) {
990 sendto_opmask_butone(0, found_g
== -2 ? SNO_GLINE
: SNO_OPERKILL
,
991 found_g
== -2 ? "G-line active for %s%s" :
992 "K-line active for %s%s",
993 IsUnknown(acptr
) ? "Unregistered Client ":"",
994 get_client_name(acptr
, SHOW_IP
));
995 if (exit_client(cptr
, acptr
, &me
, found_g
== -2 ? "G-lined" :
996 "K-lined") == CPTR_KILLED
)
1002 attach_conf_uworld(&me
);
1007 /** Read configuration file for the very first time.
1008 * @return Non-zero on success, zero on failure.
1013 if (read_configuration_file()) {
1015 * make sure we're sane to start if the config
1016 * file read didn't get everything we need.
1017 * XXX - should any of these abort the server?
1018 * TODO: add warning messages
1020 if (0 == localConf
.name
|| 0 == localConf
.numeric
)
1025 if (0 == localConf
.location1
)
1026 DupString(localConf
.location1
, "");
1027 if (0 == localConf
.location2
)
1028 DupString(localConf
.location2
, "");
1029 if (0 == localConf
.contact
)
1030 DupString(localConf
.contact
, "");
1037 /** Searches for a K/G-line for a client. If one is found, notify the
1038 * user and disconnect them.
1039 * @param cptr Client to search for.
1040 * @param glinecheck Whether we check for glines.
1041 * @return 0 if client is accepted; -1 if client was locally denied
1042 * (K-line); -2 if client was globally denied (G-line).
1044 int find_kill(struct Client
*cptr
, int glinecheck
)
1048 const char* realname
;
1049 struct DenyConf
* deny
;
1050 struct Gline
* agline
= NULL
;
1054 if (!cli_user(cptr
))
1057 host
= cli_sockhost(cptr
);
1058 name
= cli_user(cptr
)->username
;
1059 realname
= cli_info(cptr
);
1061 assert(strlen(host
) <= HOSTLEN
);
1062 assert((name
? strlen(name
) : 0) <= HOSTLEN
);
1063 assert((realname
? strlen(realname
) : 0) <= REALLEN
);
1065 /* 2000-07-14: Rewrote this loop for massive speed increases.
1068 for (deny
= denyConfList
; deny
; deny
= deny
->next
) {
1069 if (deny
->usermask
&& match(deny
->usermask
, name
))
1071 if (deny
->realmask
&& match(deny
->realmask
, realname
))
1073 if (deny
->bits
> 0) {
1074 if (!ipmask_check(&cli_ip(cptr
), &deny
->address
, deny
->bits
))
1076 } else if (deny
->hostmask
&& match(deny
->hostmask
, host
))
1079 if (EmptyString(deny
->message
))
1080 send_reply(cptr
, SND_EXPLICIT
| ERR_YOUREBANNEDCREEP
,
1081 ":Connection from your host is refused on this server.");
1083 if (deny
->flags
& DENY_FLAGS_FILE
)
1084 killcomment(cptr
, deny
->message
);
1086 send_reply(cptr
, SND_EXPLICIT
| ERR_YOUREBANNEDCREEP
, ":%s.", deny
->message
);
1091 /* added glinecheck to define if we check for glines too, shouldn't happen
1092 * when rehashing as it is causing problems with big servers and lots of glines.
1093 * Think of a 18000 user leaf with 18000 glines present, this will probably
1094 * cause the server to split from the net.
1097 if (glinecheck
&& (agline
= gline_lookup(cptr
, 0)) && GlineIsActive(agline
)) {
1099 * find active glines
1100 * added a check against the user's IP address to find_gline() -Kev
1102 send_reply(cptr
, SND_EXPLICIT
| ERR_YOUREBANNEDCREEP
, ":%s.", GlineReason(agline
));
1109 /** Attempt to attach Client blocks to \a cptr. If attach_iline()
1110 * fails for the client, emit a debugging message.
1111 * @param cptr Client to check for access.
1112 * @return Access check result.
1114 enum AuthorizationCheckResult
conf_check_client(struct Client
*cptr
)
1116 enum AuthorizationCheckResult acr
= ACR_OK
;
1118 if ((acr
= attach_iline(cptr
))) {
1119 Debug((DEBUG_DNS
, "ch_cl: access denied: %s[%s]",
1120 cli_name(cptr
), cli_sockhost(cptr
)));
1126 /** Check access for a server given its name (passed in cptr struct).
1127 * Must check for all C/N lines which have a name which matches the
1128 * name given and a host which matches. A host alias which is the
1129 * same as the server name is also acceptable in the host field of a
1131 * @param cptr Peer server to check.
1132 * @return 0 if accepted, -1 if access denied.
1134 int conf_check_server(struct Client
*cptr
)
1136 struct ConfItem
* c_conf
= NULL
;
1139 Debug((DEBUG_DNS
, "sv_cl: check access for %s[%s]",
1140 cli_name(cptr
), cli_sockhost(cptr
)));
1142 if (IsUnknown(cptr
) && !attach_confs_byname(cptr
, cli_name(cptr
), CONF_SERVER
)) {
1143 Debug((DEBUG_DNS
, "No C/N lines for %s", cli_sockhost(cptr
)));
1146 lp
= cli_confs(cptr
);
1148 * We initiated this connection so the client should have a C and N
1149 * line already attached after passing through the connect_server()
1152 if (IsConnecting(cptr
) || IsHandshake(cptr
)) {
1153 c_conf
= find_conf_byname(lp
, cli_name(cptr
), CONF_SERVER
);
1155 sendto_opmask_butone(0, SNO_OLDSNO
,
1156 "Connect Error: lost Connect block for %s",
1158 det_confs_butmask(cptr
, 0);
1163 /* Try finding the Connect block by DNS name and IP next. */
1164 if (!c_conf
&& !(c_conf
= find_conf_byhost(lp
, cli_sockhost(cptr
), CONF_SERVER
)))
1165 c_conf
= find_conf_byip(lp
, &cli_ip(cptr
), CONF_SERVER
);
1168 * Attach by IP# only if all other checks have failed.
1169 * It is quite possible to get here with the strange things that can
1170 * happen when using DNS in the way the irc server does. -avalon
1173 c_conf
= find_conf_byip(lp
, &cli_ip(cptr
), CONF_SERVER
);
1175 * detach all conf lines that got attached by attach_confs()
1177 det_confs_butmask(cptr
, 0);
1179 * if no Connect block, then deny access
1182 Debug((DEBUG_DNS
, "sv_cl: access denied: %s[%s@%s]",
1183 cli_name(cptr
), cli_username(cptr
), cli_sockhost(cptr
)));
1187 * attach the Connect block to the client structure for later use.
1189 attach_conf(cptr
, c_conf
);
1191 if (!irc_in_addr_valid(&c_conf
->address
.addr
))
1192 memcpy(&c_conf
->address
.addr
, &cli_ip(cptr
), sizeof(c_conf
->address
.addr
));
1194 Debug((DEBUG_DNS
, "sv_cl: access ok: %s[%s]",
1195 cli_name(cptr
), cli_sockhost(cptr
)));
1199 void clear_slines(void)
1201 struct sline
*sline
;
1202 while ((sline
= GlobalSList
)) {
1203 GlobalSList
= sline
->next
;
1204 MyFree(sline
->spoofhost
);
1205 if (!EmptyString(sline
->passwd
))
1206 MyFree(sline
->passwd
);
1207 if (!EmptyString(sline
->realhost
))
1208 MyFree(sline
->realhost
);
1209 if (!EmptyString(sline
->username
))
1210 MyFree(sline
->username
);
1216 * conf_check_slines()
1218 * Check S lines for the specified client, passed in cptr struct.
1219 * If the client's IP is S-lined, process the substitution here.
1225 * 0 = No S-line found
1226 * 1 = S-line found and substitution done.
1234 conf_check_slines(struct Client
*cptr
)
1236 struct sline
*sconf
;
1239 for (sconf
= GlobalSList
; sconf
; sconf
= sconf
->next
) {
1240 if (sconf
->flags
== SLINE_FLAGS_IP
) {
1241 if (!ipmask_check(&(cli_ip(cptr
)), &(sconf
->address
), sconf
->bits
))
1243 } else if (sconf
->flags
== SLINE_FLAGS_HOSTNAME
) {
1244 if ((match(sconf
->realhost
, cli_sockhost(cptr
)) != 0) &&
1245 (match(sconf
->realhost
, cli_sock_ip(cptr
)) != 0)) /* wildcarded IP address */
1251 if (match(sconf
->username
, cli_user(cptr
)->username
) == 0) {
1252 /* Ignore user part if u@h. */
1253 if ((hostonly
= strchr(sconf
->spoofhost
, '@')))
1256 hostonly
= sconf
->spoofhost
;
1261 ircd_strncpy(cli_user(cptr
)->host
, hostonly
, HOSTLEN
);
1262 log_write(LS_USER
, L_INFO
, LOG_NOSNOTICE
, "S-Line (%s@%s) by (%#R)",
1263 cli_user(cptr
)->username
, hostonly
, cptr
);
1270 void free_spoofhost(struct sline
*spoof
) {
1271 MyFree(spoof
->spoofhost
);
1272 MyFree(spoof
->passwd
);
1273 MyFree(spoof
->realhost
);
1274 MyFree(spoof
->username
);