unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

md.c (15443B)

      1 /*
      2  * Module Data module (command MD)
      3  * (C) Copyright 2014-.. Bram Matthys and The UnrealIRCd Team
      4  *
      5  * This file contains all commands that deal with sending and
      6  * receiving module data over the network.
      7  */
      8 
      9 #include "unrealircd.h"
     10 
     11 ModuleHeader MOD_HEADER
     12   = {
     13 	"md",
     14 	"5.0",
     15 	"command /MD (S2S only)",
     16 	"UnrealIRCd Team",
     17 	"unrealircd-6",
     18     };
     19 
     20 CMD_FUNC(cmd_md);
     21 void _broadcast_md_client(ModDataInfo *mdi, Client *client, ModData *md);
     22 void _broadcast_md_channel(ModDataInfo *mdi, Channel *channel, ModData *md);
     23 void _broadcast_md_member(ModDataInfo *mdi, Channel *channel, Member *m, ModData *md);
     24 void _broadcast_md_membership(ModDataInfo *mdi, Client *client, Membership *m, ModData *md);
     25 void _broadcast_md_globalvar(ModDataInfo *mdi, ModData *md);
     26 void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, const char *varname, const char *value);
     27 void _broadcast_md_channel_cmd(Client *except, Client *sender, Channel *channel, const char *varname, const char *value);
     28 void _broadcast_md_member_cmd(Client *except, Client *sender, Channel *channel, Client *client, const char *varname, const char *value);
     29 void _broadcast_md_membership_cmd(Client *except, Client *sender, Client *client, Channel *channel, const char *varname, const char *value);
     30 void _broadcast_md_globalvar_cmd(Client *except, Client *sender, const char *varname, const char *value);
     31 void _moddata_add_s2s_mtags(Client *client, MessageTag **mtags);
     32 void _moddata_extract_s2s_mtags(Client *client, MessageTag *mtags);
     33 void _send_moddata_client(Client *srv, Client *client);
     34 void _send_moddata_channel(Client *srv, Channel *channel);
     35 void _send_moddata_members(Client *srv);
     36 void _broadcast_moddata_client(Client *client);
     37 
     38 extern MODVAR ModDataInfo *MDInfo;
     39 
     40 MOD_TEST()
     41 {
     42 	MARK_AS_OFFICIAL_MODULE(modinfo);
     43 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_CLIENT, _broadcast_md_client);
     44 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_CHANNEL, _broadcast_md_channel);
     45 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_MEMBER, _broadcast_md_member);
     46 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_MEMBERSHIP, _broadcast_md_membership);
     47 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_GLOBALVAR, _broadcast_md_globalvar);
     48 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_CLIENT_CMD, _broadcast_md_client_cmd);
     49 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_CHANNEL_CMD, _broadcast_md_channel_cmd);
     50 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_MEMBER_CMD, _broadcast_md_member_cmd);
     51 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_MEMBERSHIP_CMD, _broadcast_md_membership_cmd);
     52 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MD_GLOBALVAR_CMD, _broadcast_md_globalvar_cmd);
     53 	EfunctionAddVoid(modinfo->handle, EFUNC_MODDATA_ADD_S2S_MTAGS, _moddata_add_s2s_mtags);
     54 	EfunctionAddVoid(modinfo->handle, EFUNC_MODDATA_EXTRACT_S2S_MTAGS, _moddata_extract_s2s_mtags);
     55 	EfunctionAddVoid(modinfo->handle, EFUNC_SEND_MODDATA_CLIENT, _send_moddata_client);
     56 	EfunctionAddVoid(modinfo->handle, EFUNC_SEND_MODDATA_CHANNEL, _send_moddata_channel);
     57 	EfunctionAddVoid(modinfo->handle, EFUNC_SEND_MODDATA_MEMBERS, _send_moddata_members);
     58 	EfunctionAddVoid(modinfo->handle, EFUNC_BROADCAST_MODDATA_CLIENT, _broadcast_moddata_client);
     59 	return MOD_SUCCESS;
     60 }
     61 
     62 MOD_INIT()
     63 {
     64 	CommandAdd(modinfo->handle, "MD", cmd_md, MAXPARA, CMD_SERVER);
     65 	return MOD_SUCCESS;
     66 }
     67 
     68 MOD_LOAD()
     69 {
     70 	return MOD_SUCCESS;
     71 }
     72 
     73 
     74 MOD_UNLOAD()
     75 {
     76 	return MOD_SUCCESS;
     77 }
     78 
     79 /** Check if client may write to this MD object */
     80 int md_access_check(Client *client, ModDataInfo *md, Client *target)
     81 {
     82 	if ((client == target) && md->self_write)
     83 		return 1;
     84 
     85 	if (MyConnect(target) && !md->remote_write)
     86 	{
     87 		unreal_log(ULOG_WARNING, "md", "REMOTE_MD_WRITE_DENIED", client,
     88 		           "Remote server $client tried to write moddata $moddata_name "
     89 		           "of a client from ours ($target.name) -- attempt BLOCKED",
     90 		           log_data_string("moddata_name", md->name),
     91 		           log_data_client("target", target));
     92 		return 0;
     93 	}
     94 
     95 	return 1;
     96 }
     97 
     98 /** Set ModData command.
     99  *  Syntax: MD <type> <object name> <variable name> <value>
    100  * Example: MD client Syzop sslfp 123456789
    101  *
    102  * If <value> is ommitted, the variable is unset & freed.
    103  *
    104  * The appropriate module is called to set the data (unserialize) and
    105  * then the command is broadcasted to all other servers.
    106  *
    107  * Technical documentation (if writing services) is available at:
    108  * https://www.unrealircd.org/docs/Server_protocol:MD_command
    109  * Module API documentation (if writing an UnrealIRCd module):
    110  * https://www.unrealircd.org/docs/Dev:Module_Storage
    111  */
    112 CMD_FUNC(cmd_md)
    113 {
    114 	const char *type, *objname, *varname, *value;
    115 	ModDataInfo *md;
    116 
    117 	if (!IsServer(client) || (parc < 4) || BadPtr(parv[3]))
    118 		return;
    119 
    120 	type = parv[1];
    121 	objname = parv[2];
    122 	varname = parv[3];
    123 	value = parv[4]; /* may be NULL */
    124 
    125 	if (!strcmp(type, "client"))
    126 	{
    127 		Client *target = find_client(objname, NULL);
    128 		md = findmoddata_byname(varname, MODDATATYPE_CLIENT);
    129 		if (!md || !md->unserialize || !target)
    130 			return;
    131 
    132 		if (!md_access_check(client, md, target))
    133 			return;
    134 
    135 		if (value)
    136 			md->unserialize(value, &moddata_client(target, md));
    137 		else
    138 		{
    139 			if (md->free)
    140 				md->free(&moddata_client(target, md));
    141 			memset(&moddata_client(target, md), 0, sizeof(ModData));
    142 		}
    143 		/* Pass on to other servers */
    144 		broadcast_md_client_cmd(client->direction, client, target, varname, value);
    145 	} else
    146 	if (!strcmp(type, "channel"))
    147 	{
    148 		Channel *channel = find_channel(objname);
    149 		md = findmoddata_byname(varname, MODDATATYPE_CHANNEL);
    150 		if (!md || !md->unserialize || !channel)
    151 			return;
    152 		if (value)
    153 			md->unserialize(value, &moddata_channel(channel, md));
    154 		else
    155 		{
    156 			if (md->free)
    157 				md->free(&moddata_channel(channel, md));
    158 			memset(&moddata_channel(channel, md), 0, sizeof(ModData));
    159 		}
    160 		/* Pass on to other servers */
    161 		broadcast_md_channel_cmd(client->direction, client, channel, varname, value);
    162 	} else
    163 	if (!strcmp(type, "member"))
    164 	{
    165 		Client *target;
    166 		Channel *channel;
    167 		Member *m;
    168 		char *p;
    169 
    170 		/* for member the object name is like '#channel/Syzop' */
    171 		p = strchr(objname, ':');
    172 		if (!p)
    173 			return;
    174 		*p++ = '\0';
    175 
    176 		channel = find_channel(objname);
    177 		if (!channel)
    178 			return;
    179 
    180 		target = find_user(p, NULL);
    181 		if (!target)
    182 			return;
    183 
    184 		m = find_member_link(channel->members, target);
    185 		if (!m)
    186 			return;
    187 
    188 		md = findmoddata_byname(varname, MODDATATYPE_MEMBER);
    189 		if (!md || !md->unserialize)
    190 			return;
    191 
    192 		if (!md_access_check(client, md, target))
    193 			return;
    194 
    195 		if (value)
    196 			md->unserialize(value, &moddata_member(m, md));
    197 		else
    198 		{
    199 			if (md->free)
    200 				md->free(&moddata_member(m, md));
    201 			memset(&moddata_member(m, md), 0, sizeof(ModData));
    202 		}
    203 		/* Pass on to other servers */
    204 		broadcast_md_member_cmd(client->direction, client, channel, target, varname, value);
    205 	} else
    206 	if (!strcmp(type, "membership"))
    207 	{
    208 		Client *target;
    209 		Channel *channel;
    210 		Membership *m;
    211 		char *p;
    212 
    213 		/* for membership the object name is like 'Syzop/#channel' */
    214 		p = strchr(objname, ':');
    215 		if (!p)
    216 			return;
    217 		*p++ = '\0';
    218 
    219 		target = find_user(objname, NULL);
    220 		if (!target)
    221 			return;
    222 
    223 		channel = find_channel(p);
    224 		if (!channel)
    225 			return;
    226 
    227 		m = find_membership_link(target->user->channel, channel);
    228 		if (!m)
    229 			return;
    230 
    231 		md = findmoddata_byname(varname, MODDATATYPE_MEMBERSHIP);
    232 		if (!md || !md->unserialize)
    233 			return;
    234 
    235 		if (!md_access_check(client, md, target))
    236 			return;
    237 
    238 		if (value)
    239 			md->unserialize(value, &moddata_membership(m, md));
    240 		else
    241 		{
    242 			if (md->free)
    243 				md->free(&moddata_membership(m, md));
    244 			memset(&moddata_membership(m, md), 0, sizeof(ModData));
    245 		}
    246 		/* Pass on to other servers */
    247 		broadcast_md_membership_cmd(client->direction, client, target, channel, varname, value);
    248 	} else
    249 	if (!strcmp(type, "globalvar"))
    250 	{
    251 		/* objname is ignored */
    252 		md = findmoddata_byname(varname, MODDATATYPE_GLOBAL_VARIABLE);
    253 		if (!md || !md->unserialize)
    254 			return;
    255 		if (value)
    256 			md->unserialize(value, &moddata_global_variable(md));
    257 		else
    258 		{
    259 			if (md->free)
    260 				md->free(&moddata_global_variable(md));
    261 			memset(&moddata_global_variable(md), 0, sizeof(ModData));
    262 		}
    263 		/* Pass on to other servers */
    264 		broadcast_md_globalvar_cmd(client->direction, client, varname, value);
    265 	}
    266 }
    267 
    268 void _broadcast_md_client_cmd(Client *except, Client *sender, Client *client, const char *varname, const char *value)
    269 {
    270 	if (value)
    271 	{
    272 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s %s :%s",
    273 			sender->id, "client", client->id, varname, value);
    274 	}
    275 	else
    276 	{
    277 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s %s",
    278 			sender->id, "client", client->id, varname);
    279 	}
    280 }
    281 
    282 void _broadcast_md_channel_cmd(Client *except, Client *sender, Channel *channel, const char *varname, const char *value)
    283 {
    284 	if (value)
    285 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s %s :%s",
    286 			sender->id, "channel", channel->name, varname, value);
    287 	else
    288 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s %s",
    289 			sender->id, "channel", channel->name, varname);
    290 }
    291 
    292 void _broadcast_md_member_cmd(Client *except, Client *sender, Channel *channel, Client *client, const char *varname, const char *value)
    293 {
    294 	if (value)
    295 	{
    296 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s :%s",
    297 			sender->id, "member", channel->name, client->id, varname, value);
    298 	}
    299 	else
    300 	{
    301 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s",
    302 			sender->id, "member", channel->name, client->id, varname);
    303 	}
    304 }
    305 
    306 void _broadcast_md_membership_cmd(Client *except, Client *sender, Client *client, Channel *channel, const char *varname, const char *value)
    307 {
    308 	if (value)
    309 	{
    310 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s :%s",
    311 			sender->id, "membership", client->id, channel->name, varname, value);
    312 	}
    313 	else
    314 	{
    315 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s:%s %s",
    316 			sender->id, "membership", client->id, channel->name, varname);
    317 	}
    318 }
    319 
    320 void _broadcast_md_globalvar_cmd(Client *except, Client *sender, const char *varname, const char *value)
    321 {
    322 	if (value)
    323 	{
    324 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s :%s",
    325 			sender->id, "globalvar", varname, value);
    326 	}
    327 	else
    328 	{
    329 		sendto_server(except, 0, 0, NULL, ":%s MD %s %s",
    330 			sender->id, "globalvar", varname);
    331 	}
    332 }
    333 
    334 /** Send module data update to all servers.
    335  * @param mdi    Module Data Info structure (which you received from ModDataAdd)
    336  * @param client The affected client
    337  * @param md     The ModData. May be NULL for unset.
    338  */
    339  
    340 void _broadcast_md_client(ModDataInfo *mdi, Client *client, ModData *md)
    341 {
    342 	const char *value = md ? mdi->serialize(md) : NULL;
    343 
    344 	broadcast_md_client_cmd(NULL, &me, client, mdi->name, value);
    345 }
    346 
    347 void _broadcast_md_channel(ModDataInfo *mdi, Channel *channel, ModData *md)
    348 {
    349 	const char *value = md ? mdi->serialize(md) : NULL;
    350 
    351 	broadcast_md_channel_cmd(NULL, &me, channel, mdi->name, value);
    352 }
    353 
    354 void _broadcast_md_member(ModDataInfo *mdi, Channel *channel, Member *m, ModData *md)
    355 {
    356 	const char *value = md ? mdi->serialize(md) : NULL;
    357 
    358 	broadcast_md_member_cmd(NULL, &me, channel, m->client, mdi->name, value);
    359 }
    360 
    361 void _broadcast_md_membership(ModDataInfo *mdi, Client *client, Membership *m, ModData *md)
    362 {
    363 	const char *value = md ? mdi->serialize(md) : NULL;
    364 
    365 	broadcast_md_membership_cmd(NULL, &me, client, m->channel, mdi->name, value);
    366 }
    367 
    368 void _broadcast_md_globalvar(ModDataInfo *mdi, ModData *md)
    369 {
    370 	const char *value = md ? mdi->serialize(md) : NULL;
    371 
    372 	broadcast_md_globalvar_cmd(NULL, &me, mdi->name, value);
    373 }
    374 
    375 /** Send all moddata attached to client 'client' to remote server 'srv' (if the module wants this), called by .. */
    376 void _send_moddata_client(Client *srv, Client *client)
    377 {
    378 	ModDataInfo *mdi;
    379 
    380 	for (mdi = MDInfo; mdi; mdi = mdi->next)
    381 	{
    382 		if ((mdi->type == MODDATATYPE_CLIENT) && mdi->sync && mdi->serialize)
    383 		{
    384 			const char *value = mdi->serialize(&moddata_client(client, mdi));
    385 			if (value)
    386 				sendto_one(srv, NULL, ":%s MD %s %s %s :%s",
    387 					me.id, "client", client->id, mdi->name, value);
    388 		}
    389 	}
    390 }
    391 
    392 /** Enhance the command with moddata message tags, so we can send
    393  * traffic like @s2s-md/certfp=xxxxx UID ....
    394  */
    395 void _moddata_add_s2s_mtags(Client *client, MessageTag **mtags_list)
    396 {
    397 	ModDataInfo *mdi;
    398 	char name[128];
    399 
    400 	for (mdi = MDInfo; mdi; mdi = mdi->next)
    401 	{
    402 		if ((mdi->type == MODDATATYPE_CLIENT) && (mdi->sync == MODDATA_SYNC_EARLY) && mdi->serialize)
    403 		{
    404 			MessageTag *m;
    405 			const char *value = mdi->serialize(&moddata_client(client, mdi));
    406 			if (!value)
    407 				continue;
    408 			snprintf(name, sizeof(name), "s2s-md/%s", mdi->name);
    409 
    410 			m = safe_alloc(sizeof(MessageTag));
    411 			safe_strdup(m->name, name);
    412 			safe_strdup(m->value, value);
    413 			AddListItem(m, *mtags_list);
    414 		}
    415 	}
    416 }
    417 
    418 /** Extract the s2s-md/<moddataname> tags again from an incoming command,
    419  * eg @s2s-md/certfp=xxxxx UID ....
    420  */
    421 void _moddata_extract_s2s_mtags(Client *client, MessageTag *mtags)
    422 {
    423 	MessageTag *m;
    424 	ModDataInfo *md;
    425 
    426 	for (m = mtags; m; m = m->next)
    427 	{
    428 		if (!strncmp(m->name, "s2s-md/", 7))
    429 		{
    430 			char *varname = m->name + 7;
    431 			char *value = m->value;
    432 
    433 			if (!value)
    434 				continue;
    435 
    436 			md = findmoddata_byname(varname, MODDATATYPE_CLIENT);
    437 			if (!md || !md->unserialize)
    438 				continue;
    439 
    440 			if (!md_access_check(client, md, client))
    441 				return;
    442 
    443 			md->unserialize(value, &moddata_client(client, md));
    444 		}
    445 	}
    446 }
    447 
    448 /** Send all moddata attached to channel 'channel' to remote server 'srv' (if the module wants this), called by SJOIN */
    449 void _send_moddata_channel(Client *srv, Channel *channel)
    450 {
    451 	ModDataInfo *mdi;
    452 
    453 	for (mdi = MDInfo; mdi; mdi = mdi->next)
    454 	{
    455 		if ((mdi->type == MODDATATYPE_CHANNEL) && mdi->sync && mdi->serialize)
    456 		{
    457 			const char *value = mdi->serialize(&moddata_channel(channel, mdi));
    458 			if (value)
    459 				sendto_one(srv, NULL, ":%s MD %s %s %s :%s",
    460 					me.id, "channel", channel->name, mdi->name, value);
    461 		}
    462 	}
    463 }
    464 
    465 /** Send all moddata attached to member & memberships for 'channel' to remote server 'srv' (if the module wants this), called by SJOIN */
    466 void _send_moddata_members(Client *srv)
    467 {
    468 	ModDataInfo *mdi;
    469 	Channel *channel;
    470 	Client *client;
    471 
    472 	for (channel = channels; channel; channel = channel->nextch)
    473 	{
    474 		Member *m;
    475 		for (m = channel->members; m; m = m->next)
    476 		{
    477 			client = m->client;
    478 			if (client->direction == srv)
    479 				continue; /* from srv's direction */
    480 			for (mdi = MDInfo; mdi; mdi = mdi->next)
    481 			{
    482 				if ((mdi->type == MODDATATYPE_MEMBER) && mdi->sync && mdi->serialize)
    483 				{
    484 					const char *value = mdi->serialize(&moddata_member(m, mdi));
    485 					if (value)
    486 						sendto_one(srv, NULL, ":%s MD %s %s:%s %s :%s",
    487 							me.id, "member", channel->name, client->id, mdi->name, value);
    488 				}
    489 			}
    490 		}
    491 	}
    492 
    493 	list_for_each_entry(client, &client_list, client_node)
    494 	{
    495 		Membership *m;
    496 		if (!IsUser(client) || !client->user)
    497 			continue;
    498 
    499 		if (client->direction == srv)
    500 			continue; /* from srv's direction */
    501 
    502 		for (m = client->user->channel; m; m = m->next)
    503 		{
    504 			for (mdi = MDInfo; mdi; mdi = mdi->next)
    505 			{
    506 				if ((mdi->type == MODDATATYPE_MEMBERSHIP) && mdi->sync && mdi->serialize)
    507 				{
    508 					const char *value = mdi->serialize(&moddata_membership(m, mdi));
    509 					if (value)
    510 						sendto_one(srv, NULL, ":%s MD %s %s:%s %s :%s",
    511 							me.id, "membership", client->id, m->channel->name, mdi->name, value);
    512 				}
    513 			}
    514 		}
    515 	}
    516 }
    517 
    518 /** Broadcast moddata attached to client 'client' to all servers. */
    519 void _broadcast_moddata_client(Client *client)
    520 {
    521 	Client *acptr;
    522 
    523 	list_for_each_entry(acptr, &server_list, special_node)
    524 	{
    525 		send_moddata_client(acptr, client);
    526 	}
    527 }