1 /*
2  * Copyright (c) 2016 Atheme Development Group <http://atheme.github.io>
3  * Copyright (c) 2010 William Pitcock, et al.
4  * The rights to this code are as documented under doc/LICENSE.
5  *
6  * Automatically AKILL a list of clients, given their operating parameters.
7  *
8  * Basically this builds a keyword patricia.  O(NICKLEN) lookups at the price
9  * of a longer startup process.
10  *
11  * Configuration.
12  * ==============
13  *
14  * This module adds a new block to the config file:
15  *
16  *     nicklists {
17  *         all = "/home/nenolod/atheme-production.hg4576/etc/nicklists/azn.txt";
18  *         nick = "/home/nenolod/atheme-production.hg4576/etc/nicklists/bottler-nicks.txt";
19  *         user = "/home/nenolod/atheme-production.hg4576/etc/nicklists/bottler-users.txt";
20  *         real = "/home/nenolod/atheme-production.hg4576/etc/nicklists/bottler-gecos.txt";
21  *     };
22  *
23  * You can add multiple all, nick, user and real entries.  The entries will be merged.
24  * I would also like to say: fuck you GNAA, you guys need to go play in fucking traffic.
25  * Thanks for reading my crappy docs, and have a nice day.
26  */
27 
28 #include "atheme-compat.h"
29 
30 DECLARE_MODULE_V1
31 (
32 	"contrib/os_akillnicklist", false, _modinit, _moddeinit,
33 	"0.1",
34 	"Atheme Development Group <http://atheme.github.io>"
35 );
36 
37 static mowgli_patricia_t *akillalllist = NULL;
38 static mowgli_patricia_t *akillnicklist = NULL;
39 static mowgli_patricia_t *akilluserlist = NULL;
40 static mowgli_patricia_t *akillreallist = NULL;
41 
42 static mowgli_list_t conft = { NULL, NULL, 0 };
43 
44 static void
add_contents_of_file_to_list(const char * filename,mowgli_patricia_t * list)45 add_contents_of_file_to_list(const char *filename, mowgli_patricia_t *list)
46 {
47 	char value[BUFSIZE];
48 	FILE *f;
49 
50 	f = fopen(filename, "r");
51 	if (!f)
52 		return;
53 
54 	while (fgets(value, BUFSIZE, f) != NULL)
55 	{
56 		strip(value);
57 
58 		if (!*value)
59 			continue;
60 
61 		mowgli_patricia_add(list, value, (void *) 0x1);
62 	}
63 
64 	fclose(f);
65 }
66 
67 static int
nicklist_config_handler_all(mowgli_config_file_entry_t * entry)68 nicklist_config_handler_all(mowgli_config_file_entry_t *entry)
69 {
70 	add_contents_of_file_to_list(entry->vardata, akillalllist);
71 
72 	return 0;
73 }
74 
75 static int
nicklist_config_handler_nick(mowgli_config_file_entry_t * entry)76 nicklist_config_handler_nick(mowgli_config_file_entry_t *entry)
77 {
78 	add_contents_of_file_to_list(entry->vardata, akillnicklist);
79 
80 	return 0;
81 }
82 
83 static int
nicklist_config_handler_user(mowgli_config_file_entry_t * entry)84 nicklist_config_handler_user(mowgli_config_file_entry_t *entry)
85 {
86 	add_contents_of_file_to_list(entry->vardata, akilluserlist);
87 
88 	return 0;
89 }
90 
91 static int
nicklist_config_handler_real(mowgli_config_file_entry_t * entry)92 nicklist_config_handler_real(mowgli_config_file_entry_t *entry)
93 {
94 	add_contents_of_file_to_list(entry->vardata, akillreallist);
95 
96 	return 0;
97 }
98 
99 static void
aknl_nickhook(hook_user_nick_t * data)100 aknl_nickhook(hook_user_nick_t *data)
101 {
102 	user_t *u;
103 	bool doit = false;
104 	const char *username;
105 	kline_t *k;
106 
107 	return_if_fail(data != NULL);
108 
109 	if (!data->u)
110 		return;
111 
112 	u = data->u;
113 
114 	if (is_internal_client(u))
115 		return;
116 
117 	if (is_autokline_exempt(u))
118 		return;
119 
120 	username = u->user;
121 	if (*username == '~')
122 		username++;
123 
124 	if (mowgli_patricia_retrieve(akillalllist, u->nick) != NULL &&
125 	    mowgli_patricia_retrieve(akillalllist, username) != NULL &&
126 	    mowgli_patricia_retrieve(akillalllist, u->gecos) != NULL)
127 		doit = true;
128 
129 	if (mowgli_patricia_retrieve(akillnicklist, u->nick) != NULL)
130 		doit = true;
131 
132 	if (mowgli_patricia_retrieve(akilluserlist, username) != NULL)
133 		doit = true;
134 
135 	if (mowgli_patricia_retrieve(akillreallist, u->gecos) != NULL)
136 		doit = true;
137 
138 	if (!doit)
139 		return;
140 
141 	if (! (u->flags & UF_KLINESENT)) {
142 		slog(LG_INFO, "AKNL: k-lining \2%s\2!%s@%s [%s] due to appearing to be a possible spambot", u->nick, u->user, u->host, u->gecos);
143 		k = kline_add(u->user, u->host, "Possible spambot", 86400, "*");
144 		u->flags |= UF_KLINESENT;
145 	}
146 }
147 
148 void
_modinit(module_t * m)149 _modinit(module_t *m)
150 {
151 	add_subblock_top_conf("NICKLISTS", &conft);
152 	add_conf_item("ALL", &conft, nicklist_config_handler_all);
153 	add_conf_item("NICK", &conft, nicklist_config_handler_nick);
154 	add_conf_item("USER", &conft, nicklist_config_handler_user);
155 	add_conf_item("REAL", &conft, nicklist_config_handler_real);
156 
157 	akillalllist = mowgli_patricia_create(strcasecanon);
158 	akillnicklist = mowgli_patricia_create(strcasecanon);
159 	akilluserlist = mowgli_patricia_create(strcasecanon);
160 	akillreallist = mowgli_patricia_create(strcasecanon);
161 
162 	hook_add_event("user_add");
163 	hook_add_user_add(aknl_nickhook);
164 	hook_add_event("user_nickchange");
165 	hook_add_user_nickchange(aknl_nickhook);
166 }
167 
168 void
_moddeinit(module_unload_intent_t intent)169 _moddeinit(module_unload_intent_t intent)
170 {
171 	hook_del_user_add(aknl_nickhook);
172 	hook_del_user_nickchange(aknl_nickhook);
173 
174 	del_conf_item("ALL", &conft);
175 	del_conf_item("NICK", &conft);
176 	del_conf_item("USER", &conft);
177 	del_conf_item("REAL", &conft);
178 	del_top_conf("NICKLISTS");
179 }
180