/*
* IRC - Internet Relay Chat, antirandom.c
* (C) Copyright 2004-2005, Bram Matthys (Syzop) <
Bu forumdaki linkleri ve resimleri görebilmek için en az 25 mesajınız olması gerekir.>
*
* Contains ideas from Keith Dunnett <
Bu forumdaki linkleri ve resimleri görebilmek için en az 25 mesajınız olması gerekir.>
* Most of the detection mechanisms come from SpamAssassin FVGT_Tripwire.
*
* $Id: antirandom.c,v 1.21 2005/07/22 01:04:38 syzop Exp $
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 1, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "proto.h"
#include "channel.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#endif
#include <fcntl.h>
#include "h.h"
#ifdef STRIPBADWORDS
#include "badwords.h"
#endif
#ifdef _WIN32
#include "version.h"
#endif
/**** NOTE: CONFIGURATION HAS MOVED TO THE UNREALIRCD.CONF, SEE README
**** FOR MORE DETAILS.
****/
/* You can change this 'undef' into 'define' if you want to see quite
* a flood for every user that connects (and on-load if cfg.fullstatus_on_load).
* Obviously only recommended for testing, use with care!
*/
#undef DEBUGMODE
/** Change this 'undef' to 'define' to get performance information.
* This really only meant for debugging purposes.
*/
#undef TIMING
ModuleHeader MOD_HEADER(antirandom)
= {
"antirandom",
"v1.1",
"Randomness detector",
"3.2-b8-1",
NULL
};
#ifndef MAX
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#endif
#ifndef _WIN32
typedef struct {
char *regex;
int score;
} ScoreTable;
#endif
typedef struct _dynlist DynList;
struct _dynlist {
DynList *prev, *next;
char *entry;
};
#ifndef _WIN32
/* You can define regexes here.. the format is:
* {"<REGEX>", SCORE},
*/
ScoreTable regex_scores[] = {
/* These have all been moved to internal digit/vowel/consonant checks.
* But I've left the regex ability here, in case someone else uses it.
*/
{NULL, 0}
};
#endif
/* "<char1><char2>" followed by "<rest>" */
static char *triples_txt[] = {
"aj", "fqtvxz",
"aq", "deghjkmnprtxyz",
"av", "bfhjqwxz",
"az", "jwx",
"bd", "bghjkmpqvxz",
"bf", "bcfgjknpqvwxyz",
"bg", "bdfghjkmnqstvxz",
"bh", "bfhjkmnqvwxz",
"bj", "bcdfghjklmpqtvwxyz",
"bk", "dfjkmqrvwxyz",
"bl", "bgpqwxz",
"bm", "bcdflmnqz",
"bn", "bghjlmnpqtvwx",
"bp", "bfgjknqvxz",
"bq", "bcdefghijklmnopqrstvwxyz",
"bt", "dgjkpqtxz",
"bv", "bfghjklnpqsuvwxz",
"bw", "bdfjknpqsuwxyz",
"bx", "abcdfghijklmnopqtuvwxyz",
"bz", "bcdfgjklmnpqrstvwxz",
"cb", "bfghjkpqyz",
"cc", "gjqxz",
"cd", "hjkqvwxz",
"cf", "gjknqvwyz",
"cg", "bdfgjkpqvz",
"cl", "fghjmpqxz",
"cm", "bjkqv",
"cn", "bghjkpqwxz",
"cp", "gjkvxyz",
"cq", "abcdefghijklmnopqsvwxyz",
"cr", "gjqx",
"cs", "gjxz",
"cv", "bdfghjklmnquvwxyz",
"cx", "abdefghjklmnpqrstuvwxyz",
"cy", "jqy",
"cz", "bcdfghjlpqrtvwxz",
"db", "bdgjnpqtxz",
"dc", "gjqxz",
"dd", "gqz",
"df", "bghjknpqvxyz",
"dg", "bfgjqvxz",
"dh", "bfkmnqwxz",
"dj", "bdfghjklnpqrwxz",
"dk", "cdhjkpqrtuvwxz",
"dl", "bfhjknqwxz",
"dm", "bfjnqw",
"dn", "fgjkmnpqvwz",
"dp", "bgjkqvxz",
"dq", "abcefghijkmnopqtvwxyz",
"dr", "bfkqtvx",
"dt", "qtxz",
"dv", "bfghjknqruvwyz",
"dw", "cdfjkmnpqsvwxz",
"dx", "abcdeghjklmnopqrsuvwxyz",
"dy", "jyz",
"dz", "bcdfgjlnpqrstvxz",
"eb", "jqx",
"eg", "cjvxz",
"eh", "hxz",
"ej", "fghjpqtwxyz",
"ek", "jqxz",
"ep", "jvx",
"eq", "bcghijkmotvxyz",
"ev", "bfpq",
"fc", "bdjkmnqvxz",
"fd", "bgjklqsvyz",
"fg", "fgjkmpqtvwxyz",
"fh", "bcfghjkpqvwxz",
"fj", "bcdfghijklmnpqrstvwxyz",
"fk", "bcdfghjkmpqrstvwxz",
"fl", "fjkpqxz",
"fm", "dfhjlmvwxyz",
"fn", "bdfghjklnqrstvwxz",
"fp", "bfjknqtvwxz",
"fq", "abcefghijklmnopqrstvwxyz",
"fr", "nqxz",
"fs", "gjxz",
"ft", "jqx",
"fv", "bcdfhjklmnpqtuvwxyz",
"fw", "bcfgjklmpqstuvwxyz",
"fx", "bcdfghjklmnpqrstvwxyz",
"fy", "ghjpquvxy",
"fz", "abcdfghjklmnpqrtuvwxyz",
"gb", "bcdknpqvwx",
"gc", "gjknpqwxz",
"gd", "cdfghjklmqtvwxz",
"gf", "bfghjkmnpqsvwxyz",
"gg", "jkqvxz",
"gj", "bcdfghjklmnpqrstvwxyz",
"gk", "bcdfgjkmpqtvwxyz",
"gl", "fgjklnpqwxz",
"gm", "dfjkmnqvxz",
"gn", "jkqvxz",
"gp", "bjknpqtwxyz",
"gq", "abcdefghjklmnopqrsvwxyz",
"gr", "jkqt",
"gt", "fjknqvx",
"gu", "qwx",
"gv", "bcdfghjklmpqstvwxyz",
"gw", "bcdfgjknpqtvwxz",
"gx", "abcdefghjklmnopqrstvwxyz",
"gy", "jkqxy",
"gz", "bcdfgjklmnopqrstvxyz",
"hb", "bcdfghjkqstvwxz",
"hc", "cjknqvwxz",
"hd", "fgjnpvz",
"hf", "bfghjkmnpqtvwxyz",
"hg", "bcdfgjknpqsxyz",
"hh", "bcgklmpqrtvwxz",
"hj", "bcdfgjkmpqtvwxyz",
"hk", "bcdgkmpqrstvwxz",
"hl", "jxz",
"hm", "dhjqrvwxz",
"hn", "jrxz",
"hp", "bjkmqvwxyz",
"hq", "abcdefghijklmnopqrstvwyz",
"hr", "cjqx",
"hs", "jqxz",
"hv", "bcdfgjklmnpqstuvwxz",
"hw", "bcfgjklnpqsvwxz",
"hx", "abcdefghijklmnopqrstuvwxyz",
"hz", "bcdfghjklmnpqrstuvwxz",
"ib", "jqx",
"if", "jqvwz",
"ih", "bgjqx",
"ii", "bjqxy",
"ij", "cfgqxy",
"ik", "bcfqx",
"iq", "cdefgjkmnopqtvxyz",
"iu", "hiwxy",
"iv", "cfgmqx",
"iw", "dgjkmnpqtvxz",
"ix", "jkqrxz",
"iy", "bcdfghjklpqtvwx",
"jb", "bcdghjklmnopqrtuvwxyz",
"jc", "cfgjkmnopqvwxy",
"jd", "cdfghjlmnpqrtvwx",
"jf", "abcdfghjlnopqrtuvwxyz",
"jg", "bcdfghijklmnopqstuvwxyz",
"jh", "bcdfghjklmnpqrxyz",
"jj", "bcdfghjklmnopqrstuvwxyz",
"jk", "bcdfghjknqrtwxyz",
"jl", "bcfghjmnpqrstuvwxyz",
"jm", "bcdfghiklmnqrtuvwyz",
"jn", "bcfjlmnpqsuvwxz",
"jp", "bcdfhijkmpqstvwxyz",
"jq", "abcdefghijklmnopqrstuvwxyz",
"jr", "bdfhjklpqrstuvwxyz",
"
js", "bfgjmoqvxyz",
"jt", "bcdfghjlnpqrtvwxz",
"jv", "abcdfghijklpqrstvwxyz",
"jw", "bcdefghijklmpqrstuwxyz",
"jx", "abcdefghijklmnopqrstuvwxyz",
"jy", "bcdefghjkpqtuvwxyz",
"jz", "bcdfghijklmnopqrstuvwxyz",
"kb", "bcdfghjkmqvwxz",
"kc", "cdfgjknpqtwxz",
"kd", "bfghjklmnpqsvwxyz",
"kf", "bdfghjkmnpqsvwxyz",
"kg", "cghjkmnqtvwxyz",
"kh", "cfghjkqx",
"kj", "bcdfghjkmnpqrstwxyz",
"kk", "bcdfgjmpqswxz",
"kl", "cfghlmqstwxz",
"km", "bdfghjknqrstwxyz",
"kn", "bcdfhjklmnqsvwxz",
"kp", "bdfgjkmpqvxyz",
"kq", "abdefghijklmnopqrstvwxyz",
"kr", "bcdfghjmqrvwx",
"ks", "jqx",
"kt", "cdfjklqvx",
"ku", "qux",
"kv", "bcfghjklnpqrstvxyz",
"kw", "bcdfgjklmnpqsvwxz",
"kx", "abcdefghjklmnopqrstuvwxyz",
"ky", "vxy",
"kz", "bcdefghjklmnpqrstuvwxyz",
"lb", "cdgkqtvxz",
"lc", "bqx",
"lg", "cdfgpqvxz",
"lh", "cfghkmnpqrtvx",
"lk", "qxz",
"ln", "cfjqxz",
"lp", "jkqxz",
"lq", "bcdefhijklmopqrstvwxyz",
"lr", "dfgjklmpqrtvwx",
"lv", "bcfhjklmpwxz",
"lw", "bcdfgjknqxz",
"lx", "bcdfghjklmnpqrtuwz",
"lz", "cdjptvxz",
"mb", "qxz",
"md", "hjkpvz",
"mf", "fkpqvwxz",
"mg", "cfgjnpqsvwxz",
"mh", "bchjkmnqvx",
"mj", "bcdfghjknpqrstvwxyz",
"mk", "bcfgklmnpqrvwxz",
"ml", "jkqz",
"mm", "qvz",
"mn", "fhjkqxz",
"mq", "bdefhjklmnopqtwxyz",
"mr", "jklqvwz",
"mt", "jkq",
"mv", "bcfghjklmnqtvwxz",
"mw", "bcdfgjklnpqsuvwxyz",
"mx", "abcefghijklmnopqrstvwxyz",
"mz", "bcdfghjkmnpqrstvwxz",
"nb", "hkmnqxz",
"nf", "bghqvxz",
"nh", "fhjkmqtvxz",
"nk", "qxz",
"nl", "bghjknqvwxz",
"nm", "dfghjkqtvwxz",
"np", "bdjmqwxz",
"nq", "abcdfghjklmnopqrtvwxyz",
"nr", "bfjkqstvx",
"nv", "bcdfgjkmnqswxz",
"nw", "dgjpqvxz",
"nx", "abfghjknopuyz",
"nz", "cfqrxz",
"oc", "fjvw",
"og", "qxz",
"oh", "fqxz",
"oj", "bfhjmqrswxyz",
"ok", "qxz",
"oq", "bcdefghijklmnopqrstvwxyz",
"ov", "bfhjqwx",
"oy", "qxy",
"oz", "fjpqtvx",
"pb", "fghjknpqvwz",
"pc", "gjq",
"pd", "bgjkvwxz",
"pf", "hjkmqtvwyz",
"pg", "bdfghjkmqsvwxyz",
"ph", "kqvx",
"pk", "bcdfhjklmpqrvx",
"pl", "ghkqvwx",
"pm", "bfhjlmnqvwyz",
"pn", "fjklmnqrtvwz",
"pp", "gqwxz",
"pq", "abcdefghijklmnopqstvwxyz",
"
pr", "hjkqrwx",
"pt", "jqxz",
"pv", "bdfghjklquvwxyz",
"pw", "fjkmnpqsuvwxz",
"px", "abcdefghijklmnopqrstuvwxyz",
"pz", "bdefghjklmnpqrstuvwxyz",
"qa", "ceghkopqxy",
"qb", "bcdfghjklmnqrstuvwxyz",
"qc", "abcdfghijklmnopqrstuvwxyz",
"qd", "defghijklmpqrstuvwxyz",
"qe", "abceghjkmopquwxyz",
"qf", "abdfghijklmnopqrstuvwxyz",
"qg", "abcdefghijklmnopqrtuvwxz",
"qh", "abcdefghijklmnopqrstuvwxyz",
"qi", "efgijkmpwx",
"qj", "abcdefghijklmnopqrstuvwxyz",
"qk", "abcdfghijklmnopqrsuvwxyz",
"ql", "abcefghjklmnopqrtuvwxyz",
"qm", "bdehijklmnoqrtuvxyz",
"qn", "bcdefghijklmnoqrtuvwxyz",
"qo", "abcdefgijkloqstuvwxyz",
"qp", "abcdefghijkmnopqrsuvwxyz",
"qq", "bcdefghijklmnopstwxyz",
"qr", "bdefghijklmnoqruvwxyz",
"qs", "bcdefgijknqruvwxz",
"qt", "befghjklmnpqtuvwxz",
"qu", "cfgjkpwz",
"qv", "abdefghjklmnopqrtuvwxyz",
"qw", "bcdfghijkmnopqrstuvwxyz",
"qx", "abcdefghijklmnopqrstuvwxyz",
"qy", "abcdefghjklmnopqrstuvwxyz",
"qz", "abcdefghijklmnopqrstuvwxyz",
"rb", "fxz",
"rg", "jvxz",
"rh", "hjkqrxz",
"rj", "bdfghjklmpqrstvwxz",
"rk", "qxz",
"rl", "jnq",
"rp", "jxz",
"rq", "bcdefghijklmnopqrtvwxy",
"rr", "jpqxz",
"rv", "bcdfghjmpqrvwxz",
"rw", "bfgjklqsvxz",
"rx", "bcdfgjkmnopqrtuvwxz",
"rz", "djpqvxz",
"sb", "kpqtvxz",
"sd", "jqxz",
"sf", "bghjkpqw",
"sg", "cgjkqvwxz",
"sj", "bfghjkmnpqrstvwxz",
"sk", "qxz",
"sl", "gjkqwxz",
"sm", "fkqwxz",
"sn", "dhjknqvwxz",
"sq", "bfghjkmopstvwxz",
"sr", "jklqrwxz",
"sv", "bfhjklmnqtwxyz",
"sw", "jkpqvwxz",
"sx", "bcdefghjklmnopqrtuvwxyz",
"sy", "qxy",
"sz", "bdfgjpqsvxz",
"tb", "cghjkmnpqtvwx",
"tc", "jnqvx",
"td", "bfgjkpqtvxz",
"tf", "ghjkqvwyz",
"tg", "bdfghjkmpqsx",
"tj", "bdfhjklmnpqstvwxyz",
"tk", "bcdfghjklmpqvwxz",
"tl", "jkqwxz",
"tm", "bknqtwxz",
"tn", "fhjkmqvwxz",
"tp", "bjpqvwxz",
"tq", "abdefhijklmnopqrstvwxyz",
"tr", "gjqvx",
"tv", "bcfghjknpquvwxz",
"tw", "bcdfjknqvz",
"tx", "bcdefghjklmnopqrsuvwxz",
"tz", "jqxz",
"uc", "fjmvx",
"uf", "jpqvx",
"ug", "qvx",
"uh", "bcgjkpvxz",
"uj", "wbfghklmqvwx",
"uk", "fgqxz",
"uq", "bcdfghijklmnopqrtwxyz",
"uu", "fijkqvwyz",
"uv", "bcdfghjkmpqtwxz",
"uw", "dgjnquvxyz",
"ux", "jqxz",
"uy", "jqxyz",
"uz", "fgkpqrx",
"
vb", "bcdfhijklmpqrtuvxyz",
"vc", "bgjklnpqtvwxyz",
"vd", "bdghjklnqvwxyz",
"vf", "bfghijklmnpqtuvxz",
"vg", "bcdgjkmnpqtuvwxyz",
"vh", "bcghijklmnpqrtuvwxyz",
"vj", "abcdfghijklmnpqrstuvwxyz",
"vk", "bcdefgjklmnpqruvwxyz",
"vl", "hjkmpqrvwxz",
"vm", "bfghjknpquvxyz",
"vn", "bdhjkmnpqrtuvwxz",
"vp", "bcdeghjkmopqtuvwyz",
"vq", "abcdefghijklmnopqrstvwxyz",
"vr", "fghjknqrtvwxz",
"vs", "dfgjmqz",
"vt", "bdfgjklmnqtx",
"vu", "afhjquwxy",
"vv", "cdfghjkmnpqrtuwxz",
"vw", "abcdefghijklmnopqrtuvwxyz",
"vx", "abcefghjklmnopqrstuvxyz",
"vy", "oqx",
"vz", "abcdefgjklmpqrstvwxyz",
"wb", "bdfghjpqtvxz",
"wc", "bdfgjkmnqvwx",
"wd", "dfjpqvxz",
"wf", "cdghjkmqvwxyz",
"wg", "bcdfgjknpqtvwxyz",
"wh", "cdghjklpqvwxz",
"wj", "bfghijklmnpqrstvwxyz",
"wk", "cdfgjkpqtuvxz",
"wl", "jqvxz",
"wm", "dghjlnqtvwxz",
"wp", "dfgjkpqtvwxz",
"wq", "abcdefghijklmnopqrstvwxyz",
"wr", "cfghjlmpqwx",
"wt", "bdgjlmnpqtvx",
"wu", "aikoquvwy",
"wv", "bcdfghjklmnpqrtuvwxyz",
"ww", "bcdgkpqstuvxyz",
"wx", "abcdefghijklmnopqrstuvwxz",
"wy", "jquwxy",
"wz", "bcdfghjkmnopqrstuvwxz",
"xa", "ajoqy",
"xb", "bcdfghjkmnpqsvwxz",
"xc", "bcdgjkmnqsvwxz",
"xd", "bcdfghjklnpqstuvwxyz",
"xf", "bcdfghjkmnpqtvwxyz",
"xg", "bcdfghjkmnpqstvwxyz",
"xh", "cdfghjkmnpqrstvwxz",
"xi", "jkqy",
"xj", "abcdefghijklmnopqrstvwxyz",
"xk", "abcdfghjkmnopqrstuvwxyz",
"xl", "bcdfghjklmnpqrvwxz",
"xm", "bcdfghjknpqvwxz",
"xn", "bcdfghjklmnpqrvwxyz",
"xp", "bcfjknpqvxz",
"xq", "abcdefghijklmnopqrstvwxyz",
"xr", "bcdfghjklnpqrsvwyz",
"xs", "bdfgjmnqrsvxz",
"xt", "jkpqvwxz",
"xu", "fhjkquwx",
"xv", "bcdefghjklmnpqrsuvwxyz",
"xw", "bcdfghjklmnpqrtuvwxyz",
"xx", "bcdefghjkmnpqrstuwyz",
"xy", "jxy",
"xz", "abcdefghjklmnpqrstuvwxyz",
"yb", "cfghjmpqtvwxz",
"yc", "bdfgjmpqsvwx",
"yd", "chjkpqvwx",
"yf", "bcdghjmnpqsvwx",
"yg", "cfjkpqtxz",
"yh", "bcdfghjkpqx",
"yi", "hjqwxy",
"yj", "bcdfghjklmnpqrstvwxyz",
"yk", "bcdfgpqvwxz",
"ym", "dfgjqvxz",
"yp", "bcdfgjkmqxz",
"yq", "abcdefghijklmnopqrstvwxyz",
"yr", "jqx",
"yt", "bcfgjnpqx",
"yv", "bcdfghjlmnpqstvwxz",
"yw", "bfgjklmnpqstuvwxz",
"yx", "bcdfghjknpqrstuvwxz",
"yy", "bcdfghjklpqrstvwxz",
"yz", "bcdfjklmnpqtvwx",
"zb", "dfgjklmnpqstvwxz",
"zc", "bcdfgjmnpqstvwxy",
"zd", "bcdfghjklmnpqstvwxy",
"zf", "bcdfghijkmnopqrstvwxyz",
"zg", "bcdfgjkmnpqtvwxyz",
"zh", "bcfghjlpqstvwxz",
"zj", "abcdfghjklmnpqrstuvwxyz",
"zk", "bcdfghjklmpqstvwxz",
"zl", "bcdfghjlnpqrstvwxz",
"zm", "bdfghjklmpqstvwxyz",
"zn", "bcdfghjlmnpqrstuvwxz",
"zp", "bcdfhjklmnpqstvwxz",
"zq", "abcdefghijklmnopqrstvwxyz",
"zr", "bcfghjklmnpqrstvwxyz",
"zs", "bdfgjmnqrsuwxyz",
"zt", "bcdfgjkmnpqtuvwxz",
"zu", "ajqx",
"zv", "bcdfghjklmnpqrstuvwxyz",
"zw", "bcdfghjklmnpqrstuvwxyz",
"zx", "abcdefghijklmnopqrstuvwxyz",
"zy", "fxy",
"zz", "cdfhjnpqrvx",
NULL, NULL
};
#ifndef _WIN32
/* Used for parsed sregexes */
typedef struct _regexlist RegexList;
struct _regexlist {
RegexList *next;
regex_t regex;
#ifdef DEBUGMODE
char *regextxt;
#endif
int score;
};
#endif
/* Used for parsed triples: */
#define TRIPLES_REST_SIZE 32
typedef struct _triples Triples;
struct _triples {
Triples *next;
char two[3];
char rest[TRIPLES_REST_SIZE];
};
#ifndef _WIN32
RegexList *sregexes = NULL;
#endif
Triples *triples = NULL;
struct {
int threshold;
int ban_action;
int ban_reason;
int ban_time;
} req;
struct {
int threshold;
int ban_action;
char *ban_reason;
long ban_time;
int show_failedconnects;
int fullstatus_on_load;
DynList *except_hosts;
} cfg;
/* Forward declarations */
static int init_stuff(void);
static int init_sregexes(void);
static int init_triples(void);
static void free_stuff(void);
static void free_config(void);
DLLFUNC int antirandom_config_test(ConfigFile *, ConfigEntry *, int, int *);
DLLFUNC int antirandom_config_run(ConfigFile *, ConfigEntry *, int);
DLLFUNC int antirandom_config_posttest(int *);
static int is_except_host(aClient *sptr);
DLLFUNC int antirandom_preconnect(aClient *sptr);
DLLFUNC int MOD_TEST(antirandom)(ModuleInfo *modinfo)
{
memset(&req, 0, sizeof(req));
memset(&cfg, 0, sizeof(cfg));
HookAddEx(modinfo->handle, HOOKTYPE_CONFIGTEST, antirandom_config_test);
HookAddEx(modinfo->handle, HOOKTYPE_CONFIGPOSTTEST, antirandom_config_posttest);
return MOD_SUCCESS;
}
DLLFUNC int MOD_INIT(antirandom)(ModuleInfo *modinfo)
{
if (!init_stuff())
{
config_error("antirandom: loading aborted");
free_stuff();
return MOD_FAILED;
}
cfg.fullstatus_on_load = 1; /* default */
HookAddEx(modinfo->handle, HOOKTYPE_PRE_LOCAL_CONNECT, antirandom_preconnect);
HookAddEx(modinfo->handle, HOOKTYPE_CONFIGRUN, antirandom_config_run);
return MOD_SUCCESS;
}
void check_all_users(void);
DLLFUNC int MOD_LOAD(antirandom)(int module_load)
{
if (cfg.fullstatus_on_load)
check_all_users();
return MOD_SUCCESS;
}
DLLFUNC int MOD_UNLOAD(antirandom)(int module_unload)
{
free_stuff();
free_config();
return MOD_SUCCESS;
}
static void free_config(void)
{
DynList *d, *d_next;
if (cfg.ban_reason)
MyFree(cfg.ban_reason);
for (d=cfg.except_hosts; d; d=d_next)
{
d_next = d->next;
MyFree(d->entry);
MyFree(d);
}
memset(&cfg, 0, sizeof(cfg)); /* needed! */
}
DLLFUNC int antirandom_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{
int errors = 0;
ConfigEntry *cep;
if (type != CONFIG_SET)
return 0;
/* We are only interrested in set::antirandom... */
if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "antirandom"))
return 0;
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{
if (!cep->ce_varname)
{
config_error("%s:%i: blank set::antirandom item",
cep->ce_fileptr->cf_filename, cep->ce_varlinenum);
errors++;
} else
if (!strcmp(cep->ce_varname, "except-hosts"))
{
} else
if (!cep->ce_vardata)
{
config_error("%s:%i: set::antirandom::%s with no value",
cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
errors++;
} else
if (!strcmp(cep->ce_varname, "threshold"))
{
req.threshold = 1;
} else
if (!strcmp(cep->ce_varname, "ban-action"))
{
if (!banact_stringtoval(cep->ce_vardata))
{
config_error("%s:%i: set::antirandom::ban-action: unknown action '%s'",
cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_vardata);
errors++;
} else
req.ban_action = 1;
} else
if (!strcmp(cep->ce_varname, "ban-reason"))
{
req.ban_reason = 1;
} else
if (!strcmp(cep->ce_varname, "ban-time"))
{
req.ban_time = 1;
} else
if (!strcmp(cep->ce_varname, "fullstatus-on-load"))
{
} else
if (!strcmp(cep->ce_varname, "show-failedconnects"))
{
} else
{
config_error("%s:%i: unknown directive set::antirandom::%s",
cep->ce_fileptr->cf_filename, cep->ce_varlinenum, cep->ce_varname);
errors++;
}
}
*errs = errors;
return errors ? -1 : 1;
}
DLLFUNC int antirandom_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{
ConfigEntry *cep, *cep2;
DynList *d;
if (type != CONFIG_SET)
return 0;
/* We are only interrested in set::antirandom... */
if (!ce || !ce->ce_varname || strcmp(ce->ce_varname, "antirandom"))
return 0;
for (cep = ce->ce_entries; cep; cep = cep->ce_next)
{
if (!strcmp(cep->ce_varname, "except-hosts"))
{
for (cep2 = cep->ce_entries; cep2; cep2 = cep2->ce_next)
{
d = MyMallocEx(sizeof(DynList));
d->entry = strdup(cep2->ce_varname);
AddListItem(d, cfg.except_hosts);
}
} else
if (!strcmp(cep->ce_varname, "threshold"))
{
cfg.threshold = atoi(cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "ban-action"))
{
cfg.ban_action = banact_stringtoval(cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "ban-reason"))
{
if (cfg.ban_reason)
MyFree(cfg.ban_reason);
cfg.ban_reason = strdup(cep->ce_vardata);
} else
if (!strcmp(cep->ce_varname, "ban-time"))
{
cfg.ban_time = config_checkval(cep->ce_vardata, CFG_TIME);
} else
if (!strcmp(cep->ce_varname, "show-failedconnects"))
{
cfg.show_failedconnects = config_checkval(cep->ce_vardata, CFG_YESNO);
} else
if (!strcmp(cep->ce_varname, "fullstatus-on-load"))
{
cfg.fullstatus_on_load = config_checkval(cep->ce_vardata, CFG_YESNO);
}
}
return 1;
}
DLLFUNC int antirandom_config_posttest(int *errs)
{
int errors = 0;
if (!req.threshold) { config_error("set::antirandom::threshold missing"); errors++; }
if (!req.ban_action) { config_error("set::antirandom::ban-action missing"); errors++; }
if (!req.ban_time) { config_error("set::antirandom::ban-time missing"); errors++; }
if (!req.ban_reason) { config_error("set::antirandom::ban-reason missing"); errors++; }
*errs = errors;
return errors ? -1 : 1;
}
static int init_stuff(void)
{
if (!init_sregexes() || !init_triples())
return 0;
return 1;
}
/** Initializes the sregexes regex list */
static int init_sregexes(void)
{
#ifndef _WIN32
ScoreTable *s = ®ex_scores[0];
RegexList *e, *last=NULL;
int cnt=0, n;
char *res;
for (s=®ex_scores[0]; s->regex; s++)
{
cnt++;
e = MyMallocEx(sizeof(RegexList));
/* validate regex */
res = unreal_checkregex(s->regex, 0, 1);
if (res)
{
config_error("init_sregexes: sregexes_txt contains invalid regex (nr %d): %s",
cnt, res);
return 0;
}
/* parse regex here (should go fine, checked above) */
n = regcomp(&e->regex, s->regex, REG_ICASE|REG_EXTENDED);
if (n)
{
/* should never happen (yes I'm too lazy to get the errormsg) */
config_error("init_sregexes: weird regcomp() failure: item=%d, errorcode=%d, aborting...",
cnt, n);
return 0;
}
#ifdef DEBUGMODE
e->regextxt = strdup(s->regex);
#endif
e->score = s->score;
/* Append at end of list (to keep it in order, not importent yet, but..) */
if (last)
last->next = e;
else
sregexes = e; /*(head)*/
last = e;
}
#endif
return 1;
}
/** Initializes the triples list. */
static int init_triples(void)
{
char **s;
Triples *e, *last=NULL;
int cnt=0;
for (s=triples_txt; *s; *s++)
{
cnt++;
e = MyMallocEx(sizeof(Triples));
if (strlen(*s) > 2)
{
config_error("init_triples: error parsing triples_txt, cnt=%d, item='%s' (length>2)",
cnt, *s);
return 0;
}
strcpy(e->two, *s); /* (SAFE) */
*s++;
if (!*s)
{
config_error("init_triples: error parsing triples_txt, cnt=%d, got NULL expected param",
cnt);
return 0;
}
if (strlen(*s) > TRIPLES_REST_SIZE-1)
{
config_error("init_triples: error parsing triples_txt, cnt=%d, item='%s' (length>%d)",
cnt, *s, TRIPLES_REST_SIZE-1);
return 0;
}
strcpy(e->rest, *s); /* (SAFE) */
/* Append at end of list (to keep it in order, not importent yet, but..) */
if (last)
last->next = e;
else
triples = e; /*(head)*/
last = e;
}
return 1;
}
/** Run the actual tests over this string.
* There are 3 tests:
* - weird chars (not used)
* - sregexes (easy stuff)
* - triples (three-letter combinations)
*/
static int internal_getscore(char *str)
{
#ifndef _WIN32
RegexList *r;
#endif
Triples *t;
register char *s;
int score = 0;
int highest_vowels=0, highest_consonants=0, highest_digits=0;
int vowels=0, consonants=0, digits=0;
#ifndef _WIN32
for (r=sregexes; r; r=r->next)
{
if (!regexec(&r->regex, str, 0, NULL, 0))
{
score += r->score; /* note: in the draft this returns the # of occurances, not 1 */
#ifdef DEBUGMODE
sendto_realops("score@'%s': MATCH for '%s'", str, r->regextxt);
#endif
}
}
#endif
/* Fast digit/consonant/vowel checks... */
for (s=str; *s; s++)
{
if ((*s >= '0') && (*s <= '9'))
digits++;
else {
highest_digits = MAX(highest_digits, digits);
digits = 0;
}
if (strchr("bcdfghjklmnpqrstvwxz", *s))
consonants++;
else {
highest_consonants = MAX(highest_consonants, consonants);
consonants = 0;
}
if (strchr("aeiou", *s))
vowels++;
else {
highest_vowels = MAX(highest_vowels, vowels);
vowels = 0;
}
}
digits = MAX(highest_digits, digits);
consonants = MAX(highest_consonants, consonants);
vowels = MAX(highest_vowels, vowels);
if (digits >= 5)
{
score += 5 + (digits - 5);
#ifdef DEBUGMODE
sendto_realops("score@'%s': MATCH for digits check", str);
#endif
}
if (vowels >= 4)
{
score += 4 + (vowels - 4);
#ifdef DEBUGMODE
sendto_realops("score@'%s': MATCH for vowels check", str);
#endif
}
if (consonants >= 4)
{
score += 4 + (consonants - 4);
#ifdef DEBUGMODE
sendto_realops("score@'%s': MATCH for consonants check", str);
#endif
}
for (t=triples; t; t=t->next)
{
for (s=str; *s; s++)
if ((t->two[0] == s[0]) && (t->two[1] == s[1]) && s[2] && strchr(t->rest, s[2]))
{
score++; /* OK */
#ifdef DEBUGMODE
sendto_realops("score@'%s': MATCH for '%s[%s]' %c/%c/%c", str, t->two, t->rest,
s[0], s[1], s[2]);
#endif
}
}
return score;
}
/** Returns "spam score".
* @note a user is expected, do not call for anything else (eg: servers)
*/
static int get_spam_score(aClient *sptr)
{
char *nick = sptr->name;
char *user = sptr->user->username;
char *gecos = sptr->info;
int nscore, uscore, gscore, score;
#ifdef TIMING
struct timeval tv_alpha, tv_beta;
gettimeofday(&tv_alpha, NULL);
#endif
nscore = internal_getscore(nick);
uscore = internal_getscore(user);
gscore = internal_getscore(gecos);
score = nscore + uscore + gscore;
#ifdef TIMING
gettimeofday(&tv_beta, NULL);
ircd_log(LOG_ERROR, "AntiRandom Timing: %ld microseconds",
((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec));
#endif
#ifdef DEBUGMODE
sendto_realops("got score: %d/%d/%d = %d",
nscore, uscore, gscore, score);
#endif
return score;
}
void check_all_users(void)
{
aClient *acptr;
int i, matches=0, score;
for (i = LastSlot; i >= 0; i--)
{
if ((acptr = local[i]) && IsPerson(acptr))
{
if (is_except_host(acptr))
continue;
score = get_spam_score(acptr);
if (score > cfg.threshold)
{
if (!matches)
sendto_realops("[antirandom] Full status report follows:");
sendto_realops("%d points: %s!%s@%s:%s",
score, acptr->name, acptr->user->username, acptr->user->realhost, acptr->info);
matches++;
}
}
}
if (matches)
sendto_realops("[antirandom] %d match%s", matches, matches == 1 ? "" : "es");
}
DLLFUNC int antirandom_preconnect(aClient *sptr)
{
int score;
if (!is_except_host(sptr))
{
score = get_spam_score(sptr);
if (score > cfg.threshold)
{
if (cfg.show_failedconnects)
sendto_realops("[antirandom] denied access to user with score %d: %s!%s@%s:%s",
score, sptr->name, sptr->user->username, sptr->user->realhost, sptr->info);
return place_host_ban(sptr, cfg.ban_action, cfg.ban_reason, cfg.ban_time);
}
}
return 0;
}
static void free_stuff(void)
{
#ifndef _WIN32
RegexList *r, *r_next;
#endif
Triples *t, *t_next;
#ifndef _WIN32
for (r=sregexes; r; r=r_next)
{
r_next = r->next;
regfree(&r->regex);
#ifdef DEBUGMODE
if (r->regextxt)
MyFree(r->regextxt);
#endif
MyFree(r);
}
sregexes = NULL;
#endif
for (t=triples; t; t=t_next)
{
t_next = t->next;
MyFree(t);
}
triples = NULL;
}
/** Finds out if the host is on the except list. 1 if yes, 0 if no */
static int is_except_host(aClient *sptr)
{
char *host, *ip;
DynList *d;
#ifdef TIMING
struct timeval tv_alpha, tv_beta;
gettimeofday(&tv_alpha, NULL);
#endif
host = sptr->user ? sptr->user->realhost : "???";
ip = GetIP(sptr) ? GetIP(sptr) : "???";
for (d=cfg.except_hosts; d; d=d->next)
if (!match(d->entry, host) || !match(d->entry, ip))
return 1;
#ifdef TIMING
gettimeofday(&tv_beta, NULL);
ircd_log(LOG_ERROR, "AntiRandom is_except_host (full search): %ld microseconds",
((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec));
#endif
return 0;
}