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 }