unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
api-extban.c (11828B)
1 /************************************************************************ 2 * IRC - Internet Relay Chat, api-extban.c 3 * (C) 2003 Bram Matthys (Syzop) and the UnrealIRCd Team 4 * 5 * See file AUTHORS in IRC package for additional names of 6 * the programmers. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 1, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include "unrealircd.h" 24 25 /** List of all extbans, their handlers, etc */ 26 MODVAR Extban *extbans = NULL; 27 28 void set_isupport_extban(void) 29 { 30 char extbanstr[512]; 31 Extban *e; 32 char *p = extbanstr; 33 34 for (e = extbans; e; e = e->next) 35 *p++ = e->letter; 36 *p = '\0'; 37 38 ISupportSetFmt(NULL, "EXTBAN", "~,%s", extbanstr); 39 } 40 41 Extban *findmod_by_bantype_raw(const char *str, int ban_name_length) 42 { 43 Extban *e; 44 45 for (e=extbans; e; e = e->next) 46 { 47 if ((ban_name_length == 1) && (e->letter == str[0])) 48 return e; 49 if (e->name) 50 { 51 int namelen = strlen(e->name); 52 if ((namelen == ban_name_length) && !strncmp(e->name, str, namelen)) 53 return e; 54 } 55 } 56 57 return NULL; 58 } 59 60 Extban *findmod_by_bantype(const char *str, const char **remainder) 61 { 62 int ban_name_length; 63 const char *p = strchr(str, ':'); 64 65 if (!p || !p[1]) 66 { 67 if (remainder) 68 *remainder = NULL; 69 return NULL; 70 } 71 if (remainder) 72 *remainder = p+1; 73 74 ban_name_length = p - str - 1; 75 return findmod_by_bantype_raw(str+1, ban_name_length); 76 } 77 78 /* Check if this is a valid extended ban name */ 79 int is_valid_extban_name(const char *p) 80 { 81 if (!*p) 82 return 0; /* empty name */ 83 if (strlen(p) > 32) 84 return 0; /* too long */ 85 for (; *p; p++) 86 if (!islower(*p) && !isdigit(*p) && !strchr("_-", *p)) 87 return 0; 88 return 1; 89 } 90 91 static void extban_add_sorted(Extban *n) 92 { 93 Extban *m; 94 95 if (extbans == NULL) 96 { 97 extbans = n; 98 return; 99 } 100 101 for (m = extbans; m; m = m->next) 102 { 103 if (m->letter == '\0') 104 abort(); 105 if (sort_character_lowercase_before_uppercase(n->letter, m->letter)) 106 { 107 /* Insert us before */ 108 if (m->prev) 109 m->prev->next = n; 110 else 111 extbans = n; /* new head */ 112 n->prev = m->prev; 113 114 n->next = m; 115 m->prev = n; 116 return; 117 } 118 if (!m->next) 119 { 120 /* Append us at end */ 121 m->next = n; 122 n->prev = m; 123 return; 124 } 125 } 126 } 127 128 Extban *ExtbanAdd(Module *module, ExtbanInfo req) 129 { 130 Extban *e; 131 ModuleObject *banobj; 132 int existing = 0; 133 134 if (!req.name) 135 { 136 module->errorcode = MODERR_INVALID; 137 unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, 138 "ExtbanAdd(): name must be specified for ban (new in U6). Module: $module_name", 139 log_data_string("module_name", module->header->name)); 140 return NULL; 141 } 142 143 if (!req.is_banned_events && req.is_banned) 144 { 145 module->errorcode = MODERR_INVALID; 146 unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, 147 "ExtbanAdd(): module must indicate via .is_banned_events on which BANCHK_* " 148 "events to listen on (new in U6). Module: $module_name", 149 log_data_string("module_name", module->header->name)); 150 return NULL; 151 } 152 153 if (!isalnum(req.letter)) 154 { 155 module->errorcode = MODERR_INVALID; 156 unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, 157 "ExtbanAdd(): module tried to add extban which is not alphanumeric. " 158 "Module: $module_name", 159 log_data_string("module_name", module->header->name)); 160 return NULL; 161 } 162 163 if (!is_valid_extban_name(req.name)) 164 { 165 module->errorcode = MODERR_INVALID; 166 unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, 167 "ExtbanAdd(): module tried to add extban with an invalid name ($extban_name). " 168 "Module: $module_name", 169 log_data_string("module_name", module->header->name), 170 log_data_string("extban_name", req.name)); 171 return NULL; 172 } 173 174 if (!req.conv_param) 175 { 176 module->errorcode = MODERR_INVALID; 177 unreal_log(ULOG_ERROR, "module", "EXTBANADD_API_ERROR", NULL, 178 "ExtbanAdd(): conv_param event missing. Module: $module_name", 179 log_data_string("module_name", module->header->name)); 180 return NULL; 181 } 182 183 for (e=extbans; e; e = e->next) 184 { 185 if (e->letter == req.letter) 186 { 187 /* Extban already exists in our list, let's see... */ 188 if (e->unloaded) 189 { 190 e->unloaded = 0; 191 existing = 1; 192 break; 193 } else 194 if ((module->flags == MODFLAG_TESTING) && e->preregistered) 195 { 196 /* We are in MOD_INIT (yeah confusing, isn't it?) 197 * and the extban already exists and it was preregistered. 198 * Then go ahead with really registering it. 199 */ 200 e->preregistered = 0; 201 existing = 1; 202 break; 203 } else 204 if (module->flags == MODFLAG_NONE) 205 { 206 /* Better don't touch it, as we may still fail at this stage 207 * and if we would set .conv_param etc to this and the new module 208 * gets unloaded because of a config typo then we would be screwed 209 * (now we are not). 210 * NOTE: this does mean that if you hot-load an extban module 211 * then it may only be available for config stuff the 2nd rehash. 212 */ 213 return e; 214 } else 215 { 216 module->errorcode = MODERR_EXISTS; 217 return NULL; 218 } 219 } 220 } 221 222 if (!e) 223 { 224 /* Not found, create */ 225 e = safe_alloc(sizeof(Extban)); 226 e->letter = req.letter; 227 extban_add_sorted(e); 228 } 229 e->letter = req.letter; 230 safe_strdup(e->name, req.name); 231 e->is_ok = req.is_ok; 232 e->conv_param = req.conv_param; 233 e->is_banned = req.is_banned; 234 e->is_banned_events = req.is_banned_events; 235 e->owner = module; 236 e->options = req.options; 237 238 if (module->flags == MODFLAG_NONE) 239 e->preregistered = 1; 240 241 banobj = safe_alloc(sizeof(ModuleObject)); 242 banobj->object.extban = e; 243 banobj->type = MOBJ_EXTBAN; 244 AddListItem(banobj, module->objects); 245 module->errorcode = MODERR_NOERROR; 246 247 set_isupport_extban(); 248 return e; 249 } 250 251 static void unload_extban_commit(Extban *e) 252 { 253 /* Should we mass unban everywhere? 254 * Hmmm. Not needed per se, user can always unset 255 * themselves. Leaning towards no atm. 256 */ 257 // noop 258 259 /* Then unload the extban */ 260 DelListItem(e, extbans); 261 safe_free(e->name); 262 safe_free(e); 263 set_isupport_extban(); 264 } 265 266 /** Unload all unused extended bans after a REHASH */ 267 void unload_all_unused_extbans(void) 268 { 269 Extban *e, *e_next; 270 271 for (e=extbans; e; e = e_next) 272 { 273 e_next = e->next; 274 if (e->letter && e->unloaded) 275 { 276 unload_extban_commit(e); 277 } 278 } 279 280 } 281 282 void ExtbanDel(Extban *e) 283 { 284 /* Always free the module object */ 285 if (e->owner) 286 { 287 ModuleObject *banobj; 288 for (banobj = e->owner->objects; banobj; banobj = banobj->next) 289 { 290 if (banobj->type == MOBJ_EXTBAN && banobj->object.extban == e) 291 { 292 DelListItem(banobj, e->owner->objects); 293 safe_free(banobj); 294 break; 295 } 296 } 297 } 298 299 /* Whether we can actually (already) free the Extban, it depends... */ 300 if (loop.rehashing) 301 e->unloaded = 1; 302 else 303 unload_extban_commit(e); 304 } 305 306 /** General is_ok for n!u@h stuff that also deals with recursive extbans. 307 */ 308 int extban_is_ok_nuh_extban(BanContext *b) 309 { 310 int isok; 311 static int extban_is_ok_recursion = 0; 312 313 /* Mostly copied from clean_ban_mask - but note MyUser checks aren't needed here: extban->is_ok() according to cmd_mode isn't called for nonlocal. */ 314 if (is_extended_ban(b->banstr)) 315 { 316 const char *nextbanstr; 317 Extban *extban = NULL; 318 319 /* We're dealing with a stacked extended ban. 320 * Rules: 321 * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z... 322 * 2) The second item may never be an action modifier, nor have the 323 * EXTBOPT_NOSTACKCHILD letter set (for things like a textban). 324 */ 325 326 if (extban_is_ok_recursion) 327 return 0; /* Rule #1 violation (more than one stacked extban) */ 328 329 if ((b->is_ok_check == EXBCHK_PARAM) && RESTRICT_EXTENDEDBANS && !ValidatePermissionsForPath("immune:restrict-extendedbans",b->client,NULL,b->channel,NULL)) 330 { 331 /* Test if this specific extban has been disabled. 332 * (We can be sure RESTRICT_EXTENDEDBANS is not *. Else this extended ban wouldn't be happening at all.) 333 */ 334 if (strchr(RESTRICT_EXTENDEDBANS, b->banstr[1])) 335 { 336 sendnotice(b->client, "Setting/removing of extended bantypes '%s' has been disabled.", RESTRICT_EXTENDEDBANS); 337 return 0; /* Fail */ 338 } 339 } 340 extban = findmod_by_bantype(b->banstr, &nextbanstr); 341 if (!extban) 342 { 343 if (b->what == MODE_DEL) 344 { 345 return 1; /* Always allow killing unknowns. */ 346 } 347 return 0; /* Don't add unknown extbans. */ 348 } 349 350 if ((extban->options & EXTBOPT_ACTMODIFIER) || (extban->options & EXTBOPT_NOSTACKCHILD)) 351 { 352 /* Rule #2 violation */ 353 return 0; 354 } 355 356 /* Now we have to ask the stacked extban if it's ok. */ 357 if (extban->is_ok) 358 { 359 b->banstr = nextbanstr; 360 extban_is_ok_recursion++; 361 isok = extban->is_ok(b); 362 extban_is_ok_recursion--; 363 return isok; 364 } 365 } 366 return 1; /* Either not an extban, or extban has NULL is_ok. Good to go. */ 367 } 368 369 /** Some kind of general conv_param routine, 370 * to ensure the parameter is nick!user@host. 371 * most of the code is just copied from clean_ban_mask. 372 */ 373 const char *extban_conv_param_nuh(BanContext *b, Extban *extban) 374 { 375 char tmpbuf[USERLEN + NICKLEN + HOSTLEN + 32]; 376 static char retbuf[USERLEN + NICKLEN + HOSTLEN + 32]; 377 378 /* Work on a copy */ 379 strlcpy(tmpbuf, b->banstr, sizeof(retbuf)); 380 return convert_regular_ban(tmpbuf, retbuf, sizeof(retbuf)); 381 } 382 383 /** conv_param to deal with stacked extbans. 384 */ 385 const char *extban_conv_param_nuh_or_extban(BanContext *b, Extban *self_extban) 386 { 387 #if (USERLEN + NICKLEN + HOSTLEN + 32) > 256 388 #error "wtf?" 389 #endif 390 static char retbuf[256]; 391 static char printbuf[256]; 392 char *mask; 393 char tmpbuf[USERLEN + NICKLEN + HOSTLEN + 32]; 394 const char *ret = NULL; 395 const char *nextbanstr; 396 Extban *extban = NULL; 397 static int extban_recursion = 0; 398 399 if (!is_extended_ban(b->banstr)) 400 return extban_conv_param_nuh(b, self_extban); 401 402 /* We're dealing with a stacked extended ban. 403 * Rules: 404 * 1) You can only stack once, so: ~x:~y:something and not ~x:~y:~z... 405 * 2) The second item may never be an action modifier, nor have the 406 * EXTBOPT_NOSTACKCHILD letter set (for things like a textban). 407 */ 408 409 /* Rule #1. Yes the recursion check is also in extban_is_ok_nuh_extban, 410 * but it's possible to get here without the is_ok() function ever 411 * being called (think: non-local client). And no, don't delete it 412 * there either. It needs to be in BOTH places. -- Syzop 413 */ 414 if (extban_recursion) 415 return NULL; 416 417 strlcpy(tmpbuf, b->banstr, sizeof(tmpbuf)); 418 extban = findmod_by_bantype(tmpbuf, &nextbanstr); 419 420 if (!extban) 421 { 422 /* Handling unknown bantypes in is_ok. Assume that it's ok here. */ 423 return b->banstr; 424 } 425 426 b->banstr = nextbanstr; 427 428 if ((extban->options & EXTBOPT_ACTMODIFIER) || (extban->options & EXTBOPT_NOSTACKCHILD)) 429 { 430 /* Rule #2 violation */ 431 return NULL; 432 } 433 434 extban_recursion++; 435 ret = extban->conv_param(b, extban); 436 extban_recursion--; 437 ret = prefix_with_extban(ret, b, extban, retbuf, sizeof(retbuf)); 438 return ret; 439 } 440 441 char *prefix_with_extban(const char *remainder, BanContext *b, Extban *extban, char *buf, size_t buflen) 442 { 443 /* Yes, we support this because it makes code at the caller cleaner */ 444 if (remainder == NULL) 445 return NULL; 446 447 if (iConf.named_extended_bans && !(b->conv_options & BCTX_CONV_OPTION_WRITE_LETTER_BANS)) 448 snprintf(buf, buflen, "~%s:%s", extban->name, remainder); 449 else 450 snprintf(buf, buflen, "~%c:%s", extban->letter, remainder); 451 452 return buf; 453 }