]> jfr.im git - irc/quakenet/newserv.git/blob - glines/glines.c
BUILD: add require-all build mode
[irc/quakenet/newserv.git] / glines / glines.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <assert.h>
4 #include "../lib/irc_string.h"
5 #include "../lib/version.h"
6 #include "../core/schedule.h"
7 #include "../irc/irc.h"
8 #include "../trusts/trusts.h"
9 #include "../control/control.h"
10 #include "glines.h"
11
12 MODULE_VERSION("");
13
14 void _init() {
15 /* If we're connected to IRC, force a disconnect. */
16 if (connected) {
17 irc_send("%s SQ %s 0 :Resync [adding gline support]", mynumeric->content, myserver->content);
18 irc_disconnected(0);
19 }
20
21 registerserverhandler("GL", handleglinemsg, 6);
22 registerhook(HOOK_CORE_STATSREQUEST, handleglinestats);
23 }
24
25 void _fini() {
26 deregisterserverhandler("GL", handleglinemsg);
27 deregisterhook(HOOK_CORE_STATSREQUEST, handleglinestats);
28 }
29
30 int gline_match_nick(gline *gl, nick *np) {
31 if (gl->flags & GLINE_BADCHAN)
32 return 0;
33
34 if (gl->flags & GLINE_REALNAME) {
35 if (gl->user && match(gl->user->content, np->realname->name->content) != 0)
36 return 0;
37
38 return 1;
39 }
40
41 if (gl->nick && match(gl->nick->content, np->nick) != 0)
42 return 0;
43
44 if (gl->user && match(gl->user->content, np->ident) != 0)
45 return 0;
46
47 if (gl->flags & GLINE_IPMASK) {
48 if (!ipmask_check(&gl->ip, &np->ipaddress, gl->bits))
49 return 0;
50 } else {
51 if (gl->host && match(gl->host->content, np->host->name->content) != 0)
52 return 0;
53 }
54
55 return 1;
56 }
57
58 int gline_match_channel(gline *gl, channel *cp) {
59 if (!(gl->flags & GLINE_BADCHAN))
60 return 0;
61
62 if (match(gl->user->content, cp->index->name->content) != 0)
63 return 0;
64
65 return 1;
66 }
67
68 gline *findgline(const char *mask) {
69 gline *gl, *next;
70 gline *globalgline;
71 time_t curtime = time(0);
72
73 globalgline = makegline(mask);
74
75 if (!globalgline)
76 return NULL; /* gline mask couldn't be processed */
77
78 for (gl = glinelist; gl; gl = next) {
79 next = gl->next;
80
81 if (gl->lifetime <= curtime) {
82 removegline(gl);
83 continue;
84 } else if (gl->expire <= curtime) {
85 gl->flags &= ~GLINE_ACTIVE;
86 }
87
88 if (glineequal(globalgline, gl)) {
89 freegline(globalgline);
90 return gl;
91 }
92 }
93
94 freegline(globalgline);
95 return 0;
96 }
97
98 void gline_activate(gline *agline, time_t lastmod, int propagate) {
99 time_t now = getnettime();
100
101 agline->flags |= GLINE_ACTIVE;
102
103 if (lastmod)
104 agline->lastmod = lastmod;
105 else if (now <= agline->lastmod)
106 agline->lastmod++;
107 else
108 agline->lastmod = now;
109
110 if (propagate)
111 gline_propagate(agline);
112 }
113
114 void gline_deactivate(gline *agline, time_t lastmod, int propagate) {
115 time_t now = getnettime();
116
117 if (agline->lastmod == 0) {
118 Error("gline", ERR_WARNING, "Tried to deactivate gline with lastmod == 0: %s", glinetostring(agline));
119 return;
120 }
121
122 agline->flags &= ~GLINE_ACTIVE;
123
124 if (lastmod)
125 agline->lastmod = lastmod;
126 else if (now <= agline->lastmod)
127 agline->lastmod++;
128 else
129 agline->lastmod = now;
130
131 if (propagate)
132 gline_propagate(agline);
133 }
134
135 void gline_destroy(gline *agline, time_t lastmod, int propagate) {
136 time_t now = getnettime();
137
138 agline->flags &= ~GLINE_ACTIVE;
139 agline->flags |= GLINE_DESTROYED;
140
141 if (agline->lastmod == 0) {
142 Error("gline", ERR_WARNING, "Tried to destroy gline with lastmod == 0: %s", glinetostring(agline));
143 return;
144 }
145
146 if (lastmod)
147 agline->lastmod = lastmod;
148 else if (now <= agline->lastmod)
149 agline->lastmod++;
150 else
151 agline->lastmod = now;
152
153 if (propagate)
154 gline_propagate(agline);
155
156 removegline(agline);
157 }
158
159 void gline_propagate(gline *agline) {
160 /* Don't propagate Ulined glines. */
161 if (agline->lastmod == 0) {
162 Error("gline", ERR_WARNING, "Tried to propagate gline with lastmod == 0: %s", glinetostring(agline));
163 return;
164 }
165
166 #if SNIRCD_VERSION >= 140
167 #error TODO: implement 6 parameter glines for snircd >=1.4.0
168 #endif /* SNIRCD_VERSION */
169
170 if (agline->flags & GLINE_DESTROYED) {
171 #if SNIRCD_VERSION < 135
172 controlwall(NO_OPER, NL_GLINES_AUTO, "Tried to destroy G-Line on '%s' however SNIRCD_VERSION is too old.", glinetostring(agline));
173 #else
174 controlwall(NO_OPER, NL_GLINES_AUTO, "Destroying G-Line on '%s' lasting %s with reason '%s', created by: %s",
175 glinetostring(agline), longtoduration(agline->expire-getnettime(), 0),
176 agline->reason ? agline->reason->content : "", agline->creator->content);
177
178 irc_send("%s GL * %%-%s %lu %lu :%s\r\n", mynumeric->content,
179 glinetostring(agline), agline->expire - getnettime(),
180 agline->lastmod, agline->reason ? agline->reason->content : "");
181 #endif /* SNIRCD_VERSION */
182 } else if (agline->flags & GLINE_ACTIVE) {
183 controlwall(NO_OPER, NL_GLINES_AUTO, "Activating G-Line on '%s' lasting %s created by %s with reason '%s'",
184 glinetostring(agline), longtoduration(agline->expire-getnettime(), 0),
185 agline->creator->content, agline->reason ? agline->reason->content : "");
186
187 irc_send("%s GL * +%s %lu %lu :%s\r\n", mynumeric->content,
188 glinetostring(agline), agline->expire - getnettime(),
189 agline->lastmod, agline->reason ? agline->reason->content : "");
190 } else {
191 controlwall(NO_OPER, NL_GLINES_AUTO, "Deactivating G-Line on '%s' lasting %s created by %s with reason '%s'",
192 glinetostring(agline), longtoduration(agline->expire-getnettime(), 0),
193 agline->creator->content, agline->reason ? agline->reason->content : "");
194
195 irc_send("%s GL * -%s %lu %lu :%s\r\n", mynumeric->content,
196 glinetostring(agline), agline->expire - getnettime(),
197 agline->lastmod, agline->reason ? agline->reason->content : "");
198 }
199 }
200
201 /* returns non-zero if the glines are exactly the same */
202 int glineequal(gline *gla, gline *glb) {
203 if ((gla->flags & GLINE_BADCHAN) != (glb->flags & GLINE_BADCHAN))
204 return 0;
205
206 if ((gla->flags & GLINE_REALNAME) != (glb->flags & GLINE_REALNAME))
207 return 0;
208
209 if ((gla->flags & GLINE_IPMASK) != (glb->flags & GLINE_IPMASK))
210 return 0;
211
212 if ((!gla->nick && glb->nick) || (gla->nick && !glb->nick))
213 return 0;
214
215 if (gla->nick && ircd_strcmp(gla->nick->content, glb->nick->content) != 0)
216 return 0;
217
218 if ((!gla->user && glb->user) || (gla->user && !glb->user))
219 return 0;
220
221 if (gla->user && ircd_strcmp(gla->user->content, glb->user->content) != 0)
222 return 0;
223
224 if (gla->flags & GLINE_IPMASK) {
225 if (gla->bits != glb->bits)
226 return 0;
227
228 if (!ipmask_check(&gla->ip, &glb->ip, gla->bits))
229 return 0;
230 } else {
231 if ((!gla->host && glb->host) || (gla->host && !glb->host))
232 return 0;
233
234 if (gla->host && ircd_strcmp(gla->host->content, glb->host->content) != 0)
235 return 0;
236 }
237
238 return 1;
239 }
240
241 /* returns non-zero on match */
242 int gline_match_mask(gline *gla, gline *glb) {
243 if ((gla->flags & GLINE_BADCHAN) != (glb->flags & GLINE_BADCHAN))
244 return 0;
245
246 if ((gla->flags & GLINE_REALNAME) != (glb->flags & GLINE_REALNAME))
247 return 0;
248
249 if (gla->nick && !glb->nick)
250 return 0;
251
252 if (gla->nick && glb->nick && match(gla->nick->content, glb->nick->content) != 0)
253 return 0;
254
255 if (gla->user && !glb->user)
256 return 0;
257
258 if (gla->user && glb->user && match(gla->user->content, glb->user->content) != 0)
259 return 0;
260
261 if (gla->flags & GLINE_IPMASK && glb->flags & GLINE_IPMASK) {
262 if (gla->bits > glb->bits)
263 return 0;
264
265 if (!ipmask_check(&gla->ip, &glb->ip, gla->bits))
266 return 0;
267 } else {
268 if (gla->host && !glb->host)
269 return 0;
270
271 if (gla->host && glb->host && match(gla->host->content, glb->host->content) != 0)
272 return 0;
273 }
274
275 return 1;
276 }
277
278 int isglinesane(gline *gl, const char **hint) {
279 int wildcard, nowildcardcount;
280 char *pos;
281 trusthost *th;
282
283 /* Reason is too short */
284 if (!gl->reason || strlen(gl->reason->content) < MINGLINEREASONLEN) {
285 *hint = "G-Line reason is too short.";
286 return 0;
287 }
288
289 /* Duration is too long */
290 if (gl->expire - getnettime() > MAXGLINEDURATION) {
291 *hint = "G-Line duration is too long.";
292 return 0;
293 }
294
295 /* Hits all realnames. */
296 if ((gl->flags & GLINE_REALNAME) && !gl->user) {
297 *hint = "Matches all realnames.";
298 return 0;
299 }
300
301 /* Hits all local/non-local channels. */
302 if ((gl->flags & GLINE_BADCHAN) && (strcmp(gl->user->content, "&*") == 0 || strcmp(gl->user->content, "#*") == 0)) {
303 *hint = "Matches all local/non-local channels.";
304 return 0;
305 }
306
307 /* Hostmask is too long. */
308 if (!(gl->flags & (GLINE_BADCHAN | GLINE_REALNAME)) &&
309 ((gl->nick && strlen(gl->nick->content) > NICKLEN) ||
310 (gl->user && strlen(gl->user->content) > USERLEN) ||
311 (gl->host && strlen(gl->host->content) > HOSTLEN))) {
312 *hint = "Hostmask components are too long.";
313 return 0;
314 }
315
316 /* Skip the other checks for nickname glines. */
317 if (gl->nick)
318 return 1;
319
320 /* Mask wider than /16 for IPv4 or /32 for IPv6. */
321 if ((gl->flags & GLINE_IPMASK) && gl->bits < (irc_in_addr_is_ipv4(&gl->ip) ? (96 + 16) : 32)) {
322 *hint = "CIDR mask too wide.";
323 return 0;
324 }
325
326 /* Doesn't have at least two non-wildcarded host components. */
327 if (gl->flags & GLINE_HOSTMASK) {
328 nowildcardcount = 0;
329
330 wildcard = 0;
331 pos = gl->host->content;
332
333 for (;;) {
334 switch (*pos) {
335 case '*':
336 /* fall through */
337 case '?':
338 wildcard = 1;
339 break;
340
341 case '.':
342 case '\0':
343 if (!wildcard)
344 nowildcardcount++;
345 else
346 wildcard = 0;
347
348 if (*pos == '\0')
349 pos = NULL; /* Leave the loop. */
350
351 break;
352 }
353
354 if (pos)
355 pos++;
356 else
357 break;
358 }
359
360 if (nowildcardcount < 2) {
361 *hint = "Needs at least 2 non-wildcarded host components.";
362 return 0;
363 }
364 }
365
366 /* Wildcard username match for trusted host with reliable usernames. */
367 if ((gl->flags & GLINE_IPMASK) && (!gl->user || strchr(gl->user->content, '*') || strchr(gl->user->content, '?'))) {
368 th = th_getbyhost(&gl->ip);
369
370 if (th && (th->group->flags & TRUST_RELIABLE_USERNAME)) {
371 *hint = "Wildcard username match for a trusted host that has reliable usernames.";
372 return 0;
373 }
374 }
375
376 return 1;
377 }
378
379 gline *glinedup(gline *gl) {
380 gline *sgl;
381
382 sgl = newgline();
383
384 if (!sgl)
385 return NULL;
386
387 if (gl->nick)
388 sgl->nick = getsstring(gl->nick->content, 512);
389
390 if (gl->user)
391 sgl->user = getsstring(gl->user->content, 512);
392
393 if (gl->host)
394 sgl->host = getsstring(gl->host->content, 512);
395
396 sgl->reason = gl->reason ? getsstring(gl->reason->content, 512) : NULL;
397 sgl->creator = getsstring(gl->creator->content, 512);
398
399 memcpy(&sgl->ip, &gl->ip, sizeof(gl->ip));
400 sgl->bits = gl->bits;
401
402 sgl->expire = gl->expire;
403 sgl->lastmod = gl->lastmod;
404 sgl->lifetime = gl->lifetime;
405
406 sgl->flags = gl->flags;
407
408 return sgl;
409 }