X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=src%2Fopserv.c;h=d6aedf50ed7fe9ee83a46902ea13026ba93d8c60;hb=839ab9c7c30c63c0bb584da4ab6fe41f54f22ac4;hp=74a4380998bf6aa75f0c0cfd4fe890a5d0a2ba60;hpb=4d6ca4a951af494f04357fec18caaf3eaed0b67a;p=srvx.git diff --git a/src/opserv.c b/src/opserv.c index 74a4380..d6aedf5 100644 --- a/src/opserv.c +++ b/src/opserv.c @@ -78,7 +78,7 @@ #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." @@ -115,6 +115,7 @@ static const struct message_entry msgtab[] = { { "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" }, @@ -150,6 +151,7 @@ static const struct message_entry msgtab[] = { { "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)" }, @@ -194,7 +196,7 @@ static const struct message_entry msgtab[] = { { "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." }, @@ -317,14 +319,15 @@ opserv_free_hostinfo(void *data) 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 char ip_mask_bits; unsigned int match_opers : 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 { @@ -397,20 +400,20 @@ static MODCMD_FUNC(cmd_ban) 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; } @@ -455,9 +458,13 @@ static MODCMD_FUNC(cmd_chaninfo) } reply("OSMSG_CHANINFO_USER_COUNT", channel->members.used); for (n=0; nmembers.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; nmembers.used; n++) { moden = channel->members.list[n]; @@ -472,7 +479,7 @@ static MODCMD_FUNC(cmd_chaninfo) return 1; } -static MODCMD_FUNC(cmd_warn) +static MODCMD_FUNC(cmd_warn) { char *reason, *message; @@ -521,9 +528,11 @@ static MODCMD_FUNC(cmd_clearbans) change = mod_chanmode_alloc(channel->banlist.used); for (ii=0; iibanlist.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; iiargc; ++ii) + free((char*)change->args[ii].u.hostmask); mod_chanmode_free(change); reply("OSMSG_CLEARBANS_DONE", channel->name); return 1; @@ -537,9 +546,8 @@ static MODCMD_FUNC(cmd_clearmodes) 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; @@ -559,7 +567,7 @@ static MODCMD_FUNC(cmd_deop) || !(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; @@ -581,7 +589,7 @@ static MODCMD_FUNC(cmd_deopall) 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; @@ -744,15 +752,14 @@ static MODCMD_FUNC(cmd_restart) 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) @@ -760,18 +767,23 @@ static MODCMD_FUNC(cmd_block) struct userNode *target; struct gline *gline; char *reason; + unsigned long duration = 0; + unsigned int offset = 2; 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(argc > 2 && (duration = ParseInterval(argv[2]))) { + offset = 3; } - 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; } @@ -796,7 +808,7 @@ static MODCMD_FUNC(cmd_gline) 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; } @@ -886,7 +898,7 @@ static MODCMD_FUNC(cmd_inviteme) 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); @@ -920,10 +932,10 @@ static MODCMD_FUNC(cmd_join) return 0; } else { struct mod_chanmode change; - change.modes_set = change.modes_clear = 0; + 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); @@ -966,9 +978,9 @@ static MODCMD_FUNC(cmd_kickall) * 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) { @@ -1015,10 +1027,10 @@ static MODCMD_FUNC(cmd_kickban) 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); @@ -1038,15 +1050,16 @@ static MODCMD_FUNC(cmd_kickbanall) 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); @@ -1067,7 +1080,7 @@ static MODCMD_FUNC(cmd_kickbanall) 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) @@ -1116,7 +1129,7 @@ static MODCMD_FUNC(cmd_op) 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; @@ -1138,7 +1151,7 @@ static MODCMD_FUNC(cmd_opall) 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; @@ -1159,17 +1172,17 @@ static MODCMD_FUNC(cmd_whois) 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) @@ -1177,12 +1190,13 @@ static MODCMD_FUNC(cmd_whois) 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; if (bpos > 0) reply("OSMSG_WHOIS_MODES", buffer); @@ -1205,10 +1219,10 @@ static MODCMD_FUNC(cmd_whois) 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; @@ -1225,7 +1239,7 @@ static MODCMD_FUNC(cmd_voiceall) 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; @@ -1244,10 +1258,10 @@ static MODCMD_FUNC(cmd_devoiceall) 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; @@ -1295,11 +1309,6 @@ static MODCMD_FUNC(cmd_stats_bad) { 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; @@ -1476,11 +1485,15 @@ static MODCMD_FUNC(cmd_stats_uplink) { } 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) @@ -1492,12 +1505,27 @@ static MODCMD_FUNC(cmd_stats_uptime) { 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; } @@ -1576,11 +1604,33 @@ static MODCMD_FUNC(cmd_stats_warn) { 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)) { @@ -1593,9 +1643,9 @@ static MODCMD_FUNC(cmd_dump) 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. */ @@ -1620,7 +1670,7 @@ opserv_add_reserve(struct svccmd *cmd, struct userNode *user, const char *nick, 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; @@ -1696,6 +1746,7 @@ opserv_new_user_check(struct userNode *user) { 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)) @@ -1707,16 +1758,17 @@ opserv_new_user_check(struct userNode *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); @@ -1734,8 +1786,10 @@ opserv_new_user_check(struct userNode *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 */ @@ -1744,9 +1798,9 @@ opserv_new_user_check(struct userNode *user) for (nn=0; nnclients.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); } } @@ -1757,6 +1811,7 @@ static void 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 @@ -1765,9 +1820,11 @@ opserv_user_cleanup(struct userNode *user, UNUSED_ARG(struct userNode *killer), 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); } } @@ -1775,9 +1832,10 @@ int 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)) @@ -1799,9 +1857,9 @@ opserv_shutdown_channel(struct chanNode *channel, const char *reason) 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; ) { @@ -1874,17 +1932,16 @@ opserv_join_check(struct modeNode *mNode) /* 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"); @@ -2092,21 +2149,21 @@ static MODCMD_FUNC(cmd_addtrust) { unsigned long interval; char *reason, *tmp; - struct in_addr tmpaddr; - int count; + irc_in_addr_t tmpaddr; + unsigned int count; if (dict_find(opserv_trusted_hosts, argv[1], NULL)) { reply("OSMSG_ALREADY_TRUSTED", argv[1]); return 0; } - if (!inet_aton(argv[1], &tmpaddr)) { + if (!irc_pton(&tmpaddr, NULL, argv[1])) { reply("OSMSG_BAD_IP", argv[1]); return 0; } count = strtoul(argv[2], &tmp, 10); - if (!count || *tmp != '\0') { + if (*tmp != '\0') { reply("OSMSG_BAD_NUMBER", argv[2]); return 0; } @@ -2128,7 +2185,7 @@ static MODCMD_FUNC(cmd_edittrust) unsigned long interval; struct trusted_host *th; char *reason, *tmp; - int count; + unsigned int count; th = dict_find(opserv_trusted_hosts, argv[1], NULL); if (!th) { @@ -2215,7 +2272,7 @@ static MODCMD_FUNC(cmd_clone) 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; } @@ -2279,11 +2336,11 @@ static MODCMD_FUNC(cmd_clone) 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; } @@ -2396,7 +2453,7 @@ int add_reserved(const char *key, void *data, void *extra) 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); } @@ -2420,15 +2477,8 @@ foreach_matching_user(const char *hostmask, discrim_search_func func, void *extr 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); @@ -2536,9 +2586,14 @@ opserv_add_user_alert(struct userNode *req, const char *name, opserv_alert_react 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); return alert; } @@ -2908,8 +2963,11 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in } 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"); @@ -3003,7 +3061,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in 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]; @@ -3023,7 +3081,7 @@ opserv_discrim_create(struct userNode *user, unsigned int argc, char *argv[], in 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) && (irccasecmp(argv[++i], "opers") == 0)) { @@ -3075,21 +3133,20 @@ discrim_match(discrim_t discrim, struct userNode *user) || (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; } @@ -3116,8 +3173,8 @@ opserv_discrim_search(discrim_t discrim, discrim_search_func dsf, void *data) 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; @@ -3262,29 +3319,42 @@ static int 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--; @@ -3412,6 +3482,9 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]) 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. */ @@ -3439,7 +3512,7 @@ opserv_cdiscrim_create(struct userNode *user, unsigned int argc, char *argv[]) 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]; @@ -3482,10 +3555,10 @@ cdiscrim_match(cdiscrim_t discrim, struct chanNode *chan) { 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; @@ -3591,11 +3664,38 @@ static void 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, ""); + 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 @@ -3708,7 +3808,7 @@ opserv_alert_check_nick(struct userNode *user, UNUSED_ARG(const char *old_nick)) /* 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; } @@ -3733,7 +3833,7 @@ opserv_staff_alert(struct userNode *user, UNUSED_ARG(struct handle_info *old_han 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); @@ -4019,8 +4119,10 @@ void init_opserv(const char *nick) { OS_LOG = log_register_type("OpServ", "file:opserv.log"); - if (nick) - opserv = AddService(nick, "Oper Services"); + 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)); @@ -4097,6 +4199,9 @@ init_opserv(const char *nick) 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); @@ -4131,7 +4236,7 @@ init_opserv(const char *nick) opserv_db_init(); saxdb_register("OpServ", opserv_saxdb_read, opserv_saxdb_write); if (nick) - service_register(opserv, '?'); + service_register(opserv)->trigger = '?'; reg_exit_func(opserv_db_cleanup); message_register_table(msgtab);