]> jfr.im git - solanum.git/blame - extensions/extb_combi.c
sslproc: handle ssl_cipher_list being NULL better
[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;
91 size_t datalen;
92
d63f3f80
AC
93 if (recursion_depth >= 5) {
94 DEBUG("combo invalid: recursion depth too high");
95 return EXTBAN_INVALID;
96 }
97
4ef511eb
AC
98 if (EmptyString(data)) {
99 DEBUG("combo invalid: empty data");
100 return EXTBAN_INVALID;
101 }
102
103 datalen = strlen(data);
104 if (datalen > BANLEN) {
105 /* I'd be sad if this ever happened, but if it does we
106 * could overflow the buffer used below, so...
107 */
108 DEBUG("combo invalid: > BANLEN");
109 return EXTBAN_INVALID;
110 }
111 banend = data + datalen;
112
113 if (data[0] == '(') {
114 p = data + 1;
115 banend--;
116 if (*banend != ')') {
117 DEBUG("combo invalid: starting but no closing paren");
118 return EXTBAN_INVALID;
119 }
120 } else {
121 p = data;
122 }
123
124 /* Empty combibans are invalid. */
125 if (banend == p) {
126 DEBUG("combo invalid: no data (after removing parens)");
127 return EXTBAN_INVALID;
128 }
129
130 /* Implementation note:
131 * I want it to be impossible to set a syntactically invalid combi-ban.
132 * (mismatched parens).
133 * That is: valid_extban should return false for those.
134 * Ideally we do not parse the entire ban when actually matching it:
135 * we can just short-circuit if we already know the ban is valid.
136 * Unfortunately there is no separate hook or mode_type for validation,
137 * so we always keep parsing even after we have determined a result.
138 */
139
d63f3f80
AC
140 recursion_depth++;
141
4ef511eb
AC
142 while (TRUE) {
143 int invert = FALSE;
144 char *child_data, child_data_buf[BANLEN];
145 ExtbanFunc f;
146
147 if (*p == '~') {
148 invert = TRUE;
149 p++;
150 if (p == banend) {
151 DEBUG("combo invalid: no data after ~");
d63f3f80 152 RETURN_INVALID;
4ef511eb
AC
153 }
154 }
155
156 f = extban_table[(unsigned char) *p++];
157 if (!f) {
158 DEBUG("combo invalid: non-existant child extban");
d63f3f80 159 RETURN_INVALID;
4ef511eb
AC
160 }
161
162 if (*p == ':') {
163 unsigned int parencount = 0;
164 int escaped = FALSE, done = FALSE;
165 char *o;
166
167 p++;
168
169 /* Possible optimization: we can skip the actual copy if
170 * we already have_result.
171 */
172 o = child_data = child_data_buf;
173 while (TRUE) {
174 if (p == banend) {
175 if (parencount) {
176 DEBUG("combo invalid: EOD while in parens");
d63f3f80 177 RETURN_INVALID;
4ef511eb
AC
178 }
179 break;
180 }
181
182 if (escaped) {
183 if (*p != '(' && *p != ')' && *p != '\\' && *p != ',')
184 *o++ = '\\';
185 *o++ = *p++;
186 escaped = FALSE;
187 } else {
188 switch (*p) {
189 case '\\':
190 escaped = TRUE;
191 break;
192 case '(':
193 parencount++;
194 *o++ = *p;
195 break;
196 case ')':
197 if (!parencount) {
198 DEBUG("combo invalid: negative parencount");
d63f3f80 199 RETURN_INVALID;
4ef511eb
AC
200 }
201 parencount--;
202 *o++ = *p;
203 break;
204 case ',':
205 if (parencount)
206 *o++ = *p;
207 else
208 done = TRUE;
209 break;
210 default:
211 *o++ = *p;
212 break;
213 }
214 if (done)
215 break;
216 p++;
217 }
218 }
219 *o = '\0';
220 } else {
221 child_data = NULL;
222 }
223
224 if (!have_result) {
225 int child_result = f(child_data, client_p, chptr, mode_type);
226
227 if (child_result == EXTBAN_INVALID) {
228 DEBUG("combo invalid: child invalid");
d63f3f80 229 RETURN_INVALID;
4ef511eb
AC
230 }
231
232 /* Convert child_result to a plain boolean result */
233 if (invert)
234 child_result = child_result == EXTBAN_NOMATCH;
235 else
236 child_result = child_result == EXTBAN_MATCH;
237
238 if (is_and ? !child_result : child_result)
239 have_result = TRUE;
240 }
241
242 if (p == banend)
243 break;
244
245 if (*p++ != ',') {
246 DEBUG("combo invalid: no ',' after ban");
d63f3f80 247 RETURN_INVALID;
4ef511eb
AC
248 }
249
250 if (p == banend) {
251 DEBUG("combo invalid: banend after ','");
d63f3f80 252 RETURN_INVALID;
4ef511eb
AC
253 }
254 }
255
d63f3f80
AC
256 recursion_depth--;
257
4ef511eb
AC
258 if (is_and)
259 return have_result ? EXTBAN_NOMATCH : EXTBAN_MATCH;
260 else
261 return have_result ? EXTBAN_MATCH : EXTBAN_NOMATCH;
262}