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