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