#define KEY_ISSUED "issued"
#define IDENT_FORMAT "%s [%s@%s/%s]"
-#define IDENT_DATA(user) user->nick, user->ident, user->hostname, inet_ntoa(user->ip)
+#define IDENT_DATA(user) user->nick, user->ident, user->hostname, irc_ntoa(&user->ip)
#define MAX_CHANNELS_WHOIS 50
#define OSMSG_PART_REASON "%s has no reason."
#define OSMSG_KICK_REQUESTED "Kick requested by %s."
{ "OSMSG_NEED_CHANNEL", "You must specify a channel for $b%s$b." },
{ "OSMSG_INVALID_IRCMASK", "$b%s$b is an invalid IRC hostmask." },
{ "OSMSG_ADDED_BAN", "I have banned $b%s$b from $b%s$b." },
+ { "OSMSG_NO_GLINE_CMD", "The GLINE command is not bound so you can only block with the default duration." },
+ { "OSMSG_BLOCK_TRUSTED", "$b%s$b is on a trusted ip. If you really want to G-line him, use the GLINE command." },
{ "OSMSG_GLINE_ISSUED", "G-line issued for $b%s$b." },
{ "OSMSG_GLINE_REMOVED", "G-line removed for $b%s$b." },
{ "OSMSG_GLINE_FORCE_REMOVED", "Unknown/expired G-line removed for $b%s$b." },
{ "OSMSG_WHOIS_IDENT", "%s (%s@%s) from %d.%d.%d.%d" },
{ "OSMSG_WHOIS_NICK", "Nick : %s" },
{ "OSMSG_WHOIS_HOST", "Host : %s@%s" },
+ { "OSMSG_WHOIS_FAKEHOST", "Fakehost: %s" },
{ "OSMSG_WHOIS_IP", "Real IP : %s" },
{ "OSMSG_WHOIS_MODES", "Modes : +%s " },
{ "OSMSG_WHOIS_INFO", "Info : %s" },
{ "OSMSG_BADWORD_LIST", "Bad words: %s" },
{ "OSMSG_EXEMPTED_LIST", "Exempted channels: %s" },
{ "OSMSG_GLINE_COUNT", "There are %d glines active on the network." },
+ { "OSMSG_NO_GLINE", "$b%s$b is not a known G-line." },
{ "OSMSG_LINKS_SERVER", "%s%s (%u clients; %s)" },
{ "OSMSG_MAX_CLIENTS", "Max clients: %d at %s" },
{ "OSMSG_NETWORK_INFO", "Total users: %d (%d invisible, %d opers)" },
{ "OSMSG_GLINE_SEARCH_RESULTS", "The following glines were found:" },
{ "OSMSG_LOG_SEARCH_RESULTS", "The following log entries were found:" },
{ "OSMSG_GSYNC_RUNNING", "Synchronizing glines from %s." },
- { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, expires %s): %s" },
+ { "OSMSG_GTRACE_FORMAT", "%s (issued %s by %s, lastmod %s, expires %s): %s" },
{ "OSMSG_GAG_APPLIED", "Gagged $b%s$b, affecting %d users." },
{ "OSMSG_GAG_ADDED", "Gagged $b%s$b." },
{ "OSMSG_REDUNDANT_GAG", "Gag $b%s$b is redundant." },
static dict_t opserv_user_alerts; /* data is struct opserv_user_alert* */
static dict_t opserv_nick_based_alerts; /* data is struct opserv_user_alert* */
static dict_t opserv_channel_alerts; /* data is struct opserv_user_alert* */
+static dict_t opserv_account_alerts; /* data is struct opserv_user_alert* */
static struct module *opserv_module;
static struct log_type *OS_LOG;
static unsigned int new_user_flood;
typedef struct opservDiscrim {
struct chanNode *channel;
- char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *ip_mask_str, *reason, *accountmask;
- unsigned long limit, ip_mask;
- struct in_addr ip_addr;
+ char *mask_nick, *mask_ident, *mask_host, *mask_info, *server, *reason, *accountmask;
+ irc_in_addr_t ip_mask;
+ unsigned long limit;
+ time_t min_ts, max_ts;
unsigned int min_level, max_level, domain_depth, duration, min_clones, min_channels, max_channels;
- unsigned int match_opers : 1, option_log : 1;
+ unsigned char ip_mask_bits;
+ unsigned int match_opers : 1, match_trusted : 1, option_log : 1;
unsigned int chan_req_modes : 2, chan_no_modes : 2;
int authed : 2, info_space : 2;
- time_t min_ts, max_ts;
} *discrim_t;
struct discrim_and_source {
struct mod_chanmode change;
struct userNode *victim;
- change.modes_set = change.modes_clear = 0;
+ mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_BAN;
if (is_ircmask(argv[1]))
- change.args[0].hostmask = strdup(argv[1]);
+ change.args[0].u.hostmask = strdup(argv[1]);
else if ((victim = GetUserH(argv[1])))
- change.args[0].hostmask = generate_hostmask(victim, 0);
+ change.args[0].u.hostmask = generate_hostmask(victim, 0);
else {
reply("OSMSG_INVALID_IRCMASK", argv[1]);
return 0;
}
modcmd_chanmode_announce(&change);
- reply("OSMSG_ADDED_BAN", change.args[0].hostmask, channel->name);
- free((char*)change.args[0].hostmask);
+ reply("OSMSG_ADDED_BAN", change.args[0].u.hostmask, channel->name);
+ free((char*)change.args[0].u.hostmask);
return 1;
}
}
reply("OSMSG_CHANINFO_USER_COUNT", channel->members.used);
for (n=0; n<channel->members.used; n++) {
- moden = channel->members.list[n];
- if (moden->modes & MODE_CHANOP)
- send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
+ moden = channel->members.list[n];
+ if (moden->modes & MODE_CHANOP) {
+ if (moden->oplevel >= 0)
+ send_message_type(4, user, cmd->parent->bot, " @%s:%d (%s@%s)", moden->user->nick, moden->oplevel, moden->user->ident, moden->user->hostname);
+ else
+ send_message_type(4, user, cmd->parent->bot, " @%s (%s@%s)", moden->user->nick, moden->user->ident, moden->user->hostname);
+ }
}
for (n=0; n<channel->members.used; n++) {
moden = channel->members.list[n];
return 1;
}
-static MODCMD_FUNC(cmd_warn)
+static MODCMD_FUNC(cmd_warn)
{
char *reason, *message;
change = mod_chanmode_alloc(channel->banlist.used);
for (ii=0; ii<channel->banlist.used; ii++) {
change->args[ii].mode = MODE_REMOVE | MODE_BAN;
- change->args[ii].hostmask = channel->banlist.list[ii]->ban;
+ change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
}
modcmd_chanmode_announce(change);
+ for (ii=0; ii<change->argc; ++ii)
+ free((char*)change->args[ii].u.hostmask);
mod_chanmode_free(change);
reply("OSMSG_CLEARBANS_DONE", channel->name);
return 1;
reply("OSMSG_NO_CHANNEL_MODES", channel->name);
return 0;
}
- change.modes_set = 0;
+ mod_chanmode_init(&change);
change.modes_clear = channel->modes;
- change.argc = 0;
modcmd_chanmode_announce(&change);
reply("OSMSG_CLEARMODES_DONE", channel->name);
return 1;
|| !(mn->modes & MODE_CHANOP))
continue;
change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
if (IsService(mn->user) || !(mn->modes & MODE_CHANOP))
continue;
change->args[count].mode = MODE_REMOVE | MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
static struct gline *
opserv_block(struct userNode *target, char *src_handle, char *reason, unsigned long duration)
{
- char *mask;
- mask = alloca(MAXLEN);
- snprintf(mask, MAXLEN, "*@%s", target->hostname);
- if (!reason) {
- reason = alloca(MAXLEN);
- snprintf(reason, MAXLEN, "G-line requested by %s.", src_handle);
- }
- if (!duration) duration = opserv_conf.block_gline_duration;
- return gline_add(src_handle, mask, duration, reason, now, 1);
+ char mask[IRC_NTOP_MAX_SIZE+3] = { '*', '@', '\0' };
+ irc_ntop(mask + 2, sizeof(mask) - 2, &target->ip);
+ if (!reason)
+ snprintf(reason = alloca(MAXLEN), MAXLEN,
+ "G-line requested by %s.", src_handle);
+ if (!duration)
+ duration = opserv_conf.block_gline_duration;
+ return gline_add(src_handle, mask, duration, reason, now, now, 1);
}
static MODCMD_FUNC(cmd_block)
struct userNode *target;
struct gline *gline;
char *reason;
+ unsigned long duration = 0;
+ unsigned int offset = 2;
+ struct svccmd *gline_cmd;
target = GetUserH(argv[1]);
if (!target) {
- reply("MSG_NICK_UNKNOWN", argv[1]);
- return 0;
+ reply("MSG_NICK_UNKNOWN", argv[1]);
+ return 0;
}
if (IsService(target)) {
- reply("MSG_SERVICE_IMMUNE", target->nick);
- return 0;
+ reply("MSG_SERVICE_IMMUNE", target->nick);
+ return 0;
+ }
+ if (dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL)) {
+ reply("OSMSG_BLOCK_TRUSTED", target->nick);
+ return 0;
+ }
+ if(argc > 2 && (duration = ParseInterval(argv[2]))) {
+ offset = 3;
+ }
+ if(duration && duration != opserv_conf.block_gline_duration) {
+ // We require more access when the duration is not the default block duration.
+ gline_cmd = dict_find(cmd->parent->commands, "gline", NULL);
+ if(!gline_cmd)
+ {
+ reply("OSMSG_NO_GLINE_CMD");
+ return 0;
+ }
+ if(!svccmd_can_invoke(user, cmd->parent->bot, gline_cmd, channel, SVCCMD_NOISY))
+ return 0;
}
- reason = (argc > 2) ? unsplit_string(argv+2, argc-2, NULL) : NULL;
- gline = opserv_block(target, user->handle_info->handle, reason, 0);
+ reason = (argc > offset) ? unsplit_string(argv+offset, argc-offset, NULL) : NULL;
+ gline = opserv_block(target, user->handle_info->handle, reason, duration);
reply("OSMSG_GLINE_ISSUED", gline->target);
return 1;
}
reply("MSG_INVALID_DURATION", argv[2]);
return 0;
}
- gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, 1);
+ gline = gline_add(user->handle_info->handle, argv[1], duration, reason, now, now, 1);
reply("OSMSG_GLINE_ISSUED", gline->target);
return 1;
}
return 0;
}
if (GetUserMode(opserv_conf.debug_channel, user)) {
- reply("OSMSG_ALREADY_THERE", channel->name);
+ reply("OSMSG_ALREADY_THERE", opserv_conf.debug_channel->name);
return 0;
}
irc_invite(cmd->parent->bot, target, opserv_conf.debug_channel);
mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = AddChannelUser(bot, channel);
+ change.args[0].u.member = AddChannelUser(bot, channel);
modcmd_chanmode_announce(&change);
}
irc_fetchtopic(bot, channel->name);
* channel, we have to join it in temporarily. */
if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
struct mod_chanmode change;
- memset(&change, 0, sizeof(change));
+ mod_chanmode_init(&change);
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = AddChannelUser(bot, channel);
+ change.args[0].u.member = AddChannelUser(bot, channel);
modcmd_chanmode_announce(&change);
}
if (argc < 2) {
reply("OSMSG_NOT_ON_CHANNEL", target->nick, channel->name);
return 0;
}
- change.modes_set = change.modes_clear = 0;
+ mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_BAN;
- change.args[0].hostmask = mask = generate_hostmask(target, 0);
+ change.args[0].u.hostmask = mask = generate_hostmask(target, 0);
modcmd_chanmode_announce(&change);
KickChannelUser(target, channel, cmd->parent->bot, reason);
free(mask);
if (!(inchan = GetUserMode(channel, bot) ? 1 : 0)) {
change = mod_chanmode_alloc(2);
change->args[0].mode = MODE_CHANOP;
- change->args[0].member = AddChannelUser(bot, channel);
+ change->args[0].u.member = AddChannelUser(bot, channel);
change->args[1].mode = MODE_BAN;
- change->args[1].hostmask = "*!*@*";
+ change->args[1].u.hostmask = "*!*@*";
} else {
change = mod_chanmode_alloc(1);
change->args[0].mode = MODE_BAN;
- change->args[0].hostmask = "*!*@*";
+ change->args[0].u.hostmask = "*!*@*";
}
modcmd_chanmode_announce(change);
+ mod_chanmode_free(change);
if (argc < 2) {
reason = alloca(strlen(OSMSG_KICK_REQUESTED)+strlen(user->nick)+1);
sprintf(reason, OSMSG_KICK_REQUESTED, user->nick);
if (!inchan)
DelChannelUser(bot, channel, "My work here is done", 0);
reply("OSMSG_KICKALL_DONE", channel->name);
- return 1;
+ return 1;
}
static MODCMD_FUNC(cmd_part)
if (mn->modes & MODE_CHANOP)
continue;
change->args[count].mode = MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
if (mn->modes & MODE_CHANOP)
continue;
change->args[count].mode = MODE_CHANOP;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
if (argv[1][0] == '*')
target = GetUserN(argv[1]+1);
else
- target = GetUserH(argv[1]);
-#else
- target = GetUserH(argv[1]);
#endif
+ target = GetUserH(argv[1]);
if (!target) {
reply("MSG_NICK_UNKNOWN", argv[1]);
return 0;
}
reply("OSMSG_WHOIS_NICK", target->nick);
reply("OSMSG_WHOIS_HOST", target->ident, target->hostname);
- reply("OSMSG_WHOIS_IP", inet_ntoa(target->ip));
+ if (IsFakeHost(target))
+ reply("OSMSG_WHOIS_FAKEHOST", target->fakehost);
+ reply("OSMSG_WHOIS_IP", irc_ntoa(&target->ip));
if (target->modes) {
bpos = 0;
#define buffer_cat(str) (herelen = strlen(str), memcpy(buffer+bpos, str, herelen), bpos += herelen)
if (IsWallOp(target)) buffer[bpos++] = 'w';
if (IsOper(target)) buffer[bpos++] = 'o';
if (IsGlobal(target)) buffer[bpos++] = 'g';
- if (IsServNotice(target)) buffer[bpos++] = 's';
- if (IsHelperIrcu(target)) buffer[bpos++] = 'h';
if (IsService(target)) buffer[bpos++] = 'k';
if (IsDeaf(target)) buffer[bpos++] = 'd';
+ if (IsNoChan(target)) buffer[bpos++] = 'n';
if (IsHiddenHost(target)) buffer[bpos++] = 'x';
+ if (IsNoIdle(target)) buffer[bpos++] = 'I';
if (IsGagged(target)) buffer_cat(" (gagged)");
if (IsRegistering(target)) buffer_cat(" (registered account)");
buffer[bpos] = 0;
static MODCMD_FUNC(cmd_unban)
{
struct mod_chanmode change;
- change.modes_set = change.modes_clear = 0;
+ mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_REMOVE | MODE_BAN;
- change.args[0].hostmask = argv[1];
+ change.args[0].u.hostmask = argv[1];
modcmd_chanmode_announce(&change);
reply("OSMSG_UNBAN_DONE", channel->name);
return 1;
if (mn->modes & (MODE_CHANOP|MODE_VOICE))
continue;
change->args[count].mode = MODE_VOICE;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
change = mod_chanmode_alloc(channel->members.used);
for (ii = count = 0; ii < channel->members.used; ++ii) {
struct modeNode *mn = channel->members.list[ii];
- if (mn->modes & MODE_VOICE)
+ if (!(mn->modes & MODE_VOICE))
continue;
change->args[count].mode = MODE_REMOVE | MODE_VOICE;
- change->args[count++].member = mn;
+ change->args[count++].u.member = mn;
}
if (count) {
change->argc = count;
return 1;
}
-static MODCMD_FUNC(cmd_stats_glines) {
- reply("OSMSG_GLINE_COUNT", gline_count());
- return 1;
-}
-
static void
trace_links(struct userNode *bot, struct userNode *user, struct server *server, unsigned int depth) {
unsigned int nn, pos;
}
static MODCMD_FUNC(cmd_stats_uptime) {
+ extern int lines_processed;
+ extern time_t boot_time;
+ double kernel_time;
+ double user_time;
char uptime[INTERVALLEN];
+
+#if defined(HAVE_TIMES)
+ static double clocks_per_sec;
struct tms buf;
- extern time_t boot_time;
- extern int lines_processed;
- static long clocks_per_sec;
if (!clocks_per_sec) {
#if defined(HAVE_SYSCONF) && defined(_SC_CLK_TCK)
clocks_per_sec = CLOCKS_PER_SEC;
}
}
- intervalString(uptime, time(NULL)-boot_time, user->handle_info);
times(&buf);
- reply("OSMSG_UPTIME_STATS",
- uptime, lines_processed,
- buf.tms_utime/(double)clocks_per_sec,
- buf.tms_stime/(double)clocks_per_sec);
+ user_time = buf.tms_utime / clocks_per_sec;
+ kernel_time = buf.tms_stime / clocks_per_sec;
+#elif defined(HAVE_GETPROCESSTIMES)
+ FILETIME times[4];
+ LARGE_INTEGER li[2];
+
+ GetProcessTimes(GetCurrentProcess(), ×[0], ×[1], ×[2], ×[3]);
+ li[0].LowPart = times[2].dwLowDateTime;
+ li[0].HighPart = times[2].dwHighDateTime;
+ kernel_time = li[0].QuadPart * 1e-7;
+ li[1].LowPart = times[3].dwLowDateTime;
+ li[1].HighPart = times[3].dwHighDateTime;
+ user_time = li[1].QuadPart * 1e-7;
+#else
+ user_time = NAN;
+ system_time = NAN;
+#endif
+
+ intervalString(uptime, time(NULL)-boot_time, user->handle_info);
+ reply("OSMSG_UPTIME_STATS", uptime, lines_processed, user_time, kernel_time);
return 1;
}
return 1;
}
+#if defined(WITH_MALLOC_SRVX)
+static MODCMD_FUNC(cmd_stats_memory) {
+ extern unsigned long alloc_count, alloc_size;
+ send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
+ "%u allocations totalling %u bytes.",
+ alloc_count, alloc_size);
+ return 1;
+}
+#elif defined(WITH_MALLOC_SLAB)
+static MODCMD_FUNC(cmd_stats_memory) {
+ extern unsigned long slab_alloc_count, slab_count, slab_alloc_size;
+ extern unsigned long big_alloc_count, big_alloc_size;
+ send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
+ "%u allocations in %u slabs totalling %u bytes.",
+ slab_alloc_count, slab_count, slab_alloc_size);
+ send_message_type(MSG_TYPE_NOXLATE, user, cmd->parent->bot,
+ "%u big allocations totalling %u bytes.",
+ big_alloc_count, big_alloc_size);
+ return 1;
+}
+#endif
+
static MODCMD_FUNC(cmd_dump)
{
- char linedup[MAXLEN], *original;
+ char linedup[MAXLEN], original[MAXLEN];
- original = unsplit_string(argv+1, argc-1, NULL);
+ unsplit_string(argv+1, argc-1, original);
safestrncpy(linedup, original, sizeof(linedup));
/* assume it's only valid IRC if we can parse it */
if (parse_line(linedup, 1)) {
static MODCMD_FUNC(cmd_raw)
{
- char linedup[MAXLEN], *original;
+ char linedup[MAXLEN], original[MAXLEN];
- original = unsplit_string(argv+1, argc-1, NULL);
+ unsplit_string(argv+1, argc-1, original);
safestrncpy(linedup, original, sizeof(linedup));
/* Try to parse the line before sending it; if it's too wrong,
* maybe it will core us instead of our uplink. */
return NULL;
}
}
- if ((resv = AddClone(nick, ident, host, desc))) {
+ if ((resv = AddLocalUser(nick, ident, host, desc, "+i"))) {
dict_insert(opserv_reserved_nick_dict, resv->nick, resv);
}
return resv;
{
struct opserv_hostinfo *ohi;
struct gag_entry *gag;
+ char addr[IRC_NTOP_MAX_SIZE];
/* Check to see if we should ignore them entirely. */
if (IsLocal(user) || IsService(user))
/* Gag them if appropriate. */
for (gag = gagList; gag; gag = gag->next) {
- if (user_matches_glob(user, gag->mask, 1)) {
+ if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
gag_helper_func(user, NULL);
break;
}
}
/* Add to host info struct */
- if (!(ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL))) {
+ irc_ntop(addr, sizeof(addr), &user->ip);
+ if (!(ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
ohi = calloc(1, sizeof(*ohi));
- dict_insert(opserv_hostinfo_dict, strdup(inet_ntoa(user->ip)), ohi);
+ dict_insert(opserv_hostinfo_dict, strdup(addr), ohi);
userList_init(&ohi->clients);
}
userList_append(&ohi->clients, user);
}
/* Only warn or G-line if there's an untrusted max and their IP is sane. */
- if (opserv_conf.untrusted_max && user->ip.s_addr && (ntohl(user->ip.s_addr) != INADDR_LOOPBACK)) {
- struct trusted_host *th = dict_find(opserv_trusted_hosts, inet_ntoa(user->ip), NULL);
+ if (opserv_conf.untrusted_max
+ && irc_in_addr_is_valid(user->ip)
+ && !irc_in_addr_is_loopback(user->ip)) {
+ struct trusted_host *th = dict_find(opserv_trusted_hosts, addr, NULL);
unsigned int limit = th ? th->limit : opserv_conf.untrusted_max;
if (!limit) {
/* 0 means unlimited hosts */
for (nn=0; nn<ohi->clients.used; nn++)
send_message(ohi->clients.list[nn], opserv, "OSMSG_CLONE_WARNING");
} else if (ohi->clients.used > limit) {
- char target[18];
- sprintf(target, "*@%s", inet_ntoa(user->ip));
- gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, 1);
+ char target[IRC_NTOP_MAX_SIZE + 3] = { '*', '@', '\0' };
+ strcpy(target + 2, addr);
+ gline_add(opserv->nick, target, opserv_conf.clone_gline_duration, "AUTO Excessive connections from a single host.", now, now, 1);
}
}
opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), UNUSED_ARG(const char *why))
{
struct opserv_hostinfo *ohi;
+ char addr[IRC_NTOP_MAX_SIZE];
if (IsLocal(user)) {
/* Try to remove it from the reserved nick dict without
dict_remove(opserv_reserved_nick_dict, user->nick);
return;
}
- if ((ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL))) {
+ irc_ntop(addr, sizeof(addr), &user->ip);
+ if ((ohi = dict_find(opserv_hostinfo_dict, addr, NULL))) {
userList_remove(&ohi->clients, user);
- if (ohi->clients.used == 0) dict_remove(opserv_hostinfo_dict, inet_ntoa(user->ip));
+ if (ohi->clients.used == 0)
+ dict_remove(opserv_hostinfo_dict, addr);
}
}
opserv_bad_channel(const char *name)
{
unsigned int found;
+ int present;
- dict_find(opserv_exempt_channels, name, &found);
- if (found)
+ dict_find(opserv_exempt_channels, name, &present);
+ if (present)
return 0;
if (gline_find(name))
change = mod_chanmode_alloc(2);
change->modes_set = MODE_SECRET | MODE_INVITEONLY;
change->args[0].mode = MODE_CHANOP;
- change->args[0].member = AddChannelUser(opserv, channel);
+ change->args[0].u.member = AddChannelUser(opserv, channel);
change->args[1].mode = MODE_BAN;
- change->args[1].hostmask = "*!*@*";
+ change->args[1].u.hostmask = "*!*@*";
mod_chanmode_announce(opserv, channel, change);
mod_chanmode_free(change);
for (nn=channel->members.used; nn>0; ) {
/* Don't moderate the channel unless it is activated and
the number of users in the channel is over the threshold. */
struct mod_chanmode change;
- change.modes_set = change.modes_clear = change.argc = 0;
+ mod_chanmode_init(&change);
channel->join_flooded = 1;
- if (opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) {
+ if (opserv && opserv_conf.join_flood_moderate && (channel->members.used > opserv_conf.join_flood_moderate_threshold)) {
if (!GetUserMode(channel, opserv)) {
/* If we aren't in the channel, join it. */
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = AddChannelUser(opserv, channel);
+ change.args[0].u.member = AddChannelUser(opserv, channel);
change.argc++;
}
- if (!(channel->modes & MODE_MODERATED))
- change.modes_set |= MODE_MODERATED;
+ change.modes_set = (MODE_MODERATED | MODE_DELAYJOINS) & ~channel->modes;
if (change.modes_set || change.argc)
mod_chanmode_announce(opserv, channel, &change);
send_target_message(0, channel->name, opserv, "OSMSG_FLOOD_MODERATE");
{
unsigned long interval;
char *reason, *tmp;
- struct in_addr tmpaddr;
+ irc_in_addr_t tmpaddr;
unsigned int count;
if (dict_find(opserv_trusted_hosts, argv[1], NULL)) {
return 0;
}
- if (!inet_aton(argv[1], &tmpaddr)) {
+ if (!irc_pton(&tmpaddr, NULL, argv[1])) {
reply("OSMSG_BAD_IP", argv[1]);
return 0;
}
reply("OSMSG_NOT_A_HOSTMASK");
return 0;
}
- if (!(clone = AddClone(argv[2], ident, argv[3]+i, userinfo))) {
+ if (!(clone = AddLocalUser(argv[2], ident, argv[3]+i, userinfo, "+i"))) {
reply("OSMSG_CLONE_FAILED", argv[2]);
return 0;
}
reply("MSG_CHANNEL_UNKNOWN", argv[3]);
return 0;
}
- change.modes_set = change.modes_clear = 0;
+ mod_chanmode_init(&change);
change.argc = 1;
change.args[0].mode = MODE_CHANOP;
- change.args[0].member = GetUserMode(channel, clone);
- if (!change.args[0].member) {
+ change.args[0].u.member = GetUserMode(channel, clone);
+ if (!change.args[0].u.member) {
reply("OSMSG_NOT_ON_CHANNEL", clone->nick, channel->name);
return 0;
}
log_module(OS_LOG, LOG_ERROR, "Missing description for reserve of %s", key);
return 0;
}
- if ((reserve = AddClone(key, ident, hostname, desc))) {
+ if ((reserve = AddLocalUser(key, ident, hostname, desc, "+i"))) {
reserve->modes |= FLAGS_PERSISTENT;
dict_insert(extra, reserve->nick, reserve);
}
discrim->info_space = -1;
dupmask = strdup(hostmask);
if (split_ircmask(dupmask, &discrim->mask_nick, &discrim->mask_ident, &discrim->mask_host)) {
- if (discrim->mask_host && !discrim->mask_host[strspn(discrim->mask_host, "0123456789.?*")]) {
- if (!parse_ipmask(discrim->mask_host, &discrim->ip_addr, &discrim->ip_mask)) {
- log_module(OS_LOG, LOG_ERROR, "Couldn't parse %s as an IP mask!", discrim->mask_host);
- free(discrim);
- free(dupmask);
- return 0;
- }
- discrim->mask_host = 0;
- }
+ if (!irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, discrim->mask_host))
+ discrim->ip_mask_bits = 0;
matched = opserv_discrim_search(discrim, func, extra);
} else {
log_module(OS_LOG, LOG_ERROR, "Couldn't split IRC mask for gag %s!", hostmask);
alert->discrim->reason = strdup(name);
alert->reaction = reaction;
dict_insert(opserv_user_alerts, name_dup, alert);
- if (alert->discrim->channel)
+ /* Stick the alert into the appropriate additional alert dict(s).
+ * For channel alerts, we only use channels and min_channels;
+ * max_channels would have to be checked on /part, which we do not
+ * yet do, and which seems of questionable value.
+ */
+ if (alert->discrim->channel || alert->discrim->min_channels)
dict_insert(opserv_channel_alerts, name_dup, alert);
- else if (alert->discrim->mask_nick)
+ if (alert->discrim->mask_nick)
dict_insert(opserv_nick_based_alerts, name_dup, alert);
+ if (alert->discrim->accountmask || alert->discrim->authed != -1)
+ dict_insert(opserv_account_alerts, name_dup, alert);
return alert;
}
} else if (irccasecmp(argv[i], "server") == 0) {
discrim->server = argv[++i];
} else if (irccasecmp(argv[i], "ip") == 0) {
- j = parse_ipmask(argv[++i], &discrim->ip_addr, &discrim->ip_mask);
- if (!j) discrim->ip_mask_str = argv[i];
+ j = irc_pton(&discrim->ip_mask, &discrim->ip_mask_bits, argv[++i]);
+ if (!j) {
+ send_message(user, opserv, "OSMSG_BAD_IP", argv[i]);
+ goto fail;
+ }
} else if (irccasecmp(argv[i], "account") == 0) {
if (discrim->authed == 0) {
send_message(user, opserv, "OSMSG_ACCOUNTMASK_AUTHED");
discrim->max_ts = now - (ParseInterval(cmp+1) - 1);
}
} else {
- discrim->min_ts = now - ParseInterval(cmp+2);
+ discrim->min_ts = now - ParseInterval(cmp);
}
} else if (irccasecmp(argv[i], "access") == 0) {
const char *cmp = argv[++i];
discrim->min_level = strtoul(cmp+1, NULL, 0) + 1;
}
} else {
- discrim->min_level = strtoul(cmp+2, NULL, 0);
+ discrim->min_level = strtoul(cmp, NULL, 0);
+ }
+ } else if (irccasecmp(argv[i], "abuse") == 0) {
+ const char *abuse_what = argv[++i];
+ if (irccasecmp(abuse_what, "opers") == 0) {
+ discrim->match_opers = 1;
+ } else if (irccasecmp(abuse_what, "trusted") == 0) {
+ discrim->match_trusted = 1;
}
- } else if ((irccasecmp(argv[i], "abuse") == 0)
- && (irccasecmp(argv[++i], "opers") == 0)) {
- discrim->match_opers = 1;
} else if (irccasecmp(argv[i], "depth") == 0) {
discrim->domain_depth = strtoul(argv[++i], NULL, 0);
} else if (irccasecmp(argv[i], "clones") == 0) {
|| (discrim->mask_info && !match_ircglob(user->info, discrim->mask_info))
|| (discrim->server && !match_ircglob(user->uplink->name, discrim->server))
|| (discrim->accountmask && (!user->handle_info || !match_ircglob(user->handle_info->handle, discrim->accountmask)))
- || (discrim->ip_mask && !MATCH_IPMASK(user->ip, discrim->ip_addr, discrim->ip_mask))) {
+ || (discrim->ip_mask_bits && !irc_check_mask(&user->ip, &discrim->ip_mask, discrim->ip_mask_bits))
+ )
+ return 0;
+ if (discrim->channel && !GetUserMode(discrim->channel, user))
return 0;
- }
- if (discrim->channel && !GetUserMode(discrim->channel, user)) return 0;
access = user->handle_info ? user->handle_info->opserv_level : 0;
if ((access < discrim->min_level)
|| (access > discrim->max_level)) {
return 0;
}
- if (discrim->ip_mask_str) {
- if (!match_ircglob(inet_ntoa(user->ip), discrim->ip_mask_str)) return 0;
- }
if (discrim->min_clones > 1) {
- struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, inet_ntoa(user->ip), NULL);
- if (!ohi || (ohi->clients.used < discrim->min_clones)) return 0;
+ struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&user->ip), NULL);
+ if (!ohi || (ohi->clients.used < discrim->min_clones))
+ return 0;
}
return 1;
}
userList_append(&matched, mn->user);
}
}
- } else if (discrim->ip_mask_str && !discrim->ip_mask_str[strcspn(discrim->ip_mask_str, "?*")]) {
- struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, discrim->ip_mask_str, NULL);
+ } else if (discrim->ip_mask_bits == 128) {
+ struct opserv_hostinfo *ohi = dict_find(opserv_hostinfo_dict, irc_ntoa(&discrim->ip_mask), NULL);
if (!ohi) {
userList_clean(&matched);
return 0;
&& target->handle_info->opserv_level > user->handle_info->opserv_level));
}
+static int
+is_trust_victim(struct userNode *target, int match_trusted)
+{
+ return (match_trusted || !dict_find(opserv_trusted_hosts, irc_ntoa(&target->ip), NULL));
+}
+
static int
trace_gline_func(struct userNode *match, void *extra)
{
struct discrim_and_source *das = extra;
- if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
+ if (is_oper_victim(das->source, match, das->discrim->match_opers) && is_trust_victim(match, das->discrim->match_trusted)) {
opserv_block(match, das->source->handle_info->handle, das->discrim->reason, das->discrim->duration);
}
{
struct discrim_and_source *das = extra;
- if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
+ if (is_oper_victim(das->source, match, das->discrim->match_opers) && is_trust_victim(match, das->discrim->match_trusted)) {
char *reason;
if (das->discrim->reason) {
reason = das->discrim->reason;
{
struct discrim_and_source *das = extra;
- if (is_oper_victim(das->source, match, das->discrim->match_opers)) {
+ if (is_oper_victim(das->source, match, das->discrim->match_opers) && is_trust_victim(match, das->discrim->match_trusted)) {
char *reason, *mask;
int masksize;
if (das->discrim->reason) {
trace_domains_func(struct userNode *match, void *extra)
{
struct discrim_and_source *das = extra;
+ irc_in_addr_t ip;
unsigned long *count;
unsigned int depth;
char *hostname;
+ char ipmask[IRC_NTOP_MASK_MAX_SIZE];
- if (!match->hostname[strspn(match->hostname, "0123456789.")]) {
- char ipmask[16];
- unsigned long matchip = ntohl(match->ip.s_addr);
- /* raw IP address.. use up to first three octets of IP */
- switch (das->discrim->domain_depth) {
- default:
- snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255);
- break;
- case 2:
- snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255);
- break;
- case 1:
- snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255);
- break;
- }
+ if (irc_pton(&ip, NULL, match->hostname)) {
+ if (irc_in_addr_is_ipv4(ip)) {
+ unsigned long matchip = ntohl(ip.in6_32[3]);
+ /* raw IP address.. use up to first three octets of IP */
+ switch (das->discrim->domain_depth) {
+ default:
+ snprintf(ipmask, sizeof(ipmask), "%lu.%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255, (matchip>>8)&255);
+ break;
+ case 2:
+ snprintf(ipmask, sizeof(ipmask), "%lu.%lu.*", (matchip>>24)&255, (matchip>>16)&255);
+ break;
+ case 1:
+ snprintf(ipmask, sizeof(ipmask), "%lu.*", (matchip>>24)&255);
+ break;
+ }
+ } else if (irc_in_addr_is_ipv6(ip)) {
+ switch (das->discrim->domain_depth) {
+ case 1: depth = 16; goto ipv6_pfx;
+ case 2: depth = 24; goto ipv6_pfx;
+ case 3: depth = 32; goto ipv6_pfx;
+ default: depth = das->discrim->domain_depth;
+ ipv6_pfx:
+ irc_ntop_mask(ipmask, sizeof(ipmask), &ip, depth);
+ }
+ } else safestrncpy(ipmask, match->hostname, sizeof(ipmask));
+ ipmask[sizeof(ipmask) - 1] = '\0';
hostname = ipmask;
} else {
hostname = match->hostname + strlen(match->hostname);
- for (depth=das->discrim->domain_depth;
+ for (depth=das->discrim->domain_depth;
depth && (hostname > match->hostname);
depth--) {
hostname--;
discrim = calloc(1, sizeof(*discrim));
discrim->limit = 25;
+ discrim->max_users = ~0;
+ /* So, time_t is frequently signed. Fun. */
+ discrim->max_ts = (1ul << (CHAR_BIT * sizeof(time_t) - 1)) - 1;
for (i = 0; i < argc; i++) {
/* Assume all criteria require arguments. */
else
discrim->min_users = strtoul(cmp+1, NULL, 0) + 1;
} else {
- discrim->min_users = strtoul(cmp+2, NULL, 0);
+ discrim->min_users = strtoul(cmp, NULL, 0);
}
} else if (!irccasecmp(argv[i], "timestamp")) {
const char *cmp = argv[++i];
{
if ((discrim->name && !match_ircglob(chan->name, discrim->name)) ||
(discrim->topic && !match_ircglob(chan->topic, discrim->topic)) ||
- (discrim->min_users && chan->members.used < discrim->min_users) ||
- (discrim->max_users && chan->members.used > discrim->max_users) ||
- (discrim->min_ts && chan->timestamp < discrim->min_ts) ||
- (discrim->max_ts && chan->timestamp > discrim->max_ts)) {
+ (chan->members.used < discrim->min_users) ||
+ (chan->members.used > discrim->max_users) ||
+ (chan->timestamp < discrim->min_ts) ||
+ (chan->timestamp > discrim->max_ts)) {
return 0;
}
return 1;
gtrace_print_func(struct gline *gline, void *extra)
{
struct gline_extra *xtra = extra;
- char *when_text, set_text[20];
- strftime(set_text, sizeof(set_text), "%Y-%m-%d", localtime(&gline->issued));
- when_text = asctime(localtime(&gline->expires));
- when_text[strlen(when_text)-1] = 0; /* strip lame \n */
- send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, set_text, gline->issuer, when_text, gline->reason);
+ char issued[INTERVALLEN];
+ char lastmod[INTERVALLEN];
+ char expires[INTERVALLEN];
+
+ intervalString(issued, now - gline->issued, xtra->user->handle_info);
+ if (gline->lastmod)
+ intervalString(lastmod, now - gline->lastmod, xtra->user->handle_info);
+ else
+ strcpy(lastmod, "<unknown>");
+ if (gline->expires)
+ intervalString(expires, gline->expires - now, xtra->user->handle_info);
+ else
+ strcpy(expires, "never");
+ send_message(xtra->user, opserv, "OSMSG_GTRACE_FORMAT", gline->target, issued, gline->issuer, lastmod, expires, gline->reason);
+}
+
+static MODCMD_FUNC(cmd_stats_glines) {
+ if (argc < 2) {
+ reply("OSMSG_GLINE_COUNT", gline_count());
+ return 1;
+ } else if (argc < 3) {
+ struct gline_extra extra;
+ struct gline *gl;
+
+ extra.user = user;
+ gl = gline_find(argv[1]);
+ if (!gl)
+ reply("OSMSG_NO_GLINE", argv[1]);
+ else
+ gtrace_print_func(gl, &extra);
+ return 1;
+ } else return 0;
}
static void
return 0;
}
+ if ((alert->reaction != REACT_NOTICE)
+ && !is_trust_victim(user, alert->discrim->match_trusted)) {
+ return 0;
+ }
+
/* The user matches the alert criteria, so trigger the reaction. */
if (alert->discrim->option_log)
log_module(OS_LOG, LOG_INFO, "Alert %s triggered by user %s!%s@%s (%s).", key, user->nick, user->ident, user->hostname, alert->discrim->reason);
/* Gag them if appropriate (and only if). */
user->modes &= ~FLAGS_GAGGED;
for (gag = gagList; gag; gag = gag->next) {
- if (user_matches_glob(user, gag->mask, 1)) {
+ if (user_matches_glob(user, gag->mask, MATCH_USENICK)) {
gag_helper_func(user, NULL);
break;
}
else
return;
- if (user->ip.s_addr)
+ if (irc_in_addr_is_valid(user->ip))
send_channel_notice(opserv_conf.staff_auth_channel, opserv, IDENT_FORMAT" authed to %s account %s", IDENT_DATA(user), type, user->handle_info->handle);
else
send_channel_notice(opserv_conf.staff_auth_channel, opserv, "%s [%s@%s] authed to %s account %s", user->nick, user->ident, user->hostname, type, user->handle_info->handle);
+
+ dict_foreach(opserv_account_alerts, alert_check_user, user);
}
static MODCMD_FUNC(cmd_log)
for (i=1; i<argc; i++) {
dict_remove(opserv_nick_based_alerts, argv[i]);
dict_remove(opserv_channel_alerts, argv[i]);
- if (dict_remove(opserv_user_alerts, argv[i]))
- reply("OSMSG_REMOVED_ALERT", argv[i]);
+ dict_remove(opserv_account_alerts, argv[i]);
+ if (dict_remove(opserv_user_alerts, argv[i]))
+ reply("OSMSG_REMOVED_ALERT", argv[i]);
else
- reply("OSMSG_NO_SUCH_ALERT", argv[i]);
+ reply("OSMSG_NO_SUCH_ALERT", argv[i]);
}
return 1;
}
dict_set_free_keys(opserv_chan_warn, free);
dict_set_free_data(opserv_chan_warn, free);
/* set up opserv_user_alerts */
+ dict_delete(opserv_account_alerts);
+ opserv_account_alerts = dict_new();
dict_delete(opserv_channel_alerts);
opserv_channel_alerts = dict_new();
dict_delete(opserv_nick_based_alerts);
unreg_del_user_func(opserv_user_cleanup);
dict_delete(opserv_hostinfo_dict);
dict_delete(opserv_nick_based_alerts);
+ dict_delete(opserv_account_alerts);
dict_delete(opserv_channel_alerts);
dict_delete(opserv_user_alerts);
for (nn=0; nn<ArrayLength(level_strings); ++nn)
init_opserv(const char *nick)
{
OS_LOG = log_register_type("OpServ", "file:opserv.log");
- if (nick)
- opserv = AddService(nick, "Oper Services", NULL);
+ if (nick) {
+ const char *modes = conf_get_data("services/opserv/modes", RECDB_QSTRING);
+ opserv = AddLocalUser(nick, nick, NULL, "Oper Services", modes);
+ }
conf_register_reload(opserv_conf_read);
memset(level_strings, 0, sizeof(level_strings));
opserv_define_func("STATS UPLINK", cmd_stats_uplink, 0, 0, 0);
opserv_define_func("STATS UPTIME", cmd_stats_uptime, 0, 0, 0);
opserv_define_func("STATS WARN", cmd_stats_warn, 0, 0, 0);
+#if defined(WITH_MALLOC_SRVX) || defined(WITH_MALLOC_SLAB)
+ opserv_define_func("STATS MEMORY", cmd_stats_memory, 0, 0, 0);
+#endif
opserv_define_func("TRACE", cmd_trace, 100, 0, 3);
opserv_define_func("TRACE PRINT", NULL, 0, 0, 0);
opserv_define_func("TRACE COUNT", NULL, 0, 0, 0);