]> jfr.im git - solanum.git/blame - extensions/extb_combi.c
extb_combi: try limiting the number of allowed nodes per depth to 3
[solanum.git] / extensions / extb_combi.c
CommitLineData
4ef511eb
AC
1/*
2 * Extban that combines other extbans.
3 *
4 * Basic example:
5 * $&:~a,m:*!*@gateway/web/cgi-irc*
6 * Which means: match unidentified webchat users.
7 * ("m" is another new extban type, which just does a normal match).
8 *
9 * More complicated example:
10 * $&:~a,|:(m:*!*@gateway/web/foo,m:*!*@gateway/web/bar)
11 * Which means: unidentified and using the foo or bar gateway.
12 *
13 * Rules:
14 *
15 * - Optional pair of parens around data.
16 *
17 * - component bans are separated by commas, but commas between
18 * matching pairs of parens are skipped.
19 *
20 * - Unbalanced parens are an error.
21 *
22 * - Parens, commas and backslashes can be escaped by backslashes.
23 *
24 * - A backslash before any character other than a paren or backslash
25 * is just a backslash (backslash and character are both used).
26 *
27 * - Non-existant extbans are invalid.
28 * This is primarily for consistency with non-combined bans:
29 * the ircd does not let you set +b $f unless the 'f' extban is loaded,
30 * so setting $&:f should be impossible too.
31 *
32 * Issues:
33 * - Backslashes double inside nested bans.
34 * Hopefully acceptable because they should be rare.
35 *
36 * - Is performance good enough?
37 * I suspect it is, but have done no load testing.
38 */
39
40#include "stdinc.h"
41#include "modules.h"
42#include "client.h"
43#include "ircd.h"
44
45// #define DEBUG(s) sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, (s))
46#define DEBUG(s)
d63f3f80 47#define RETURN_INVALID { recursion_depth--; return EXTBAN_INVALID; }
4ef511eb
AC
48
49static int _modinit(void);
50static void _moddeinit(void);
51static int eb_or(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);
52static int eb_and(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);
53static int eb_combi(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type, int is_and);
d63f3f80 54static int recursion_depth = 0;
4ef511eb
AC
55
56DECLARE_MODULE_AV1(extb_extended, _modinit, _moddeinit, NULL, NULL, NULL, "$Revision: 1 $");
57
58static int
59_modinit(void)
60{
61 extban_table['&'] = eb_and;
62 extban_table['|'] = eb_or;
63
64 return 0;
65}
66
67static void
68_moddeinit(void)
69{
70 extban_table['&'] = NULL;
71 extban_table['|'] = NULL;
72}
73
74static int eb_or(const char *data, struct Client *client_p,
75 struct Channel *chptr, long mode_type)
76{
77 return eb_combi(data, client_p, chptr, mode_type, FALSE);
78}
79
80static int eb_and(const char *data, struct Client *client_p,
81 struct Channel *chptr, long mode_type)
82{
83 return eb_combi(data, client_p, chptr, mode_type, TRUE);
84}
85
86static int eb_combi(const char *data, struct Client *client_p,
87 struct Channel *chptr, long mode_type, int is_and)
88{
89 const char *p, *banend;
90 int have_result = FALSE;
2e548a8a 91 int allowed_nodes = 3;
4ef511eb
AC
92 size_t datalen;
93
d63f3f80
AC
94 if (recursion_depth >= 5) {
95 DEBUG("combo invalid: recursion depth too high");
96 return EXTBAN_INVALID;
97 }
98
4ef511eb
AC
99 if (EmptyString(data)) {
100 DEBUG("combo invalid: empty data");
101 return EXTBAN_INVALID;
102 }
103
104 datalen = strlen(data);
105 if (datalen > BANLEN) {
106 /* I'd be sad if this ever happened, but if it does we
107 * could overflow the buffer used below, so...
108 */
109 DEBUG("combo invalid: > BANLEN");
110 return EXTBAN_INVALID;
111 }
112 banend = data + datalen;
113
114 if (data[0] == '(') {
115 p = data + 1;
116 banend--;
117 if (*banend != ')') {
118 DEBUG("combo invalid: starting but no closing paren");
119 return EXTBAN_INVALID;
120 }
121 } else {
122 p = data;
123 }
124
125 /* Empty combibans are invalid. */
126 if (banend == p) {
127 DEBUG("combo invalid: no data (after removing parens)");
128 return EXTBAN_INVALID;
129 }
130
131 /* Implementation note:
132 * I want it to be impossible to set a syntactically invalid combi-ban.
133 * (mismatched parens).
134 * That is: valid_extban should return false for those.
135 * Ideally we do not parse the entire ban when actually matching it:
136 * we can just short-circuit if we already know the ban is valid.
137 * Unfortunately there is no separate hook or mode_type for validation,
138 * so we always keep parsing even after we have determined a result.
139 */
140
d63f3f80
AC
141 recursion_depth++;
142
2e548a8a 143 while (--allowed_nodes) {
4ef511eb
AC
144 int invert = FALSE;
145 char *child_data, child_data_buf[BANLEN];
146 ExtbanFunc f;
147
148 if (*p == '~') {
149 invert = TRUE;
150 p++;
151 if (p == banend) {
152 DEBUG("combo invalid: no data after ~");
d63f3f80 153 RETURN_INVALID;
4ef511eb
AC
154 }
155 }
156
157 f = extban_table[(unsigned char) *p++];
158 if (!f) {
159 DEBUG("combo invalid: non-existant child extban");
d63f3f80 160 RETURN_INVALID;
4ef511eb
AC
161 }
162
163 if (*p == ':') {
164 unsigned int parencount = 0;
165 int escaped = FALSE, done = FALSE;
166 char *o;
167
168 p++;
169
170 /* Possible optimization: we can skip the actual copy if
171 * we already have_result.
172 */
173 o = child_data = child_data_buf;
174 while (TRUE) {
175 if (p == banend) {
176 if (parencount) {
177 DEBUG("combo invalid: EOD while in parens");
d63f3f80 178 RETURN_INVALID;
4ef511eb
AC
179 }
180 break;
181 }
182
183 if (escaped) {
184 if (*p != '(' && *p != ')' && *p != '\\' && *p != ',')
185 *o++ = '\\';
186 *o++ = *p++;
187 escaped = FALSE;
188 } else {
189 switch (*p) {
190 case '\\':
191 escaped = TRUE;
192 break;
193 case '(':
194 parencount++;
195 *o++ = *p;
196 break;
197 case ')':
198 if (!parencount) {
199 DEBUG("combo invalid: negative parencount");
d63f3f80 200 RETURN_INVALID;
4ef511eb
AC
201 }
202 parencount--;
203 *o++ = *p;
204 break;
205 case ',':
206 if (parencount)
207 *o++ = *p;
208 else
209 done = TRUE;
210 break;
211 default:
212 *o++ = *p;
213 break;
214 }
215 if (done)
216 break;
217 p++;
218 }
219 }
220 *o = '\0';
221 } else {
222 child_data = NULL;
223 }
224
225 if (!have_result) {
226 int child_result = f(child_data, client_p, chptr, mode_type);
227
228 if (child_result == EXTBAN_INVALID) {
229 DEBUG("combo invalid: child invalid");
d63f3f80 230 RETURN_INVALID;
4ef511eb
AC
231 }
232
233 /* Convert child_result to a plain boolean result */
234 if (invert)
235 child_result = child_result == EXTBAN_NOMATCH;
236 else
237 child_result = child_result == EXTBAN_MATCH;
238
239 if (is_and ? !child_result : child_result)
240 have_result = TRUE;
241 }
242
243 if (p == banend)
244 break;
245
246 if (*p++ != ',') {
247 DEBUG("combo invalid: no ',' after ban");
d63f3f80 248 RETURN_INVALID;
4ef511eb
AC
249 }
250
251 if (p == banend) {
252 DEBUG("combo invalid: banend after ','");
d63f3f80 253 RETURN_INVALID;
4ef511eb
AC
254 }
255 }
256
d63f3f80
AC
257 recursion_depth--;
258
4ef511eb
AC
259 if (is_and)
260 return have_result ? EXTBAN_NOMATCH : EXTBAN_MATCH;
261 else
262 return have_result ? EXTBAN_MATCH : EXTBAN_NOMATCH;
263}