1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
|
/* Host and IP (incl. CIDR) banning routines
*
* (C) 2003-2005 Anope Team
* Contact us at info@anope.org
*
* Please read COPYING and README for further details.
*
* Based on the original code of Epona by Lara.
* Based on the original code of Services by Andy Church.
*
* $Id$
*
*/
#include "services.h"
/*************************************************************************/
#define BANHOSTHASH(x) (*(x) & 0x1F)
#define BANINFOHASH(x) ((((x)->type & BANTYPE_CIDR4) || (*((x)->host) == '*') || (*((x)->host) == '?')) ? 32 : BANHOSTHASH((x)->host))
/*************************************************************************/
/* This 2 functions were originally found in Bahamut */
/**
* Turn a cidr value into a netmask
* @param cidr CIDR value
* @return Netmask value
*/
uint32 cidr_to_netmask(uint16 cidr)
{
if (cidr == 0)
return 0;
return (0xFFFFFFFF - (1 << (32 - cidr)) + 1);
}
/**
* Turn a netmask into a cidr value
* @param mask Netmask
* @return CIDR value
*/
uint16 netmask_to_cidr(uint32 mask)
{
int tmp = 0;
while (!(mask & (1 << tmp)) && (tmp < 32))
tmp++;
return (32 - tmp);
}
/*************************************************************************/
/**
* Check if the given string is some sort of wildcard
* @param str String to check
* @return 1 for wildcard, 0 for anything else
*/
int str_is_wildcard(const char *str)
{
while (*str) {
if ((*str == '*') || (*str == '?'))
return 1;
str++;
}
return 0;
}
/**
* Check if the given string is a pure wildcard
* @param str String to check
* @return 1 for pure wildcard, 0 for anything else
*/
int str_is_pure_wildcard(const char *str)
{
while (*str) {
if (*str != '*')
return 0;
str++;
}
return 1;
}
/*************************************************************************/
/**
* Check if the given string is an IP or CIDR mask, and fill the given
* ip/cidr params if so.
* @param str String to check
* @param ip The ipmask to fill when a CIDR is found
* @param mask The CIDR mask to fill when a CIDR is found
* @param host Displayed host
* @return 1 for IP/CIDR, 0 for anything else
*/
int str_is_cidr(char *str, uint32 * ip, uint32 * mask, char **host)
{
int i;
int octets[4] = { -1, -1, -1, -1 };
char *s = str;
char buf[512];
uint16 cidr;
for (i = 0; i < 4; i++) {
octets[i] = strtol(s, &s, 10);
/* Bail out if the octet is invalid or wrongly terminated */
if ((octets[i] < 0) || (octets[i] > 255)
|| ((i < 3) && (*s != '.')))
return 0;
if (i < 3)
s++;
}
/* Fill the IP - the dirty way */
*ip = octets[3];
*ip += octets[2] * 256;
*ip += octets[1] * 65536;
*ip += octets[0] * 16777216;
if (*s == '/') {
s++;
/* There's a CIDR mask here! */
cidr = strtol(s, &s, 10);
/* Bail out if the CIDR is invalid or the string isn't done yet */
if ((cidr > 32) || (*s))
return 0;
} else {
/* No CIDR mask here - use 32 so the whole ip will be matched */
cidr = 32;
}
*mask = cidr_to_netmask(cidr);
/* Apply the mask to avoid 255.255.255.255/8 bans */
*ip &= *mask;
/* Refill the octets to fill the host */
octets[0] = (*ip & 0xFF000000) / 16777216;
octets[1] = (*ip & 0x00FF0000) / 65536;
octets[2] = (*ip & 0x0000FF00) / 256;
octets[3] = (*ip & 0x000000FF);
if (cidr == 32)
snprintf(buf, 512, "%d.%d.%d.%d", octets[0], octets[1], octets[2],
octets[3]);
else
snprintf(buf, 512, "%d.%d.%d.%d/%d", octets[0], octets[1],
octets[2], octets[3], cidr);
*host = sstrdup(buf);
return 1;
}
/*************************************************************************/
/**
* Create a BanInfo for the given mask. This function destroys the given
* mask as a side effect.
* @param mask Host/IP/CIDR mask to parse
* @param id Ban ID to use for this ban
* @return BanInfo struct for the given mask, NULL if creation failed
*/
BanInfo *ban_create(char *mask, uint32 id)
{
char *user, *host, *cidrhost;
char buf[BUFSIZE];
BanInfo *ban;
uint32 ip;
uint32 cidr;
ban = scalloc(1, sizeof(BanInfo));
ban->id = id;
ban->type = BANTYPE_NONE;
host = strchr(mask, '@');
if (host) {
*host++ = '\0';
/* If the user is purely a wildcard, ignore it */
if (str_is_pure_wildcard(mask))
user = NULL;
else
user = mask;
} else {
/* If there's no user in the mask, assume a pure wildcard */
user = NULL;
host = mask;
}
if (user) {
ban->user = sstrdup(user);
/* Check if we have a wildcard user */
if (str_is_wildcard(user))
ban->type |= BANTYPE_USER_WILD;
else
ban->type |= BANTYPE_USER;
}
/* Only check the host if it's not a pure wildcard */
if (*host && !str_is_pure_wildcard(host)) {
if (ircd->nickip && str_is_cidr(host, &ip, &cidr, &cidrhost)) {
ban->cidr_ip = ip;
ban->cidr_mask = cidr;
ban->type |= BANTYPE_CIDR4;
host = cidrhost;
} else if (strchr(host, '/')) {
/* If we still have a CIDR we're fucked (scientific term) */
return NULL;
} else {
ban->host = sstrdup(host);
if (str_is_wildcard(host))
ban->type |= BANTYPE_HOST_WILD;
else
ban->type |= BANTYPE_HOST;
}
}
if (!user)
user = "*";
if (!host)
host = "*";
snprintf(buf, BUFSIZE, "%s@%s", user, host);
ban->mask = sstrdup(buf);
return ban;
}
/*************************************************************************/
/**
* Match the given BanInfo to the given user/host and optional IP addy
* @param ban BanInfo struct to match against
* @param user User to match against
* @param host Host to match against
* @param ip IP to match against, set to 0 to not match this
* @return 1 for a match, 0 for no match
*/
int ban_match(BanInfo * ban, char *user, char *host, uint32 ip)
{
if ((ban->type & BANTYPE_CIDR4) && ip
&& ((ip & ban->cidr_mask) != ban->cidr_ip))
return 0;
if ((ban->type & BANTYPE_USER) && (stricmp(ban->user, user) != 0))
return 0;
if ((ban->type & BANTYPE_HOST) && (stricmp(ban->host, host) != 0))
return 0;
if ((ban->type & BANTYPE_USER_WILD)
&& !match_wild_nocase(ban->user, user))
return 0;
if ((ban->type & BANTYPE_HOST_WILD)
&& !match_wild_nocase(ban->host, host))
return 0;
return 1;
}
/**
* Match the given BanInfo to the given hostmask and optional IP addy
* @param ban BanInfo struct to match against
* @param mask Hostmask to match against
* @param ip IP to match against, set to 0 to not match this
* @return 1 for a match, 0 for no match
*/
int ban_match_mask(BanInfo * ban, char *mask, uint32 ip)
{
char *user;
char *host;
if (ban->type & (BANTYPE_USER | BANTYPE_USER_WILD)) {
host = strchr(mask, '@');
if (host) {
*host++ = '\0';
user = mask;
} else {
user = NULL;
}
}
if (ban->type & (BANTYPE_HOST | BANTYPE_HOST_WILD)) {
if (ban->type & (BANTYPE_USER | BANTYPE_USER_WILD)) {
if (!user)
host = mask;
} else {
host = strchr(mask, '@');
if (host)
host++;
else
host = mask;
}
}
return ban_match(ban, user, host, ip);
}
/*************************************************************************/
/**
* Create and initialize a new ban list
* @param hashed Should the list be hashed or not?
* @return Pointer to the created BanList object
*/
BanList *banlist_create(int hashed)
{
BanList *bl;
bl = scalloc(1, sizeof(BanList));
bl->flags = BANLIST_NONE;
if (hashed) {
bl->flags |= BANLIST_HASHED;
bl->bans = scalloc(33, sizeof(BanInfo));
} else {
bl->bans = scalloc(1, sizeof(BanInfo));
}
bl->next_id = 1;
return bl;
}
/*************************************************************************/
/**
* Create a ban and add it to the given banlist
* @param bl BanList object the banmask should be added to
* @param mask The mask to parse and add to the banlist
* @return 1 for success, 0 for failure
*/
int ban_add(BanList * bl, char *mask)
{
int hash;
char *hostmask;
BanInfo *ban;
hostmask = sstrdup(mask);
ban = ban_create(hostmask, bl->next_id);
free(hostmask);
if (!ban)
return 0;
bl->next_id++;
if (bl->flags & BANLIST_HASHED) {
hash = BANINFOHASH(ban);
ban->next = bl->bans[hash];
ban->prev = NULL;
if (bl->bans[hash])
bl->bans[hash]->prev = ban;
bl->bans[hash] = ban;
} else {
ban->next = *(bl->bans);
ban->prev = NULL;
if (*(bl->bans))
(*(bl->bans))->prev = ban;
*(bl->bans) = ban;
}
return 1;
}
/*************************************************************************/
/**
* Delete the given ban from the given banlist
* @param bl BanList object the banmask should be deleted from
* @param ban The ban to be deleted, has to be in the given banlist
*/
void ban_del(BanList * bl, BanInfo * ban)
{
if (ban->next)
ban->next->prev = ban->prev;
if (ban->prev)
ban->prev->next = ban->next;
else if (bl->flags & BANLIST_HASHED)
bl->bans[BANINFOHASH(ban)] = ban->next;
else
*(bl->bans) = ban->next;
if (ban->user)
free(ban->user);
if (ban->host)
free(ban->host);
free(ban->mask);
free(ban);
}
/*************************************************************************/
/**
* Match a user, host, and ip to a banlist
* @param bl BanList that should be matched against
* @param user The user to match
* @param host The host to match
* @param ip The ip to match
* @return 1 for match, 0 for no match
*/
int banlist_match(BanList * bl, char *user, char *host, uint32 ip)
{
BanInfo *ban;
if (bl->flags & BANLIST_HASHED) {
for (ban = bl->bans[BANHOSTHASH(host)]; ban; ban = ban->next) {
if (ban_match(ban, user, host, ip))
return 1;
}
/* Now check for all wildcard-qualified bans */
for (ban = bl->bans[32]; ban; ban = ban->next) {
if (ban_match(ban, user, host, ip))
return 1;
}
} else {
for (ban = *(bl->bans); ban; ban = ban->next) {
if (ban_match(ban, user, host, ip))
return 1;
}
}
/* We matched none, yay */
return 0;
}
/**
* Match a mask and ip to a banlist
* @param bl BanList that should be matched against
* @param mask The user@host mask to match
* @return 1 for match, 0 for no match
*/
int banlist_match_mask(BanList * bl, char *mask, uint32 ip)
{
char *user;
char *host;
host = strchr(mask, '@');
if (host) {
*host++ = '\0';
user = mask;
} else {
user = NULL;
host = mask;
}
return banlist_match(bl, user, host, ip);
}
/*************************************************************************/
/**
* Lookup a ban id in a banlist
* @param bl BanList to search in
* @param id Ban id to lookup
* @return Pointer to BanInfo for the given id, or NULL if not found
*/
BanInfo *banlist_lookup_id(BanList * bl, uint32 id)
{
int i;
BanInfo *ban;
if (bl->flags & BANLIST_HASHED) {
for (i = 0; i < 33; i++) {
for (ban = bl->bans[i]; ban; ban = ban->next) {
if (ban->id == id)
return ban;
}
}
} else {
for (ban = *(bl->bans); ban; ban = ban->next) {
if (ban->id == id)
return ban;
}
}
return NULL;
}
/* EOF */
|