/* src/bandbi.c * An interface to the ban db. * * Copyright (C) 2006 Lee Hardy * Copyright (C) 2006 ircd-ratbox development team * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1.Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2.Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3.The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $Id$ */ #include "stdinc.h" #include "ratbox_lib.h" #include "client.h" #include "s_conf.h" #include "logger.h" #include "match.h" #include "bandbi.h" #include "parse.h" #include "channel.h" #include "hostmask.h" #include "hash.h" #include "s_newconf.h" #include "reject.h" #include "send.h" #include "ircd.h" #include "msg.h" /* XXX: MAXPARA */ #include "operhash.h" static char bandb_add_letter[LAST_BANDB_TYPE] = { 'K', 'D', 'X', 'R' }; rb_dlink_list bandb_pending; static rb_helper *bandb_helper; static int start_bandb(void); static void bandb_parse(rb_helper *); static void bandb_restart_cb(rb_helper *); static char *bandb_path; void init_bandb(void) { if(start_bandb()) { ilog(L_MAIN, "Unable to start bandb helper: %s", strerror(errno)); exit(0); } } static int start_bandb(void) { char fullpath[PATH_MAX + 1]; #ifdef _WIN32 const char *suffix = ".exe"; #else const char *suffix = ""; #endif rb_setenv("BANDB_DBPATH", PKGLOCALSTATEDIR "/ban.db", 1); if(bandb_path == NULL) { rb_snprintf(fullpath, sizeof(fullpath), "%s/bandb%s", PKGLIBEXECDIR, suffix); if(access(fullpath, X_OK) == -1) { rb_snprintf(fullpath, sizeof(fullpath), "%s/bin/bandb%s", ConfigFileEntry.dpath, suffix); if(access(fullpath, X_OK) == -1) { ilog(L_MAIN, "Unable to execute bandb%s in %s or %s/bin", suffix, PKGLIBEXECDIR, ConfigFileEntry.dpath); return 0; } } bandb_path = rb_strdup(fullpath); } bandb_helper = rb_helper_start("bandb", bandb_path, bandb_parse, bandb_restart_cb); if(bandb_helper == NULL) { ilog(L_MAIN, "Unable to start bandb: %s", strerror(errno)); sendto_realops_snomask(SNO_GENERAL, L_ALL, "Unable to start bandb: %s", strerror(errno)); return 1; } rb_helper_run(bandb_helper); return 0; } void bandb_add(bandb_type type, struct Client *source_p, const char *mask1, const char *mask2, const char *reason, const char *oper_reason, int perm) { if(bandb_helper == NULL) return; static char buf[BUFSIZE]; rb_snprintf(buf, sizeof(buf), "%c %s ", bandb_add_letter[type], mask1); if(!EmptyString(mask2)) rb_snprintf_append(buf, sizeof(buf), "%s ", mask2); rb_snprintf_append(buf, sizeof(buf), "%s %ld %d :%s", get_oper_name(source_p), (long int)rb_current_time(), perm, reason); if(!EmptyString(oper_reason)) rb_snprintf_append(buf, sizeof(buf), "|%s", oper_reason); rb_helper_write(bandb_helper, "%s", buf); } static char bandb_del_letter[LAST_BANDB_TYPE] = { 'k', 'd', 'x', 'r' }; void bandb_del(bandb_type type, const char *mask1, const char *mask2) { if(bandb_helper == NULL) return; static char buf[BUFSIZE]; buf[0] = '\0'; rb_snprintf_append(buf, sizeof(buf), "%c %s", bandb_del_letter[type], mask1); if(!EmptyString(mask2)) rb_snprintf_append(buf, sizeof(buf), " %s", mask2); rb_helper_write(bandb_helper, "%s", buf); } static void bandb_handle_ban(char *parv[], int parc) { struct ConfItem *aconf; char *p; int para = 1; aconf = make_conf(); aconf->port = 0; if(parv[0][0] == 'K') aconf->user = rb_strdup(parv[para++]); aconf->host = rb_strdup(parv[para++]); aconf->info.oper = operhash_add(parv[para++]); switch (parv[0][0]) { case 'K': aconf->status = CONF_KILL; break; case 'D': aconf->status = CONF_DLINE; break; case 'X': aconf->status = CONF_XLINE; break; case 'R': if(IsChannelName(aconf->host)) aconf->status = CONF_RESV_CHANNEL; else aconf->status = CONF_RESV_NICK; break; } if((p = strchr(parv[para], '|'))) { *p++ = '\0'; aconf->spasswd = rb_strdup(p); } aconf->passwd = rb_strdup(parv[para]); rb_dlinkAddAlloc(aconf, &bandb_pending); } static int bandb_check_kline(struct ConfItem *aconf) { struct rb_sockaddr_storage daddr; struct ConfItem *kconf = NULL; int aftype; const char *p; aftype = parse_netmask(aconf->host, &daddr, NULL); if(aftype != HM_HOST) { #ifdef RB_IPV6 if(aftype == HM_IPV6) aftype = AF_INET6; else #endif aftype = AF_INET; kconf = find_conf_by_address(aconf->host, NULL, NULL, (struct sockaddr *)&daddr, CONF_KILL, aftype, aconf->user, NULL); } else kconf = find_conf_by_address(aconf->host, NULL, NULL, NULL, CONF_KILL, 0, aconf->user, NULL); if(kconf && ((kconf->flags & CONF_FLAGS_TEMPORARY) == 0)) return 0; for(p = aconf->user; *p; p++) { if(!IsUserChar(*p) && !IsKWildChar(*p)) return 0; } for(p = aconf->host; *p; p++) { if(!IsHostChar(*p) && !IsKWildChar(*p)) return 0; } return 1; } static int bandb_check_dline(struct ConfItem *aconf) { struct rb_sockaddr_storage daddr; int bits; if(!parse_netmask(aconf->host, &daddr, &bits)) return 0; return 1; } static int bandb_check_xline(struct ConfItem *aconf) { struct ConfItem *xconf; /* XXX perhaps convert spaces to \s? -- jilles */ xconf = find_xline_mask(aconf->host); if(xconf != NULL && !(xconf->flags & CONF_FLAGS_TEMPORARY)) return 0; return 1; } static int bandb_check_resv_channel(struct ConfItem *aconf) { const char *p; if(hash_find_resv(aconf->host) || strlen(aconf->host) > CHANNELLEN) return 0; for(p = aconf->host; *p; p++) { if(!IsChanChar(*p)) return 0; } return 1; } static int bandb_check_resv_nick(struct ConfItem *aconf) { if(!clean_resv_nick(aconf->host)) return 0; if(find_nick_resv(aconf->host)) return 0; return 1; } static void bandb_handle_clear(void) { rb_dlink_node *ptr, *next_ptr; RB_DLINK_FOREACH_SAFE(ptr, next_ptr, bandb_pending.head) { free_conf(ptr->data); rb_dlinkDestroy(ptr, &bandb_pending); } } static void bandb_handle_finish(void) { struct ConfItem *aconf; rb_dlink_node *ptr, *next_ptr; clear_out_address_conf_bans(); clear_s_newconf_bans(); RB_DLINK_FOREACH_SAFE(ptr, next_ptr, bandb_pending.head) { aconf = ptr->data; rb_dlinkDestroy(ptr, &bandb_pending); switch (aconf->status) { case CONF_KILL: if(bandb_check_kline(aconf)) add_conf_by_address(aconf->host, CONF_KILL, aconf->user, NULL, aconf); else free_conf(aconf); break; case CONF_DLINE: if(bandb_check_dline(aconf)) add_conf_by_address(aconf->host, CONF_DLINE, aconf->user, NULL, aconf); else free_conf(aconf); break; case CONF_XLINE: if(bandb_check_xline(aconf)) rb_dlinkAddAlloc(aconf, &xline_conf_list); else free_conf(aconf); break; case CONF_RESV_CHANNEL: if(bandb_check_resv_channel(aconf)) add_to_resv_hash(aconf->host, aconf); else free_conf(aconf); break; case CONF_RESV_NICK: if(bandb_check_resv_nick(aconf)) rb_dlinkAddAlloc(aconf, &resv_conf_list); else free_conf(aconf); break; } } check_banned_lines(); } static void __attribute__((noreturn)) bandb_handle_failure(rb_helper *helper, char **parv, int parc) { if(server_state_foreground) fprintf(stderr, "bandb - bandb failure: %s\n", parv[1]); ilog(L_MAIN, "bandb - bandb failure: %s", parv[1]); sendto_realops_snomask(SNO_GENERAL, L_ALL, "bandb - bandb failure: %s", parv[1]); exit(1); } static void bandb_parse(rb_helper *helper) { static char buf[READBUF_SIZE]; char *parv[MAXPARA + 1]; int len, parc; while((len = rb_helper_read(helper, buf, sizeof(buf)))) { parc = rb_string_to_array(buf, parv, MAXPARA); if(parc < 1) continue; switch (parv[0][0]) { case '!': bandb_handle_failure(helper, parv, parc); /* UNREACHABLE */ case 'K': case 'D': case 'X': case 'R': bandb_handle_ban(parv, parc); break; case 'C': bandb_handle_clear(); break; case 'F': bandb_handle_finish(); break; } } } void bandb_rehash_bans(void) { if(bandb_helper != NULL) rb_helper_write(bandb_helper, "L"); } static void bandb_restart_cb(rb_helper *helper) { ilog(L_MAIN, "bandb - bandb_restart_cb called, bandb helper died?"); sendto_realops_snomask(SNO_GENERAL, L_ALL, "bandb - bandb_restart_cb called, bandb helper died?"); if(helper != NULL) { rb_helper_close(helper); bandb_helper = NULL; } start_bandb(); return; }