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