unrealircd

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

server.c (9966B)

      1 /* server.* RPC calls
      2  * (C) Copyright 2022-.. Bram Matthys (Syzop) and the UnrealIRCd team
      3  * License: GPLv2 or later
      4  */
      5 
      6 #include "unrealircd.h"
      7 
      8 ModuleHeader MOD_HEADER
      9 = {
     10 	"rpc/server",
     11 	"1.0.0",
     12 	"server.* RPC calls",
     13 	"UnrealIRCd Team",
     14 	"unrealircd-6",
     15 };
     16 
     17 /* Forward declarations */
     18 RPC_CALL_FUNC(rpc_server_list);
     19 RPC_CALL_FUNC(rpc_server_get);
     20 RPC_CALL_FUNC(rpc_server_rehash);
     21 RPC_CALL_FUNC(rpc_server_connect);
     22 RPC_CALL_FUNC(rpc_server_disconnect);
     23 RPC_CALL_FUNC(rpc_server_module_list);
     24 
     25 int rpc_server_rehash_log(int failure, json_t *rehash_log);
     26 
     27 MOD_INIT()
     28 {
     29 	RPCHandlerInfo r;
     30 
     31 	MARK_AS_OFFICIAL_MODULE(modinfo);
     32 
     33 	memset(&r, 0, sizeof(r));
     34 	r.method = "server.list";
     35 	r.loglevel = ULOG_DEBUG;
     36 	r.call = rpc_server_list;
     37 	if (!RPCHandlerAdd(modinfo->handle, &r))
     38 	{
     39 		config_error("[rpc/server] Could not register RPC handler");
     40 		return MOD_FAILED;
     41 	}
     42 	memset(&r, 0, sizeof(r));
     43 	r.method = "server.get";
     44 	r.loglevel = ULOG_DEBUG;
     45 	r.call = rpc_server_get;
     46 	if (!RPCHandlerAdd(modinfo->handle, &r))
     47 	{
     48 		config_error("[rpc/server] Could not register RPC handler");
     49 		return MOD_FAILED;
     50 	}
     51 	memset(&r, 0, sizeof(r));
     52 	r.method = "server.rehash";
     53 	r.call = rpc_server_rehash;
     54 	if (!RPCHandlerAdd(modinfo->handle, &r))
     55 	{
     56 		config_error("[rpc/server] Could not register RPC handler");
     57 		return MOD_FAILED;
     58 	}
     59 	memset(&r, 0, sizeof(r));
     60 	r.method = "server.connect";
     61 	r.call = rpc_server_connect;
     62 	if (!RPCHandlerAdd(modinfo->handle, &r))
     63 	{
     64 		config_error("[rpc/server] Could not register RPC handler");
     65 		return MOD_FAILED;
     66 	}
     67 	memset(&r, 0, sizeof(r));
     68 	r.method = "server.disconnect";
     69 	r.call = rpc_server_disconnect;
     70 	if (!RPCHandlerAdd(modinfo->handle, &r))
     71 	{
     72 		config_error("[rpc/server] Could not register RPC handler");
     73 		return MOD_FAILED;
     74 	}
     75 	memset(&r, 0, sizeof(r));
     76 	r.method = "server.module_list";
     77 	r.loglevel = ULOG_DEBUG;
     78 	r.call = rpc_server_module_list;
     79 	if (!RPCHandlerAdd(modinfo->handle, &r))
     80 	{
     81 		config_error("[rpc/server] Could not register RPC handler");
     82 		return MOD_FAILED;
     83 	}
     84 
     85 	HookAdd(modinfo->handle, HOOKTYPE_REHASH_LOG, 0, rpc_server_rehash_log);
     86 
     87 	return MOD_SUCCESS;
     88 }
     89 
     90 MOD_LOAD()
     91 {
     92 	return MOD_SUCCESS;
     93 }
     94 
     95 MOD_UNLOAD()
     96 {
     97 	return MOD_SUCCESS;
     98 }
     99 
    100 RPC_CALL_FUNC(rpc_server_list)
    101 {
    102 	json_t *result, *list, *item;
    103 	Client *acptr;
    104 
    105 	result = json_object();
    106 	list = json_array();
    107 	json_object_set_new(result, "list", list);
    108 
    109 	list_for_each_entry(acptr, &global_server_list, client_node)
    110 	{
    111 		if (!IsServer(acptr) && !IsMe(acptr))
    112 			continue;
    113 
    114 		item = json_object();
    115 		json_expand_client(item, NULL, acptr, 99);
    116 		json_array_append_new(list, item);
    117 	}
    118 
    119 	rpc_response(client, request, result);
    120 	json_decref(result);
    121 }
    122 
    123 RPC_CALL_FUNC(rpc_server_get)
    124 {
    125 	json_t *result, *list, *item;
    126 	const char *server;
    127 	Client *acptr;
    128 
    129 	OPTIONAL_PARAM_STRING("server", server);
    130 	if (server)
    131 	{
    132 		if (!(acptr = find_server(server, NULL)))
    133 		{
    134 			rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server not found");
    135 			return;
    136 		}
    137 	} else {
    138 		acptr = &me;
    139 	}
    140 
    141 	result = json_object();
    142 	json_expand_client(result, "server", acptr, 99);
    143 	rpc_response(client, request, result);
    144 	json_decref(result);
    145 }
    146 
    147 RPC_CALL_FUNC(rpc_server_rehash)
    148 {
    149 	json_t *result, *list, *item;
    150 	const char *server;
    151 	Client *acptr;
    152 
    153 	OPTIONAL_PARAM_STRING("server", server);
    154 	if (server)
    155 	{
    156 		if (!(acptr = find_server(server, NULL)))
    157 		{
    158 			rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server not found");
    159 			return;
    160 		}
    161 	} else {
    162 		acptr = &me;
    163 	}
    164 
    165 	if (acptr != &me)
    166 	{
    167 		/* Forward to remote */
    168 		if (rrpc_supported_simple(acptr, NULL))
    169 		{
    170 			/* Server supports RRPC and will handle the response */
    171 			rpc_send_request_to_remote(client, acptr, request);
    172 		} else {
    173 			/* Server does not support RRPC, so we can only do best effort: */
    174 			sendto_one(acptr, NULL, ":%s REHASH %s", me.id, acptr->name);
    175 			result = json_boolean(1);
    176 			rpc_response(client, request, result);
    177 			json_decref(result);
    178 		}
    179 		return;
    180 	}
    181 
    182 	if (client->rpc->rehash_request)
    183 	{
    184 		rpc_error(client, request, JSON_RPC_ERROR_INVALID_REQUEST, "A rehash initiated by you is already in progress");
    185 		return;
    186 	}
    187 
    188 	/* It's for me... */
    189 
    190 	SetMonitorRehash(client);
    191 	SetAsyncRPC(client);
    192 	client->rpc->rehash_request = json_copy(request); // or json_deep_copy ??
    193 
    194 	if (!loop.rehashing)
    195 	{
    196 		unreal_log(ULOG_INFO, "config", "CONFIG_RELOAD", client, "Rehashing server configuration file [by: $client.details]");
    197 		request_rehash(client);
    198 	} /* else.. we simply joined the rehash request so we are notified as well ;) */
    199 
    200 	/* Do NOT send the JSON Response here, it is done by rpc_server_rehash_log()
    201 	 * after the REHASH completed (which may take several seconds).
    202 	 */
    203 }
    204 
    205 int rpc_server_rehash_log(int failure, json_t *rehash_log)
    206 {
    207 	Client *client, *next;
    208 
    209 	list_for_each_entry(client, &unknown_list, lclient_node)
    210 	{
    211 		if (IsRPC(client) && IsMonitorRehash(client) && client->rpc && client->rpc->rehash_request)
    212 		{
    213 			rpc_response(client, client->rpc->rehash_request, rehash_log);
    214 			json_decref(client->rpc->rehash_request);
    215 			client->rpc->rehash_request = NULL;
    216 		}
    217 	}
    218 	list_for_each_entry_safe(client, next, &rpc_remote_list, client_node)
    219 	{
    220 		if (IsMonitorRehash(client) && client->rpc && client->rpc->rehash_request)
    221 		{
    222 			rpc_response(client, client->rpc->rehash_request, rehash_log);
    223 			json_decref(client->rpc->rehash_request);
    224 			client->rpc->rehash_request = NULL;
    225 			free_client(client);
    226 		}
    227 	}
    228 	return 0;
    229 }
    230 
    231 RPC_CALL_FUNC(rpc_server_connect)
    232 {
    233 	json_t *result, *list, *item;
    234 	const char *server, *link_name;
    235 	Client *acptr;
    236 	ConfigItem_link *link;
    237 	const char *err;
    238 
    239 	OPTIONAL_PARAM_STRING("server", server);
    240 	if (server)
    241 	{
    242 		if (!(acptr = find_server(server, NULL)))
    243 		{
    244 			rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server not found");
    245 			return;
    246 		}
    247 	} else {
    248 		acptr = &me;
    249 	}
    250 	REQUIRE_PARAM_STRING("link", link_name);
    251 
    252 	if (acptr != &me)
    253 	{
    254 		/* Not supported atm */
    255 		result = json_boolean(0);
    256 		rpc_response(client, request, result);
    257 		json_decref(result);
    258 		return;
    259 	}
    260 
    261 	if (find_server_quick(link_name))
    262 	{
    263 		rpc_error(client, request, JSON_RPC_ERROR_ALREADY_EXISTS, "Server is already linked");
    264 		return;
    265 	}
    266 
    267 	link = find_link(link_name);
    268 	if (!link)
    269 	{
    270 		rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server with that name does not exist in any link block");
    271 		return;
    272 	}
    273 	if (!link->outgoing.hostname && !link->outgoing.file)
    274 	{
    275 		rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server with that name exists but is not configured as an OUTGOING server.");
    276 		return;
    277 	}
    278 
    279 	if ((err = check_deny_link(link, 0)))
    280 	{
    281 		rpc_error_fmt(client, request, JSON_RPC_ERROR_DENIED, "Server linking is denied via a deny link { } block: %s", err);
    282 		return;
    283 	}
    284 
    285 	unreal_log(ULOG_INFO, "link", "LINK_REQUEST", client,
    286 		   "CONNECT: Link to $link_block requested by $client",
    287 		   log_data_link_block(link));
    288 
    289 	connect_server(link, client, NULL);
    290 	result = json_boolean(1);
    291 	rpc_response(client, request, result);
    292 	json_decref(result);
    293 }
    294 
    295 RPC_CALL_FUNC(rpc_server_disconnect)
    296 {
    297 	json_t *result, *list, *item;
    298 	const char *server, *link_name, *reason;
    299 	Client *acptr, *target;
    300 	MessageTag *mtags = NULL;
    301 
    302 	OPTIONAL_PARAM_STRING("server", server);
    303 	if (server)
    304 	{
    305 		if (!(acptr = find_server(server, NULL)))
    306 		{
    307 			rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server not found");
    308 			return;
    309 		}
    310 	} else {
    311 		acptr = &me;
    312 	}
    313 	REQUIRE_PARAM_STRING("link", link_name);
    314 	REQUIRE_PARAM_STRING("reason", reason);
    315 
    316 	if (acptr != &me)
    317 	{
    318 		/* Not supported atm */
    319 		result = json_boolean(0);
    320 		rpc_response(client, request, result);
    321 		json_decref(result);
    322 		return;
    323 	}
    324 
    325 	target = find_server_quick(link_name);
    326 	if (!target)
    327 	{
    328 		rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server link not found");
    329 		return;
    330 	}
    331 
    332 	if (target == &me)
    333 	{
    334 		rpc_error(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "We cannot disconnect ourselves");
    335 		return;
    336 	}
    337 
    338 	unreal_log(ULOG_INFO, "link", "SQUIT", client,
    339 	           "SQUIT: Forced server disconnect of $target by $client ($reason)",
    340 	           log_data_client("target", target),
    341 	           log_data_string("reason", reason));
    342 
    343 	/* The actual SQUIT: */
    344 	new_message(client, NULL, &mtags);
    345 	exit_client_ex(target, NULL, mtags, reason);
    346 	free_message_tags(mtags);
    347 
    348 	/* Return true */
    349 	result = json_boolean(1);
    350 	rpc_response(client, request, result);
    351 	json_decref(result);
    352 }
    353 
    354 void json_expand_module(json_t *j, const char *key, Module *m, int detail)
    355 {
    356 	char buf[BUFSIZE+1];
    357 	json_t *child;
    358 
    359 	if (key)
    360 	{
    361 		child = json_object();
    362 		json_object_set_new(j, key, child);
    363 	} else {
    364 		child = j;
    365 	}
    366 
    367 	json_object_set_new(child, "name", json_string_unreal(m->header->name));
    368 	json_object_set_new(child, "version", json_string_unreal(m->header->version));
    369 	json_object_set_new(child, "author", json_string_unreal(m->header->author));
    370 	json_object_set_new(child, "description", json_string_unreal(m->header->description));
    371 	json_object_set_new(child, "third_party", json_boolean(m->options & MOD_OPT_OFFICIAL ? 0 : 1));
    372 	json_object_set_new(child, "permanent", json_boolean(m->options & MOD_OPT_PERM ? 1 : 0));
    373 	json_object_set_new(child, "permanent_but_reloadable", json_boolean(m->options & MOD_OPT_PERM_RELOADABLE ? 1 : 0));
    374 }
    375 
    376 RPC_CALL_FUNC(rpc_server_module_list)
    377 {
    378 	json_t *result, *list, *item;
    379 	const char *server;
    380 	Client *acptr;
    381 	Module *m;
    382 
    383 	OPTIONAL_PARAM_STRING("server", server);
    384 	if (server)
    385 	{
    386 		if (!(acptr = find_server(server, NULL)))
    387 		{
    388 			rpc_error(client, request, JSON_RPC_ERROR_NOT_FOUND, "Server not found");
    389 			return;
    390 		}
    391 	} else {
    392 		acptr = &me;
    393 	}
    394 
    395 	if (acptr != &me)
    396 	{
    397 		/* Forward to remote */
    398 		rpc_send_request_to_remote(client, acptr, request);
    399 		return;
    400 	}
    401 
    402 	/* Return true */
    403 	result = json_object();
    404 	list = json_array();
    405 	json_object_set_new(result, "list", list);
    406 
    407 	for (m = Modules; m; m = m->next)
    408 	{
    409 		item = json_object();
    410 		json_expand_module(item, NULL, m, 1);
    411 		json_array_append_new(list, item);
    412 	}
    413 
    414 	rpc_response(client, request, result);
    415 	json_decref(result);
    416 }