diff --git a/.hgignore b/.hgignore index eab4df7..3add8a5 100644 --- a/.hgignore +++ b/.hgignore @@ -10,6 +10,8 @@ Makefile .deps .libs autom4te.cache +bandb/bandb +bandb/bantool config.log config.status include/setup.h @@ -18,6 +20,7 @@ libratbox/include/librb-config.h libratbox/include/stamp-h1 libratbox/libratbox.pc libratbox/libtool +libratbox/src/version.c scripts/*.tbz2 scripts/*.tgz servlink/servlink diff --git a/CREDITS b/CREDITS index 5fd4939..68dcd9a 100644 --- a/CREDITS +++ b/CREDITS @@ -17,8 +17,6 @@ network configurations. The charybdis core team is listed in nick-alphabetical order: -dwr, Valery Yatsko -gxti, Michael Tharp jilles, Jilles Tjoelker nenolod, William Pitcock @@ -28,7 +26,9 @@ in nick-alphabetical order: AndroSyn, Aaron Sethman anfl, Lee Hardy beu, Elfyn McBratney +dwr, Valery Yatsko Entrope, Michael Poole +gxti, Michael Tharp spb, Stephen Bennett ThaPrince, Jon Christopherson twincest, River Tarnell diff --git a/bandb/bandb.c b/bandb/bandb.c index cce310c..33166b1 100644 --- a/bandb/bandb.c +++ b/bandb/bandb.c @@ -38,6 +38,8 @@ #define MAXPARA 10 +#define COMMIT_INTERVAL 3 /* seconds */ + typedef enum { BANDB_KLINE, @@ -57,9 +59,17 @@ static const char *bandb_table[LAST_BANDB_TYPE] = { static rb_helper *bandb_helper; +static int in_transaction; static void check_schema(void); +static void +bandb_commit(void *unused) +{ + rsdb_transaction(RSDB_TRANS_END); + in_transaction = 0; +} + static void parse_ban(bandb_type type, char *parv[], int parc) { @@ -89,6 +99,14 @@ parse_ban(bandb_type type, char *parv[], int parc) perm = parv[para++]; reason = parv[para++]; + if(!in_transaction) + { + rsdb_transaction(RSDB_TRANS_START); + in_transaction = 1; + rb_event_addonce("bandb_commit", bandb_commit, NULL, + COMMIT_INTERVAL); + } + rsdb_exec(NULL, "INSERT INTO %s (mask1, mask2, oper, time, perm, reason) VALUES('%Q', '%Q', '%Q', %s, %s, '%Q')", bandb_table[type], mask1, mask2 ? mask2 : "", oper, curtime, perm, reason); @@ -113,6 +131,14 @@ parse_unban(bandb_type type, char *parv[], int parc) if(type == BANDB_KLINE) mask2 = parv[2]; + if(!in_transaction) + { + rsdb_transaction(RSDB_TRANS_START); + in_transaction = 1; + rb_event_addonce("bandb_commit", bandb_commit, NULL, + COMMIT_INTERVAL); + } + rsdb_exec(NULL, "DELETE FROM %s WHERE mask1='%Q' AND mask2='%Q'", bandb_table[type], mask1, mask2 ? mask2 : ""); } @@ -215,6 +241,8 @@ parse_request(rb_helper *helper) static void error_cb(rb_helper *helper) { + if(in_transaction) + rsdb_transaction(RSDB_TRANS_END); exit(1); } diff --git a/doc/example.conf b/doc/example.conf index 3a852a4..aaf47cf 100755 --- a/doc/example.conf +++ b/doc/example.conf @@ -530,6 +530,7 @@ general { identify_command = "IDENTIFY"; non_redundant_klines = yes; warn_no_nline = yes; + use_propagated_bans = yes; stats_e_disabled = no; stats_c_oper_only=no; stats_h_oper_only=no; diff --git a/doc/reference.conf b/doc/reference.conf index 6c53d27..b915dfc 100755 --- a/doc/reference.conf +++ b/doc/reference.conf @@ -1198,6 +1198,13 @@ general { */ warn_no_nline = yes; + /* use propagated bans: KLINE, XLINE and RESV set fully propagated bans. + * That means the bans are part of the netburst and restarted/split + * servers will get them, but they will not apply to 3.2 and older + * servers at all. + */ + use_propagated_bans = yes; + /* stats e disabled: disable stats e. useful if server ips are * exempted and you dont want them listing on irc. */ diff --git a/doc/technical/ts6-protocol.txt b/doc/technical/ts6-protocol.txt index d86e34c..902cdd7 100644 --- a/doc/technical/ts6-protocol.txt +++ b/doc/technical/ts6-protocol.txt @@ -55,10 +55,10 @@ send its own PASS, CAPAB and SERVER messages, followed by SVINFO and the burst. Upon receiving the SERVER, the initiator will send SVINFO and the burst. If ziplinks are used, SVINFO is the first compressed message. -The burst consists of SID and SERVER messages for all known servers, UID or -EUID messages for all known users (possibly followed by ENCAP REALHOST, ENCAP -LOGIN and/or AWAY) and SJOIN messages for all known channels (possibly followed -by BMASK and/or TB). +The burst consists of SID and SERVER messages for all known servers, BAN +messages for all propagated bans, UID or EUID messages for all known users +(possibly followed by ENCAP REALHOST, ENCAP LOGIN and/or AWAY) and SJOIN +messages for all known channels (possibly followed by BMASK and/or TB). user modes: (all) @@ -148,6 +148,43 @@ Otherwise, mark the user as away. Changing away reason from one non-empty string to another non-empty string may not be propagated. +BAN +charybdis TS6 +capab: BAN +source: any +propagation: broadcast (restricted) +parameters: type, user mask, host mask, creation TS, duration, lifetime, oper, reason + +Propagates a network wide ban. + +The type is K for K:lines; other types are reserved. + +The creation TS indicates when this ban was last modified. An incoming ban MUST +be ignored and not propagated if the creation TS is older than the creation TS +of the current ban. If the ban is identical, it SHOULD NOT be propagated to +avoid unnecessary network traffic. (Two changes to bans that set the TS to the +same value may cause desynchronization.) + +The duration is 0 for an unban and relative to the creation TS for a ban. +When the duration has passed, the ban is no longer active but it may still +be necessary to remember it. + +The lifetime is relative to the creation TS and indicates for how long this +ban needs to be remembered and propagated. This MUST be at least the duration. +Initially, it is usually set the same as the duration but when the ban is +modified later, it SHOULD be set such that the modified ban is remembered at +least as long as the original ban. This ensures that the original ban does not +revive via split servers. This requirement is only a SHOULD to allow for +implementations that only inject bans and do not remember any; implementations +that remember and propagate bans MUST set the lifetime appropriately. + +The oper field indicates the oper that originally set the ban. If this message +is the initial propagation of a change, it SHOULD be sent as * (an asterisk). + +The reason field indicates the reason for the ban. Any part after a | (vertical +bar) MUST NOT be shown to normal users. The rest of the field and the creation +TS and duration MAY be shown to normal users. + BMASK source: server propagation: broadcast diff --git a/extensions/m_webirc.c b/extensions/m_webirc.c index c46ce1a..1ddb634 100644 --- a/extensions/m_webirc.c +++ b/extensions/m_webirc.c @@ -91,7 +91,7 @@ mr_webirc(struct Client *client_p, struct Client *source_p, int parc, const char client_p->localClient->ip.ss_family, NULL); if (aconf == NULL || !(aconf->status & CONF_CLIENT)) return 0; - if (!IsConfDoSpoofIp(aconf) || irccmp(aconf->name, "webirc.")) + if (!IsConfDoSpoofIp(aconf) || irccmp(aconf->info.name, "webirc.")) { /* XXX */ sendto_one(source_p, "NOTICE * :Not a CGI:IRC auth block"); diff --git a/help/opers/stats b/help/opers/stats index 3017eb3..fb7bb34 100644 --- a/help/opers/stats +++ b/help/opers/stats @@ -16,6 +16,7 @@ X B - Shows hash statistics * e - Shows exemptions to D lines X E - Shows Events X f - Shows File Descriptors +* g - Shows global K lines ^ h - Shows hub_mask/leaf_mask (Old H:/L: lines) ^ i - Shows auth blocks (Old I: lines) ^ K - Shows K lines (or matched klines) @@ -28,14 +29,14 @@ X f - Shows File Descriptors ^ o - Shows operator blocks (Old O: lines) ^ P - Shows configured ports p - Shows online opers -* q - Shows temporary resv'd nicks and channels +* q - Shows temporary and global resv'd nicks and channels * Q - Shows resv'd nicks and channels * r - Shows resource usage by ircd * t - Shows generic server stats * U - Shows shared blocks (Old U: lines) u - Shows server uptime ^ v - Shows connected servers and brief status information -* x - Shows temporary gecos bans +* x - Shows temporary and global gecos bans * X - Shows gecos bans (Old X: lines) ^ y - Shows connection classes (Old Y: lines) * z - Shows memory stats diff --git a/include/channel.h b/include/channel.h index 6a0a56a..cc8a18c 100644 --- a/include/channel.h +++ b/include/channel.h @@ -277,6 +277,8 @@ extern void unset_chcap_usage_counts(struct Client *serv_p); extern void send_cap_mode_changes(struct Client *client_p, struct Client *source_p, struct Channel *chptr, struct ChModeChange foo[], int); +void resv_chan_forcepart(const char *name, const char *reason, int temp_time); + extern void set_channel_mode(struct Client *client_p, struct Client *source_p, struct Channel *chptr, struct membership *msptr, int parc, const char *parv[]); diff --git a/include/logger.h b/include/logger.h index 6efa0ca..c7f7a6e 100644 --- a/include/logger.h +++ b/include/logger.h @@ -61,7 +61,7 @@ extern void inotice(const char *fmt, ...) AFP(1, 2); extern void iwarn(const char *fmt, ...) AFP(1, 2); extern void ierror(const char *fmt, ...) AFP(1, 2); extern void report_operspy(struct Client *, const char *, const char *); -extern const char *smalldate(void); +extern const char *smalldate(time_t); extern void ilog_error(const char *); #endif diff --git a/include/numeric.h b/include/numeric.h index d60f283..a85c17e 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -364,6 +364,9 @@ extern const char *form_str(int); #define RPL_NOTESTLINE 726 #define RPL_TESTMASKGECOS 727 +#define RPL_QUIETLIST 728 +#define RPL_ENDOFQUIETLIST 729 + #define RPL_MONONLINE 730 #define RPL_MONOFFLINE 731 #define RPL_MONLIST 732 diff --git a/include/operhash.h b/include/operhash.h new file mode 100644 index 0000000..c999b72 --- /dev/null +++ b/include/operhash.h @@ -0,0 +1,9 @@ +#ifndef INCLUDED_operhash_h +#define INCLUDED_operhash_h + +void init_operhash(void); +const char *operhash_add(const char *name); +const char *operhash_find(const char *name); +void operhash_delete(const char *name); + +#endif diff --git a/include/s_conf.h b/include/s_conf.h index 3ceb2b5..31b9a74 100644 --- a/include/s_conf.h +++ b/include/s_conf.h @@ -56,11 +56,14 @@ extern char conf_line_in[256]; struct ConfItem { - struct ConfItem *next; /* list node pointer */ unsigned int status; /* If CONF_ILLEGAL, delete when no clients */ unsigned int flags; int clients; /* Number of *LOCAL* clients using this */ - char *name; /* IRC name, nick, server name, or original u@h */ + union + { + char *name; /* IRC name, nick, server name, or original u@h */ + const char *oper; + } info; char *host; /* host part of user@host */ char *passwd; /* doubles as kline reason *ugh* */ char *spasswd; /* Password to send. */ @@ -69,6 +72,8 @@ struct ConfItem char *user; /* user part of user@host */ int port; time_t hold; /* Hold action until this time (calendar time) */ + time_t created; /* Creation time (for klines etc) */ + time_t lifetime; /* Propagated lines: remember until this time */ char *className; /* Name of class */ struct Class *c_class; /* Class of connection */ rb_patricia_node_t *pnode; /* Our patricia node */ @@ -92,6 +97,7 @@ struct ConfItem /* Generic flags... */ #define CONF_FLAGS_TEMPORARY 0x00800000 #define CONF_FLAGS_NEED_SSL 0x00000002 +#define CONF_FLAGS_MYOPER 0x00080000 /* need to rewrite info.oper on burst */ /* auth{} flags... */ #define CONF_FLAGS_NO_TILDE 0x00000004 #define CONF_FLAGS_NEED_IDENTD 0x00000008 @@ -111,6 +117,9 @@ struct ConfItem /* Macros for struct ConfItem */ +#define IsConfBan(x) ((x)->status & (CONF_KILL|CONF_XLINE|CONF_DLINE|\ + CONF_RESV_CHANNEL|CONF_RESV_NICK)) + #define IsNoTilde(x) ((x)->flags & CONF_FLAGS_NO_TILDE) #define IsNeedIdentd(x) ((x)->flags & CONF_FLAGS_NEED_IDENTD) #define IsConfExemptKline(x) ((x)->flags & CONF_FLAGS_EXEMPTKLINE) @@ -220,6 +229,7 @@ struct config_file_entry int default_umodes; int global_snotices; int operspy_dont_care_user_info; + int use_propagated_bans; int secret_channels_in_whois; int expire_override_time; }; @@ -320,6 +330,8 @@ extern struct admin_info AdminInfo; /* defined in ircd.c */ extern rb_dlink_list service_list; +extern rb_dlink_list prop_bans; + typedef enum temp_list { TEMP_MIN, @@ -337,6 +349,10 @@ extern void init_s_conf(void); extern struct ConfItem *make_conf(void); extern void free_conf(struct ConfItem *); +extern rb_dlink_node *find_prop_ban(unsigned int status, const char *user, const char *host); +extern void deactivate_conf(struct ConfItem *, rb_dlink_node *); +extern void replace_old_ban(struct ConfItem *); + extern void read_conf_files(int cold); extern int attach_conf(struct Client *, struct ConfItem *); @@ -348,6 +364,7 @@ extern struct ConfItem *find_tkline(const char *, const char *, struct sockaddr extern char *show_iline_prefix(struct Client *, struct ConfItem *, char *); extern void get_printable_conf(struct ConfItem *, char **, char **, char **, char **, int *, char **); +extern char *get_user_ban_reason(struct ConfItem *aconf); extern void get_printable_kline(struct Client *, struct ConfItem *, char **, char **, char **, char **); @@ -355,6 +372,7 @@ extern void yyerror(const char *); extern int conf_yy_fatal_error(const char *); extern int conf_fgets(char *, int, FILE *); +extern int valid_wild_card(const char *, const char *); extern void add_temp_kline(struct ConfItem *); extern void add_temp_dline(struct ConfItem *); extern void report_temp_klines(struct Client *); diff --git a/include/s_serv.h b/include/s_serv.h index c52c78d..73327c7 100644 --- a/include/s_serv.h +++ b/include/s_serv.h @@ -72,12 +72,14 @@ struct Capability #define CAP_SAVE 0x40000 /* supports SAVE (nick collision FNC) */ #define CAP_EUID 0x80000 /* supports EUID (ext UID + nonencap CHGHOST) */ #define CAP_EOPMOD 0x100000 /* supports EOPMOD (ext +z + ext topic) */ +#define CAP_BAN 0x200000 /* supports propagated bans */ #define CAP_MASK (CAP_QS | CAP_EX | CAP_CHW | \ CAP_IE | CAP_KLN | CAP_SERVICE |\ CAP_CLUSTER | CAP_ENCAP | \ CAP_ZIP | CAP_KNOCK | CAP_UNKLN | \ - CAP_RSFNC | CAP_SAVE | CAP_EUID | CAP_EOPMOD) + CAP_RSFNC | CAP_SAVE | CAP_EUID | CAP_EOPMOD | \ + CAP_BAN) #ifdef HAVE_LIBZ #define CAP_ZIP_SUPPORTED CAP_ZIP diff --git a/libratbox/src/balloc.c b/libratbox/src/balloc.c index c827614..a147feb 100644 --- a/libratbox/src/balloc.c +++ b/libratbox/src/balloc.c @@ -492,11 +492,14 @@ rb_bh_usage(rb_bh *bh, size_t *bused, size_t *bfree, size_t *bmemusage, const ch if(desc != NULL) *desc = bh->desc; #else - static char *noballoc = "no blockheap"; - *bused = 0; - *bfree = 0; - *bmemusage = 0; - *desc = noballoc; + if(bused != NULL) + *bused = 0; + if(bfree != NULL) + *bfree = 0; + if(bmemusage != NULL) + *bmemusage = 0; + if(desc != NULL) + *desc = "no blockheap"; #endif } diff --git a/libratbox/src/linebuf.c b/libratbox/src/linebuf.c index 5e7fbb4..b3d2bb9 100644 --- a/libratbox/src/linebuf.c +++ b/libratbox/src/linebuf.c @@ -28,9 +28,7 @@ #include #include -#ifndef NOBALLOC static rb_bh *rb_linebuf_heap; -#endif static int bufline_count = 0; diff --git a/modules/Makefile.in b/modules/Makefile.in index 6c80e21..ad1aace 100644 --- a/modules/Makefile.in +++ b/modules/Makefile.in @@ -36,6 +36,7 @@ INCLUDES = -I../include -I../libratbox/include $(SSL_INCLUDES) CPPFLAGS = ${INCLUDES} @CPPFLAGS@ CORE_SRCS = \ + core/m_ban.c \ core/m_die.c \ core/m_error.c \ core/m_join.c \ diff --git a/modules/core/m_ban.c b/modules/core/m_ban.c new file mode 100644 index 0000000..d966edd --- /dev/null +++ b/modules/core/m_ban.c @@ -0,0 +1,303 @@ +/* + * charybdis: An advanced ircd. + * m_ban.c: Propagates network bans across servers. + * + * Copyright (C) 2010 Jilles Tjoelker + * + * 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. + * + * 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. + */ + +#include "stdinc.h" +#include "send.h" +#include "channel.h" +#include "client.h" +#include "common.h" +#include "config.h" +#include "ircd.h" +#include "match.h" +#include "s_conf.h" +#include "s_newconf.h" +#include "msg.h" +#include "modules.h" +#include "hash.h" +#include "s_serv.h" +#include "operhash.h" +#include "reject.h" +#include "hostmask.h" + +static int ms_ban(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]); + +struct Message ban_msgtab = { + "BAN", 0, 0, 0, MFLG_SLOW, + {mg_unreg, mg_ignore, {ms_ban, 9}, {ms_ban, 9}, mg_ignore, mg_ignore} +}; + +mapi_clist_av1 ban_clist[] = { &ban_msgtab, NULL }; +DECLARE_MODULE_AV1(ban, NULL, NULL, ban_clist, NULL, NULL, "$Revision: 1349 $"); + +/* ms_ban() + * + * parv[1] - type + * parv[2] - username mask or * + * parv[3] - hostname mask + * parv[4] - creation TS + * parv[5] - duration (relative to creation) + * parv[6] - lifetime (relative to creation) + * parv[7] - oper or * + * parv[8] - reason (possibly with |operreason) + */ +static int +ms_ban(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) +{ + rb_dlink_node *ptr; + struct ConfItem *aconf; + unsigned int ntype; + const char *oper, *stype; + time_t created, hold, lifetime; + char *p; + int act; + int valid; + + if (strlen(parv[1]) != 1) + { + sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, + "Unknown BAN type %s from %s", + parv[1], source_p->name); + return 0; + } + switch (parv[1][0]) + { + case 'K': + ntype = CONF_KILL; + stype = "K-Line"; + break; + case 'X': + ntype = CONF_XLINE; + stype = "X-Line"; + break; + case 'R': + ntype = IsChannelName(parv[3]) ? CONF_RESV_CHANNEL : + CONF_RESV_NICK; + stype = "RESV"; + break; + default: + sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, + "Unknown BAN type %s from %s", + parv[1], source_p->name); + return 0; + } + created = atol(parv[4]); + hold = created + atoi(parv[5]); + lifetime = created + atoi(parv[6]); + if (!strcmp(parv[7], "*")) + oper = IsServer(source_p) ? source_p->name : get_oper_name(source_p); + else + oper = parv[7]; + ptr = find_prop_ban(ntype, parv[2], parv[3]); + if (ptr != NULL) + { + aconf = ptr->data; + if (aconf->created > created || + (aconf->created == created && + aconf->lifetime >= lifetime)) + { + if (IsPerson(source_p)) + sendto_one_notice(source_p, + ":Your %s [%s%s%s] has been superseded", + stype, + aconf->user ? aconf->user : "", + aconf->user ? "@" : "", + aconf->host); + return 0; + } + act = !(aconf->status & CONF_ILLEGAL) || (hold != created && + hold > rb_current_time()); + if (lifetime > aconf->lifetime) + aconf->lifetime = lifetime; + /* already expired, hmm */ + if (aconf->lifetime <= rb_current_time()) + return 0; + deactivate_conf(aconf, ptr); + rb_free(aconf->user); + aconf->user = NULL; + rb_free(aconf->host); + aconf->host = NULL; + operhash_delete(aconf->info.oper); + aconf->info.oper = NULL; + rb_free(aconf->passwd); + aconf->passwd = NULL; + rb_free(aconf->spasswd); + aconf->spasswd = NULL; + } + else + { + aconf = make_conf(); + aconf->status = CONF_ILLEGAL | ntype; + aconf->lifetime = lifetime; + rb_dlinkAddAlloc(aconf, &prop_bans); + act = hold != created && hold > rb_current_time(); + } + aconf->flags &= ~CONF_FLAGS_MYOPER; + aconf->flags |= CONF_FLAGS_TEMPORARY; + aconf->user = ntype == CONF_KILL ? rb_strdup(parv[2]) : NULL; + aconf->host = rb_strdup(parv[3]); + aconf->info.oper = operhash_add(oper); + aconf->created = created; + aconf->hold = hold; + if (ntype != CONF_KILL || (p = strchr(parv[parc - 1], '|')) == NULL) + aconf->passwd = rb_strdup(parv[parc - 1]); + else + { + aconf->passwd = rb_strndup(parv[parc - 1], p - parv[parc - 1] + 1); + aconf->spasswd = rb_strdup(p + 1); + } + switch (ntype) + { + case CONF_KILL: + valid = valid_wild_card(aconf->user, aconf->host); + break; + case CONF_RESV_CHANNEL: + valid = 1; + break; + default: + valid = valid_wild_card_simple(aconf->host); + break; + } + if (act && hold != created && !valid) + { + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "Ignoring global %d min. %s from %s%s%s for [%s%s%s]: too few non-wildcard characters", + (int)((hold - rb_current_time()) / 60), + stype, + IsServer(source_p) ? source_p->name : get_oper_name(source_p), + strcmp(parv[7], "*") ? " on behalf of " : "", + strcmp(parv[7], "*") ? parv[7] : "", + aconf->user ? aconf->user : "", + aconf->user ? "@" : "", + aconf->host); + if(IsPerson(source_p)) + sendto_one_notice(source_p, + ":Your %s [%s%s%s] has too few non-wildcard characters", + stype, + aconf->user ? aconf->user : "", + aconf->user ? "@" : "", + aconf->host); + /* Propagate it, but do not apply it locally. */ + } + else if (act && hold != created) + { + /* Keep the notices in sync with modules/m_kline.c etc. */ + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s added global %d min. %s%s%s for [%s%s%s] [%s]", + IsServer(source_p) ? source_p->name : get_oper_name(source_p), + (int)((hold - rb_current_time()) / 60), + stype, + strcmp(parv[7], "*") ? " from " : "", + strcmp(parv[7], "*") ? parv[7] : "", + aconf->user ? aconf->user : "", + aconf->user ? "@" : "", + aconf->host, + parv[parc - 1]); + ilog(L_KLINE, "%s %s %d %s%s%s %s", parv[1], + IsServer(source_p) ? source_p->name : get_oper_name(source_p), + (int)((hold - rb_current_time()) / 60), + aconf->user ? aconf->user : "", + aconf->user ? " " : "", + aconf->host, + parv[parc - 1]); + aconf->status &= ~CONF_ILLEGAL; + } + else if (act) + { + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s has removed the global %s for: [%s%s%s]%s%s", + IsServer(source_p) ? source_p->name : get_oper_name(source_p), + stype, + aconf->user ? aconf->user : "", + aconf->user ? "@" : "", + aconf->host, + strcmp(parv[7], "*") ? " on behalf of " : "", + strcmp(parv[7], "*") ? parv[7] : ""); + ilog(L_KLINE, "U%s %s %s%s %s", parv[1], + IsServer(source_p) ? source_p->name : get_oper_name(source_p), + aconf->user ? aconf->user : "", + aconf->user ? " " : "", + aconf->host); + } + switch (ntype) + { + case CONF_KILL: + if (aconf->status & CONF_ILLEGAL) + remove_reject_mask(aconf->user, aconf->host); + else + { + add_conf_by_address(aconf->host, CONF_KILL, aconf->user, NULL, aconf); + if(ConfigFileEntry.kline_delay || + (IsServer(source_p) && + !HasSentEob(source_p))) + { + if(kline_queued == 0) + { + rb_event_addonce("check_klines", check_klines_event, NULL, + ConfigFileEntry.kline_delay); + kline_queued = 1; + } + } + else + check_klines(); + } + break; + case CONF_XLINE: + if (aconf->status & CONF_ILLEGAL) + remove_reject_mask(aconf->host, NULL); + else + { + rb_dlinkAddAlloc(aconf, &xline_conf_list); + check_xlines(); + } + break; + case CONF_RESV_CHANNEL: + if (!(aconf->status & CONF_ILLEGAL)) + { + add_to_resv_hash(aconf->host, aconf); + resv_chan_forcepart(aconf->host, aconf->passwd, hold - rb_current_time()); + } + break; + case CONF_RESV_NICK: + if (!(aconf->status & CONF_ILLEGAL)) + rb_dlinkAddAlloc(aconf, &resv_conf_list); + break; + } + sendto_server(client_p, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN %s %s %s %s %s %s %s :%s", + source_p->id, + parv[1], + parv[2], + parv[3], + parv[4], + parv[5], + parv[6], + parv[7], + parv[parc - 1]); + return 0; +} diff --git a/modules/m_dline.c b/modules/m_dline.c index 5ac171b..bb928ac 100644 --- a/modules/m_dline.c +++ b/modules/m_dline.c @@ -43,6 +43,7 @@ #include "parse.h" #include "modules.h" #include "bandbi.h" +#include "operhash.h" static int mo_dline(struct Client *, struct Client *, int, const char **); static int me_dline(struct Client *, struct Client *, int, const char **); @@ -213,8 +214,6 @@ apply_dline(struct Client *source_p, const char *dlhost, int tdline_time, char * { struct ConfItem *aconf; char *oper_reason; - char dlbuffer[IRCD_BUFSIZE]; - const char *current_date; struct rb_sockaddr_storage daddr; int t = AF_INET, ty, b; const char *creason; @@ -285,11 +284,13 @@ apply_dline(struct Client *source_p, const char *dlhost, int tdline_time, char * } rb_set_time(); - current_date = smalldate(); aconf = make_conf(); aconf->status = CONF_DLINE; + aconf->created = rb_current_time(); aconf->host = rb_strdup(dlhost); + aconf->passwd = rb_strdup(reason); + aconf->info.oper = operhash_add(get_oper_name(source_p)); /* Look for an oper reason */ if((oper_reason = strchr(reason, '|')) != NULL) @@ -303,10 +304,6 @@ apply_dline(struct Client *source_p, const char *dlhost, int tdline_time, char * if(tdline_time > 0) { - rb_snprintf(dlbuffer, sizeof(dlbuffer), - "Temporary D-line %d min. - %s (%s)", - (int) (tdline_time / 60), reason, current_date); - aconf->passwd = rb_strdup(dlbuffer); aconf->hold = rb_current_time() + tdline_time; add_temp_dline(aconf); @@ -335,8 +332,6 @@ apply_dline(struct Client *source_p, const char *dlhost, int tdline_time, char * } else { - rb_snprintf(dlbuffer, sizeof(dlbuffer), "%s (%s)", reason, current_date); - aconf->passwd = rb_strdup(dlbuffer); add_conf_by_address(aconf->host, CONF_DLINE, NULL, NULL, aconf); bandb_add(BANDB_DLINE, source_p, aconf->host, NULL, diff --git a/modules/m_info.c b/modules/m_info.c index 716ca9d..e829127 100644 --- a/modules/m_info.c +++ b/modules/m_info.c @@ -530,6 +530,12 @@ static struct InfoStruct info_table[] = { &ConfigFileEntry.warn_no_nline, "Display warning if connecting server lacks N-line" }, + { + "use_propagated_bans", + OUTPUT_BOOLEAN, + &ConfigFileEntry.use_propagated_bans, + "KLINE sets fully propagated bans" + }, { "default_split_server_count", OUTPUT_DECIMAL, diff --git a/modules/m_kline.c b/modules/m_kline.c index 44012ed..89ba235 100644 --- a/modules/m_kline.c +++ b/modules/m_kline.c @@ -44,6 +44,7 @@ #include "modules.h" #include "reject.h" #include "bandbi.h" +#include "operhash.h" static int mo_kline(struct Client *, struct Client *, int, const char **); static int ms_kline(struct Client *, struct Client *, int, const char **); @@ -70,19 +71,21 @@ DECLARE_MODULE_AV1(kline, NULL, NULL, kline_clist, NULL, NULL, "$Revision$"); static int find_user_host(struct Client *source_p, const char *userhost, char *user, char *host); static int valid_comment(struct Client *source_p, char *comment); static int valid_user_host(struct Client *source_p, const char *user, const char *host); -static int valid_wild_card(struct Client *source_p, const char *user, const char *host); static void handle_remote_kline(struct Client *source_p, int tkline_time, const char *user, const char *host, const char *reason); static void apply_kline(struct Client *source_p, struct ConfItem *aconf, - const char *reason, const char *oper_reason, const char *current_date); + const char *reason, const char *oper_reason); static void apply_tkline(struct Client *source_p, struct ConfItem *aconf, - const char *, const char *, const char *, int); + const char *, const char *, int); +static void apply_prop_kline(struct Client *source_p, struct ConfItem *aconf, + const char *, const char *, int); static int already_placed_kline(struct Client *, const char *, const char *, int); static void handle_remote_unkline(struct Client *source_p, const char *user, const char *host); static void remove_permkline_match(struct Client *, struct ConfItem *); static int remove_temp_kline(struct Client *, struct ConfItem *); +static void remove_prop_kline(struct Client *, struct ConfItem *); /* mo_kline() * @@ -98,14 +101,13 @@ mo_kline(struct Client *client_p, struct Client *source_p, int parc, const char char def[] = "No Reason"; char user[USERLEN + 2]; char host[HOSTLEN + 2]; - char buffer[IRCD_BUFSIZE]; char *reason = def; char *oper_reason; - const char *current_date; const char *target_server = NULL; struct ConfItem *aconf; int tkline_time = 0; int loc = 1; + int propagated = ConfigFileEntry.use_propagated_bans; if(!IsOperK(source_p)) { @@ -154,27 +156,46 @@ mo_kline(struct Client *client_p, struct Client *source_p, int parc, const char /* If we are sending it somewhere that doesnt include us, stop */ if(!match(target_server, me.name)) return 0; + + /* Set as local-only. */ + propagated = 0; } /* if we have cluster servers, send it to them.. */ - else if(rb_dlink_list_length(&cluster_conf_list) > 0) + else if(!propagated && rb_dlink_list_length(&cluster_conf_list) > 0) cluster_generic(source_p, "KLINE", (tkline_time > 0) ? SHARED_TKLINE : SHARED_PKLINE, CAP_KLN, "%lu %s %s :%s", tkline_time, user, host, reason); if(!valid_user_host(source_p, user, host) || - !valid_wild_card(source_p, user, host) || !valid_comment(source_p, reason)) + !valid_comment(source_p, reason)) return 0; + if(!valid_wild_card(user, host)) + { + sendto_one_notice(source_p, + ":Please include at least %d non-wildcard " + "characters with the user@host", + ConfigFileEntry.min_nonwildcard); + return 0; + } + + if(propagated && tkline_time == 0) + { + sendto_one_notice(source_p, ":Cannot set a permanent global ban"); + return 0; + } + if(already_placed_kline(source_p, user, host, tkline_time)) return 0; rb_set_time(); - current_date = smalldate(); aconf = make_conf(); aconf->status = CONF_KILL; + aconf->created = rb_current_time(); aconf->host = rb_strdup(host); aconf->user = rb_strdup(user); aconf->port = 0; + aconf->info.oper = operhash_add(get_oper_name(source_p)); /* Look for an oper reason */ if((oper_reason = strchr(reason, '|')) != NULL) @@ -185,21 +206,14 @@ mo_kline(struct Client *client_p, struct Client *source_p, int parc, const char if(!EmptyString(oper_reason)) aconf->spasswd = rb_strdup(oper_reason); } + aconf->passwd = rb_strdup(reason); - if(tkline_time > 0) - { - rb_snprintf(buffer, sizeof(buffer), - "Temporary K-line %d min. - %s (%s)", - (int) (tkline_time / 60), reason, current_date); - aconf->passwd = rb_strdup(buffer); - apply_tkline(source_p, aconf, reason, oper_reason, current_date, tkline_time); - } + if(propagated) + apply_prop_kline(source_p, aconf, reason, oper_reason, tkline_time); + else if(tkline_time > 0) + apply_tkline(source_p, aconf, reason, oper_reason, tkline_time); else - { - rb_snprintf(buffer, sizeof(buffer), "%s (%s)", reason, current_date); - aconf->passwd = rb_strdup(buffer); - apply_kline(source_p, aconf, reason, oper_reason, current_date); - } + apply_kline(source_p, aconf, reason, oper_reason); if(ConfigFileEntry.kline_delay) { @@ -264,8 +278,6 @@ static void handle_remote_kline(struct Client *source_p, int tkline_time, const char *user, const char *host, const char *kreason) { - char buffer[BUFSIZE]; - const char *current_date; char *reason = LOCAL_COPY(kreason); struct ConfItem *aconf = NULL; char *oper_reason; @@ -276,17 +288,28 @@ handle_remote_kline(struct Client *source_p, int tkline_time, return; if(!valid_user_host(source_p, user, host) || - !valid_wild_card(source_p, user, host) || !valid_comment(source_p, reason)) + !valid_comment(source_p, reason)) return; + if(!valid_wild_card(user, host)) + { + sendto_one_notice(source_p, + ":Please include at least %d non-wildcard " + "characters with the user@host", + ConfigFileEntry.min_nonwildcard); + return; + } + if(already_placed_kline(source_p, user, host, tkline_time)) return; aconf = make_conf(); aconf->status = CONF_KILL; + aconf->created = rb_current_time(); aconf->user = rb_strdup(user); aconf->host = rb_strdup(host); + aconf->info.oper = operhash_add(get_oper_name(source_p)); /* Look for an oper reason */ if((oper_reason = strchr(reason, '|')) != NULL) @@ -297,23 +320,12 @@ handle_remote_kline(struct Client *source_p, int tkline_time, if(!EmptyString(oper_reason)) aconf->spasswd = rb_strdup(oper_reason); } - - current_date = smalldate(); + aconf->passwd = rb_strdup(reason); if(tkline_time > 0) - { - rb_snprintf(buffer, sizeof(buffer), - "Temporary K-line %d min. - %s (%s)", - (int) (tkline_time / 60), reason, current_date); - aconf->passwd = rb_strdup(buffer); - apply_tkline(source_p, aconf, reason, oper_reason, current_date, tkline_time); - } + apply_tkline(source_p, aconf, reason, oper_reason, tkline_time); else - { - rb_snprintf(buffer, sizeof(buffer), "%s (%s)", reason, current_date); - aconf->passwd = rb_strdup(buffer); - apply_kline(source_p, aconf, reason, oper_reason, current_date); - } + apply_kline(source_p, aconf, reason, oper_reason); if(ConfigFileEntry.kline_delay) { @@ -344,6 +356,7 @@ mo_unkline(struct Client *client_p, struct Client *source_p, int parc, const cha char splat[] = "*"; char *h = LOCAL_COPY(parv[1]); struct ConfItem *aconf; + int propagated = 1; if(!IsOperUnkline(source_p)) { @@ -395,17 +408,32 @@ mo_unkline(struct Client *client_p, struct Client *source_p, int parc, const cha if(match(parv[3], me.name) == 0) return 0; + + propagated = 0; } - else if(rb_dlink_list_length(&cluster_conf_list) > 0) + + aconf = find_exact_conf_by_address(host, CONF_KILL, user); + + /* No clustering for removing a propagated kline */ + if(propagated && (aconf == NULL || !aconf->lifetime) && + rb_dlink_list_length(&cluster_conf_list) > 0) cluster_generic(source_p, "UNKLINE", SHARED_UNKLINE, CAP_UNKLN, "%s %s", user, host); - aconf = find_exact_conf_by_address(host, CONF_KILL, user); if(aconf == NULL) { sendto_one_notice(source_p, ":No K-Line for %s@%s", user, host); return 0; } + + if(aconf->lifetime) + { + if(propagated) + remove_prop_kline(source_p, aconf); + else + sendto_one_notice(source_p, ":Cannot remove global K-Line %s@%s on specific servers", user, host); + return 0; + } if(remove_temp_kline(source_p, aconf)) return 0; @@ -464,6 +492,11 @@ handle_remote_unkline(struct Client *source_p, const char *user, const char *hos sendto_one_notice(source_p, ":No K-Line for %s@%s", user, host); return; } + if(aconf->lifetime) + { + sendto_one_notice(source_p, ":Cannot remove global K-Line %s@%s on specific servers", user, host); + return; + } if(remove_temp_kline(source_p, aconf)) return; @@ -480,7 +513,7 @@ handle_remote_unkline(struct Client *source_p, const char *user, const char *hos */ static void apply_kline(struct Client *source_p, struct ConfItem *aconf, - const char *reason, const char *oper_reason, const char *current_date) + const char *reason, const char *oper_reason) { add_conf_by_address(aconf->host, CONF_KILL, aconf->user, NULL, aconf); bandb_add(BANDB_KLINE, source_p, aconf->user, aconf->host, @@ -517,7 +550,7 @@ apply_kline(struct Client *source_p, struct ConfItem *aconf, */ static void apply_tkline(struct Client *source_p, struct ConfItem *aconf, - const char *reason, const char *oper_reason, const char *current_date, int tkline_time) + const char *reason, const char *oper_reason, int tkline_time) { aconf->hold = rb_current_time() + tkline_time; add_temp_kline(aconf); @@ -547,6 +580,54 @@ apply_tkline(struct Client *source_p, struct ConfItem *aconf, tkline_time / 60, aconf->user, aconf->host); } +static void +apply_prop_kline(struct Client *source_p, struct ConfItem *aconf, + const char *reason, const char *oper_reason, int tkline_time) +{ + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + aconf->hold = rb_current_time() + tkline_time; + aconf->lifetime = aconf->hold; + + replace_old_ban(aconf); + + rb_dlinkAddAlloc(aconf, &prop_bans); + add_conf_by_address(aconf->host, CONF_KILL, aconf->user, NULL, aconf); + + /* no oper reason.. */ + if(EmptyString(oper_reason)) + { + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s added global %d min. K-Line for [%s@%s] [%s]", + get_oper_name(source_p), tkline_time / 60, + aconf->user, aconf->host, reason); + ilog(L_KLINE, "K %s %d %s %s %s", + get_oper_name(source_p), tkline_time / 60, aconf->user, aconf->host, reason); + } + else + { + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s added global %d min. K-Line for [%s@%s] [%s|%s]", + get_oper_name(source_p), tkline_time / 60, + aconf->user, aconf->host, reason, oper_reason); + ilog(L_KLINE, "K %s %d %s %s %s|%s", + get_oper_name(source_p), tkline_time / 60, + aconf->user, aconf->host, reason, oper_reason); + } + + sendto_one_notice(source_p, ":Added global %d min. K-Line [%s@%s]", + tkline_time / 60, aconf->user, aconf->host); + + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN K %s %s %lu %d %d * :%s%s%s", + source_p->id, aconf->user, aconf->host, + (unsigned long)aconf->created, + (int)(aconf->hold - aconf->created), + (int)(aconf->lifetime - aconf->created), + reason, + oper_reason ? "|" : "", + oper_reason ? oper_reason : ""); +} + /* find_user_host() * * inputs - client placing kline, user@host, user buffer, host buffer @@ -610,65 +691,6 @@ valid_user_host(struct Client *source_p, const char *luser, const char *lhost) return 1; } -/* valid_wild_card() - * - * input - user buffer, host buffer - * output - 0 if invalid, 1 if valid - * side effects - - */ -static int -valid_wild_card(struct Client *source_p, const char *luser, const char *lhost) -{ - const char *p; - char tmpch; - int nonwild = 0; - int bitlen; - - /* user has no wildcards, always accept -- jilles */ - if(!strchr(luser, '?') && !strchr(luser, '*')) - return 1; - - /* check there are enough non wildcard chars */ - p = luser; - while((tmpch = *p++)) - { - if(!IsKWildChar(tmpch)) - { - /* found enough chars, return */ - if(++nonwild >= ConfigFileEntry.min_nonwildcard) - return 1; - } - } - - /* try host, as user didnt contain enough */ - /* special case for cidr masks -- jilles */ - if((p = strrchr(lhost, '/')) != NULL && IsDigit(p[1])) - { - bitlen = atoi(p + 1); - /* much like non-cidr for ipv6, rather arbitrary for ipv4 */ - if(bitlen > 0 - && bitlen >= - (strchr(lhost, ':') ? 4 * (ConfigFileEntry.min_nonwildcard - nonwild) : 6 - - 2 * nonwild)) - return 1; - } - else - { - p = lhost; - while((tmpch = *p++)) - { - if(!IsKWildChar(tmpch)) - if(++nonwild >= ConfigFileEntry.min_nonwildcard) - return 1; - } - } - - sendto_one_notice(source_p, - ":Please include at least %d non-wildcard " - "characters with the user@host", ConfigFileEntry.min_nonwildcard); - return 0; -} - /* * valid_comment * inputs - pointer to client @@ -819,3 +841,39 @@ remove_temp_kline(struct Client *source_p, struct ConfItem *aconf) return NO; } + +static void +remove_prop_kline(struct Client *source_p, struct ConfItem *aconf) +{ + rb_dlink_node *ptr; + + ptr = rb_dlinkFind(aconf, &prop_bans); + if (!ptr) + return; + sendto_one_notice(source_p, + ":Un-klined [%s@%s] from global k-lines", + aconf->user, aconf->host); + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s has removed the global K-Line for: [%s@%s]", + get_oper_name(source_p), aconf->user, + aconf->host); + + ilog(L_KLINE, "UK %s %s %s", + get_oper_name(source_p), aconf->user, aconf->host); + if(aconf->created < rb_current_time()) + aconf->created = rb_current_time(); + else + aconf->created++; + aconf->hold = aconf->created; + operhash_delete(aconf->info.oper); + aconf->info.oper = operhash_add(get_oper_name(source_p)); + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN K %s %s %lu %d %d * :*", + source_p->id, aconf->user, aconf->host, + (unsigned long)aconf->created, + 0, + (int)(aconf->lifetime - aconf->created)); + remove_reject_mask(aconf->user, aconf->host); + deactivate_conf(aconf, ptr); +} diff --git a/modules/m_rehash.c b/modules/m_rehash.c index 40cc7de..8557f8c 100644 --- a/modules/m_rehash.c +++ b/modules/m_rehash.c @@ -174,7 +174,7 @@ rehash_txlines(struct Client *source_p) { aconf = ptr->data; - if(!aconf->hold) + if(!aconf->hold || aconf->lifetime) continue; free_conf(aconf); @@ -199,7 +199,7 @@ rehash_tresvs(struct Client *source_p) { aconf = ptr->data; - if(!aconf->hold) + if(!aconf->hold || aconf->lifetime) continue; free_conf(aconf); @@ -211,7 +211,7 @@ rehash_tresvs(struct Client *source_p) { aconf = ptr->data; - if(!aconf->hold) + if(!aconf->hold || aconf->lifetime) continue; free_conf(aconf); diff --git a/modules/m_resv.c b/modules/m_resv.c index 21de7f3..af72747 100644 --- a/modules/m_resv.c +++ b/modules/m_resv.c @@ -38,6 +38,7 @@ #include "hash.h" #include "logger.h" #include "bandbi.h" +#include "operhash.h" static int mo_resv(struct Client *, struct Client *, int, const char **); static int ms_resv(struct Client *, struct Client *, int, const char **); @@ -61,15 +62,14 @@ mapi_clist_av1 resv_clist[] = { &resv_msgtab, &unresv_msgtab, NULL }; DECLARE_MODULE_AV1(resv, NULL, NULL, resv_clist, NULL, NULL, "$Revision$"); static void parse_resv(struct Client *source_p, const char *name, - const char *reason, int temp_time); + const char *reason, int temp_time, int propagated); static void propagate_resv(struct Client *source_p, const char *target, int temp_time, const char *name, const char *reason); static void cluster_resv(struct Client *source_p, int temp_time, const char *name, const char *reason); static void handle_remote_unresv(struct Client *source_p, const char *name); -static void remove_resv(struct Client *source_p, const char *name); -static void resv_chan_forcepart(const char *name, const char *reason, int temp_time); +static void remove_resv(struct Client *source_p, const char *name, int propagated); /* * mo_resv() @@ -85,6 +85,7 @@ mo_resv(struct Client *client_p, struct Client *source_p, int parc, const char * const char *target_server = NULL; int temp_time; int loc = 1; + int propagated = ConfigFileEntry.use_propagated_bans; if(!IsOperResv(source_p)) { @@ -114,6 +115,9 @@ mo_resv(struct Client *client_p, struct Client *source_p, int parc, const char * target_server = parv[loc + 1]; loc += 2; + + /* Set as local-only. */ + propagated = 0; } if(parc <= loc || EmptyString(parv[loc])) @@ -132,10 +136,16 @@ mo_resv(struct Client *client_p, struct Client *source_p, int parc, const char * if(match(target_server, me.name) == 0) return 0; } - else if(rb_dlink_list_length(&cluster_conf_list) > 0) + else if(!propagated && rb_dlink_list_length(&cluster_conf_list) > 0) cluster_resv(source_p, temp_time, name, reason); - parse_resv(source_p, name, reason, temp_time); + if(propagated && temp_time == 0) + { + sendto_one_notice(source_p, ":Cannot set a permanent global ban"); + return 0; + } + + parse_resv(source_p, name, reason, temp_time, propagated); return 0; } @@ -160,7 +170,7 @@ ms_resv(struct Client *client_p, struct Client *source_p, int parc, const char * if(!IsPerson(source_p)) return 0; - parse_resv(source_p, parv[2], parv[3], 0); + parse_resv(source_p, parv[2], parv[3], 0, 0); return 0; } @@ -171,7 +181,7 @@ me_resv(struct Client *client_p, struct Client *source_p, int parc, const char * if(!IsPerson(source_p)) return 0; - parse_resv(source_p, parv[2], parv[4], atoi(parv[1])); + parse_resv(source_p, parv[2], parv[4], atoi(parv[1]), 0); return 0; } @@ -184,7 +194,7 @@ me_resv(struct Client *client_p, struct Client *source_p, int parc, const char * * side effects - will parse the resv and create it if valid */ static void -parse_resv(struct Client *source_p, const char *name, const char *reason, int temp_time) +parse_resv(struct Client *source_p, const char *name, const char *reason, int temp_time, int propagated) { struct ConfItem *aconf; @@ -218,12 +228,36 @@ parse_resv(struct Client *source_p, const char *name, const char *reason, int te aconf = make_conf(); aconf->status = CONF_RESV_CHANNEL; aconf->port = 0; + aconf->created = rb_current_time(); aconf->host = rb_strdup(name); aconf->passwd = rb_strdup(reason); - add_to_resv_hash(aconf->host, aconf); - resv_chan_forcepart(aconf->host, aconf->passwd, temp_time); + aconf->info.oper = operhash_add(get_oper_name(source_p)); - if(temp_time > 0) + if(propagated) + { + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + aconf->hold = rb_current_time() + temp_time; + aconf->lifetime = aconf->hold; + replace_old_ban(aconf); + rb_dlinkAddAlloc(aconf, &prop_bans); + + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s added global %d min. RESV for [%s] [%s]", + get_oper_name(source_p), temp_time / 60, + name, reason); + ilog(L_KLINE, "R %s %d %s %s", + get_oper_name(source_p), temp_time / 60, name, reason); + sendto_one_notice(source_p, ":Added global %d min. RESV [%s]", + temp_time / 60, name); + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN R * %s %lu %d %d * :%s", + source_p->id, aconf->host, + (unsigned long)aconf->created, + (int)(aconf->hold - aconf->created), + (int)(aconf->lifetime - aconf->created), + reason); + } + else if(temp_time > 0) { aconf->hold = rb_current_time() + temp_time; @@ -247,6 +281,9 @@ parse_resv(struct Client *source_p, const char *name, const char *reason, int te bandb_add(BANDB_RESV, source_p, aconf->host, NULL, aconf->passwd, NULL, 0); } + + add_to_resv_hash(aconf->host, aconf); + resv_chan_forcepart(aconf->host, aconf->passwd, temp_time); } else if(clean_resv_nick(name)) { @@ -281,11 +318,36 @@ parse_resv(struct Client *source_p, const char *name, const char *reason, int te aconf = make_conf(); aconf->status = CONF_RESV_NICK; aconf->port = 0; + aconf->created = rb_current_time(); aconf->host = rb_strdup(name); aconf->passwd = rb_strdup(reason); - rb_dlinkAddAlloc(aconf, &resv_conf_list); + aconf->info.oper = operhash_add(get_oper_name(source_p)); - if(temp_time > 0) + if(propagated) + { + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + aconf->hold = rb_current_time() + temp_time; + aconf->lifetime = aconf->hold; + replace_old_ban(aconf); + rb_dlinkAddAlloc(aconf, &prop_bans); + + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s added global %d min. RESV for [%s] [%s]", + get_oper_name(source_p), temp_time / 60, + name, reason); + ilog(L_KLINE, "R %s %d %s %s", + get_oper_name(source_p), temp_time / 60, name, reason); + sendto_one_notice(source_p, ":Added global %d min. RESV [%s]", + temp_time / 60, name); + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN R * %s %lu %d %d * :%s", + source_p->id, aconf->host, + (unsigned long)aconf->created, + (int)(aconf->hold - aconf->created), + (int)(aconf->lifetime - aconf->created), + reason); + } + else if(temp_time > 0) { aconf->hold = rb_current_time() + temp_time; @@ -309,6 +371,8 @@ parse_resv(struct Client *source_p, const char *name, const char *reason, int te bandb_add(BANDB_RESV, source_p, aconf->host, NULL, aconf->passwd, NULL, 0); } + + rb_dlinkAddAlloc(aconf, &resv_conf_list); } else sendto_one_notice(source_p, ":You have specified an invalid resv: [%s]", name); @@ -375,6 +439,8 @@ cluster_resv(struct Client *source_p, int temp_time, const char *name, const cha static int mo_unresv(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { + int propagated = 1; + if(!IsOperResv(source_p)) { sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "resv"); @@ -394,11 +460,16 @@ mo_unresv(struct Client *client_p, struct Client *source_p, int parc, const char if(match(parv[3], me.name) == 0) return 0; + + propagated = 0; } +#if 0 else if(rb_dlink_list_length(&cluster_conf_list) > 0) cluster_generic(source_p, "UNRESV", SHARED_UNRESV, CAP_CLUSTER, "%s", parv[1]); +#endif + /* cluster{} moved to remove_resv */ - remove_resv(source_p, parv[1]); + remove_resv(source_p, parv[1], propagated); return 0; } @@ -443,24 +514,63 @@ handle_remote_unresv(struct Client *source_p, const char *name) source_p->servptr->name, SHARED_UNRESV)) return; - remove_resv(source_p, name); + remove_resv(source_p, name, 0); return; } static void -remove_resv(struct Client *source_p, const char *name) +remove_resv(struct Client *source_p, const char *name, int propagated) { struct ConfItem *aconf = NULL; + rb_dlink_node *ptr; if(IsChannelName(name)) { if((aconf = hash_find_resv(name)) == NULL) { + if(propagated && rb_dlink_list_length(&cluster_conf_list)) + cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER, "%s", name); + sendto_one_notice(source_p, ":No RESV for %s", name); return; } + if(aconf->lifetime) + { + if(!propagated) + { + sendto_one_notice(source_p, ":Cannot remove global RESV %s on specific servers", name); + return; + } + ptr = rb_dlinkFind(aconf, &prop_bans); + if(ptr == NULL) + return; + sendto_one_notice(source_p, ":RESV for [%s] is removed", name); + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s has removed the global RESV for: [%s]", + get_oper_name(source_p), name); + ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name); + if(aconf->created < rb_current_time()) + aconf->created = rb_current_time(); + else + aconf->created++; + aconf->hold = aconf->created; + operhash_delete(aconf->info.oper); + aconf->info.oper = operhash_add(get_oper_name(source_p)); + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN R * %s %lu %d %d * :*", + source_p->id, aconf->host, + (unsigned long)aconf->created, + 0, + (int)(aconf->lifetime - aconf->created)); + deactivate_conf(aconf, ptr); + return; + } + else if(propagated && rb_dlink_list_length(&cluster_conf_list) > 0) + cluster_generic(source_p, "UNRESV", SHARED_UNRESV, CAP_CLUSTER, "%s", name); + sendto_one_notice(source_p, ":RESV for [%s] is removed", name); ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name); if(!aconf->hold) @@ -480,8 +590,6 @@ remove_resv(struct Client *source_p, const char *name) } else { - rb_dlink_node *ptr; - RB_DLINK_FOREACH(ptr, resv_conf_list.head) { aconf = ptr->data; @@ -494,19 +602,62 @@ remove_resv(struct Client *source_p, const char *name) if(aconf == NULL) { + if(propagated && rb_dlink_list_length(&cluster_conf_list)) + cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER, "%s", name); + sendto_one_notice(source_p, ":No RESV for %s", name); return; } - if(!aconf->hold) - bandb_del(BANDB_RESV, aconf->host, NULL); - else + if(aconf->lifetime) { + if(!propagated) + { + sendto_one_notice(source_p, ":Cannot remove global RESV %s on specific servers", name); + return; + } + ptr = rb_dlinkFind(aconf, &prop_bans); + if(ptr == NULL) + return; sendto_one_notice(source_p, ":RESV for [%s] is removed", name); + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s has removed the global RESV for: [%s]", + get_oper_name(source_p), name); + ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name); + if(aconf->created < rb_current_time()) + aconf->created = rb_current_time(); + else + aconf->created++; + aconf->hold = aconf->created; + operhash_delete(aconf->info.oper); + aconf->info.oper = operhash_add(get_oper_name(source_p)); + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN R * %s %lu %d %d * :*", + source_p->id, aconf->host, + (unsigned long)aconf->created, + 0, + (int)(aconf->lifetime - aconf->created)); + deactivate_conf(aconf, ptr); + return; + } + else if(propagated && rb_dlink_list_length(&cluster_conf_list) > 0) + cluster_generic(source_p, "UNRESV", SHARED_UNRESV, CAP_CLUSTER, "%s", name); + + sendto_one_notice(source_p, ":RESV for [%s] is removed", name); + ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name); + if(!aconf->hold) + { + bandb_del(BANDB_RESV, aconf->host, NULL); sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s has removed the RESV for: [%s]", get_oper_name(source_p), name); - ilog(L_KLINE, "UR %s %s", get_oper_name(source_p), name); + } + else + { + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s has removed the temporary RESV for: [%s]", + get_oper_name(source_p), name); } /* already have ptr from the loop above.. */ rb_dlinkDestroy(ptr, &resv_conf_list); @@ -515,54 +666,3 @@ remove_resv(struct Client *source_p, const char *name) return; } - -static void -resv_chan_forcepart(const char *name, const char *reason, int temp_time) -{ - rb_dlink_node *ptr; - rb_dlink_node *next_ptr; - struct Channel *chptr; - struct membership *msptr; - struct Client *target_p; - - if(!ConfigChannel.resv_forcepart) - return; - - /* for each user on our server in the channel list - * send them a PART, and notify opers. - */ - chptr = find_channel(name); - if(chptr != NULL) - { - RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head) - { - msptr = ptr->data; - target_p = msptr->client_p; - - if(IsExemptResv(target_p)) - continue; - - sendto_server(target_p, chptr, CAP_TS6, NOCAPS, - ":%s PART %s", target_p->id, chptr->chname); - - sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s :%s", - target_p->name, target_p->username, - target_p->host, chptr->chname, target_p->name); - - remove_user_from_channel(msptr); - - /* notify opers & user they were removed from the channel */ - sendto_realops_snomask(SNO_GENERAL, L_ALL, - "Forced PART for %s!%s@%s from %s (%s)", - target_p->name, target_p->username, - target_p->host, name, reason); - - if(temp_time > 0) - sendto_one_notice(target_p, ":*** Channel %s is temporarily unavailable on this server.", - name); - else - sendto_one_notice(target_p, ":*** Channel %s is no longer available on this server.", - name); - } - } -} diff --git a/modules/m_stats.c b/modules/m_stats.c index f621270..67e53bb 100644 --- a/modules/m_stats.c +++ b/modules/m_stats.c @@ -94,6 +94,7 @@ static void stats_tdeny(struct Client *); static void stats_deny(struct Client *); static void stats_exempt(struct Client *); static void stats_events(struct Client *); +static void stats_prop_klines(struct Client *); static void stats_hubleaf(struct Client *); static void stats_auth(struct Client *); static void stats_tklines(struct Client *); @@ -137,6 +138,7 @@ static struct StatsStruct stats_cmd_table[] = { {'E', stats_events, 1, 1, }, {'f', stats_comm, 1, 1, }, {'F', stats_comm, 1, 1, }, + {'g', stats_prop_klines, 1, 0, }, {'h', stats_hubleaf, 0, 0, }, {'H', stats_hubleaf, 0, 0, }, {'i', stats_auth, 0, 0, }, @@ -450,6 +452,32 @@ stats_events (struct Client *source_p) rb_dump_events(stats_events_cb, source_p); } +static void +stats_prop_klines(struct Client *source_p) +{ + struct ConfItem *aconf; + rb_dlink_node *ptr; + char *user, *host, *pass, *oper_reason; + + RB_DLINK_FOREACH(ptr, prop_bans.head) + { + aconf = ptr->data; + + /* Skip non-klines and deactivated klines. */ + if(aconf->status != CONF_KILL) + continue; + + get_printable_kline(source_p, aconf, &host, &pass, + &user, &oper_reason); + + sendto_one_numeric(source_p, RPL_STATSKLINE, + form_str(RPL_STATSKLINE), + 'g', host, user, pass, + oper_reason ? "|" : "", + oper_reason ? oper_reason : ""); + } +} + static void stats_hubleaf(struct Client *source_p) { diff --git a/modules/m_testline.c b/modules/m_testline.c index 7efc62c..05b7148 100644 --- a/modules/m_testline.c +++ b/modules/m_testline.c @@ -218,7 +218,7 @@ mo_testline(struct Client *client_p, struct Client *source_p, int parc, const ch if(aconf && aconf->status & CONF_CLIENT) { sendto_one_numeric(source_p, RPL_STATSILINE, form_str(RPL_STATSILINE), - aconf->name, EmptyString(aconf->spasswd) ? "" : aconf->spasswd, + aconf->info.name, EmptyString(aconf->spasswd) ? "" : aconf->spasswd, show_iline_prefix(source_p, aconf, aconf->user), aconf->host, aconf->port, aconf->className); return 0; diff --git a/modules/m_xline.c b/modules/m_xline.c index 1ff8655..d1975ec 100644 --- a/modules/m_xline.c +++ b/modules/m_xline.c @@ -51,6 +51,7 @@ #include "s_newconf.h" #include "reject.h" #include "bandbi.h" +#include "operhash.h" static int mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]); static int ms_xline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]); @@ -78,7 +79,7 @@ DECLARE_MODULE_AV1(xline, NULL, NULL, xline_clist, NULL, NULL, "$Revision$"); static int valid_xline(struct Client *, const char *, const char *); static void apply_xline(struct Client *client_p, const char *name, - const char *reason, int temp_time); + const char *reason, int temp_time, int propagated); static void propagate_xline(struct Client *source_p, const char *target, int temp_time, const char *name, const char *type, const char *reason); static void cluster_xline(struct Client *source_p, int temp_time, @@ -88,7 +89,8 @@ static void handle_remote_xline(struct Client *source_p, int temp_time, const char *name, const char *reason); static void handle_remote_unxline(struct Client *source_p, const char *name); -static void remove_xline(struct Client *source_p, const char *name); +static void remove_xline(struct Client *source_p, const char *name, + int propagated); /* m_xline() @@ -106,6 +108,7 @@ mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char const char *target_server = NULL; int temp_time; int loc = 1; + int propagated = ConfigFileEntry.use_propagated_bans; if(!IsOperXline(source_p)) { @@ -151,8 +154,11 @@ mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char if(!match(target_server, me.name)) return 0; + + /* Set as local-only. */ + propagated = 0; } - else if(rb_dlink_list_length(&cluster_conf_list) > 0) + else if(!propagated && rb_dlink_list_length(&cluster_conf_list) > 0) cluster_xline(source_p, temp_time, name, reason); if((aconf = find_xline_mask(name)) != NULL) @@ -165,7 +171,13 @@ mo_xline(struct Client *client_p, struct Client *source_p, int parc, const char if(!valid_xline(source_p, name, reason)) return 0; - apply_xline(source_p, name, reason, temp_time); + if(propagated && temp_time == 0) + { + sendto_one_notice(source_p, ":Cannot set a permanent global ban"); + return 0; + } + + apply_xline(source_p, name, reason, temp_time, propagated); return 0; } @@ -225,7 +237,7 @@ handle_remote_xline(struct Client *source_p, int temp_time, const char *name, co return; } - apply_xline(source_p, name, reason, temp_time); + apply_xline(source_p, name, reason, temp_time, 0); } /* valid_xline() @@ -269,17 +281,45 @@ valid_xline(struct Client *source_p, const char *gecos, const char *reason) } void -apply_xline(struct Client *source_p, const char *name, const char *reason, int temp_time) +apply_xline(struct Client *source_p, const char *name, const char *reason, int temp_time, int propagated) { struct ConfItem *aconf; aconf = make_conf(); aconf->status = CONF_XLINE; + aconf->created = rb_current_time(); aconf->host = rb_strdup(name); aconf->passwd = rb_strdup(reason); collapse(aconf->host); - if(temp_time > 0) + aconf->info.oper = operhash_add(get_oper_name(source_p)); + + if(propagated) + { + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + aconf->hold = rb_current_time() + temp_time; + aconf->lifetime = aconf->hold; + + replace_old_ban(aconf); + rb_dlinkAddAlloc(aconf, &prop_bans); + + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s added global %d min. X-Line for [%s] [%s]", + get_oper_name(source_p), temp_time / 60, + aconf->host, reason); + ilog(L_KLINE, "X %s %d %s %s", + get_oper_name(source_p), temp_time / 60, name, reason); + sendto_one_notice(source_p, ":Added global %d min. X-Line [%s]", + temp_time / 60, aconf->host); + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN X * %s %lu %d %d * :%s", + source_p->id, aconf->host, + (unsigned long)aconf->created, + (int)(aconf->hold - aconf->created), + (int)(aconf->lifetime - aconf->created), + reason); + } + else if(temp_time > 0) { aconf->hold = rb_current_time() + temp_time; @@ -362,6 +402,8 @@ cluster_xline(struct Client *source_p, int temp_time, const char *name, const ch static int mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) { + int propagated = 1; + if(!IsOperXline(source_p)) { sendto_one(source_p, form_str(ERR_NOPRIVS), me.name, source_p->name, "xline"); @@ -381,11 +423,12 @@ mo_unxline(struct Client *client_p, struct Client *source_p, int parc, const cha if(match(parv[3], me.name) == 0) return 0; - } - else if(rb_dlink_list_length(&cluster_conf_list)) - cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER, "%s", parv[1]); - remove_xline(source_p, parv[1]); + propagated = 0; + } + /* cluster{} moved to remove_xline */ + + remove_xline(source_p, parv[1], propagated); return 0; } @@ -430,13 +473,13 @@ handle_remote_unxline(struct Client *source_p, const char *name) source_p->servptr->name, SHARED_UNXLINE)) return; - remove_xline(source_p, name); + remove_xline(source_p, name, 0); return; } static void -remove_xline(struct Client *source_p, const char *name) +remove_xline(struct Client *source_p, const char *name, int propagated) { struct ConfItem *aconf; rb_dlink_node *ptr; @@ -447,6 +490,41 @@ remove_xline(struct Client *source_p, const char *name) if(!irccmp(aconf->host, name)) { + if(aconf->lifetime) + { + if(!propagated) + { + sendto_one_notice(source_p, ":Cannot remove global X-Line %s on specific servers", name); + return; + } + ptr = rb_dlinkFind(aconf, &prop_bans); + if(ptr == NULL) + return; + sendto_one_notice(source_p, ":X-Line for [%s] is removed", name); + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "%s has removed the global X-Line for: [%s]", + get_oper_name(source_p), name); + ilog(L_KLINE, "UX %s %s", get_oper_name(source_p), name); + if(aconf->created < rb_current_time()) + aconf->created = rb_current_time(); + else + aconf->created++; + aconf->hold = aconf->created; + operhash_delete(aconf->info.oper); + aconf->info.oper = operhash_add(get_oper_name(source_p)); + aconf->flags |= CONF_FLAGS_MYOPER | CONF_FLAGS_TEMPORARY; + sendto_server(NULL, NULL, CAP_BAN|CAP_TS6, NOCAPS, + ":%s BAN X * %s %lu %d %d * :*", + source_p->id, aconf->host, + (unsigned long)aconf->created, + 0, + (int)(aconf->lifetime - aconf->created)); + remove_reject_mask(aconf->host, NULL); + deactivate_conf(aconf, ptr); + return; + } + else if(propagated && rb_dlink_list_length(&cluster_conf_list)) + cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER, "%s", name); if(!aconf->hold) { bandb_del(BANDB_XLINE, aconf->host, NULL); @@ -473,6 +551,9 @@ remove_xline(struct Client *source_p, const char *name) } } + if(propagated && rb_dlink_list_length(&cluster_conf_list)) + cluster_generic(source_p, "UNXLINE", SHARED_UNXLINE, CAP_CLUSTER, "%s", name); + sendto_one_notice(source_p, ":No X-Line for %s", name); return; diff --git a/src/Makefile.in b/src/Makefile.in index c24cd75..ab69afb 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -76,6 +76,7 @@ SRCS = \ monitor.c \ newconf.c \ numeric.c \ + operhash.c \ packet.c \ parse.c \ privilege.c \ diff --git a/src/bandbi.c b/src/bandbi.c index 4de4422..eda44b3 100644 --- a/src/bandbi.c +++ b/src/bandbi.c @@ -46,6 +46,7 @@ #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' @@ -170,8 +171,7 @@ bandb_handle_ban(char *parv[], int parc) aconf->user = rb_strdup(parv[para++]); aconf->host = rb_strdup(parv[para++]); - /* We do not have the 'oper' field yet. */ - para++; + aconf->info.oper = operhash_add(parv[para++]); switch (parv[0][0]) { diff --git a/src/channel.c b/src/channel.c index 52f5b2b..74767f4 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1532,6 +1532,57 @@ send_cap_mode_changes(struct Client *client_p, struct Client *source_p, } } +void +resv_chan_forcepart(const char *name, const char *reason, int temp_time) +{ + rb_dlink_node *ptr; + rb_dlink_node *next_ptr; + struct Channel *chptr; + struct membership *msptr; + struct Client *target_p; + + if(!ConfigChannel.resv_forcepart) + return; + + /* for each user on our server in the channel list + * send them a PART, and notify opers. + */ + chptr = find_channel(name); + if(chptr != NULL) + { + RB_DLINK_FOREACH_SAFE(ptr, next_ptr, chptr->locmembers.head) + { + msptr = ptr->data; + target_p = msptr->client_p; + + if(IsExemptResv(target_p)) + continue; + + sendto_server(target_p, chptr, CAP_TS6, NOCAPS, + ":%s PART %s", target_p->id, chptr->chname); + + sendto_channel_local(ALL_MEMBERS, chptr, ":%s!%s@%s PART %s :%s", + target_p->name, target_p->username, + target_p->host, chptr->chname, target_p->name); + + remove_user_from_channel(msptr); + + /* notify opers & user they were removed from the channel */ + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "Forced PART for %s!%s@%s from %s (%s)", + target_p->name, target_p->username, + target_p->host, name, reason); + + if(temp_time > 0) + sendto_one_notice(target_p, ":*** Channel %s is temporarily unavailable on this server.", + name); + else + sendto_one_notice(target_p, ":*** Channel %s is no longer available on this server.", + name); + } + } +} + /* Check what we will forward to, without sending any notices to the user * -- jilles */ diff --git a/src/chmode.c b/src/chmode.c index 23491f7..3161b64 100644 --- a/src/chmode.c +++ b/src/chmode.c @@ -775,8 +775,8 @@ chm_ban(struct Client *source_p, struct Channel *chptr, case CHFL_QUIET: list = &chptr->quietlist; errorval = SM_ERR_RPL_Q; - rpl_list = RPL_BANLIST; - rpl_endlist = RPL_ENDOFBANLIST; + rpl_list = RPL_QUIETLIST; + rpl_endlist = RPL_ENDOFQUIETLIST; mems = ALL_MEMBERS; caps = 0; break; @@ -824,10 +824,7 @@ chm_ban(struct Client *source_p, struct Channel *chptr, me.name, source_p->name, chptr->chname, banptr->banstr, banptr->who, banptr->when); } - if (mode_type == CHFL_QUIET) - sendto_one(source_p, ":%s %d %s %s :End of Channel Quiet List", me.name, rpl_endlist, source_p->name, chptr->chname); - else - sendto_one(source_p, form_str(rpl_endlist), me.name, source_p->name, chptr->chname); + sendto_one(source_p, form_str(rpl_endlist), me.name, source_p->name, chptr->chname); return; } diff --git a/src/client.c b/src/client.c index 1cc32b4..534bec2 100644 --- a/src/client.c +++ b/src/client.c @@ -424,10 +424,10 @@ notify_banned_client(struct Client *client_p, struct ConfItem *aconf, int ban) const char *reason = NULL; const char *exit_reason = conn_closed; - if(ConfigFileEntry.kline_with_reason && !EmptyString(aconf->passwd)) + if(ConfigFileEntry.kline_with_reason) { - reason = aconf->passwd; - exit_reason = aconf->passwd; + reason = get_user_ban_reason(aconf); + exit_reason = reason; } else { diff --git a/src/hostmask.c b/src/hostmask.c index 806ecb8..1058df5 100644 --- a/src/hostmask.c +++ b/src/hostmask.c @@ -390,7 +390,7 @@ find_address_conf(const char *host, const char *sockhost, const char *user, /* if theres a spoof, check it against klines.. */ if(IsConfDoSpoofIp(iconf)) { - char *p = strchr(iconf->name, '@'); + char *p = strchr(iconf->info.name, '@'); /* note, we dont need to pass sockhost here, as its * guaranteed to not match by whats above.. --anfl @@ -398,11 +398,11 @@ find_address_conf(const char *host, const char *sockhost, const char *user, if(p) { *p = '\0'; - kconf = find_conf_by_address(p+1, NULL, NULL, ip, CONF_KILL, aftype, iconf->name, NULL); + kconf = find_conf_by_address(p+1, NULL, NULL, ip, CONF_KILL, aftype, iconf->info.name, NULL); *p = '@'; } else - kconf = find_conf_by_address(iconf->name, NULL, NULL, ip, CONF_KILL, aftype, vuser, NULL); + kconf = find_conf_by_address(iconf->info.name, NULL, NULL, ip, CONF_KILL, aftype, vuser, NULL); if(kconf) return kconf; diff --git a/src/listener.c b/src/listener.c index 489aee7..3b8a1fb 100644 --- a/src/listener.c +++ b/src/listener.c @@ -480,6 +480,7 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi char buf[BUFSIZE]; struct ConfItem *aconf; static time_t last_oper_notice = 0; + int len; if(listener->ssl && (!ssl_ok || !get_ssld_count())) { @@ -519,7 +520,8 @@ accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, voi if(ConfigFileEntry.dline_with_reason) { - if (rb_snprintf(buf, sizeof(buf), "ERROR :*** Banned: %s\r\n", aconf->passwd) >= (int)(sizeof(buf)-1)) + len = rb_snprintf(buf, sizeof(buf), "ERROR :*** Banned: %s\r\n", get_user_ban_reason(aconf)); + if (len >= (int)(sizeof(buf)-1)) { buf[sizeof(buf) - 3] = '\r'; buf[sizeof(buf) - 2] = '\n'; diff --git a/src/logger.c b/src/logger.c index 53cd72b..bb3f0a6 100644 --- a/src/logger.c +++ b/src/logger.c @@ -180,7 +180,8 @@ ilog(ilogfile dest, const char *format, ...) rb_vsnprintf(buf, sizeof(buf), format, args); va_end(args); - rb_snprintf(buf2, sizeof(buf2), "%s %s\n", smalldate(), buf); + rb_snprintf(buf2, sizeof(buf2), "%s %s\n", + smalldate(rb_current_time()), buf); if(fputs(buf2, logfile) < 0) { @@ -266,11 +267,10 @@ report_operspy(struct Client *source_p, const char *token, const char *arg) } const char * -smalldate(void) +smalldate(time_t ltime) { static char buf[MAX_DATE_STRING]; struct tm *lt; - time_t ltime = rb_current_time(); lt = localtime(<ime); diff --git a/src/messages.tab b/src/messages.tab index 481a0ca..b8fc7d7 100644 --- a/src/messages.tab +++ b/src/messages.tab @@ -749,8 +749,8 @@ static const char * replies[] = { /* 725 RPL_TESTLINE */ ":%s 725 %s %c %ld %s :%s", /* 726 RPL_NOTESTLINE */ ":%s 726 %s %s :No matches", /* 727 RPL_TESTMASKGECOS */ ":%s 727 %s %d %d %s!%s@%s %s :Local/remote clients match", -/* 728 */ NULL, -/* 729 */ NULL, +/* 728 RPL_QUIETLIST */ ":%s 728 %s %s q %s %s %lu", +/* 729 RPL_ENDOFQUIETLIST */ ":%s 729 %s %s q :End of Channel Quiet List", /* 730 RPL_MONONLINE */ ":%s 730 %s :%s", /* 731 RPL_MONOFFLINE */ ":%s 731 %s :%s", /* 732 RPL_MONLIST */ ":%s 732 %s :%s", @@ -761,8 +761,8 @@ static const char * replies[] = { /* 737 ERR_NOCOMMONCHAN*/ "%s :is in +G mode (server-side ignore) and you do not share a common channel with them.", /* 738 */ NULL, /* 739 */ NULL, -/* 740 */ ":%s 740 %s :%s", -/* 741 */ ":%s 741 %s :End of CHALLENGE", +/* 740 RPL_RSACHALLENGE2*/ ":%s 740 %s :%s", +/* 741 RPL_ENDOFRSACHALLENGE2*/ ":%s 741 %s :End of CHALLENGE", /* 742 */ NULL, /* 743 */ NULL, /* 744 */ NULL, diff --git a/src/modules.c b/src/modules.c index 43a2f51..189df07 100644 --- a/src/modules.c +++ b/src/modules.c @@ -58,6 +58,7 @@ struct module **modlist = NULL; static const char *core_module_table[] = { + "m_ban", "m_die", "m_error", "m_join", @@ -880,7 +881,7 @@ unload_one_module(const char *name, int warn) dlclose(modlist[modindex]->address); rb_free(modlist[modindex]->name); - memcpy(&modlist[modindex], &modlist[modindex + 1], + memmove(&modlist[modindex], &modlist[modindex + 1], sizeof(struct module) * ((num_mods - 1) - modindex)); if(num_mods != 0) diff --git a/src/newconf.c b/src/newconf.c index 7e186f2..d71788a 100644 --- a/src/newconf.c +++ b/src/newconf.c @@ -952,8 +952,8 @@ conf_end_auth(struct TopConf *tc) rb_dlink_node *ptr; rb_dlink_node *next_ptr; - if(EmptyString(yy_aconf->name)) - yy_aconf->name = rb_strdup("NOMATCH"); + if(EmptyString(yy_aconf->info.name)) + yy_aconf->info.name = rb_strdup("NOMATCH"); /* didnt even get one ->host? */ if(EmptyString(yy_aconf->host)) @@ -987,7 +987,7 @@ conf_end_auth(struct TopConf *tc) yy_tmp->spasswd = rb_strdup(yy_aconf->spasswd); /* this will always exist.. */ - yy_tmp->name = rb_strdup(yy_aconf->name); + yy_tmp->info.name = rb_strdup(yy_aconf->info.name); if(yy_aconf->className) yy_tmp->className = rb_strdup(yy_aconf->className); @@ -1138,8 +1138,8 @@ conf_set_auth_spoof(void *data) return; } - rb_free(yy_aconf->name); - yy_aconf->name = rb_strdup(data); + rb_free(yy_aconf->info.name); + yy_aconf->info.name = rb_strdup(data); yy_aconf->flags |= CONF_FLAGS_SPOOF_IP; } @@ -1155,8 +1155,8 @@ static void conf_set_auth_redir_serv(void *data) { yy_aconf->flags |= CONF_FLAGS_REDIR; - rb_free(yy_aconf->name); - yy_aconf->name = rb_strdup(data); + rb_free(yy_aconf->info.name); + yy_aconf->info.name = rb_strdup(data); } static void @@ -2253,6 +2253,7 @@ static struct ConfEntry conf_general_table[] = { "ts_warn_delta", CF_TIME, NULL, 0, &ConfigFileEntry.ts_warn_delta }, { "use_whois_actually", CF_YESNO, NULL, 0, &ConfigFileEntry.use_whois_actually }, { "warn_no_nline", CF_YESNO, NULL, 0, &ConfigFileEntry.warn_no_nline }, + { "use_propagated_bans",CF_YESNO, NULL, 0, &ConfigFileEntry.use_propagated_bans }, { "expire_override_time", CF_TIME, NULL, 0, &ConfigFileEntry.expire_override_time}, { "\0", 0, NULL, 0, NULL } }; diff --git a/src/operhash.c b/src/operhash.c new file mode 100644 index 0000000..ed6cda1 --- /dev/null +++ b/src/operhash.c @@ -0,0 +1,136 @@ +/* ircd-ratbox: an advanced Internet Relay Chat Daemon(ircd). + * operhash.c - Hashes nick!user@host{oper} + * + * Copyright (C) 2005 Lee Hardy + * Copyright (C) 2005 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: operhash.c 26094 2008-09-19 15:33:46Z androsyn $ + */ +#include +#include "stdinc.h" +#include "match.h" +#include "hash.h" +#include "operhash.h" + +#define OPERHASH_MAX_BITS 7 +#define OPERHASH_MAX (1<data; + + if(!irccmp(ohash->name, name)) + { + ohash->refcount++; + return ohash->name; + } + } + + ohash = rb_malloc(sizeof(struct operhash_entry)); + ohash->refcount = 1; + ohash->name = rb_strdup(name); + + rb_dlinkAddAlloc(ohash, &operhash_table[hashv]); + + return ohash->name; +} + +const char * +operhash_find(const char *name) +{ + struct operhash_entry *ohash; + unsigned int hashv; + rb_dlink_node *ptr; + + if(EmptyString(name)) + return NULL; + + hashv = hash_opername(name); + + RB_DLINK_FOREACH(ptr, operhash_table[hashv].head) + { + ohash = ptr->data; + + if(!irccmp(ohash->name, name)) + return ohash->name; + } + + return NULL; +} + +void +operhash_delete(const char *name) +{ + struct operhash_entry *ohash; + unsigned int hashv; + rb_dlink_node *ptr; + + if(EmptyString(name)) + return; + + hashv = hash_opername(name); + + RB_DLINK_FOREACH(ptr, operhash_table[hashv].head) + { + ohash = ptr->data; + + if(irccmp(ohash->name, name)) + continue; + + ohash->refcount--; + + if(ohash->refcount == 0) + { + rb_free(ohash->name); + rb_free(ohash); + rb_dlinkDestroy(ptr, &operhash_table[hashv]); + return; + } + } +} diff --git a/src/s_conf.c b/src/s_conf.c index 4263528..5f91e6c 100644 --- a/src/s_conf.c +++ b/src/s_conf.c @@ -51,6 +51,7 @@ #include "privilege.h" #include "sslproc.h" #include "bandbi.h" +#include "operhash.h" struct config_server_hide ConfigServerHide; @@ -63,6 +64,8 @@ extern char linebuf[]; static rb_bh *confitem_heap = NULL; +rb_dlink_list prop_bans; + rb_dlink_list temp_klines[LAST_TEMP_TYPE]; rb_dlink_list temp_dlines[LAST_TEMP_TYPE]; rb_dlink_list service_list; @@ -73,6 +76,7 @@ static void validate_conf(void); static void read_conf(FILE *); static void clear_out_old_conf(void); +static void expire_prop_bans(void *list); static void expire_temp_kd(void *list); static void reorganise_temp_kd(void *list); @@ -87,6 +91,8 @@ init_s_conf(void) { confitem_heap = rb_bh_create(sizeof(struct ConfItem), CONFITEM_HEAP_SIZE, "confitem_heap"); + rb_event_addish("expire_prop_bans", expire_prop_bans, &prop_bans, 60); + rb_event_addish("expire_temp_klines", expire_temp_kd, &temp_klines[TEMP_MIN], 60); rb_event_addish("expire_temp_dlines", expire_temp_kd, &temp_dlines[TEMP_MIN], 60); @@ -143,11 +149,15 @@ free_conf(struct ConfItem *aconf) rb_free(aconf->passwd); rb_free(aconf->spasswd); - rb_free(aconf->name); rb_free(aconf->className); rb_free(aconf->user); rb_free(aconf->host); + if(IsConfBan(aconf)) + operhash_delete(aconf->info.oper); + else + rb_free(aconf->info.name); + rb_bh_free(confitem_heap, aconf); } @@ -334,7 +344,7 @@ verify_access(struct Client *client_p, const char *username) if(aconf->flags & CONF_FLAGS_REDIR) { sendto_one_numeric(client_p, RPL_REDIR, form_str(RPL_REDIR), - aconf->name ? aconf->name : "", aconf->port); + aconf->info.name ? aconf->info.name : "", aconf->port); return (NOT_AUTHORISED); } @@ -351,35 +361,34 @@ verify_access(struct Client *client_p, const char *username) sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s spoofing: %s as %s", client_p->name, - show_ip(NULL, client_p) ? client_p->host : aconf->name, - aconf->name); + show_ip(NULL, client_p) ? client_p->host : aconf->info.name, + aconf->info.name); } /* user@host spoof */ - if((p = strchr(aconf->name, '@')) != NULL) + if((p = strchr(aconf->info.name, '@')) != NULL) { char *host = p+1; *p = '\0'; - rb_strlcpy(client_p->username, aconf->name, + rb_strlcpy(client_p->username, aconf->info.name, sizeof(client_p->username)); rb_strlcpy(client_p->host, host, sizeof(client_p->host)); *p = '@'; } else - rb_strlcpy(client_p->host, aconf->name, sizeof(client_p->host)); + rb_strlcpy(client_p->host, aconf->info.name, sizeof(client_p->host)); } return (attach_iline(client_p, aconf)); } else if(aconf->status & CONF_KILL) { if(ConfigFileEntry.kline_with_reason) - { sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP), - me.name, client_p->name, aconf->passwd); - } + me.name, client_p->name, + get_user_ban_reason(aconf)); add_reject(client_p, aconf->user, aconf->host); return (BANNED_CLIENT); } @@ -740,6 +749,7 @@ set_default_conf(void) ConfigFileEntry.collision_fnc = YES; ConfigFileEntry.global_snotices = YES; ConfigFileEntry.operspy_dont_care_user_info = NO; + ConfigFileEntry.use_propagated_bans = YES; ConfigFileEntry.secret_channels_in_whois = NO; #ifdef HAVE_LIBZ @@ -998,6 +1008,192 @@ add_temp_dline(struct ConfItem *aconf) add_conf_by_address(aconf->host, CONF_DLINE, aconf->user, NULL, aconf); } +/* valid_wild_card() + * + * input - user buffer, host buffer + * output - 0 if invalid, 1 if valid + * side effects - + */ +int +valid_wild_card(const char *luser, const char *lhost) +{ + const char *p; + char tmpch; + int nonwild = 0; + int bitlen; + + /* user has no wildcards, always accept -- jilles */ + if(!strchr(luser, '?') && !strchr(luser, '*')) + return 1; + + /* check there are enough non wildcard chars */ + p = luser; + while((tmpch = *p++)) + { + if(!IsKWildChar(tmpch)) + { + /* found enough chars, return */ + if(++nonwild >= ConfigFileEntry.min_nonwildcard) + return 1; + } + } + + /* try host, as user didnt contain enough */ + /* special case for cidr masks -- jilles */ + if((p = strrchr(lhost, '/')) != NULL && IsDigit(p[1])) + { + bitlen = atoi(p + 1); + /* much like non-cidr for ipv6, rather arbitrary for ipv4 */ + if(bitlen > 0 + && bitlen >= + (strchr(lhost, ':') ? 4 * (ConfigFileEntry.min_nonwildcard - nonwild) : 6 - + 2 * nonwild)) + return 1; + } + else + { + p = lhost; + while((tmpch = *p++)) + { + if(!IsKWildChar(tmpch)) + if(++nonwild >= ConfigFileEntry.min_nonwildcard) + return 1; + } + } + + return 0; +} + +rb_dlink_node * +find_prop_ban(unsigned int status, const char *user, const char *host) +{ + rb_dlink_node *ptr; + struct ConfItem *aconf; + + RB_DLINK_FOREACH(ptr, prop_bans.head) + { + aconf = ptr->data; + + if((aconf->status & ~CONF_ILLEGAL) == status && + (!user || !aconf->user || + !irccmp(aconf->user, user)) && + !irccmp(aconf->host, host)) + return ptr; + } + return NULL; +} + +void +deactivate_conf(struct ConfItem *aconf, rb_dlink_node *ptr) +{ + int i; + + s_assert(ptr->data == aconf); + + switch (aconf->status) + { + case CONF_KILL: + if (aconf->lifetime == 0 && + aconf->flags & CONF_FLAGS_TEMPORARY) + for (i = 0; i < LAST_TEMP_TYPE; i++) + rb_dlinkFindDestroy(aconf, &temp_klines[i]); + /* Make sure delete_one_address_conf() does not + * free the aconf. + */ + aconf->clients++; + delete_one_address_conf(aconf->host, aconf); + aconf->clients--; + break; + case CONF_DLINE: + if (aconf->lifetime == 0 && + aconf->flags & CONF_FLAGS_TEMPORARY) + for (i = 0; i < LAST_TEMP_TYPE; i++) + rb_dlinkFindDestroy(aconf, &temp_dlines[i]); + aconf->clients++; + delete_one_address_conf(aconf->host, aconf); + aconf->clients--; + break; + case CONF_XLINE: + rb_dlinkFindDestroy(aconf, &xline_conf_list); + break; + case CONF_RESV_NICK: + rb_dlinkFindDestroy(aconf, &resv_conf_list); + break; + case CONF_RESV_CHANNEL: + del_from_resv_hash(aconf->host, aconf); + break; + } + if (aconf->lifetime != 0 && rb_current_time() < aconf->lifetime) + aconf->status |= CONF_ILLEGAL; + else + { + if (aconf->lifetime != 0) + rb_dlinkDestroy(ptr, &prop_bans); + free_conf(aconf); + } +} + +/* Given a new ban ConfItem, look for any matching ban, update the lifetime + * from it and delete it. + */ +void +replace_old_ban(struct ConfItem *aconf) +{ + rb_dlink_node *ptr; + struct ConfItem *oldconf; + + ptr = find_prop_ban(aconf->status, aconf->user, aconf->host); + if(ptr != NULL) + { + oldconf = ptr->data; + /* Remember at least as long as the old one. */ + if(oldconf->lifetime > aconf->lifetime) + aconf->lifetime = oldconf->lifetime; + /* Force creation time to increase. */ + if(oldconf->created >= aconf->created) + aconf->created = oldconf->created + 1; + /* Leave at least one second of validity. */ + if(aconf->hold <= aconf->created) + aconf->hold = aconf->created + 1; + if(aconf->lifetime < aconf->hold) + aconf->lifetime = aconf->hold; + /* Tell deactivate_conf() to destroy it. */ + oldconf->lifetime = rb_current_time(); + deactivate_conf(oldconf, ptr); + } +} + +static void +expire_prop_bans(void *list) +{ + rb_dlink_node *ptr; + rb_dlink_node *next_ptr; + struct ConfItem *aconf; + + RB_DLINK_FOREACH_SAFE(ptr, next_ptr, ((rb_dlink_list *) list)->head) + { + aconf = ptr->data; + + if(aconf->lifetime <= rb_current_time() || + (aconf->hold <= rb_current_time() && + !(aconf->status & CONF_ILLEGAL))) + { + /* Alert opers that a TKline expired - Hwy */ + /* XXX show what type of ban it is */ + if(ConfigFileEntry.tkline_expire_notices && + !(aconf->status & CONF_ILLEGAL)) + sendto_realops_snomask(SNO_GENERAL, L_ALL, + "Propagated ban for [%s%s%s] expired", + aconf->user ? aconf->user : "", + aconf->user ? "@" : "", + aconf->host ? aconf->host : "*"); + + /* will destroy or mark illegal */ + deactivate_conf(aconf, ptr); + } + } +} + /* expire_tkline() * * inputs - list pointer @@ -1116,7 +1312,7 @@ get_printable_conf(struct ConfItem *aconf, char **name, char **host, static char null[] = ""; static char zero[] = "default"; - *name = EmptyString(aconf->name) ? null : aconf->name; + *name = EmptyString(aconf->info.name) ? null : aconf->info.name; *host = EmptyString(aconf->host) ? null : aconf->host; *pass = EmptyString(aconf->passwd) ? null : aconf->passwd; *user = EmptyString(aconf->user) ? null : aconf->user; @@ -1124,21 +1320,55 @@ get_printable_conf(struct ConfItem *aconf, char **name, char **host, *port = (int) aconf->port; } +char * +get_user_ban_reason(struct ConfItem *aconf) +{ + static char reasonbuf[BUFSIZE]; + + if (aconf->flags & CONF_FLAGS_TEMPORARY && + (aconf->status == CONF_KILL || aconf->status == CONF_DLINE)) + rb_snprintf(reasonbuf, sizeof reasonbuf, + "Temporary %c-line %d min. - ", + aconf->status == CONF_DLINE ? 'D' : 'K', + (int)((aconf->hold - aconf->created) / 60)); + else + reasonbuf[0] = '\0'; + if (aconf->passwd) + rb_strlcat(reasonbuf, aconf->passwd, sizeof reasonbuf); + else + rb_strlcat(reasonbuf, "No Reason", sizeof reasonbuf); + if (aconf->created) + { + rb_strlcat(reasonbuf, " (", sizeof reasonbuf); + rb_strlcat(reasonbuf, smalldate(aconf->created), + sizeof reasonbuf); + rb_strlcat(reasonbuf, ")", sizeof reasonbuf); + } + return reasonbuf; +} + void get_printable_kline(struct Client *source_p, struct ConfItem *aconf, char **host, char **reason, char **user, char **oper_reason) { static char null[] = ""; + static char operreasonbuf[BUFSIZE]; *host = EmptyString(aconf->host) ? null : aconf->host; - *reason = EmptyString(aconf->passwd) ? null : aconf->passwd; *user = EmptyString(aconf->user) ? null : aconf->user; + *reason = get_user_ban_reason(aconf); - if(EmptyString(aconf->spasswd) || !IsOper(source_p)) + if(!IsOper(source_p)) *oper_reason = NULL; else - *oper_reason = aconf->spasswd; + { + rb_snprintf(operreasonbuf, sizeof operreasonbuf, "%s%s(%s)", + EmptyString(aconf->spasswd) ? "" : aconf->spasswd, + EmptyString(aconf->spasswd) ? "" : " ", + aconf->info.oper); + *oper_reason = operreasonbuf; + } } /* diff --git a/src/s_newconf.c b/src/s_newconf.c index 592b410..36e94a5 100644 --- a/src/s_newconf.c +++ b/src/s_newconf.c @@ -651,6 +651,7 @@ valid_temp_time(const char *p) return(result * 60); } +/* Propagated bans are expired elsewhere. */ static void expire_temp_rxlines(void *unused) { @@ -663,6 +664,8 @@ expire_temp_rxlines(void *unused) { aconf = ptr->data; + if(aconf->lifetime != 0) + continue; if(aconf->hold && aconf->hold <= rb_current_time()) { if(ConfigFileEntry.tkline_expire_notices) @@ -680,6 +683,8 @@ expire_temp_rxlines(void *unused) { aconf = ptr->data; + if(aconf->lifetime != 0) + continue; if(aconf->hold && aconf->hold <= rb_current_time()) { if(ConfigFileEntry.tkline_expire_notices) @@ -695,6 +700,8 @@ expire_temp_rxlines(void *unused) { aconf = ptr->data; + if(aconf->lifetime != 0) + continue; if(aconf->hold && aconf->hold <= rb_current_time()) { if(ConfigFileEntry.tkline_expire_notices) diff --git a/src/s_serv.c b/src/s_serv.c index 315e18a..2569d56 100644 --- a/src/s_serv.c +++ b/src/s_serv.c @@ -89,6 +89,7 @@ struct Capability captab[] = { { "SAVE", CAP_SAVE }, { "EUID", CAP_EUID }, { "EOPMOD", CAP_EOPMOD }, + { "BAN", CAP_BAN }, {0, 0} }; @@ -392,6 +393,67 @@ send_capabilities(struct Client *client_p, int cap_can_send) sendto_one(client_p, "CAPAB :%s", msgbuf); } +static void +burst_ban(struct Client *client_p) +{ + rb_dlink_node *ptr; + struct ConfItem *aconf; + const char *type, *oper; + /* +5 for !,@,{,} and null */ + char operbuf[NICKLEN + USERLEN + HOSTLEN + HOSTLEN + 5]; + char *p; + size_t melen; + + melen = strlen(me.name); + RB_DLINK_FOREACH(ptr, prop_bans.head) + { + aconf = ptr->data; + + /* Skip expired stuff. */ + if(aconf->lifetime < rb_current_time()) + continue; + switch(aconf->status & ~CONF_ILLEGAL) + { + case CONF_KILL: type = "K"; break; + case CONF_DLINE: type = "D"; break; + case CONF_XLINE: type = "X"; break; + case CONF_RESV_NICK: type = "R"; break; + case CONF_RESV_CHANNEL: type = "R"; break; + default: + continue; + } + oper = aconf->info.oper; + if(aconf->flags & CONF_FLAGS_MYOPER) + { + /* Our operator{} names may not be meaningful + * to other servers, so rewrite to our server + * name. + */ + rb_strlcpy(operbuf, aconf->info.oper, sizeof buf); + p = strrchr(operbuf, '{'); + if (p != NULL && + operbuf + sizeof operbuf - p > (ptrdiff_t)(melen + 2)) + { + memcpy(p + 1, me.name, melen); + p[melen + 1] = '}'; + p[melen + 2] = '\0'; + oper = operbuf; + } + } + sendto_one(client_p, ":%s BAN %s %s %s %lu %d %d %s :%s%s%s", + me.id, + type, + aconf->user ? aconf->user : "*", aconf->host, + (unsigned long)aconf->created, + (int)(aconf->hold - aconf->created), + (int)(aconf->lifetime - aconf->created), + oper, + aconf->passwd, + aconf->spasswd ? "|" : "", + aconf->spasswd ? aconf->spasswd : ""); + } +} + /* burst_modes_TS6() * * input - client to burst to, channel name, list to burst, mode flag @@ -881,6 +943,9 @@ server_estab(struct Client *client_p) target_p->serv->fullcaps); } + if(IsCapable(client_p, CAP_BAN)) + burst_ban(client_p); + burst_TS6(client_p); /* Always send a PING after connect burst is done */ diff --git a/src/s_user.c b/src/s_user.c index c315565..7e2f11d 100644 --- a/src/s_user.c +++ b/src/s_user.c @@ -336,7 +336,7 @@ register_local_user(struct Client *client_p, struct Client *source_p, const char } /* dont replace username if its supposed to be spoofed --fl */ - if(!IsConfDoSpoofIp(aconf) || !strchr(aconf->name, '@')) + if(!IsConfDoSpoofIp(aconf) || !strchr(aconf->info.name, '@')) { p = username;