2 * syn: a utility bot to manage IRC network access
3 * Copyright (C) 2009-2016 Stephen Bennett
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Affero General Public License for more details.
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
25 void facility_newuser(hook_user_nick_t
*data
);
27 void syn_cmd_facility(sourceinfo_t
*si
, int parc
, char **parv
);
28 static void syn_facility_help(sourceinfo_t
*si
, const char *subcmd
);
30 mowgli_patricia_t
*syn_facility_cmds
;
32 command_t syn_facility
= { "FACILITY", N_("Inspects or modifies facility lists"), "syn:facility", 4, syn_cmd_facility
, { .func
= syn_facility_help
} };
34 static void syn_cmd_facility_list(sourceinfo_t
*si
, int parc
, char **parv
);
35 static void syn_cmd_facility_add(sourceinfo_t
*si
, int parc
, char **parv
);
36 static void syn_cmd_facility_del(sourceinfo_t
*si
, int parc
, char **parv
);
37 static void syn_cmd_facility_set(sourceinfo_t
*si
, int parc
, char **parv
);
38 static void syn_cmd_facility_addbl(sourceinfo_t
*si
, int parc
, char **parv
);
39 static void syn_cmd_facility_rmbl(sourceinfo_t
*si
, int parc
, char **parv
);
40 static void syn_cmd_facility_show(sourceinfo_t
*si
, int parc
, char **parv
);
42 command_t syn_facility_list
= { "LIST", N_("Displays defined facilities"), "syn:facility", 1, syn_cmd_facility_list
, { .path
= "syn/facility_list" } };
43 command_t syn_facility_add
= { "ADD", N_("Configures a new facility"), "syn:facility:admin", 2, syn_cmd_facility_add
, { .path
= "syn/facility_add" } };
44 command_t syn_facility_del
= { "DEL", N_("Removes a configured facility"), "syn:facility:admin", 1, syn_cmd_facility_del
, { .path
= "syn/facility_del" } };
45 command_t syn_facility_set
= { "SET", N_("Modifies a configured facility"), "syn:facility:admin", 3, syn_cmd_facility_set
, { .path
= "syn/facility_set" } };
46 command_t syn_facility_addbl
= { "ADDBL", N_("Adds a blacklist entry for a faciltiy"), "syn:facility", 2, syn_cmd_facility_addbl
, { .path
= "syn/facility_addbl" } };
47 command_t syn_facility_rmbl
= { "RMBL", N_("Removes a blacklist entry from a facility"), "syn:facility", 2, syn_cmd_facility_rmbl
, { .path
= "syn/facility_rmbl" } };
48 command_t syn_facility_show
= { "SHOW", N_("Displays information about a facility"), "syn:facility", 1, syn_cmd_facility_show
, { .path
= "syn/facility_show" } };
52 facility_cloak_undefined
,
54 facility_cloak_random
,
55 facility_cloak_hex_ident
,
57 facility_cloak_account
,
58 } facility_cloak_type
;
62 facility_cloak_type value
;
63 } cloak_type_map
[] = {
64 { "undefined", facility_cloak_undefined
},
65 { "none", facility_cloak_none
},
66 { "random", facility_cloak_random
},
67 { "hexip", facility_cloak_hex_ident
},
68 { "ident", facility_cloak_ident
},
69 { "account", facility_cloak_account
},
73 static facility_cloak_type
cloak_type_from_string(const char *name
)
76 return facility_cloak_undefined
;
78 for (int i
=0; cloak_type_map
[i
].name
!= NULL
; ++i
)
80 if (0 == strcmp(name
, cloak_type_map
[i
].name
))
81 return cloak_type_map
[i
].value
;
83 return facility_cloak_undefined
;
86 static const char *string_from_cloak_type(facility_cloak_type type
)
88 for (int i
=0; cloak_type_map
[i
].name
!= NULL
; ++i
)
90 if (type
== cloak_type_map
[i
].value
)
91 return cloak_type_map
[i
].name
;
98 char hostpart
[HOSTLEN
];
103 char *throttlemessage
;
106 facility_cloak_type cloaking
;
109 mowgli_list_t blacklist
;
111 time_t throttle_latest
;
120 mowgli_patricia_t
*facilities
;
122 unsigned int block_report_interval
= 60;
123 time_t last_block_report
= 0;
125 mowgli_heap_t
*facility_heap
, *blacklist_heap
;
127 // Horrible hack to work around the race condition when
128 // NickServ and syn both cloak somebody.
129 static void on_host_change(void *vdata
);
131 void free_facility(const char *key
, void *facility
, void *unused
)
133 facility_t
*f
= facility
;
136 free(f
->blockmessage
);
137 if (f
->throttlemessage
)
138 free(f
->throttlemessage
);
140 mowgli_node_t
*n
, *tn
;
141 MOWGLI_LIST_FOREACH_SAFE(n
, tn
, f
->blacklist
.head
)
143 bl_entry_t
*bl
= n
->data
;
145 regex_destroy(bl
->re
);
147 mowgli_heap_free(blacklist_heap
, bl
);
149 mowgli_node_delete(n
, &f
->blacklist
);
153 mowgli_heap_free(facility_heap
, f
);
156 void load_facilities()
158 FILE *f
= fopen(DATADIR
"/facilities.db", "r");
161 slog(LG_DEBUG
, "Couldn't open facilities list: %s", strerror(errno
));
165 facility_t
*curr_facility
= NULL
;
168 while (fgets(line
, BUFSIZE
, f
))
170 char *token
= strtok(line
, " ");
172 if (0 == strcmp(token
, "F"))
174 curr_facility
= mowgli_heap_alloc(facility_heap
);
175 char *hostpart
= strtok(NULL
, " ");
176 char *cloaking
= strtok(NULL
, " ");
177 char *blocked
= strtok(NULL
, " ");
178 char *throttle0
= strtok(NULL
, " ");
179 char *throttle1
= strtok(NULL
, " ");
181 char *cloak_override
= strtok(NULL
, " ");
183 strncpy(curr_facility
->hostpart
, hostpart
, HOSTLEN
);
184 curr_facility
->cloaking
= cloak_type_from_string(cloaking
);
185 curr_facility
->blocked
= atoi(blocked
);
186 curr_facility
->throttle
[0] = atoi(throttle0
);
187 curr_facility
->throttle
[1] = atoi(throttle1
);
188 curr_facility
->cloak_override
= cloak_override
? atoi(cloak_override
) : 0;
190 mowgli_patricia_add(facilities
, curr_facility
->hostpart
, curr_facility
);
194 if (curr_facility
== NULL
)
197 if (0 == strcmp(token
, "BM"))
199 char *msg
= strtok(NULL
, "");
203 curr_facility
->blockmessage
= sstrdup(msg
);
206 else if (0 == strcmp(token
, "TM"))
208 char *msg
= strtok(NULL
, "");
212 curr_facility
->throttlemessage
= sstrdup(msg
);
215 else if (0 == strcmp(token
, "BL"))
217 char *regex
= strtok(NULL
, "");
223 bl_entry_t
*bl
= mowgli_heap_alloc(blacklist_heap
);
224 bl
->regex
= sstrdup(regex
);
225 bl
->re
= regex_create(bl
->regex
, AREGEX_ICASE
| AREGEX_PCRE
);
226 mowgli_node_add(bl
, mowgli_node_create(), &curr_facility
->blacklist
);
232 void save_facilities()
234 FILE *db
= fopen(DATADIR
"/facilities.db.tmp", "w");
238 slog(LG_ERROR
, "save_facilities(): cannot open facilities database for writing: %s", strerror(errno
));
242 mowgli_patricia_iteration_state_t state
;
244 MOWGLI_PATRICIA_FOREACH(f
, &state
, facilities
)
246 fprintf(db
, "F %s %s %d %d %d %d\n", f
->hostpart
, string_from_cloak_type(f
->cloaking
),
247 f
->blocked
, f
->throttle
[0], f
->throttle
[1], f
->cloak_override
);
249 fprintf(db
, "BM %s\n", f
->blockmessage
);
250 if (f
->throttlemessage
)
251 fprintf(db
, "TM %s\n", f
->throttlemessage
);
253 MOWGLI_LIST_FOREACH(n
, f
->blacklist
.head
)
255 bl_entry_t
*bl
= n
->data
;
256 fprintf(db
, "BL %s\n", bl
->regex
);
260 if (rename(DATADIR
"/facilities.db.tmp", DATADIR
"/facilities.db") < 0)
262 slog(LG_ERROR
, "Couldn't rename facilities.db.tmp to facilities.db: %s", strerror(errno
));
266 static void mod_init(module_t
*m
)
268 use_syn_main_symbols(m
);
269 use_syn_util_symbols(m
);
270 use_syn_kline_symbols(m
);
272 hook_add_event("user_add");
273 hook_add_user_add(facility_newuser
);
274 hook_add_event("incoming_host_change");
275 hook_add_hook("incoming_host_change", on_host_change
);
277 service_named_bind_command("syn", &syn_facility
);
279 syn_facility_cmds
= mowgli_patricia_create(strcasecanon
);
280 command_add(&syn_facility_list
, syn_facility_cmds
);
281 command_add(&syn_facility_add
, syn_facility_cmds
);
282 command_add(&syn_facility_del
, syn_facility_cmds
);
283 command_add(&syn_facility_set
, syn_facility_cmds
);
284 command_add(&syn_facility_addbl
, syn_facility_cmds
);
285 command_add(&syn_facility_rmbl
, syn_facility_cmds
);
286 command_add(&syn_facility_show
, syn_facility_cmds
);
288 facility_heap
= mowgli_heap_create(sizeof(facility_t
), 64, BH_NOW
);
289 blacklist_heap
= mowgli_heap_create(sizeof(bl_entry_t
), 64, BH_NOW
);
290 facilities
= mowgli_patricia_create(strcasecanon
);
292 add_uint_conf_item("FACILITY_REPORT_RATE", &syn
->conf_table
, 0, &block_report_interval
, 0, 3600, 60);
297 static void mod_deinit(module_unload_intent_t intent
)
301 del_conf_item("FACILITY_REPORT_RATE", &syn
->conf_table
);
303 mowgli_patricia_destroy(facilities
, free_facility
, NULL
);
304 mowgli_heap_destroy(facility_heap
);
305 mowgli_heap_destroy(blacklist_heap
);
307 mowgli_patricia_destroy(syn_facility_cmds
, NULL
, NULL
);
309 service_named_unbind_command("syn", &syn_facility
);
311 hook_del_user_add(facility_newuser
);
312 hook_del_hook("incoming_host_change", on_host_change
);
315 static void facility_set_cloak(user_t
*u
, const char * cloak
, bool cloak_override
)
317 metadata_add(u
, "syn:facility-cloak", cloak
);
320 metadata_add(u
, "syn:facility-cloak-override", "1");
322 // Check whether they've already been cloaked. If vhost != host, and
323 // vhost isn't unaffiliated/*, then they have a project cloak that we shouldn't override.
324 // If vhost != host, and vhost *is* unaffiliated but we don't override unaffiliated cloaks,
325 // don't do so either.
326 if ((strncmp(u
->vhost
, "unaffiliated/", 13) != 0 || !cloak_override
) &&
327 strncmp(u
->vhost
, u
->host
, HOSTLEN
) != 0)
330 // Don't send out a no-op cloak change either
331 if (strcmp(u
->vhost
, cloak
))
332 user_sethost(syn
->me
, u
, cloak
);
335 void facility_newuser(hook_user_nick_t
*data
)
339 mowgli_patricia_iteration_state_t state
;
341 /* If the user has already been killed, don't try to do anything */
345 int blocked
= 0, throttled
= 0, blacklisted
= 0, cloak_override
= 0;
346 char *blockmessage
= NULL
, *throttlemessage
= NULL
;
347 facility_cloak_type cloak
= facility_cloak_none
;
348 facility_t
*blocking_facility
= NULL
, *throttling_facility
= NULL
, *cloaking_facility
= NULL
;
349 char *blocking_regex
= NULL
;
353 MOWGLI_PATRICIA_FOREACH(f
, &state
, facilities
)
355 if (0 != strncasecmp(u
->host
, f
->hostpart
, strlen(f
->hostpart
)))
358 syn_debug(2, "User %s matches facility %s", u
->nick
, f
->hostpart
);
360 u
->flags
|= SYN_UF_FACILITY_USER
;
365 blocking_facility
= f
;
371 blockmessage
= f
->blockmessage
;
373 if (f
->throttle
[0] > 0 && !me
.bursting
)
375 if (f
->throttle_latest
< CURRTIME
)
376 f
->throttle_latest
= CURRTIME
;
378 f
->throttle_latest
+= f
->throttle
[0];
380 if (f
->throttle_latest
> (f
->throttle
[1] * f
->throttle
[0]) + CURRTIME
)
383 throttling_facility
= f
;
384 throttlemessage
= f
->throttlemessage
;
388 if (f
->cloaking
!= facility_cloak_undefined
)
391 cloaking_facility
= f
;
394 if (f
->cloak_override
)
395 cloak_override
= f
->cloak_override
;
397 char nuh
[NICKLEN
+USERLEN
+HOSTLEN
+GECOSLEN
];
398 snprintf(nuh
, sizeof(nuh
), "%s!%s@%s %s", u
->nick
, u
->user
, u
->host
, u
->gecos
);
401 MOWGLI_LIST_FOREACH(n
, f
->blacklist
.head
)
403 bl_entry_t
*bl
= n
->data
;
406 if (regex_match(bl
->re
, nuh
))
408 syn_debug(1, "User %s blacklisted in %s (%s)", u
->nick
, f
->hostpart
, bl
->regex
);
409 blocking_facility
= f
;
410 blocking_regex
= bl
->regex
;
422 if (last_block_report
+ block_report_interval
< CURRTIME
)
424 last_block_report
= CURRTIME
;
425 syn_report("Killing user %s due to throttle [%d,%d] on facility %s",
426 u
->nick
, throttling_facility
->throttle
[0], throttling_facility
->throttle
[1],
427 throttling_facility
->hostpart
);
429 syn_kill2(u
, "Throttled", "%s", throttlemessage
);
436 if (last_block_report
+ block_report_interval
< CURRTIME
)
438 last_block_report
= CURRTIME
;
439 syn_report("Killing user %s; blocked by facility %s",
440 u
->nick
, blocking_facility
? blocking_facility
->hostpart
: "(unknown)");
442 syn_kill2(u
, "Facility Blocked", "%s", blockmessage
);
449 if (last_block_report
+ block_report_interval
< CURRTIME
)
451 last_block_report
= CURRTIME
;
452 syn_report("Killing user %s; blacklisted in facility %s (%s)",
453 u
->nick
, blocking_facility
->hostpart
, blocking_regex
);
455 syn_kill(u
, "%s", blockmessage
);
460 if (cloak_override
> 0)
465 char new_vhost
[HOSTLEN
];
466 mowgli_strlcpy(new_vhost
, u
->host
, HOSTLEN
);
469 case facility_cloak_none
:
470 case facility_cloak_undefined
:
473 case facility_cloak_account
:
475 facility_set_cloak(u
, u
->host
, cloak_override
);
479 case facility_cloak_hex_ident
:
481 char *ipstart
= strstr(new_vhost
, "session");
484 syn_debug(2, "Hex IP cloaking used for %s, but I couldn't find a session marker in %s", u
->nick
, new_vhost
);
487 const char *ident
= u
->user
;
490 const char *ip
= decode_hex_ip(ident
);
494 strncpy(ipstart
, "ip.", new_vhost
+ HOSTLEN
- ipstart
);
496 strncpy(ipstart
, ip
, new_vhost
+ HOSTLEN
- ipstart
);
497 facility_set_cloak(u
, new_vhost
, cloak_override
);
501 syn_report("Killing user %s; facility %s requires hexip but none was found",
502 u
->nick
, cloaking_facility
->hostpart
);
503 // If we couldn't decode an IP, block the connection
504 syn_kill2(u
, "No IP address supplied", "Your gateway requires an underlying IP address to be supplied, which could not be found.");
511 case facility_cloak_ident
:
513 char *identstart
= strstr(new_vhost
, "session");
514 if (identstart
== NULL
)
516 syn_debug(2, "Ident cloaking used for %s, but I couldn't find a session marker in %s", u
->nick
, new_vhost
);
520 const char *ident
= u
->user
;
524 const char *suffix
= encode_ident_for_host(ident
);
527 syn_debug(2, "Cannot host-encode ident %s for user %s, giving up", ident
, u
->nick
);
531 strncpy(identstart
, suffix
, new_vhost
+ HOSTLEN
- identstart
);
532 facility_set_cloak(u
, new_vhost
, cloak_override
);
535 case facility_cloak_random
:
537 char *randstart
= strstr(new_vhost
, "session");
538 if (randstart
== NULL
)
540 syn_debug(2, "Random cloaking used for %s, but I couldn't find a session marker in %s", u
->nick
, new_vhost
);
543 strncpy(randstart
, get_random_host_part(u
), new_vhost
+ HOSTLEN
- randstart
);
544 facility_set_cloak(u
, new_vhost
, cloak_override
);
549 if (dospam
&& !me
.bursting
)
550 syn_report2(2, "Allowed %s!%s@%s [%s]", u
->nick
, u
->user
, u
->vhost
, u
->gecos
);
553 static void syn_facility_help(sourceinfo_t
*si
, const char *subcmd
)
557 // This can't go into one big command_success_nodata with \n in it
558 // because syn defines its own sourceinfo vtable which doesn't bother
559 // to split on \n in its command_success_nodata implementation
560 command_success_nodata(si
, _("***** \2%s Help\2 *****"), si
->service
->nick
);
561 command_success_nodata(si
, _("Help for \2FACILITY\2:"));
562 command_success_nodata(si
, " ");
563 command_success_nodata(si
, "The FACILITY command displays and manipulates facility");
564 command_success_nodata(si
, "definitions.");
565 command_success_nodata(si
, " ");
566 command_success_nodata(si
, "A facility is roughly a related group of gateways, defined");
567 command_success_nodata(si
, "by a host prefix. Any new client connecting is checked");
568 command_success_nodata(si
, "against the list of facilities, and various actions may be");
569 command_success_nodata(si
, "taken based on this.");
570 command_success_nodata(si
, " ");
571 command_success_nodata(si
, "Each facility has some or all of the following information");
572 command_success_nodata(si
, "defined:");
573 command_success_nodata(si
, " ");
574 command_success_nodata(si
, " - The hostname prefix");
575 command_success_nodata(si
, " - Whether this facility is currently blocked");
576 command_success_nodata(si
, " - The message to send to clients blocked by this facility.");
577 command_success_nodata(si
, " - A throttle limit. This is of the form x,y and translates");
578 command_success_nodata(si
, " rougly to y clients per x*y seconds.");
579 command_success_nodata(si
, " - The message to send to clients denied because of this");
580 command_success_nodata(si
, " facility's throttle.");
581 command_success_nodata(si
, " - The cloaking scheme applied to matching clients.");
582 command_success_nodata(si
, " - Whether facility cloaks will override unaffiliated cloaks.");
583 command_success_nodata(si
, " - A blacklist of regular expressions. If any of these match");
584 command_success_nodata(si
, " a client that matches this facility, it will be denied.");
585 command_success_nodata(si
, " ");
586 command_success_nodata(si
, "Facilities are checked in order from least specific to most");
587 command_success_nodata(si
, "specific. If any facility is blocked, the client is killed,");
588 command_success_nodata(si
, "with the exception that a negative block value can override");
589 command_success_nodata(si
, "a positive one from a more general facility. If the client");
590 command_success_nodata(si
, "is determined to be blocked, the most specific configured");
591 command_success_nodata(si
, "block message is used.");
592 command_success_nodata(si
, " ");
593 command_success_nodata(si
, "The throttle settings for every matching facility are");
594 command_success_nodata(si
, "checked and updated, even if the client is blocked. If the");
595 command_success_nodata(si
, "client is denied due to throttling, the most specific");
596 command_success_nodata(si
, "configured throttle message is used.");
597 command_success_nodata(si
, " ");
598 command_success_nodata(si
, "For every matching facility, the client is checked against");
599 command_success_nodata(si
, "that facility's blacklist. If any of those regular");
600 command_success_nodata(si
, "expressions matches, then the client is denied, and the most");
601 command_success_nodata(si
, "specific block message so far encountered is used.");
602 command_success_nodata(si
, " ");
603 command_success_nodata(si
, "If the client is allowed to connect, then the cloaking");
604 command_success_nodata(si
, "setting is used to determine whether to modify the client's");
605 command_success_nodata(si
, "visible host name. Again the most specific defined value");
606 command_success_nodata(si
, "is used. Possible values are:");
607 command_success_nodata(si
, " ");
608 command_success_nodata(si
, " - none. Leave the client's host alone.");
609 command_success_nodata(si
, " - random. A 'session' marker in the client's host is");
610 command_success_nodata(si
, " replaced by a random text string.");
611 command_success_nodata(si
, " - hexip. The user's ident is treated as a hex-encoded IP");
612 command_success_nodata(si
, " address, as used by several web gateways. A 'session'");
613 command_success_nodata(si
, " marker in the user's host is replaced by ");
614 command_success_nodata(si
, " 'ip.<decoded ip>'. If the ident cannot be decoded this");
615 command_success_nodata(si
, " way, it falls back to the random method.");
616 command_success_nodata(si
, " - account. This is meant to be used with a sasl_usercloak");
617 command_success_nodata(si
, " auth {} block and applies the host generated by the ircd");
618 command_success_nodata(si
, " as gateway cloak, overriding unaffiliated cloaks.");
619 command_success_nodata(si
, " ");
620 command_success_nodata(si
, "If no matching facility has a defined cloaking method, then");
621 command_success_nodata(si
, "the default is \2none\2.");
622 command_success_nodata(si
, " ");
623 command_success_nodata(si
, "As with the 'blocked' setting, the override_unaff setting");
624 command_success_nodata(si
, "may be set to 1 to disallow unaffiliated cloaks or -1");
625 command_success_nodata(si
, "to specifically allow them even though a more general");
626 command_success_nodata(si
, "facility would not allow them. The default is to allow them.");
627 command_help(si
, syn_facility_cmds
);
628 command_success_nodata(si
, " ");
629 command_success_nodata(si
, _("For more information, use \2/msg %s HELP FACILITY \37command\37\2."), si
->service
->nick
);
630 command_success_nodata(si
, _("***** \2End of Help\2 *****"));
633 help_display_as_subcmd(si
, si
->service
, "FACILITY", subcmd
, syn_facility_cmds
);
636 void syn_cmd_facility(sourceinfo_t
*si
, int parc
, char **parv
)
643 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY");
644 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY LIST|ADD|DEL|SET|ADDBL|RMBL [parameters]");
648 c
= command_find(syn_facility_cmds
, cmd
);
651 command_fail(si
, fault_badparams
, "Invalid command. Possible commands are LIST ADD DEL SET ADDBL RMBL");
655 command_exec(si
->service
, si
, c
, parc
- 1, parv
+ 1);
658 void syn_cmd_facility_list(sourceinfo_t
*si
, int parc
, char **parv
)
666 mowgli_patricia_iteration_state_t state
;
667 MOWGLI_PATRICIA_FOREACH(f
, &state
, facilities
)
669 if (match
&& 0 != strncmp(match
, f
->hostpart
, strlen(match
)))
672 command_success_nodata(si
, "[%d] %s (cloaking %s, %s, throttle %d/%d)",
673 ++count
, f
->hostpart
, string_from_cloak_type(f
->cloaking
),
674 (f
->blocked
> 0 ? "blocked" : (f
->blocked
< 0 ? "unblocked" : "not blocked")),
675 f
->throttle
[0], f
->throttle
[1]);
678 command_success_nodata(si
, "%d facilit%s configured", count
, count
== 1 ? "y" : "ies");
681 void syn_cmd_facility_add(sourceinfo_t
*si
, int parc
, char **parv
)
685 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY ADD");
686 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY ADD <hostpart> [cloaktype]");
690 const char *hostpart
= parv
[0];
691 facility_cloak_type cloak
= cloak_type_from_string(parc
> 1 ? parv
[1] : NULL
);
693 facility_t
*f
= mowgli_heap_alloc(facility_heap
);
694 strncpy(f
->hostpart
, hostpart
, HOSTLEN
);
697 mowgli_patricia_add(facilities
, f
->hostpart
, f
);
699 syn_report("\002FACILITY ADD\002 %s by %s", f
->hostpart
, get_oper_name(si
));
701 command_success_nodata(si
, "Added facility %s", f
->hostpart
);
706 void syn_cmd_facility_del(sourceinfo_t
*si
, int parc
, char **parv
)
710 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY DEL");
711 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY DEL <hostpart>");
715 facility_t
*f
= mowgli_patricia_retrieve(facilities
, parv
[0]);
719 command_fail(si
, fault_badparams
, "No such facility %s was found.", parv
[0]);
723 free_facility(NULL
, f
, NULL
);
724 mowgli_patricia_delete(facilities
, parv
[0]);
726 syn_report("\002FACILITY DEL\002 %s by %s", parv
[0], get_oper_name(si
));
728 command_success_nodata(si
, "Facility %s deleted", parv
[0]);
735 void syn_cmd_facility_set(sourceinfo_t
*si
, int parc
, char **parv
)
739 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY SET");
740 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY SET <hostpart> <setting> [arguments]");
744 facility_t
*f
= mowgli_patricia_retrieve(facilities
, parv
[0]);
747 command_fail(si
, fault_badparams
, "No such facility %s", parv
[0]);
751 if (0 == strcasecmp(parv
[1], "cloaking"))
753 facility_cloak_type cloak
= cloak_type_from_string(parv
[2]);
756 syn_report("\002FACILITY SET\002 cloaking->%s for %s by %s",
757 string_from_cloak_type(cloak
), f
->hostpart
, get_oper_name(si
));
758 command_success_nodata(si
, "Cloaking method for %s set to %s", f
->hostpart
, string_from_cloak_type(cloak
));
764 if (0 == strcasecmp(parv
[1], "override_unaff"))
767 f
->cloak_override
= 0;
769 f
->cloak_override
= atoi(parv
[2]);
771 syn_report("\002FACILITY SET\002 override_unaff->%d for %s by %s",
772 f
->cloak_override
, f
->hostpart
, get_oper_name(si
));
773 command_success_nodata(si
, "Overriding unaffiliated cloaks for %s was set to %d", f
->hostpart
, f
->cloak_override
);
779 if (0 == strcasecmp(parv
[1], "blocked"))
784 f
->blocked
= atoi(parv
[2]);
786 syn_report("\002FACILITY SET\002 blocked->%d for %s by %s",
787 f
->blocked
, f
->hostpart
, get_oper_name(si
));
788 command_success_nodata(si
, "Blocked for %s was set to %d", f
->hostpart
, f
->blocked
);
794 if (0 == strcasecmp(parv
[1], "throttle"))
797 strncpy(buf
, parv
[2], 32);
798 char *p
= strchr(buf
, ',');
802 command_fail(si
, fault_badparams
, STR_INVALID_PARAMS
, "FACILITY SET THROTTLE");
803 command_fail(si
, fault_badparams
, "Syntax: FACILITY SET <name> THROTTLE n,m");
808 f
->throttle
[0] = atoi(buf
);
809 f
->throttle
[1] = atoi(p
);
811 syn_report("\002FACILITY SET\002 throttle->%d/%d for %s by %s",
812 f
->throttle
[0], f
->throttle
[1], f
->hostpart
, get_oper_name(si
));
813 command_success_nodata(si
, "Throttle for %s was set to %d seconds, burst %d",
814 f
->hostpart
, f
->throttle
[0], f
->throttle
[1]);
820 if (0 == strcasecmp(parv
[1], "blockmessage"))
823 free(f
->blockmessage
);
825 if (0 == strcmp(parv
[2], "-"))
826 f
->blockmessage
= NULL
;
828 f
->blockmessage
= sstrdup(parv
[2]);
830 syn_report("\002FACILITY SET\002 block message->%s for %s by %s",
831 f
->blockmessage
, f
->hostpart
, get_oper_name(si
));
832 command_success_nodata(si
, "Block message for %s was set to %s", f
->hostpart
, f
->blockmessage
);
838 if (0 == strcasecmp(parv
[1], "throttlemessage"))
840 if (f
->throttlemessage
)
841 free(f
->throttlemessage
);
843 if (0 == strcmp(parv
[2], "-"))
844 f
->throttlemessage
= NULL
;
846 f
->throttlemessage
= sstrdup(parv
[2]);
848 syn_report("\002FACILITY SET\002 throttle message->%s for %s by %s",
849 f
->throttlemessage
, f
->hostpart
, get_oper_name(si
));
850 command_success_nodata(si
, "Throttle message for %s was set to %s", f
->hostpart
, f
->throttlemessage
);
856 command_fail(si
, fault_badparams
, "Unknown setting name");
861 void syn_cmd_facility_addbl(sourceinfo_t
*si
, int parc
, char **parv
)
865 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY ADDBL");
866 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY ADDBL <hostpart> <regex>");
870 facility_t
*f
= mowgli_patricia_retrieve(facilities
, parv
[0]);
873 command_fail(si
, fault_badparams
, "No such facility %s", parv
[0]);
877 bl_entry_t
*bl
= mowgli_heap_alloc(blacklist_heap
);
878 bl
->regex
= sstrdup(parv
[1]);
879 bl
->re
= regex_create(bl
->regex
, AREGEX_ICASE
| AREGEX_PCRE
);
883 command_fail(si
, fault_badparams
, "Failed to compile regex \"%s\"", bl
->regex
);
887 mowgli_node_add(bl
, mowgli_node_create(), &f
->blacklist
);
889 syn_report("\002FACILITY ADDBL\002 %s to %s by %s", bl
->regex
, f
->hostpart
, get_oper_name(si
));
890 command_success_nodata(si
, "Added blacklist \"%s\" for %s", bl
->regex
, f
->hostpart
);
897 void syn_cmd_facility_rmbl(sourceinfo_t
*si
, int parc
, char **parv
)
901 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY RMBL");
902 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY RMBL <hostpart> <regex>");
906 facility_t
*f
= mowgli_patricia_retrieve(facilities
, parv
[0]);
909 command_fail(si
, fault_badparams
, "No such facility %s", parv
[0]);
913 mowgli_node_t
*n
, *tn
;
914 MOWGLI_LIST_FOREACH_SAFE(n
, tn
, f
->blacklist
.head
)
916 bl_entry_t
*bl
= n
->data
;
917 if (0 != strcmp(parv
[1], bl
->regex
))
921 regex_destroy(bl
->re
);
923 mowgli_heap_free(blacklist_heap
, bl
);
925 mowgli_node_delete(n
, &f
->blacklist
);
928 syn_report("\002FACILITY RMBL\002 %s from %s by %s", parv
[1], f
->hostpart
, get_oper_name(si
));
929 command_success_nodata(si
, "Removed blacklist \"%s\" from %s", parv
[1], f
->hostpart
);
935 void syn_cmd_facility_show(sourceinfo_t
*si
, int parc
, char **parv
)
939 command_fail(si
, fault_needmoreparams
, STR_INSUFFICIENT_PARAMS
, "FACILITY SHOW");
940 command_fail(si
, fault_needmoreparams
, "Syntax: FACILITY SHOW <hostpart>");
944 facility_t
*f
= mowgli_patricia_retrieve(facilities
, parv
[0]);
947 command_fail(si
, fault_badparams
, "No such facility %s", parv
[0]);
951 command_success_nodata(si
, "Facility %s:", f
->hostpart
);
952 command_success_nodata(si
, " cloaking method: %s", string_from_cloak_type(f
->cloaking
));
953 command_success_nodata(si
, " unaffiliated cloaks: %s",
954 f
->cloak_override
> 0 ? "disallowed" : (f
->cloak_override
< 0 ? "allowed" : "(see parent facility)"));
955 command_success_nodata(si
, " %s, block message \"%s\"",
956 f
->blocked
> 0 ? "blocked" : ( f
->blocked
< 0 ? "unblocked" : "not blocked"),
958 command_success_nodata(si
, " Throttle rate %d/%d, throttle message \"%s\"",
959 f
->throttle
[0], f
->throttle
[1], f
->throttlemessage
);
961 command_success_nodata(si
, "Blacklist:");
965 MOWGLI_LIST_FOREACH(n
, f
->blacklist
.head
)
967 bl_entry_t
*bl
= n
->data
;
968 command_success_nodata(si
, "[%d] %s", ++count
, bl
->regex
);
970 command_success_nodata(si
, "%d blacklist entries for %s", count
, f
->hostpart
);
973 static void on_host_change(void *vdata
)
975 hook_incoming_host_change_t
*data
= vdata
;
977 metadata_t
*md
= metadata_find(data
->user
, "syn:facility-cloak");
981 metadata_t
*override
= metadata_find(data
->user
, "syn:facility-cloak-override");
983 if ((0 == strncmp(data
->user
->vhost
, "unaffiliated/", 13) && override
) ||
984 0 == strncmp(data
->user
->vhost
, data
->user
->host
, HOSTLEN
))
986 // Override the host change -- a facility cloak is being replaced by unaffiliated while we're disallowing it,
987 // or a facility by another facility (this happens when removing a nickserv account vhost while a gateway user is logged in)
988 strshare_unref(data
->user
->vhost
);
989 data
->user
->vhost
= strshare_get(md
->value
);
993 // Bounce the sethost, to fix the race condition where services and syn both set a vhost on connect.
995 sethost_sts(syn
->me
, data
->user
, data
->user
->vhost
);
1000 "syn/facilities", false, mod_init
, mod_deinit
,
1002 "Stephen Bennett <stephen -at- freenode.net>"