]> jfr.im git - irc/quakenet/newserv.git/blob - bans/bans.c
Add MODULE_VERSION to all default modules.
[irc/quakenet/newserv.git] / bans / bans.c
1 /* channelbans.c */
2
3 #include <assert.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include "bans.h"
7
8 #include "../nick/nick.h"
9 #include "../lib/irc_string.h"
10 #include "../irc/irc_config.h"
11 #include "../core/nsmalloc.h"
12 #include "../lib/flags.h"
13 #include "../lib/version.h"
14
15 MODULE_VERSION("")
16
17 #define ALLOCUNIT 100
18
19 chanban *freebans;
20
21 void _init() {
22 freebans=NULL;
23 }
24
25 void _fini() {
26 nsfreeall(POOL_BANS);
27 }
28
29 chanban *getchanban() {
30 int i;
31 chanban *cbp;
32
33 if (freebans==NULL) {
34 freebans=(chanban *)nsmalloc(POOL_BANS, ALLOCUNIT*sizeof(chanban));
35 for (i=0;i<ALLOCUNIT-1;i++) {
36 freebans[i].next=(struct chanban *)&(freebans[i+1]);
37 }
38 freebans[ALLOCUNIT-1].next=NULL;
39 }
40
41 cbp=freebans;
42 freebans=cbp->next;
43
44 cbp->nick=NULL;
45 cbp->user=NULL;
46 cbp->host=NULL;
47
48 return cbp;
49 }
50
51 void freechanban(chanban *cbp) {
52 cbp->next=(struct chanban *)freebans;
53
54 if (cbp->nick)
55 freesstring(cbp->nick);
56 if (cbp->user)
57 freesstring(cbp->user);
58 if (cbp->host)
59 freesstring(cbp->host);
60
61 freebans=cbp;
62 }
63
64 /*
65 * makeban:
66 * Converts the specified ban into a ban structure
67 */
68
69 chanban *makeban(const char *mask) {
70 int len;
71 int foundat=-1,foundbang=-1;
72 int dotcount=0;
73 int notip=0;
74 int foundwild=0;
75 int foundslash=0;
76 int i;
77 int checklen;
78 chanban *cbp;
79 char tmpbuf[512];
80
81 cbp=getchanban();
82 len=strlen(mask);
83
84 cbp->flags=0;
85
86 /* Let's catch a silly case first */
87 if (!strcmp(mask,"*")) {
88 cbp->flags=CHANBAN_HOSTANY | CHANBAN_USERANY | CHANBAN_NICKANY;
89 cbp->nick=NULL;
90 cbp->user=NULL;
91 cbp->host=NULL;
92 cbp->timeset=time(NULL);
93 return cbp;
94 }
95
96 for (i=(len-1);i>=0;i--) {
97 if (mask[i]=='@') {
98 /* Got @ sign: everything after here is host */
99 if ((len-i)-1 > HOSTLEN) {
100 /* This is too long, we need to truncate it */
101 strncpy(tmpbuf,&mask[len-HOSTLEN],HOSTLEN);
102 tmpbuf[HOSTLEN]='\0';
103 tmpbuf[0]='*';
104 cbp->host=getsstring(tmpbuf,HOSTLEN);
105 cbp->flags |= CHANBAN_HOSTMASK;
106 } else if (i==(len-1)) {
107 /* Ban ending with @, just mark it invalid */
108 /* Note that "moo@*" overrides "moo@", so mark it as having a host too */
109 cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTNULL);
110 cbp->host=NULL;
111 } else if (i==(len-2) && mask[i+1]=='*') {
112 /* Special case: "@*" */
113 cbp->flags |= CHANBAN_HOSTANY;
114 cbp->host=NULL;
115 } else if (foundslash) {
116 /* If we found a slash (/), this can only be a CIDR ban */
117 /* However, it might be broken, so we need to retain the exact string
118 * to track it accurately */
119 cbp->host=getsstring(&mask[i+1],HOSTLEN);
120 if ((notip || dotcount!=3) && !foundwild) {
121 cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTEXACT);
122 } else if (foundwild) {
123 cbp->flags |= (CHANBAN_INVALID | CHANBAN_HOSTMASK);
124 } else {
125 unsigned int a,b,c,d,l;
126 /* CIDR bans have to match this pattern. */
127 if ((sscanf(&mask[i+1], "%u.%u.%u.%u/%u",&a,&b,&c,&d,&l) != 5) ||
128 (a>255) || (b>255) || (c>255) || (d>255) || (l>32) ) {
129 cbp->flags |= (CHANBAN_HOSTEXACT | CHANBAN_INVALID);
130 } else {
131 /* Save the IP address and mask for later */
132 cbp->ipaddr=(a<<24)|(b<<16)|(c<<8)|d;
133 cbp->mask=0xffffffff;
134 if (l==0) {
135 cbp->mask=0;
136 } else if (l<32) {
137 cbp->mask<<=(32-l);
138 }
139 /* pre-AND the IP with the mask here. */
140 cbp->ipaddr &= cbp->mask;
141 cbp->flags |= (CHANBAN_HOSTEXACT | CHANBAN_CIDR);
142 }
143 }
144 } else {
145 /* We have some string with between 1 and HOSTLEN characters.. */
146 cbp->host=getsstring(&mask[i+1],HOSTLEN);
147 if (foundwild) {
148 /* We check all characters after the last wildcard (if any).. if they match
149 * the corresponding bits of the hidden host string we mark it accordingly */
150 if (!(checklen=len-foundwild-1)) { /* Ban ends in *, mark it anyway.. */
151 cbp->flags |= CHANBAN_HIDDENHOST;
152 } else {
153 if (checklen>=strlen(HIS_HIDDENHOST)) {
154 if (!ircd_strcmp(cbp->host->content+(cbp->host->length-strlen(HIS_HIDDENHOST)), HIS_HIDDENHOST))
155 cbp->flags |= CHANBAN_HIDDENHOST;
156 } else {
157 if (!ircd_strcmp(cbp->host->content+(cbp->host->length-checklen),
158 (HIS_HIDDENHOST)+strlen(HIS_HIDDENHOST)-checklen))
159 cbp->flags |= CHANBAN_HIDDENHOST;
160 }
161 }
162 cbp->flags |= CHANBAN_HOSTMASK;
163 if (!notip && dotcount<=3)
164 cbp->flags |= CHANBAN_IP;
165 } else {
166 /* Exact host: see if it ends with the "hidden host" string */
167 cbp->flags |= CHANBAN_HOSTEXACT;
168 if ((cbp->host->length > (strlen(HIS_HIDDENHOST)+1)) &&
169 !ircd_strcmp(cbp->host->content+(cbp->host->length-strlen(HIS_HIDDENHOST)), HIS_HIDDENHOST)) {
170 cbp->flags |= CHANBAN_HIDDENHOST;
171 } else if (!notip && dotcount==3) {
172 unsigned int a,b,c,d;
173 if ((sscanf(&mask[i+1], "%u.%u.%u.%u",&a,&b,&c,&d) != 4) ||
174 (a > 255) || (b > 255) || (c > 255) || (d > 255) ) {
175 /* Something with only numbers and exactly 3 dots that isn't an IP address can't match anything. */
176 cbp->flags |= CHANBAN_INVALID;
177 } else {
178 cbp->ipaddr=(a<<24)|(b<<16)|(c<<8)|d;
179 cbp->flags |= CHANBAN_IP;
180 }
181 }
182 }
183 }
184 foundat=i;
185 break;
186 } else if (mask[i]=='/') {
187 foundslash=1;
188 } else if (mask[i]=='.') {
189 dotcount++;
190 } else if (mask[i]=='?' || mask[i]=='*') {
191 if (!foundwild) /* Mark last wildcard in string */
192 foundwild=i;
193 } else if (mask[i]<'0' || mask[i]>'9') {
194 notip=1;
195 }
196 }
197
198 if (foundat<0) {
199 /* If there wasn't an @, this ban matches any host */
200 cbp->host=NULL;
201 cbp->flags |= CHANBAN_HOSTANY;
202 }
203
204 foundwild=0;
205
206 for (i=0;i<foundat;i++) {
207 if (mask[i]=='!') {
208 if (i==0) {
209 /* Invalid mask: nick is empty */
210 cbp->flags |= CHANBAN_NICKNULL;
211 cbp->nick=NULL;
212 } else if (i==1 && mask[0]=='*') {
213 /* matches any nick */
214 cbp->flags |= CHANBAN_NICKANY;
215 cbp->nick=NULL;
216 } else {
217 if (i>NICKLEN) {
218 /* too long: just take the first NICKLEN chars */
219 cbp->nick=getsstring(mask,NICKLEN);
220 } else {
221 cbp->nick=getsstring(mask,i);
222 }
223 if (foundwild)
224 cbp->flags |= CHANBAN_NICKMASK;
225 else
226 cbp->flags |= CHANBAN_NICKEXACT;
227 }
228 foundbang=i;
229 break;
230 } else if (mask[i]=='?' || mask[i]=='*') {
231 if (i<NICKLEN) {
232 foundwild=1;
233 }
234 }
235 }
236
237 if (foundbang<0) {
238 /* We didn't find a !, what we do now depends on what happened
239 * with the @ */
240 if (foundat<0) {
241 /* A ban with no ! or @ is treated as a nick ban only */
242 /* Note that we've special-cased "*" at the top, so we can only
243 * hit the MASK or EXACT case here. */
244 if (len>NICKLEN)
245 cbp->nick=getsstring(mask,NICKLEN);
246 else
247 cbp->nick=getsstring(mask,len);
248
249 if (foundwild)
250 cbp->flags |= CHANBAN_NICKMASK;
251 else
252 cbp->flags |= CHANBAN_NICKEXACT;
253
254 cbp->flags |= (CHANBAN_USERANY | CHANBAN_HOSTANY);
255 cbp->host=NULL;
256 cbp->user=NULL;
257 } else {
258 /* A ban with @ only is treated as user@host */
259 cbp->nick=NULL;
260 cbp->flags |= CHANBAN_NICKANY;
261 }
262 }
263
264 if (foundat>=0) {
265 /* We found an @, so everything between foundbang+1 and foundat-1 is supposed to be ident */
266 /* This is true even if there was no !.. */
267 if (foundat==(foundbang+1)) {
268 /* empty ident matches nothing */
269 cbp->flags |= (CHANBAN_INVALID | CHANBAN_USERNULL);
270 cbp->user=NULL;
271 } else if (foundat - foundbang - 1 > USERLEN) {
272 /* It's too long.. */
273 strncpy(tmpbuf,&mask[foundat-USERLEN],USERLEN);
274 tmpbuf[USERLEN]='\0';
275 tmpbuf[0]='*';
276 cbp->user=getsstring(tmpbuf,USERLEN);
277 cbp->flags |= CHANBAN_USERMASK;
278 } else if ((foundat - foundbang - 1 == 1) && mask[foundbang+1]=='*') {
279 cbp->user=NULL;
280 cbp->flags |= CHANBAN_USERANY;
281 } else {
282 cbp->user=getsstring(&mask[foundbang+1],(foundat-foundbang-1));
283 if (strchr(cbp->user->content,'*') || strchr(cbp->user->content,'?'))
284 cbp->flags |= CHANBAN_USERMASK;
285 else
286 cbp->flags |= CHANBAN_USEREXACT;
287 }
288 /* Username part can't contain an @ */
289 if (cbp->user && strchr(cbp->user->content,'@')) {
290 cbp->flags |= CHANBAN_INVALID;
291 }
292 }
293
294 assert(cbp->flags & (CHANBAN_USEREXACT | CHANBAN_USERMASK | CHANBAN_USERANY | CHANBAN_USERNULL));
295 assert(cbp->flags & (CHANBAN_NICKEXACT | CHANBAN_NICKMASK | CHANBAN_NICKANY | CHANBAN_NICKNULL));
296 assert(cbp->flags & (CHANBAN_HOSTEXACT | CHANBAN_HOSTMASK | CHANBAN_HOSTANY | CHANBAN_HOSTNULL));
297
298 cbp->timeset=time(NULL);
299
300 cbp->next=NULL;
301
302 return cbp;
303 }
304
305 /* banoverlap:
306 * Returns nonzero iff bana is a SUPERSET of banb
307 */
308
309 int banoverlap(const chanban *bana, const chanban *banb) {
310 /* This function works by looking for cases where bana DOESN'T overlap banb */
311
312 /* NICK section */
313 /* If bana has CHANBAN_NICKANY then it clearly overlaps
314 * in the nick section so there are no checks */
315
316 if ((bana->flags & CHANBAN_NICKNULL) && !(banb->flags & CHANBAN_NICKNULL)) {
317 return 0;
318 }
319
320 if (bana->flags & CHANBAN_NICKMASK) {
321 if (banb->flags & (CHANBAN_NICKANY | CHANBAN_NICKNULL)) {
322 return 0;
323 }
324 if ((banb->flags & CHANBAN_NICKMASK) && !match2patterns(bana->nick->content, banb->nick->content)) {
325 return 0;
326 }
327 if ((banb->flags & CHANBAN_NICKEXACT) && !match2strings(bana->nick->content, banb->nick->content)) {
328 return 0;
329 }
330 }
331
332 if (bana->flags & CHANBAN_NICKEXACT) {
333 if (banb->flags & (CHANBAN_NICKANY | CHANBAN_NICKMASK | CHANBAN_NICKNULL)) {
334 return 0;
335 }
336 if ((!bana->nick && banb->nick) || (bana->nick && !banb->nick)) {
337 return 0;
338 }
339 if (bana->nick && ircd_strcmp(bana->nick->content,banb->nick->content)) {
340 return 0;
341 }
342 }
343
344 /* USER section */
345 /* If bana has CHANBAN_USERANY then it clearly overlaps
346 * in the user section so there are no checks */
347
348 if ((bana->flags & CHANBAN_USERNULL) && !(banb->flags & CHANBAN_USERNULL)) {
349 return 0;
350 }
351
352 if (bana->flags & CHANBAN_USERMASK) {
353 if (banb->flags & (CHANBAN_USERANY | CHANBAN_USERNULL)) {
354 return 0;
355 }
356 if ((banb->flags & CHANBAN_USERMASK) && !match2patterns(bana->user->content, banb->user->content)) {
357 return 0;
358 }
359 if ((banb->flags & CHANBAN_USEREXACT) && !match2strings(bana->user->content, banb->user->content)) {
360 return 0;
361 }
362 }
363
364 if (bana->flags & CHANBAN_USEREXACT) {
365 if (banb->flags & (CHANBAN_USERANY | CHANBAN_USERMASK | CHANBAN_USERNULL)) {
366 return 0;
367 }
368 if ((!bana->user && banb->user) || (bana->user && !banb->user)) {
369 return 0;
370 }
371 if (bana->user && ircd_strcmp(bana->user->content,banb->user->content)) {
372 return 0;
373 }
374 }
375
376 /* HOST section */
377 /* If bana has CHANBAN_HOSTANY then it clearly overlaps
378 * in the host section so there are no checks */
379
380 if ((bana->flags & CHANBAN_HOSTNULL) && !(banb->flags & CHANBAN_HOSTNULL)) {
381 return 0;
382 }
383
384 if (bana->flags & CHANBAN_HOSTMASK) {
385 if (banb->flags & (CHANBAN_HOSTANY | CHANBAN_HOSTNULL)) {
386 return 0;
387 }
388 if ((banb->flags & CHANBAN_HOSTMASK) && !match2patterns(bana->host->content, banb->host->content)) {
389 return 0;
390 }
391 if ((banb->flags & CHANBAN_HOSTEXACT) && !match2strings(bana->host->content, banb->host->content)) {
392 return 0;
393 }
394 }
395
396 if (bana->flags & CHANBAN_HOSTEXACT) {
397 if (banb->flags & (CHANBAN_HOSTANY | CHANBAN_HOSTMASK | CHANBAN_HOSTNULL)) {
398 return 0;
399 }
400 if ((!bana->host && banb->host) || (bana->host && !banb->host)) {
401 return 0;
402 }
403 if (bana->host && ircd_strcmp(bana->host->content,banb->host->content)) {
404 return 0;
405 }
406 }
407
408 return 1;
409 }
410
411 /*
412 * banequal:
413 * Returns nonzero iff the bans are EXACTLY the same
414 */
415
416 int banequal(chanban *bana, chanban *banb) {
417 if (bana->flags != banb->flags)
418 return 0;
419
420 if ((!bana->nick && banb->nick) || (bana->nick && !banb->nick))
421 return 0;
422
423 if (bana->nick && ircd_strcmp(bana->nick->content,banb->nick->content))
424 return 0;
425
426 if ((!bana->user && banb->user) || (bana->user && !banb->user))
427 return 0;
428
429 if (bana->user && ircd_strcmp(bana->user->content,banb->user->content))
430 return 0;
431
432 if ((!bana->host && banb->host) || (bana->host && !banb->host))
433 return 0;
434
435 if (bana->host && ircd_strcmp(bana->host->content,banb->host->content))
436 return 0;
437
438 return 1;
439 }
440
441 /*
442 * bantostring:
443 * Convert the specified ban to a string
444 */
445
446 char *bantostring(chanban *bp) {
447 static char outstring[NICKLEN+USERLEN+HOSTLEN+5];
448 int strpos=0;
449
450 if (bp->flags & CHANBAN_NICKANY) {
451 strpos += sprintf(outstring+strpos,"*");
452 } else if (bp->nick) {
453 strpos += sprintf(outstring+strpos,"%s",bp->nick->content);
454 }
455
456 strpos += sprintf(outstring+strpos,"!");
457
458 if (bp->flags & CHANBAN_USERANY) {
459 strpos += sprintf(outstring+strpos,"*");
460 } else if (bp->user) {
461 strpos += sprintf(outstring+strpos,"%s",bp->user->content);
462 }
463
464 strpos += sprintf(outstring+strpos,"@");
465
466 if (bp->flags & CHANBAN_HOSTANY) {
467 strpos += sprintf(outstring+strpos,"*");
468 } else if (bp->host) {
469 strpos += sprintf(outstring+strpos,"%s",bp->host->content);
470 }
471
472 return outstring;
473 }
474
475 /*
476 * bantostringdebug:
477 * Convert the specified ban to a string (debugging version)
478 */
479
480 char *bantostringdebug(chanban *bp) {
481 static char outstring[NICKLEN+USERLEN+HOSTLEN+5];
482 int strpos=0;
483
484 strpos += sprintf(outstring+strpos, "flags=%04x ",bp->flags);
485
486 if (bp->nick) {
487 strpos += sprintf(outstring+strpos, "nick=%s ",bp->nick->content);
488 } else {
489 strpos += sprintf(outstring+strpos, "nonick ");
490 }
491
492 if (bp->user) {
493 strpos += sprintf(outstring+strpos, "user=%s ",bp->user->content);
494 } else {
495 strpos += sprintf(outstring+strpos, "nouser ");
496 }
497
498 if (bp->host) {
499 strpos += sprintf(outstring+strpos, "host=%s ",bp->host->content);
500 } else {
501 strpos += sprintf(outstring+strpos, "nohost ");
502 }
503
504
505 return outstring;
506 }
507