unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
link.c (12809B)
1 /* 2 * Robust channel forwarding system 3 * (C) Copyright 2019 Syzop, Gottem and the UnrealIRCd team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 #include "unrealircd.h" 21 22 #define MAX_EB_LEN 128 // Max extban length 23 24 ModuleHeader MOD_HEADER = { 25 "chanmodes/link", 26 "5.0", 27 "Channel Mode +L", 28 "UnrealIRCd Team", 29 "unrealircd-6", 30 }; 31 32 Cmode_t EXTMODE_LINK = 0L; 33 34 typedef struct { 35 char linked[CHANNELLEN + 1]; 36 } aModeLEntry; 37 38 typedef enum { 39 LINKTYPE_BAN = 1, // +b 40 LINKTYPE_INVITE = 2, // +i 41 LINKTYPE_OPER = 3, // +O 42 LINKTYPE_SECURE = 4, // +z 43 LINKTYPE_REG = 5, // +R 44 LINKTYPE_LIMIT = 6, // +l 45 LINKTYPE_BADKEY = 7, // +k 46 } linkType; 47 48 int cmodeL_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); 49 void *cmodeL_put_param(void *r_in, const char *param); 50 const char *cmodeL_get_param(void *r_in); 51 const char *cmodeL_conv_param(const char *param_in, Client *client, Channel *channel); 52 int cmodeL_free_param(void *r, int soft); 53 void *cmodeL_dup_struct(void *r_in); 54 int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx); 55 56 int extban_link_syntax(Client *client, int checkt, const char *reason); 57 int extban_link_is_ok(BanContext *b); 58 const char *extban_link_conv_param(BanContext *b, Extban *extban); 59 int link_doforward(Client *client, Channel *channel, const char *linked, linkType linktype); 60 int link_pre_localjoin_cb(Client *client, Channel *channel, const char *key); 61 62 MOD_INIT() 63 { 64 CmodeInfo req; 65 ExtbanInfo req_extban; 66 67 MARK_AS_OFFICIAL_MODULE(modinfo); 68 69 memset(&req, 0, sizeof(req)); 70 req.paracount = 1; 71 req.is_ok = cmodeL_is_ok; 72 req.letter = 'L'; 73 req.unset_with_param = 1; /* Oh yeah, we are special! */ 74 req.put_param = cmodeL_put_param; 75 req.get_param = cmodeL_get_param; 76 req.conv_param = cmodeL_conv_param; 77 req.free_param = cmodeL_free_param; 78 req.dup_struct = cmodeL_dup_struct; 79 req.sjoin_check = cmodeL_sjoin_check; 80 CmodeAdd(modinfo->handle, req, &EXTMODE_LINK); 81 82 memset(&req_extban, 0, sizeof(ExtbanInfo)); 83 req_extban.letter = 'f'; 84 req_extban.name = "forward"; 85 req_extban.is_ok = extban_link_is_ok; 86 req_extban.conv_param = extban_link_conv_param; 87 req_extban.options = EXTBOPT_ACTMODIFIER; 88 if (!ExtbanAdd(modinfo->handle, req_extban)) 89 { 90 config_error("could not register extended ban type"); 91 return MOD_FAILED; 92 } 93 94 HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_JOIN, -99, link_pre_localjoin_cb); 95 return MOD_SUCCESS; 96 } 97 98 MOD_LOAD() 99 { 100 return MOD_SUCCESS; 101 } 102 103 104 MOD_UNLOAD() 105 { 106 return MOD_SUCCESS; 107 } 108 109 int cmodeL_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what) 110 { 111 if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) 112 { 113 if (IsUser(client) && check_channel_access(client, channel, "oaq")) 114 return EX_ALLOW; 115 if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ 116 sendnumeric(client, ERR_NOTFORHALFOPS, 'L'); 117 return EX_DENY; 118 } else 119 if (type == EXCHK_PARAM) 120 { 121 /* Check parameter.. syntax is +L #channel */ 122 if (strchr(para, ',')) 123 return EX_DENY; /* multiple channels not permitted */ 124 if (!valid_channelname(para)) 125 { 126 if (MyUser(client)) 127 sendnumeric(client, ERR_NOSUCHCHANNEL, para); 128 return EX_DENY; 129 } 130 131 if (find_channel(para) == channel) 132 { 133 if (MyUser(client)) 134 sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'L', 135 "a channel cannot be linked to itself"); 136 return EX_DENY; 137 } 138 return EX_ALLOW; 139 } 140 141 /* fallthrough -- should not be used */ 142 return EX_DENY; 143 } 144 145 void *cmodeL_put_param(void *r_in, const char *param) 146 { 147 aModeLEntry *r = (aModeLEntry *)r_in; 148 149 if (!r) 150 { 151 /* Need to create one */ 152 r = safe_alloc(sizeof(aModeLEntry)); 153 } 154 strlcpy(r->linked, param, sizeof(r->linked)); 155 return (void *)r; 156 } 157 158 const char *cmodeL_get_param(void *r_in) 159 { 160 aModeLEntry *r = (aModeLEntry *)r_in; 161 static char retbuf[CHANNELLEN+1]; 162 163 if (!r) 164 return NULL; 165 166 strlcpy(retbuf, r->linked, sizeof(retbuf)); 167 return retbuf; 168 } 169 170 /** Convert parameter to something proper. 171 * NOTE: client may be NULL 172 */ 173 const char *cmodeL_conv_param(const char *param, Client *client, Channel *channel) 174 { 175 if (!valid_channelname(param)) 176 return NULL; 177 178 return param; 179 } 180 181 int cmodeL_free_param(void *r, int soft) 182 { 183 safe_free(r); 184 return 0; 185 } 186 187 void *cmodeL_dup_struct(void *r_in) 188 { 189 aModeLEntry *r = (aModeLEntry *)r_in; 190 aModeLEntry *w = safe_alloc(sizeof(aModeLEntry)); 191 192 memcpy(w, r, sizeof(aModeLEntry)); 193 return (void *)w; 194 } 195 196 int cmodeL_sjoin_check(Channel *channel, void *ourx, void *theirx) 197 { 198 aModeLEntry *our = (aModeLEntry *)ourx; 199 aModeLEntry *their = (aModeLEntry *)theirx; 200 201 if (!strcmp(our->linked, their->linked)) 202 return EXSJ_SAME; 203 if (strcmp(our->linked, their->linked) > 0) 204 return EXSJ_WEWON; 205 return EXSJ_THEYWON; 206 } 207 208 int extban_link_syntax(Client *client, int checkt, const char *reason) 209 { 210 if (MyUser(client) && (checkt == EXBCHK_PARAM)) 211 { 212 sendnotice(client, "Error when setting ban: %s", reason); 213 sendnotice(client, " Syntax: +b ~f:#channel:mask"); 214 sendnotice(client, "Examples:"); 215 sendnotice(client, " +b ~f:#public:*!*@badisp.org"); 216 sendnotice(client, " +b ~f:#public:~c:#badchannel"); 217 sendnotice(client, "Multiple channels are not supported"); 218 sendnotice(client, "Valid masks are nick!user@host or another extban type such as ~a, ~c, ~S, etc"); 219 } 220 return 0; // Reject ban 221 } 222 223 int extban_link_is_ok(BanContext *b) 224 { 225 static char paramtmp[MAX_EB_LEN + 1]; 226 char *matchby; // Matching method, such as 'n!u@h' 227 char *chan; 228 229 // Always permit deletion 230 if (b->what == MODE_DEL) 231 return 1; 232 233 if (b->ban_type != EXBTYPE_BAN) 234 { 235 if (b->is_ok_check == EXBCHK_PARAM) 236 sendnotice(b->client, "Ban type ~f only works with bans (+b) and not with exceptions or invex (+e/+I)"); 237 return 0; // Reject 238 } 239 240 strlcpy(paramtmp, b->banstr, sizeof(paramtmp)); // Work on a size-truncated copy 241 chan = paramtmp; 242 matchby = strchr(paramtmp, ':'); 243 if (!matchby || !matchby[1]) 244 return extban_link_syntax(b->client, b->is_ok_check, "Invalid syntax"); 245 *matchby++ = '\0'; 246 247 if (*chan != '#' || strchr(b->banstr, ',')) 248 return extban_link_syntax(b->client, b->is_ok_check, "Invalid channel"); 249 250 b->banstr = matchby; 251 if (extban_is_ok_nuh_extban(b) == 0) 252 return extban_link_syntax(b->client, b->is_ok_check, "Invalid matcher"); 253 254 return 1; // Is ok 255 } 256 257 const char *extban_link_conv_param(BanContext *b, Extban *extban) 258 { 259 static char retbuf[MAX_EB_LEN + 1]; 260 char paramtmp[MAX_EB_LEN + 1]; 261 char tmpmask[MAX_EB_LEN + 1]; 262 char *matchby; // Matching method, such as 'n!u@h' 263 const char *newmask; // Cleaned matching method, such as 'n!u@h' 264 char *chan; 265 266 strlcpy(paramtmp, b->banstr, sizeof(paramtmp)); // Work on a size-truncated copy 267 chan = paramtmp; 268 matchby = strchr(paramtmp, ':'); 269 if (!matchby || !matchby[1]) 270 return NULL; 271 *matchby++ = '\0'; 272 273 if (!valid_channelname(chan)) 274 return NULL; 275 276 b->banstr = matchby; 277 newmask = extban_conv_param_nuh_or_extban(b, extban); 278 if (BadPtr(newmask)) 279 return NULL; 280 281 snprintf(retbuf, sizeof(retbuf), "%s:%s", chan, newmask); 282 return retbuf; 283 } 284 285 int link_doforward(Client *client, Channel *channel, const char *linked, linkType type) 286 { 287 char linked_channel_buffer[CHANNELLEN+1]; 288 char desc[64]; 289 const char *parv[3]; 290 291 switch (type) 292 { 293 case LINKTYPE_BAN: 294 strncpy(desc, "you are banned", sizeof(desc)); 295 break; 296 297 case LINKTYPE_INVITE: 298 strncpy(desc, "channel is invite only", sizeof(desc)); 299 break; 300 301 case LINKTYPE_OPER: 302 strncpy(desc, "channel is oper only", sizeof(desc)); 303 break; 304 305 case LINKTYPE_SECURE: 306 strncpy(desc, "channel requires a secure connection", sizeof(desc)); 307 break; 308 309 case LINKTYPE_REG: 310 strncpy(desc, "channel requires registration", sizeof(desc)); 311 break; 312 313 case LINKTYPE_LIMIT: 314 strncpy(desc, "channel has become full", sizeof(desc)); 315 break; 316 317 case LINKTYPE_BADKEY: 318 strncpy(desc, "invalid channel key", sizeof(desc)); 319 break; 320 321 default: 322 strncpy(desc, "no reason specified", sizeof(desc)); 323 break; 324 } 325 326 sendto_one(client, NULL, 327 ":%s %d %s %s %s :[Link] Cannot join channel %s (%s) -- transferring you to %s", 328 me.name, ERR_LINKCHANNEL, client->name, channel->name, linked, 329 channel->name, desc, linked); 330 331 strlcpy(linked_channel_buffer, linked, sizeof(linked_channel_buffer)); 332 parv[0] = client->name; 333 parv[1] = linked_channel_buffer; 334 parv[2] = NULL; 335 336 do_join(client, 2, parv); 337 338 return HOOK_DENY; // Original channel join = ignored 339 } 340 341 int link_pre_localjoin_cb(Client *client, Channel *channel, const char *key) 342 { 343 const char *linked; 344 int canjoin; 345 char *error = NULL; 346 347 // User might already be on this channel, let's also exclude any possible services bots early 348 if (IsULine(client) || find_membership_link(client->user->channel, channel)) 349 return HOOK_CONTINUE; 350 351 // Extbans take precedence over +L #channel and other restrictions, 352 // only /INVITE from chanop bypasses: 353 if (!is_invited(client, channel)) 354 { 355 Ban *ban; 356 BanContext *b = safe_alloc(sizeof(BanContext)); 357 char bantmp[MAX_EB_LEN + 1]; 358 char *banchan; 359 char *banmask; 360 361 b->client = client; 362 b->channel = channel; 363 b->ban_check_types = BANCHK_JOIN; 364 365 for (ban = channel->banlist; ban; ban = ban->next) 366 { 367 if (!strncmp(ban->banstr, "~f:", 3)) 368 { 369 strlcpy(bantmp, ban->banstr + 3, sizeof(bantmp)); 370 } else 371 if (!strncmp(ban->banstr, "~forward:", 9)) 372 { 373 strlcpy(bantmp, ban->banstr + 9, sizeof(bantmp)); 374 } else 375 if (!strncmp(ban->banstr, "~t:", 3)) 376 { 377 /* A timed ban, but is it for us? Need to parse a little: 378 * ~t:dddd:~f:... 379 */ 380 char *p = strchr(ban->banstr + 3, ':'); 381 if (p && !strncmp(p, ":~f:", 4)) 382 { 383 strlcpy(bantmp, p + 4, sizeof(bantmp)); 384 } else 385 if (p && !strncmp(p, ":~forward:", 10)) 386 { 387 strlcpy(bantmp, p + 10, sizeof(bantmp)); 388 } else { 389 /* Not for us - some other ~t ban */ 390 continue; 391 } 392 } else 393 if (!strncmp(ban->banstr, "~time:", 6)) 394 { 395 /* A timed ban, but is it for us? Need to parse a little: 396 * ~t:dddd:~f:... 397 */ 398 char *p = strchr(ban->banstr + 6, ':'); 399 if (p && !strncmp(p, ":~f:", 4)) 400 { 401 strlcpy(bantmp, p + 4, sizeof(bantmp)); 402 } else 403 if (p && !strncmp(p, ":~forward:", 10)) 404 { 405 strlcpy(bantmp, p + 10, sizeof(bantmp)); 406 } else { 407 /* Not for us - some other ~t ban */ 408 continue; 409 } 410 } else 411 { 412 /* Not for us */ 413 continue; 414 } 415 banchan = bantmp; 416 banmask = strchr(bantmp, ':'); 417 if (!banmask || !banmask[1]) 418 continue; 419 *banmask++ = '\0'; 420 421 b->banstr = banmask; 422 if (ban_check_mask(b)) 423 { 424 safe_free(b); 425 return link_doforward(client, channel, banchan, LINKTYPE_BAN); 426 } 427 } 428 429 safe_free(b); 430 } 431 432 // Either +L is not set, or it is set but the parameter isn't stored somehow 433 if (!(channel->mode.mode & EXTMODE_LINK) || !(linked = cm_getparameter(channel, 'L'))) 434 return HOOK_CONTINUE; 435 436 // can_join() actually returns 0 if we *can* join a channel, so we don't need to bother checking any further conditions 437 if (!(canjoin = can_join(client, channel, key, &error))) 438 return HOOK_CONTINUE; 439 440 // Oper only channel 441 if (has_channel_mode(channel, 'O') && !IsOper(client)) 442 return link_doforward(client, channel, linked, LINKTYPE_OPER); 443 444 // TLS connected users only 445 if (has_channel_mode(channel, 'z') && !IsSecureConnect(client)) 446 return link_doforward(client, channel, linked, LINKTYPE_SECURE); 447 448 // Registered/identified users only 449 if (has_channel_mode(channel, 'R') && !IsRegNick(client)) 450 return link_doforward(client, channel, linked, LINKTYPE_REG); 451 452 // For a couple of conditions we can use the return value from can_join() =] 453 switch(canjoin) { 454 // Any ban other than our own ~f: extban 455 case ERR_BANNEDFROMCHAN: 456 return link_doforward(client, channel, linked, LINKTYPE_BAN); 457 458 // Invite only 459 case ERR_INVITEONLYCHAN: 460 return link_doforward(client, channel, linked, LINKTYPE_INVITE); 461 462 // User limit 463 case ERR_CHANNELISFULL: 464 return link_doforward(client, channel, linked, LINKTYPE_LIMIT); 465 466 // Specified channel key doesn't match 467 case ERR_BADCHANNELKEY: 468 return link_doforward(client, channel, linked, LINKTYPE_BADKEY); 469 470 default: 471 break; 472 } 473 474 // Let any other errors be handled by their respective modules 475 return HOOK_CONTINUE; 476 }