unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
rpc.c (50812B)
1 /* 2 * RPC module - for remote management of UnrealIRCd 3 * (C)Copyright 2022 Bram Matthys and the UnrealIRCd team 4 * License: GPLv2 or later 5 */ 6 7 #include "unrealircd.h" 8 #include "dns.h" 9 10 ModuleHeader MOD_HEADER 11 = { 12 "rpc/rpc", 13 "1.0.4", 14 "RPC module for remote management", 15 "UnrealIRCd Team", 16 "unrealircd-6", 17 }; 18 19 /** Maximum length of an rpc-user THIS { }. 20 * As we use the "RPC:" prefix it is nicklen minus that. 21 */ 22 #define RPCUSERLEN (NICKLEN-4) 23 24 /** Timers can be minimum every <this> msec */ 25 #define RPC_MINIMUM_TIMER_MSEC 250 26 27 /* Structs */ 28 typedef struct RPCUser RPCUser; 29 struct RPCUser { 30 RPCUser *prev, *next; 31 SecurityGroup *match; 32 char *name; 33 AuthConfig *auth; 34 }; 35 36 typedef struct RRPC RRPC; 37 struct RRPC { 38 RRPC *prev, *next; 39 int request; 40 char source[IDLEN+1]; 41 char destination[IDLEN+1]; 42 char *requestid; 43 dbuf data; 44 }; 45 46 typedef struct OutstandingRRPC OutstandingRRPC; 47 struct OutstandingRRPC { 48 OutstandingRRPC *prev, *next; 49 time_t sent; 50 char source[IDLEN+1]; 51 char destination[IDLEN+1]; 52 char *requestid; 53 }; 54 55 typedef struct RPCTimer RPCTimer; 56 struct RPCTimer { 57 RPCTimer *prev, *next; 58 long every_msec; 59 Client *client; 60 char *timer_id; 61 json_t *request; 62 struct timeval last_run; 63 }; 64 65 /* Forward declarations */ 66 int rpc_config_test_listen(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 67 int rpc_config_run_ex_listen(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr); 68 int rpc_config_test_rpc_user(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 69 int rpc_config_run_rpc_user(ConfigFile *cf, ConfigEntry *ce, int type); 70 int rpc_client_accept(Client *client); 71 int rpc_pre_local_handshake_timeout(Client *client, const char **comment); 72 void rpc_client_handshake_unix_socket(Client *client); 73 void rpc_client_handshake_web(Client *client); 74 int rpc_handle_webrequest(Client *client, WebRequest *web); 75 int rpc_handle_webrequest_websocket(Client *client, WebRequest *web); 76 int rpc_websocket_handshake_send_response(Client *client); 77 int rpc_handle_webrequest_data(Client *client, WebRequest *web, const char *buf, int len); 78 int rpc_handle_body_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2); 79 int rpc_packet_in_websocket(Client *client, char *readbuf, int length); 80 int rpc_packet_in_unix_socket(Client *client, const char *readbuf, int *length); 81 void rpc_call_text(Client *client, const char *buf, int len); 82 void rpc_call(Client *client, json_t *request); 83 void _rpc_response(Client *client, json_t *request, json_t *result); 84 void _rpc_error(Client *client, json_t *request, JsonRpcError error_code, const char *error_message); 85 void _rpc_error_fmt(Client *client, json_t *request, JsonRpcError error_code, FORMAT_STRING(const char *fmt), ...) __attribute__((format(printf,4,5))); 86 void _rpc_send_request_to_remote(Client *source, Client *target, json_t *request); 87 void _rpc_send_response_to_remote(Client *source, Client *target, json_t *response); 88 int _rrpc_supported_simple(Client *target, char **problem_server); 89 int _rrpc_supported(Client *target, const char *module, const char *minimum_version, char **problem_server); 90 int rpc_handle_auth(Client *client, WebRequest *web); 91 int rpc_parse_auth_basic_auth(Client *client, WebRequest *web, char **username, char **password); 92 int rpc_parse_auth_uri(Client *client, WebRequest *web, char **username, char **password); 93 RPC_CALL_FUNC(rpc_rpc_info); 94 RPC_CALL_FUNC(rpc_rpc_set_issuer); 95 RPC_CALL_FUNC(rpc_rpc_add_timer); 96 RPC_CALL_FUNC(rpc_rpc_del_timer); 97 CMD_FUNC(cmd_rrpc); 98 EVENT(rpc_remote_timeout); 99 EVENT(rpc_do_timers); 100 json_t *rrpc_data(RRPC *r); 101 void free_rrpc_list(ModData *m); 102 void free_outstanding_rrpc_list(ModData *m); 103 void free_rpc_timer(RPCTimer *r); 104 void free_rpc_timer_list(ModData *m); 105 void rpc_call_remote(RRPC *r); 106 void rpc_response_remote(RRPC *r); 107 int rpc_handle_free_client(Client *client); 108 int rpc_handle_server_quit(Client *client, MessageTag *mtags); 109 int rpc_json_expand_client_server(Client *client, int detail, json_t *j, json_t *child); 110 const char *rrpc_md_serialize(ModData *m); 111 void rrpc_md_unserialize(const char *str, ModData *m); 112 void rrpc_md_free(ModData *m); 113 114 /* Macros */ 115 #define RPC_PORT(client) ((client->local && client->local->listener) ? client->local->listener->rpc_options : 0) 116 #define WSU(client) ((WebSocketUser *)moddata_client(client, websocket_md).ptr) 117 118 /* Global variables */ 119 ModDataInfo *websocket_md = NULL; /* (imported) */ 120 RPCUser *rpcusers = NULL; 121 RRPC *rrpc_list = NULL; 122 OutstandingRRPC *outstanding_rrpc_list = NULL; 123 RPCTimer *rpc_timer_list = NULL; 124 ModDataInfo *rrpc_md; 125 126 MOD_TEST() 127 { 128 MARK_AS_OFFICIAL_MODULE(modinfo); 129 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, rpc_config_test_listen); 130 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, rpc_config_test_rpc_user); 131 EfunctionAddVoid(modinfo->handle, EFUNC_RPC_RESPONSE, _rpc_response); 132 EfunctionAddVoid(modinfo->handle, EFUNC_RPC_ERROR, _rpc_error); 133 EfunctionAddVoid(modinfo->handle, EFUNC_RPC_ERROR_FMT, TO_VOIDFUNC(_rpc_error_fmt)); 134 EfunctionAddVoid(modinfo->handle, EFUNC_RPC_SEND_REQUEST_TO_REMOTE, _rpc_send_request_to_remote); 135 EfunctionAddVoid(modinfo->handle, EFUNC_RPC_SEND_RESPONSE_TO_REMOTE, _rpc_send_response_to_remote); 136 EfunctionAdd(modinfo->handle, EFUNC_RRPC_SUPPORTED, _rrpc_supported); 137 EfunctionAdd(modinfo->handle, EFUNC_RRPC_SUPPORTED_SIMPLE, _rrpc_supported_simple); 138 139 /* Call MOD_INIT very early, since we manage sockets, but depend on websocket_common */ 140 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, WEBSOCKET_MODULE_PRIORITY_INIT+1); 141 142 return MOD_SUCCESS; 143 } 144 145 MOD_INIT() 146 { 147 ModDataInfo mreq; 148 RPCHandlerInfo r; 149 150 MARK_AS_OFFICIAL_MODULE(modinfo); 151 152 websocket_md = findmoddata_byname("websocket", MODDATATYPE_CLIENT); /* can be NULL */ 153 154 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN_EX, 0, rpc_config_run_ex_listen); 155 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, rpc_config_run_rpc_user); 156 HookAdd(modinfo->handle, HOOKTYPE_HANDSHAKE, -5000, rpc_client_accept); 157 HookAdd(modinfo->handle, HOOKTYPE_PRE_LOCAL_HANDSHAKE_TIMEOUT, 0, rpc_pre_local_handshake_timeout); 158 HookAdd(modinfo->handle, HOOKTYPE_RAWPACKET_IN, INT_MIN, rpc_packet_in_unix_socket); 159 HookAdd(modinfo->handle, HOOKTYPE_SERVER_QUIT, 0, rpc_handle_server_quit); 160 HookAdd(modinfo->handle, HOOKTYPE_FREE_CLIENT, 0, rpc_handle_free_client); 161 HookAdd(modinfo->handle, HOOKTYPE_JSON_EXPAND_CLIENT_SERVER, 0, rpc_json_expand_client_server); 162 163 memset(&r, 0, sizeof(r)); 164 r.method = "rpc.info"; 165 r.loglevel = ULOG_DEBUG; 166 r.call = rpc_rpc_info; 167 if (!RPCHandlerAdd(modinfo->handle, &r)) 168 { 169 config_error("[rpc.info] Could not register RPC handler"); 170 return MOD_FAILED; 171 } 172 173 memset(&r, 0, sizeof(r)); 174 r.method = "rpc.set_issuer"; 175 r.loglevel = ULOG_DEBUG; 176 r.call = rpc_rpc_set_issuer; 177 if (!RPCHandlerAdd(modinfo->handle, &r)) 178 { 179 config_error("[rpc.set_issuer] Could not register RPC handler"); 180 return MOD_FAILED; 181 } 182 183 memset(&r, 0, sizeof(r)); 184 r.method = "rpc.add_timer"; 185 r.loglevel = ULOG_DEBUG; 186 r.call = rpc_rpc_add_timer; 187 if (!RPCHandlerAdd(modinfo->handle, &r)) 188 { 189 config_error("[rpc.add_timer] Could not register RPC handler"); 190 return MOD_FAILED; 191 } 192 193 memset(&r, 0, sizeof(r)); 194 r.method = "rpc.del_timer"; 195 r.loglevel = ULOG_DEBUG; 196 r.call = rpc_rpc_del_timer; 197 if (!RPCHandlerAdd(modinfo->handle, &r)) 198 { 199 config_error("[rpc.del_timer] Could not register RPC handler"); 200 return MOD_FAILED; 201 } 202 203 memset(&mreq, 0, sizeof(mreq)); 204 mreq.name = "rrpc"; 205 mreq.type = MODDATATYPE_CLIENT; 206 mreq.serialize = rrpc_md_serialize; 207 mreq.unserialize = rrpc_md_unserialize; 208 mreq.free = rrpc_md_free; 209 mreq.sync = 1; 210 mreq.self_write = 1; 211 rrpc_md = ModDataAdd(modinfo->handle, mreq); 212 if (!rrpc_md) 213 { 214 config_error("[rpc/rpc] Unable to ModDataAdd() -- too many 3rd party modules loaded perhaps?"); 215 abort(); 216 } 217 218 LoadPersistentPointer(modinfo, rrpc_list, free_rrpc_list); 219 LoadPersistentPointer(modinfo, outstanding_rrpc_list, free_outstanding_rrpc_list); 220 LoadPersistentPointer(modinfo, rpc_timer_list, free_rpc_timer_list); 221 222 CommandAdd(modinfo->handle, "RRPC", cmd_rrpc, MAXPARA, CMD_SERVER); 223 224 EventAdd(modinfo->handle, "rpc_remote_timeout", rpc_remote_timeout, NULL, 1000, 0); 225 EventAdd(modinfo->handle, "rpc_do_timers", rpc_do_timers, NULL, RPC_MINIMUM_TIMER_MSEC, 0); 226 227 /* Call MOD_LOAD very late, since we manage sockets, but depend on websocket_common */ 228 ModuleSetOptions(modinfo->handle, MOD_OPT_PRIORITY, WEBSOCKET_MODULE_PRIORITY_UNLOAD-1); 229 230 return MOD_SUCCESS; 231 } 232 233 #define MYRRPCMODULES me.moddata[rrpc_md->slot].ptr 234 #define RRPCMODULES(client) ((NameValuePrioList *)moddata_client(client, rrpc_md).ptr) 235 236 void rpc_do_moddata(void) 237 { 238 Module *m; 239 240 free_nvplist(MYRRPCMODULES); 241 MYRRPCMODULES = NULL; 242 243 for (m = Modules; m; m = m->next) 244 if (!strncmp(m->header->name, "rpc/", 4)) 245 add_nvplist((NameValuePrioList **)&MYRRPCMODULES, 0, m->header->name + 4, m->header->version); 246 } 247 248 MOD_LOAD() 249 { 250 rpc_do_moddata(); 251 return MOD_SUCCESS; 252 } 253 254 void free_config(void) 255 { 256 RPCUser *e, *e_next; 257 for (e = rpcusers; e; e = e_next) 258 { 259 e_next = e->next; 260 safe_free(e->name); 261 free_security_group(e->match); 262 Auth_FreeAuthConfig(e->auth); 263 safe_free(e); 264 } 265 rpcusers = NULL; 266 } 267 268 MOD_UNLOAD() 269 { 270 free_config(); 271 SavePersistentPointer(modinfo, rrpc_list); 272 SavePersistentPointer(modinfo, outstanding_rrpc_list); 273 SavePersistentPointer(modinfo, rpc_timer_list); 274 return MOD_SUCCESS; 275 } 276 277 int rpc_config_test_listen(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 278 { 279 int errors = 0; 280 int ext = 0; 281 ConfigEntry *cep; 282 283 if (type != CONFIG_LISTEN_OPTIONS) 284 return 0; 285 286 /* We are only interested in listen::options::rpc.. */ 287 if (!ce || !ce->name || strcmp(ce->name, "rpc")) 288 return 0; 289 290 /* No options atm */ 291 292 *errs = errors; 293 return errors ? -1 : 1; 294 } 295 296 int rpc_config_run_ex_listen(ConfigFile *cf, ConfigEntry *ce, int type, void *ptr) 297 { 298 ConfigEntry *cep, *cepp; 299 ConfigItem_listen *l; 300 301 if (type != CONFIG_LISTEN_OPTIONS) 302 return 0; 303 304 /* We are only interrested in listen::options::rpc.. */ 305 if (!ce || !ce->name || strcmp(ce->name, "rpc")) 306 return 0; 307 308 l = (ConfigItem_listen *)ptr; 309 l->options |= LISTENER_NO_CHECK_CONNECT_FLOOD; 310 if (l->socket_type == SOCKET_TYPE_UNIX) 311 { 312 l->start_handshake = rpc_client_handshake_unix_socket; 313 } else { 314 l->options |= LISTENER_TLS; 315 l->start_handshake = rpc_client_handshake_web; 316 l->webserver = safe_alloc(sizeof(WebServer)); 317 l->webserver->handle_request = rpc_handle_webrequest; 318 l->webserver->handle_body = rpc_handle_webrequest_data; 319 } 320 l->rpc_options = 1; 321 322 return 1; 323 } 324 325 /** Valid name for rpc-user THISNAME { } ? */ 326 static int valid_rpc_user_name(const char *str) 327 { 328 const char *p; 329 330 if (strlen(str) > RPCUSERLEN) 331 return 0; 332 333 for (p = str; *p; p++) 334 if (!isalnum(*p) && !strchr("_-", *p)) 335 return 0; 336 337 return 1; 338 } 339 340 int rpc_config_test_rpc_user(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 341 { 342 int errors = 0; 343 char has_match = 1, has_password = 1; 344 ConfigEntry *cep; 345 346 /* We are only interested in rpc-user { } */ 347 if ((type != CONFIG_MAIN) || !ce || !ce->name || strcmp(ce->name, "rpc-user")) 348 return 0; 349 350 if (!ce->value) 351 { 352 config_error("%s:%d: rpc-user block needs to have a name, eg: rpc-user apiuser { }", 353 ce->file->filename, ce->line_number); 354 *errs = 1; 355 return -1; /* quick return */ 356 } 357 358 if (!valid_rpc_user_name(ce->value)) 359 { 360 config_error("%s:%d: rpc-user block has invalid name '%s'. " 361 "Can be max %d long and may only contain a-z, A-Z, 0-9, - and _.", 362 ce->file->filename, ce->line_number, 363 ce->value, RPCUSERLEN); 364 errors++; 365 } 366 for (cep = ce->items; cep; cep = cep->next) 367 { 368 if (!strcmp(cep->name, "match")) 369 { 370 has_match = 1; 371 test_match_block(cf, cep, &errors); 372 } else 373 if (!strcmp(cep->name, "password")) 374 { 375 has_password = 1; 376 if (Auth_CheckError(cep) < 0) 377 errors++; 378 } 379 } 380 381 *errs = errors; 382 return errors ? -1 : 1; 383 } 384 385 int rpc_config_run_rpc_user(ConfigFile *cf, ConfigEntry *ce, int type) 386 { 387 ConfigEntry *cep; 388 RPCUser *e; 389 390 /* We are only interested in rpc-user { } */ 391 if ((type != CONFIG_MAIN) || !ce || !ce->name || strcmp(ce->name, "rpc-user")) 392 return 0; 393 394 e = safe_alloc(sizeof(RPCUser)); 395 safe_strdup(e->name, ce->value); 396 AddListItem(e, rpcusers); 397 398 for (cep = ce->items; cep; cep = cep->next) 399 { 400 if (!strcmp(cep->name, "match")) 401 { 402 conf_match_block(cf, cep, &e->match); 403 } else 404 if (!strcmp(cep->name, "password")) 405 { 406 e->auth = AuthBlockToAuthConfig(cep); 407 } 408 } 409 return 1; 410 } 411 412 /** Incoming HTTP request: delegate it to websocket handler or HTTP POST */ 413 int rpc_handle_webrequest(Client *client, WebRequest *web) 414 { 415 if (!rpc_handle_auth(client, web)) 416 return 0; /* rejected */ 417 418 if (get_nvplist(web->headers, "Sec-WebSocket-Key")) 419 return rpc_handle_webrequest_websocket(client, web); 420 421 if (!strcmp(web->uri, "/api")) 422 { 423 if (web->method != HTTP_METHOD_POST) 424 { 425 webserver_send_response(client, 200, "To use the UnrealIRCd RPC API you need to make a POST request. See https://www.unrealircd.org/docs/RPC\n"); 426 return 0; 427 } 428 webserver_send_response(client, 200, NULL); /* continue.. */ 429 return 1; /* accept */ 430 } 431 432 webserver_send_response(client, 404, "Page not found.\n"); 433 return 0; 434 } 435 436 /** Handle HTTP request - websockets handshake. 437 */ 438 int rpc_handle_webrequest_websocket(Client *client, WebRequest *web) 439 { 440 NameValuePrioList *r; 441 const char *value; 442 443 if (!websocket_md) 444 { 445 webserver_send_response(client, 405, "Websockets are disabled on this server (module 'websocket_common' not loaded).\n"); 446 return 0; 447 } 448 449 /* Allocate a new WebSocketUser struct for this session */ 450 moddata_client(client, websocket_md).ptr = safe_alloc(sizeof(WebSocketUser)); 451 /* ...and set the default protocol (text or binary) */ 452 WSU(client)->type = WEBSOCKET_TYPE_TEXT; 453 454 value = get_nvplist(web->headers, "Sec-WebSocket-Key"); 455 if (strchr(value, ':')) 456 { 457 /* This would cause unserialization issues. Should be base64 anyway */ 458 webserver_send_response(client, 400, "Invalid characters in Sec-WebSocket-Key"); 459 return 0; // FIXME: 0 here, -1 in the other, what is it ??? 460 } 461 safe_strdup(WSU(client)->handshake_key, value); 462 463 rpc_websocket_handshake_send_response(client); 464 return 1; /* ACCEPT */ 465 } 466 467 /** Complete the handshake by sending the appropriate HTTP 101 response etc. */ 468 int rpc_websocket_handshake_send_response(Client *client) 469 { 470 char buf[512], hashbuf[64]; 471 char sha1out[20]; /* 160 bits */ 472 473 WSU(client)->handshake_completed = 1; 474 475 snprintf(buf, sizeof(buf), "%s%s", WSU(client)->handshake_key, WEBSOCKET_MAGIC_KEY); 476 sha1hash_binary(sha1out, buf, strlen(buf)); 477 b64_encode(sha1out, sizeof(sha1out), hashbuf, sizeof(hashbuf)); 478 479 snprintf(buf, sizeof(buf), 480 "HTTP/1.1 101 Switching Protocols\r\n" 481 "Upgrade: websocket\r\n" 482 "Connection: Upgrade\r\n" 483 "Sec-WebSocket-Accept: %s\r\n\r\n", 484 hashbuf); 485 486 /* Caution: we bypass sendQ flood checking by doing it this way. 487 * Risk is minimal, though, as we only permit limited text only 488 * once per session. 489 */ 490 dbuf_put(&client->local->sendQ, buf, strlen(buf)); 491 send_queued(client); 492 493 return 0; 494 } 495 496 int rpc_handle_webrequest_data(Client *client, WebRequest *web, const char *buf, int len) 497 { 498 if (WSU(client)) 499 { 500 /* Websocket user */ 501 return rpc_handle_body_websocket(client, web, buf, len); 502 } 503 504 /* We only handle POST to /api -- reject all the rest */ 505 if (strcmp(web->uri, "/api") || (web->method != HTTP_METHOD_POST)) 506 { 507 webserver_send_response(client, 404, "Page not found\n"); 508 return 0; 509 } 510 511 // NB: content_length 512 // NB: chunked transfers? 513 if (!webserver_handle_body(client, web, buf, len)) 514 { 515 webserver_send_response(client, 400, "Error handling POST body data\n"); 516 return 0; 517 } 518 519 520 if (web->request_body_complete) 521 { 522 if (!web->request_buffer) 523 { 524 webserver_send_response(client, 500, "Error while processing POST body data\n"); 525 return 0; 526 } 527 //config_status("GOT: '%s'", buf); 528 rpc_call_text(client, web->request_buffer, web->request_buffer_size); 529 send_queued(client); 530 webserver_close_client(client); 531 } 532 533 return 0; 534 } 535 536 int rpc_handle_body_websocket(Client *client, WebRequest *web, const char *readbuf2, int length2) 537 { 538 return websocket_handle_websocket(client, web, readbuf2, length2, rpc_packet_in_websocket); 539 } 540 541 int rpc_packet_in_websocket(Client *client, char *readbuf, int length) 542 { 543 rpc_call_text(client, readbuf, length); 544 return 0; /* and if dead?? */ 545 } 546 547 int rpc_packet_in_unix_socket(Client *client, const char *readbuf, int *length) 548 { 549 char buf[READBUFSIZE]; 550 551 if (!RPC_PORT(client) || !(client->local->listener->socket_type == SOCKET_TYPE_UNIX) || (*length <= 0)) 552 return 1; /* Not for us */ 553 554 dbuf_put(&client->local->recvQ, readbuf, *length); 555 556 while (DBufLength(&client->local->recvQ)) 557 { 558 int len = dbuf_getmsg(&client->local->recvQ, buf); 559 if (len <= 0) 560 break; 561 rpc_call_text(client, buf, len); 562 if (IsDead(client)) 563 break; 564 } 565 566 return 0; 567 } 568 569 void rpc_close(Client *client) 570 { 571 send_queued(client); 572 573 /* May not be a web request actually, but this works: */ 574 webserver_close_client(client); 575 } 576 577 /** Handle the RPC request: input is a buffer with a certain length. 578 * This calls rpc_call() 579 */ 580 void rpc_call_text(Client *client, const char *readbuf, int len) 581 { 582 json_t *request = NULL; 583 json_error_t jerr; 584 #if JANSSON_VERSION_HEX >= 0x020100 585 const char *buf = readbuf; 586 request = json_loadb(buf, len, JSON_REJECT_DUPLICATES, &jerr); 587 #else 588 char buf[2048]; 589 590 *buf = '\0'; 591 strlncpy(buf, readbuf, sizeof(buf), len); 592 593 request = json_loads(buf, JSON_REJECT_DUPLICATES, &jerr); 594 #endif 595 if (!request) 596 { 597 unreal_log(ULOG_INFO, "rpc", "RPC_INVALID_JSON", client, 598 "Received unparsable JSON request from $client", 599 log_data_string("json_incoming", buf)); 600 rpc_error(client, NULL, JSON_RPC_ERROR_PARSE_ERROR, "Unparsable JSON data"); 601 /* This is a fatal error */ 602 rpc_close(client); 603 return; 604 } 605 rpc_call(client, request); 606 json_decref(request); 607 } 608 609 void rpc_sendto(Client *client, const char *buf, int len) 610 { 611 if (IsDead(client)) 612 return; 613 if (MyConnect(client) && IsRPC(client) && WSU(client) && WSU(client)->handshake_completed) 614 { 615 /* Websocket */ 616 int utf8bufsize = len*2 + 16; 617 char *utf8buf = safe_alloc(utf8bufsize); 618 char *newbuf = unrl_utf8_make_valid(buf, utf8buf, utf8bufsize, 1); 619 int newlen = strlen(newbuf); 620 int ws_sendbufsize = newlen + 64 + ((newlen / 1024) * 64); // some random magic 621 char *ws_sendbuf = safe_alloc(ws_sendbufsize); 622 websocket_create_packet_ex(WSOP_TEXT, &newbuf, &newlen, ws_sendbuf, ws_sendbufsize); 623 dbuf_put(&client->local->sendQ, newbuf, newlen); 624 safe_free(ws_sendbuf); 625 safe_free(utf8buf); 626 } else { 627 /* Unix domain socket or HTTP */ 628 dbuf_put(&client->local->sendQ, buf, len); 629 dbuf_put(&client->local->sendQ, "\n", 1); 630 } 631 mark_data_to_send(client); 632 } 633 634 void _rpc_error(Client *client, json_t *request, JsonRpcError error_code, const char *error_message) 635 { 636 /* Careful, we are in the "error" routine, so everything can be NULL */ 637 const char *method = NULL; 638 json_t *id = NULL; 639 char *json_serialized; 640 json_t *error; 641 642 /* Start a new object for the error response */ 643 json_t *j = json_object(); 644 645 if (request) 646 { 647 method = json_object_get_string(request, "method"); 648 id = json_object_get(request, "id"); 649 } 650 651 json_object_set_new(j, "jsonrpc", json_string_unreal("2.0")); 652 if (method) 653 json_object_set_new(j, "method", json_string_unreal(method)); 654 if (id) 655 json_object_set(j, "id", id); 656 657 error = json_object(); 658 json_object_set_new(j, "error", error); 659 json_object_set_new(error, "code", json_integer(error_code)); 660 json_object_set_new(error, "message", json_string_unreal(error_message)); 661 662 unreal_log(ULOG_INFO, "rpc", "RPC_CALL_ERROR", client, 663 "[rpc] Client $client: RPC call $method", 664 log_data_string("method", method ? method : "<invalid>")); 665 666 667 json_serialized = json_dumps(j, 0); 668 if (!json_serialized) 669 { 670 unreal_log(ULOG_WARNING, "rpc", "BUG_RPC_ERROR_SERIALIZE_FAILED", NULL, 671 "[BUG] rpc_error() failed to serialize response " 672 "for request from $client ($method)", 673 log_data_string("method", method)); 674 json_decref(j); 675 return; 676 } 677 678 if (MyConnect(client)) 679 rpc_sendto(client, json_serialized, strlen(json_serialized)); 680 else 681 rpc_send_response_to_remote(&me, client, j); 682 683 #ifdef DEBUGMODE 684 unreal_log(ULOG_DEBUG, "rpc", "RPC_CALL_DEBUG", client, 685 "[rpc] Client $client: RPC result error: $response", 686 log_data_string("response", json_serialized)); 687 #endif 688 json_decref(j); 689 safe_free(json_serialized); 690 } 691 692 void _rpc_error_fmt(Client *client, json_t *request, JsonRpcError error_code, const char *fmt, ...) 693 { 694 char buf[512]; 695 696 va_list vl; 697 va_start(vl, fmt); 698 vsnprintf(buf, sizeof(buf), fmt, vl); 699 va_end(vl); 700 rpc_error(client, request, error_code, buf); 701 } 702 703 void _rpc_response(Client *client, json_t *request, json_t *result) 704 { 705 const char *method = json_object_get_string(request, "method"); 706 json_t *id = json_object_get(request, "id"); 707 char *json_serialized; 708 json_t *j = json_object(); 709 710 json_object_set_new(j, "jsonrpc", json_string_unreal("2.0")); 711 json_object_set_new(j, "method", json_string_unreal(method)); 712 if (id) 713 json_object_set(j, "id", id); /* 'id' is optional */ 714 json_object_set(j, "result", result); 715 716 json_serialized = json_dumps(j, 0); 717 if (!json_serialized) 718 { 719 unreal_log(ULOG_WARNING, "rpc", "BUG_RPC_RESPONSE_SERIALIZE_FAILED", NULL, 720 "[BUG] rpc_response() failed to serialize response " 721 "for request from $client ($method)", 722 log_data_string("method", method)); 723 json_decref(j); 724 return; 725 } 726 727 if (MyConnect(client)) 728 rpc_sendto(client, json_serialized, strlen(json_serialized)); 729 else 730 rpc_send_response_to_remote(&me, client, j); 731 732 #ifdef DEBUGMODE 733 unreal_log(ULOG_DEBUG, "rpc", "RPC_CALL_DEBUG", client, 734 "[rpc] Client $client: RPC response result: $response", 735 log_data_string("response", json_serialized)); 736 #endif 737 json_decref(j); 738 safe_free(json_serialized); 739 } 740 741 int sanitize_params_actual(Client *client, json_t *request, const char *str) 742 { 743 if (!str) 744 return 1; 745 746 if (strlen(str) > 510) 747 { 748 rpc_error(client, request, JSON_RPC_ERROR_INVALID_REQUEST, "Strings cannot be longer than 510 characters in the request"); 749 return 0; 750 } 751 752 if (strchr(str, '\n') || strchr(str, '\r')) 753 { 754 rpc_error(client, request, JSON_RPC_ERROR_INVALID_REQUEST, "Strings may not contain \n or \r in the request"); 755 return 0; 756 } 757 758 return 1; 759 } 760 761 int sanitize_params(Client *client, json_t *request, json_t *j) 762 { 763 /* Check the current object itself */ 764 const char *str = json_string_value(j); 765 if (str && !sanitize_params_actual(client, request, str)) 766 return 0; 767 768 /* Now walk through the object, if needed */ 769 770 if (json_is_array(j)) 771 { 772 size_t index; 773 json_t *value; 774 json_array_foreach(j, index, value) 775 { 776 if (!sanitize_params(client, request, value)) 777 return 0; 778 } 779 } else 780 if (json_is_object(j)) 781 { 782 const char *key; 783 json_t *value; 784 json_object_foreach(j, key, value) 785 { 786 if (!sanitize_params_actual(client, request, key)) 787 return 0; 788 if (!sanitize_params(client, request, value)) 789 return 0; 790 } 791 } 792 793 return 1; 794 } 795 796 /** Log the RPC request */ 797 void rpc_call_log(Client *client, RPCHandler *handler, json_t *request, const char *method, json_t *params) 798 { 799 const char *key; 800 json_t *value_object; 801 char params_string[512], tbuf[256]; 802 803 *params_string = '\0'; 804 json_object_foreach(params, key, value_object) 805 { 806 const char *value = json_get_value(value_object); 807 if (value) 808 { 809 snprintf(tbuf, sizeof(tbuf), "%s='%s', ", key, value); 810 strlcat(params_string, tbuf, sizeof(params_string)); 811 } 812 } 813 if (*params_string) 814 params_string[strlen(params_string)-2] = '\0'; /* cut off last comma */ 815 816 // TODO: pass log_data_json() or something, pass the entire 'request' ? For JSON logging 817 818 if (client->rpc && client->rpc->issuer) 819 { 820 if (*params_string) 821 { 822 unreal_log(handler->loglevel, "rpc", "RPC_CALL", client, 823 "[rpc] RPC call $method by $client ($issuer): $params_string", 824 log_data_string("issuer", client->rpc->issuer), 825 log_data_string("method", method), 826 log_data_string("params_string", params_string)); 827 } else { 828 unreal_log(handler->loglevel, "rpc", "RPC_CALL", client, 829 "[rpc] RPC call $method by $client ($issuer)", 830 log_data_string("issuer", client->rpc->issuer), 831 log_data_string("method", method)); 832 } 833 } else { 834 if (*params_string) 835 { 836 unreal_log(handler->loglevel, "rpc", "RPC_CALL", client, 837 "[rpc] RPC call $method by $client: $params_string", 838 log_data_string("method", method), 839 log_data_string("params_string", params_string)); 840 } else { 841 unreal_log(handler->loglevel, "rpc", "RPC_CALL", client, 842 "[rpc] RPC call $method by $client", 843 log_data_string("method", method)); 844 } 845 } 846 } 847 848 /** Parse an RPC request, except that it does not validate 'params'. 849 * @param client The client issuing the request 850 * @param mainrequest The underlying request that we should send errors to (usually same as 'request') 851 * @param request The request that needs parsing 852 * @param method This will be filled in if successfully parsed 853 * @param handler This will be filled in if the handler is found 854 * @retval 0 An error occured while parsing or the method was not found. 855 * @retval 1 All good. You still need to validate 'params', though. 856 */ 857 int parse_rpc_call(Client *client, json_t *mainrequest, json_t *request, const char **method, RPCHandler **handler) 858 { 859 const char *jsonrpc; 860 json_t *id; 861 const char *str; 862 863 *method = NULL; 864 *handler = NULL; 865 866 jsonrpc = json_object_get_string(request, "jsonrpc"); 867 if (!jsonrpc || strcasecmp(jsonrpc, "2.0")) 868 { 869 rpc_error(client, mainrequest, JSON_RPC_ERROR_INVALID_REQUEST, "Only JSON-RPC version 2.0 is supported"); 870 return 0; 871 } 872 873 id = json_object_get(request, "id"); 874 if (!id) 875 { 876 rpc_error(client, mainrequest, JSON_RPC_ERROR_INVALID_REQUEST, "Missing 'id'"); 877 return 0; 878 } 879 880 if ((str = json_string_value(id))) 881 { 882 if (strlen(str) > 32) 883 { 884 rpc_error(client, mainrequest, JSON_RPC_ERROR_INVALID_REQUEST, "The 'id' cannot be longer than 32 characters in UnrealIRCd JSON-RPC"); 885 return 0; 886 } 887 if (strchr(str, '\n') || strchr(str, '\r')) 888 { 889 rpc_error(client, mainrequest, JSON_RPC_ERROR_INVALID_REQUEST, "The 'id' may not contain \n or \r in UnrealIRCd JSON-RPC"); 890 return 0; 891 } 892 } else if (!json_is_integer(id)) 893 { 894 rpc_error(client, mainrequest, JSON_RPC_ERROR_INVALID_REQUEST, "The 'id' must be a string or an integer in UnrealIRCd JSON-RPC"); 895 return 0; 896 } 897 898 *method = json_object_get_string(request, "method"); 899 if (!*method) 900 { 901 rpc_error(client, mainrequest, JSON_RPC_ERROR_INVALID_REQUEST, "Missing 'method' to call"); 902 return 0; 903 } 904 905 *handler = RPCHandlerFind(*method); 906 if (!*handler) 907 { 908 rpc_error(client, mainrequest, JSON_RPC_ERROR_METHOD_NOT_FOUND, "Unsupported method"); 909 return 0; 910 } 911 912 return 1; 913 } 914 915 /** Handle the RPC request: request is in JSON */ 916 void rpc_call(Client *client, json_t *request) 917 { 918 const char *method; 919 json_t *params; 920 RPCHandler *handler; 921 922 if (!parse_rpc_call(client, request, request, &method, &handler)) 923 return; /* Error already returned to caller */ 924 925 params = json_object_get(request, "params"); 926 if (params) 927 { 928 if (!(handler->flags & RPC_HANDLER_FLAGS_UNFILTERED) && 929 !sanitize_params(client, request, params)) 930 { 931 return; 932 } 933 } else 934 { 935 /* Params is optional, so create an empty params object instead 936 * to make life easier of the RPC handlers (no need to check NULL). 937 */ 938 params = json_object(); 939 json_object_set_new(request, "params", params); 940 } 941 942 rpc_call_log(client, handler, request, method, params); 943 944 #ifdef DEBUGMODE 945 { 946 char *call = json_dumps(request, 0); 947 if (call) 948 { 949 unreal_log(ULOG_DEBUG, "rpc", "RPC_CALL_DEBUG", client, 950 "[rpc] Client $client: RPC call: $call", 951 log_data_string("call", call)); 952 safe_free(call); 953 } 954 } 955 #endif 956 handler->call(client, request, params); 957 } 958 959 /** Called very early on accept() of the socket, before TLS is ready */ 960 int rpc_client_accept(Client *client) 961 { 962 if (RPC_PORT(client)) 963 { 964 SetRPC(client); 965 client->rpc = safe_alloc(sizeof(RPCClient)); 966 } 967 return 0; 968 } 969 970 /** Called upon handshake of unix socket (direct JSON usage, no auth) */ 971 void rpc_client_handshake_unix_socket(Client *client) 972 { 973 if (client->local->listener->socket_type != SOCKET_TYPE_UNIX) 974 abort(); /* impossible */ 975 976 strlcpy(client->name, "RPC:local", sizeof(client->name)); 977 SetRPC(client); 978 client->rpc = safe_alloc(sizeof(RPCClient)); 979 safe_strdup(client->rpc->rpc_user, "<local>"); 980 981 /* Allow incoming data to be read from now on.. */ 982 fd_setselect(client->local->fd, FD_SELECT_READ, read_packet, client); 983 } 984 985 /** Called upon handshake, after TLS is ready (before any HTTP header parsing) */ 986 void rpc_client_handshake_web(Client *client) 987 { 988 RPCUser *r; 989 char found = 0; 990 991 /* Explicitly mark as RPC, since the TLS layer may 992 * have set us to SetUnknown() after the TLS handshake. 993 */ 994 SetRPC(client); 995 if (!client->rpc) 996 client->rpc = safe_alloc(sizeof(RPCClient)); 997 998 /* Is the client allowed by any rpc-user { } block? 999 * If not, reject the client immediately, before 1000 * processing any HTTP data. 1001 */ 1002 for (r = rpcusers; r; r = r->next) 1003 { 1004 if (user_allowed_by_security_group(client, r->match)) 1005 { 1006 found = 1; 1007 break; 1008 } 1009 } 1010 if (!found) 1011 { 1012 webserver_send_response(client, 401, "Access denied"); 1013 return; 1014 } 1015 1016 /* Allow incoming data to be read from now on.. */ 1017 fd_setselect(client->local->fd, FD_SELECT_READ, read_packet, client); 1018 } 1019 1020 #define RPC_WEBSOCKET_PING_TIME 120 1021 1022 int rpc_pre_local_handshake_timeout(Client *client, const char **comment) 1023 { 1024 /* Don't hang up websocket connections */ 1025 if (IsRPC(client) && WSU(client) && WSU(client)->handshake_completed) 1026 { 1027 long t = TStime() - client->local->last_msg_received; 1028 if ((t > RPC_WEBSOCKET_PING_TIME*2) && IsPingSent(client)) 1029 { 1030 *comment = "No websocket PONG received in time."; 1031 return HOOK_CONTINUE; 1032 } else 1033 if ((t > RPC_WEBSOCKET_PING_TIME) && !IsPingSent(client) && !IsDead(client)) 1034 { 1035 char pingbuf[4]; 1036 const char *pkt = pingbuf; 1037 int pktlen = sizeof(pingbuf); 1038 pingbuf[0] = 0x11; 1039 pingbuf[1] = 0x22; 1040 pingbuf[2] = 0x33; 1041 pingbuf[3] = 0x44; 1042 websocket_create_packet_simple(WSOP_PING, &pkt, &pktlen); 1043 dbuf_put(&client->local->sendQ, pkt, pktlen); 1044 send_queued(client); 1045 SetPingSent(client); 1046 } 1047 return HOOK_ALLOW; /* prevent closing the connection due to timeout */ 1048 } 1049 1050 return HOOK_CONTINUE; 1051 } 1052 1053 RPCUser *find_rpc_user(const char *username) 1054 { 1055 RPCUser *r; 1056 for (r = rpcusers; r; r = r->next) 1057 if (!strcmp(r->name, username)) 1058 return r; 1059 return NULL; 1060 } 1061 1062 /** This function deals with authentication after the HTTP request was received. 1063 * It is called for both ordinary HTTP(S) requests and Websockets. 1064 * Note that there has also been some pre-filtering done in rpc_client_handshake() 1065 * to see if the IP address was allowed to connect at all (::match), 1066 * but here we actually check the 'correct' rpc-user { } block. 1067 * @param client The client to authenticate 1068 * @param web The webrequest (containing the headers) 1069 * @return 1 on success, 0 on failure 1070 */ 1071 int rpc_handle_auth(Client *client, WebRequest *web) 1072 { 1073 char *username = NULL, *password = NULL; 1074 RPCUser *r; 1075 1076 if (!rpc_parse_auth_basic_auth(client, web, &username, &password) && 1077 !rpc_parse_auth_uri(client, web, &username, &password)) 1078 { 1079 webserver_send_response(client, 401, "Authentication required"); 1080 return 0; 1081 } 1082 1083 if (username && password && ((r = find_rpc_user(username)))) 1084 { 1085 if (user_allowed_by_security_group(client, r->match) && 1086 Auth_Check(client, r->auth, password)) 1087 { 1088 /* Authenticated! */ 1089 snprintf(client->name, sizeof(client->name), "RPC:%s", r->name); 1090 safe_strdup(client->rpc->rpc_user, r->name); 1091 return 1; 1092 } 1093 } 1094 1095 /* Authentication failed */ 1096 webserver_send_response(client, 401, "Authentication required"); 1097 return 0; 1098 } 1099 1100 int rpc_parse_auth_basic_auth(Client *client, WebRequest *web, char **username, char **password) 1101 { 1102 const char *auth_header = get_nvplist(web->headers, "Authorization"); 1103 static char buf[512]; 1104 char *p; 1105 int n; 1106 1107 if (!auth_header) 1108 return 0; 1109 1110 /* We only support basic auth */ 1111 if (strncasecmp(auth_header, "Basic ", 6)) 1112 return 0; 1113 1114 p = strchr(auth_header, ' '); 1115 skip_whitespace(&p); 1116 n = b64_decode(p, buf, sizeof(buf)-1); 1117 if (n <= 1) 1118 return 0; 1119 buf[n] = '\0'; 1120 1121 p = strchr(buf, ':'); 1122 if (!p) 1123 return 0; 1124 *p++ = '\0'; 1125 1126 *username = buf; 1127 *password = p; 1128 return 1; 1129 } 1130 1131 // TODO: the ?a=b&c=d stuff should be urldecoded by 'webserver' 1132 int rpc_parse_auth_uri(Client *client, WebRequest *web, char **username, char **password) 1133 { 1134 static char buf[2048]; 1135 char *str, *p; 1136 1137 if (!web->uri) 1138 return 0; 1139 1140 strlcpy(buf, web->uri, sizeof(buf)); 1141 str = strstr(buf, "username="); 1142 if (!str) 1143 return 0; 1144 str += 9; 1145 *username = str; 1146 p = strchr(str, '&'); 1147 if (p) 1148 { 1149 *p++ = '\0'; 1150 p = strstr(p, "password="); 1151 if (p) 1152 { 1153 p += 9; 1154 *password = p; 1155 p = strchr(str, '&'); 1156 if (p) 1157 *p = '\0'; 1158 } 1159 } 1160 return 1; 1161 } 1162 1163 RPC_CALL_FUNC(rpc_rpc_info) 1164 { 1165 json_t *result, *methods, *item; 1166 RPCHandler *r; 1167 1168 result = json_object(); 1169 methods = json_object(); 1170 json_object_set_new(result, "methods", methods); 1171 1172 for (r = rpchandlers; r; r = r->next) 1173 { 1174 item = json_object(); 1175 json_object_set_new(item, "name", json_string_unreal(r->method)); 1176 if (r->owner) 1177 { 1178 json_object_set_new(item, "module", json_string_unreal(r->owner->header->name)); 1179 json_object_set_new(item, "version", json_string_unreal(r->owner->header->version)); 1180 } 1181 json_object_set_new(methods, r->method, item); 1182 } 1183 1184 rpc_response(client, request, result); 1185 json_decref(result); 1186 } 1187 1188 RPC_CALL_FUNC(rpc_rpc_set_issuer) 1189 { 1190 json_t *result; 1191 const char *name; 1192 char buf[512]; 1193 1194 REQUIRE_PARAM_STRING("name", name); 1195 1196 /* Do some validation on the name */ 1197 strlcpy(buf, name, sizeof(buf)); 1198 if (!do_remote_nick_name(buf) || strcmp(buf, name)) 1199 { 1200 rpc_error(client, request, JSON_RPC_ERROR_INVALID_NAME, 1201 "The 'name' contains illegal characters or is too long. " 1202 "The same rules as for nick names apply."); 1203 return; 1204 } 1205 1206 safe_strdup(client->rpc->issuer, name); 1207 1208 result = json_boolean(1); 1209 rpc_response(client, request, result); 1210 json_decref(result); 1211 } 1212 1213 void free_rrpc(RRPC *r) 1214 { 1215 safe_free(r->requestid); 1216 DBufClear(&r->data); 1217 DelListItem(r, rrpc_list); 1218 safe_free(r); 1219 } 1220 1221 /* Admin unloading the RPC module for good (not called on rehash) */ 1222 void free_rrpc_list(ModData *m) 1223 { 1224 RRPC *r, *r_next; 1225 1226 for (r = rrpc_list; r; r = r_next) 1227 { 1228 r_next = r->next; 1229 free_rrpc(r); 1230 } 1231 } 1232 1233 void free_outstanding_rrpc(OutstandingRRPC *r) 1234 { 1235 safe_free(r->requestid); 1236 DelListItem(r, outstanding_rrpc_list); 1237 safe_free(r); 1238 } 1239 1240 /* Admin unloading the RPC module for good (not called on rehash) */ 1241 void free_outstanding_rrpc_list(ModData *m) 1242 { 1243 OutstandingRRPC *r, *r_next; 1244 1245 for (r = outstanding_rrpc_list; r; r = r_next) 1246 { 1247 r_next = r->next; 1248 free_outstanding_rrpc(r); 1249 } 1250 } 1251 1252 /** Remove timer from rpc_timer_list and free it */ 1253 void free_rpc_timer(RPCTimer *r) 1254 { 1255 safe_free(r->timer_id); 1256 json_decref(r->request); 1257 DelListItem(r, rpc_timer_list); 1258 safe_free(r); 1259 } 1260 1261 /* Admin unloading the RPC module for good (not called on rehash) */ 1262 void free_rpc_timer_list(ModData *m) 1263 { 1264 RPCTimer *r, *r_next; 1265 1266 for (r = rpc_timer_list; r; r = r_next) 1267 { 1268 r_next = r->next; 1269 free_rpc_timer(r); 1270 } 1271 } 1272 1273 /* Admin unloading the RPC module for good (not called on rehash) */ 1274 void free_rpc_timers_for_user(Client *client) 1275 { 1276 RPCTimer *r, *r_next; 1277 1278 for (r = rpc_timer_list; r; r = r_next) 1279 { 1280 r_next = r->next; 1281 if (r->client == client) 1282 free_rpc_timer(r); 1283 } 1284 } 1285 1286 RPCTimer *find_rpc_timer(Client *client, const char *timer_id) 1287 { 1288 RPCTimer *r; 1289 1290 for (r = rpc_timer_list; r; r = r->next) 1291 { 1292 if ((r->client == client) && !strcmp(timer_id, r->timer_id)) 1293 return r; 1294 } 1295 return NULL; 1296 } 1297 1298 /** When a server quits, cancel all the RPC requests to/from those clients */ 1299 int rpc_handle_server_quit(Client *client, MessageTag *mtags) 1300 { 1301 RRPC *r, *r_next; 1302 OutstandingRRPC *or, *or_next; 1303 1304 for (r = rrpc_list; r; r = r_next) 1305 { 1306 r_next = r->next; 1307 if (!strncmp(client->id, r->source, SIDLEN) || 1308 !strncmp(client->id, r->destination, SIDLEN)) 1309 { 1310 free_rrpc(r); 1311 } 1312 } 1313 1314 for (or = outstanding_rrpc_list; or; or = or_next) 1315 { 1316 or_next = or->next; 1317 if (!strcmp(client->id, or->destination)) 1318 { 1319 Client *client = find_client(or->source, NULL); 1320 if (client) 1321 { 1322 json_t *j = json_object(); 1323 json_object_set_new(j, "id", json_string_unreal(or->requestid)); 1324 rpc_error(client, NULL, JSON_RPC_ERROR_SERVER_GONE, "Remote server disconnected while processing the request"); 1325 json_decref(j); 1326 } 1327 free_outstanding_rrpc(or); 1328 } 1329 } 1330 1331 return 0; 1332 } 1333 1334 EVENT(rpc_remote_timeout) 1335 { 1336 OutstandingRRPC *or, *or_next; 1337 time_t deadline = TStime() - 15; 1338 1339 for (or = outstanding_rrpc_list; or; or = or_next) 1340 { 1341 or_next = or->next; 1342 if (or->sent < deadline) 1343 { 1344 Client *client = find_client(or->source, NULL); 1345 if (client) 1346 { 1347 json_t *request = json_object(); 1348 json_object_set_new(request, "id", json_string_unreal(or->requestid)); 1349 rpc_error(client, request, JSON_RPC_ERROR_TIMEOUT, "Request timed out"); 1350 json_decref(request); 1351 } 1352 free_outstanding_rrpc(or); 1353 } 1354 } 1355 } 1356 1357 RRPC *find_rrpc(const char *source, const char *destination, const char *requestid) 1358 { 1359 RRPC *r; 1360 for (r = rrpc_list; r; r = r->next) 1361 { 1362 if (!strcmp(r->source, source) && 1363 !strcmp(r->destination, destination) && 1364 !strcmp(r->requestid, requestid)) 1365 { 1366 return r; 1367 } 1368 } 1369 return NULL; 1370 } 1371 1372 OutstandingRRPC *find_outstandingrrpc(const char *source, const char *requestid) 1373 { 1374 OutstandingRRPC *r; 1375 for (r = outstanding_rrpc_list; r; r = r->next) 1376 { 1377 if (!strcmp(r->source, source) && 1378 !strcmp(r->requestid, requestid)) 1379 { 1380 return r; 1381 } 1382 } 1383 return NULL; 1384 } 1385 1386 /* Remote RPC call over the network (RRPC) 1387 * :<server> RRPC <REQ|RES> <source> <destination> <requestid> [S|C|F] :<request data> 1388 * S = Start 1389 * C = Continuation 1390 * F = Finish 1391 */ 1392 CMD_FUNC(cmd_rrpc) 1393 { 1394 int request; 1395 const char *source, *destination, *requestid, *type, *data; 1396 RRPC *r; 1397 Client *dest; 1398 char sid[SIDLEN+1]; 1399 char binarydata[BUFSIZE+1]; 1400 int binarydatalen; 1401 1402 if ((parc < 7) || BadPtr(parv[6])) 1403 { 1404 sendnumeric(client, ERR_NEEDMOREPARAMS, "KNOCK"); 1405 return; 1406 } 1407 1408 if (!strcmp(parv[1], "REQ")) 1409 { 1410 request = 1; 1411 } else if (!strcmp(parv[1], "RES")) 1412 { 1413 request = 0; 1414 } else { 1415 sendnumeric(client, ERR_CANNOTDOCOMMAND, "RRPC", "Invalid parameter"); 1416 return; 1417 } 1418 1419 source = parv[2]; 1420 destination = parv[3]; 1421 requestid = parv[4]; 1422 type = parv[5]; 1423 data = parv[6]; 1424 1425 /* Search by SID (first 3 characters of destination) 1426 * so we can always deliver, even forn unknown UID destinations 1427 * in case this is a response. 1428 */ 1429 strlcpy(sid, destination, sizeof(sid)); 1430 dest = find_server_quick(sid); 1431 if (!dest) 1432 { 1433 sendnumeric(client, ERR_NOSUCHSERVER, sid); 1434 return; 1435 } 1436 1437 if (dest != &me) 1438 { 1439 /* Just pass it along... */ 1440 sendto_one(dest, recv_mtags, ":%s RRPC %s %s %s %s %s :%s", 1441 client->id, parv[1], parv[2], parv[3], parv[4], parv[5], parv[6]); 1442 return; 1443 } 1444 1445 /* It's for us! So handle it ;) */ 1446 1447 if (strchr(type, 'S')) 1448 { 1449 r = find_rrpc(source, destination, requestid); 1450 if (r) 1451 { 1452 sendnumeric(client, ERR_CANNOTDOCOMMAND, "RRPC", "Duplicate request found"); 1453 /* We actually terminate the existing RRPC as well, 1454 * because there's a big risk of the the two different ones 1455 * merging in subsequent RRPC... C ... commands. Bad! 1456 * (and yeah this does not handle the case where you have 1457 * like 3 or more duplicate request id requests... so be it..) 1458 */ 1459 free_rrpc(r); 1460 return; 1461 } 1462 /* A new request */ 1463 r = safe_alloc(sizeof(RRPC)); 1464 strlcpy(r->source, source, sizeof(r->source)); 1465 strlcpy(r->destination, destination, sizeof(r->destination)); 1466 safe_strdup(r->requestid, requestid); 1467 r->request = request; 1468 dbuf_queue_init(&r->data); 1469 AddListItem(r, rrpc_list); 1470 } else 1471 if (strchr(type, 'C') || strchr(type, 'F')) 1472 { 1473 r = find_rrpc(source, destination, requestid); 1474 if (!r) 1475 { 1476 sendnumeric(client, ERR_CANNOTDOCOMMAND, "RRPC", "Request not found"); 1477 return; 1478 } 1479 } else 1480 { 1481 sendnumeric(client, ERR_CANNOTDOCOMMAND, "RRPC", "Only actions S/C/F are supported"); 1482 return; 1483 } 1484 1485 /* Append the data */ 1486 dbuf_put(&r->data, data, strlen(data)); 1487 1488 /* Now check if the request happens to be terminated */ 1489 if (strchr(type, 'F')) 1490 { 1491 if (r->request) 1492 rpc_call_remote(r); 1493 else 1494 rpc_response_remote(r); 1495 free_rrpc(r); 1496 return; 1497 } 1498 } 1499 1500 /** Convert the RRPC data to actual workable JSON output */ 1501 json_t *rrpc_data(RRPC *r) 1502 { 1503 int datalen; 1504 char *data; 1505 json_t *j; 1506 json_error_t jerr; 1507 1508 datalen = dbuf_get(&r->data, &data); 1509 j = json_loads(data, JSON_REJECT_DUPLICATES, &jerr); 1510 safe_free(data); 1511 1512 return j; 1513 } 1514 1515 /** Received a remote RPC request (from a client on another server) */ 1516 void rpc_call_remote(RRPC *r) 1517 { 1518 json_t *request = NULL; 1519 Client *server; 1520 Client *client; 1521 char sid[SIDLEN+1]; 1522 1523 request = rrpc_data(r); 1524 if (!request) 1525 { 1526 // TODO: handle invalid JSON 1527 return; 1528 } 1529 1530 /* Create a (fake) client structure */ 1531 strlcpy(sid, r->source, sizeof(sid)); 1532 server = find_server_quick(sid); 1533 if (!server) 1534 { 1535 return; 1536 } 1537 client = make_client(server->direction, server); 1538 strlcpy(client->id, r->source, sizeof(client->id)); 1539 client->rpc = safe_alloc(sizeof(RPCClient)); 1540 strlcpy(client->name, "RPC:remote", sizeof(client->name)); 1541 safe_strdup(client->rpc->rpc_user, "<remote>"); 1542 // Note: NOT added to hash table or id table etc. 1543 list_add(&client->client_node, &rpc_remote_list); 1544 rpc_call(client, request); 1545 json_decref(request); 1546 1547 /* And free the temporary client, unless it is async... */ 1548 if (!IsAsyncRPC(client)) 1549 free_client(client); 1550 } 1551 1552 /** Received a remote RPC response (from another server) to our local RPC client */ 1553 void rpc_response_remote(RRPC *r) 1554 { 1555 OutstandingRRPC *or; 1556 Client *client = find_client(r->destination, NULL); 1557 json_t *json, *j; 1558 1559 if (!client) 1560 return; 1561 1562 or = find_outstandingrrpc(client->id, r->requestid); 1563 if (!or) 1564 return; /* Not a known outstanding request, maybe the client left already */ 1565 1566 json = rrpc_data(r); 1567 if (!json) 1568 return; 1569 1570 if ((j = json_object_get(json, "result"))) 1571 { 1572 rpc_response(client, json, j); 1573 } else if ((j = json_object_get(json, "error"))) 1574 { 1575 json_t *x; 1576 int errorcode = 0; 1577 const char *error_message = json_object_get_string(j, "message"); 1578 if ((x = json_object_get(j, "errorcode"))) 1579 errorcode = json_integer_value(x); 1580 if (errorcode == 0) 1581 errorcode = JSON_RPC_ERROR_INTERNAL_ERROR; 1582 rpc_error(client, json, errorcode, error_message ? error_message : ""); 1583 } 1584 1585 json_decref(json); 1586 1587 free_outstanding_rrpc(or); 1588 } 1589 1590 const char *rpc_id(json_t *request) 1591 { 1592 static char rid[128]; 1593 const char *requestid; 1594 json_t *j; 1595 1596 j = json_object_get(request, "id"); 1597 if (!j) 1598 return NULL; 1599 1600 requestid = json_string_value(j); 1601 if (!requestid) 1602 { 1603 json_int_t v = json_integer_value(j); 1604 if (v == 0) 1605 return NULL; 1606 snprintf(rid, sizeof(rid), "%lld", (long long)v); 1607 requestid = rid; 1608 } 1609 1610 return requestid; 1611 } 1612 1613 /** Send a remote RPC (RRPC) request 'request' to server 'target'. */ 1614 void rpc_send_generic_to_remote(Client *source, Client *target, const char *requesttype, json_t *json) 1615 { 1616 char *json_serialized; 1617 json_t *j; 1618 const char *type; 1619 const char *requestid; 1620 char *str; 1621 int bytes; /* bytes in this frame */ 1622 int bytes_remaining; /* bytes remaining overall */ 1623 int start_frame = 1; /* set to 1 if this is the start frame */ 1624 char data[451]; 1625 1626 requestid = rpc_id(json); 1627 if (!requestid) 1628 return; 1629 1630 json_serialized = json_dumps(json, 0); 1631 if (!json_serialized) 1632 return; 1633 1634 /* :<server> RRPC REQ <source> <destination> <requestid> [S|C|F] :<request data> 1635 * S = Start 1636 * C = Continuation 1637 * F = Finish 1638 */ 1639 1640 bytes_remaining = strlen(json_serialized); 1641 for (str = json_serialized, bytes = MIN(bytes_remaining, 450); 1642 str && *str && bytes_remaining; 1643 str += bytes, bytes = MIN(bytes_remaining, 450)) 1644 { 1645 bytes_remaining -= bytes; 1646 if (start_frame == 1) 1647 { 1648 start_frame = 0; 1649 if (bytes_remaining > 0) 1650 type = "S"; /* start (with later continuation frames) */ 1651 else 1652 type = "SF"; /* start and finish */ 1653 } else 1654 if (bytes_remaining > 0) 1655 { 1656 type = "C"; /* continuation frame (with later a finish frame) */ 1657 } else { 1658 type = "F"; /* finish frame (the last frame) */ 1659 } 1660 1661 strlncpy(data, str, sizeof(data), bytes); 1662 1663 sendto_one(target, NULL, ":%s RRPC %s %s %s %s %s :%s", 1664 me.id, 1665 requesttype, 1666 source->id, 1667 target->id, 1668 requestid, 1669 type, 1670 data); 1671 } 1672 1673 safe_free(json_serialized); 1674 } 1675 1676 int _rrpc_supported_simple(Client *target, char **problem_server) 1677 { 1678 if (!moddata_client_get(target, "rrpc")) 1679 { 1680 if (problem_server) 1681 *problem_server = target->name; 1682 return 0; 1683 } 1684 if ((target != target->direction) && !rrpc_supported_simple(target->direction, problem_server)) 1685 return 0; 1686 return 1; 1687 } 1688 1689 int _rrpc_supported(Client *target, const char *module, const char *minimum_version, char **problem_server) 1690 { 1691 if (!moddata_client_get(target, "rrpc")) 1692 { 1693 if (problem_server) 1694 *problem_server = target->name; 1695 return 0; 1696 } 1697 if ((target != target->direction) && !rrpc_supported_simple(target->direction, problem_server)) 1698 return 0; 1699 return 1; 1700 } 1701 1702 /** Send a remote RPC (RRPC) request 'request' to server 'target'. */ 1703 void _rpc_send_request_to_remote(Client *source, Client *target, json_t *request) 1704 { 1705 OutstandingRRPC *r; 1706 const char *requestid = rpc_id(request); 1707 char *problem_server = NULL; 1708 1709 if (!requestid) 1710 { 1711 /* should never happen, since already covered upstream, but just to be sure... */ 1712 rpc_error(source, NULL, JSON_RPC_ERROR_INVALID_REQUEST, "The 'id' must be a string or an integer in UnrealIRCd JSON-RPC"); 1713 return; 1714 } 1715 1716 if (find_outstandingrrpc(source->id, requestid)) 1717 { 1718 rpc_error(source, NULL, JSON_RPC_ERROR_INVALID_REQUEST, "A request with that id is already in progress. Use unique id's!"); 1719 return; 1720 } 1721 1722 /* If we already detect that next server cannot satisfy the request then stop it right now */ 1723 if (!rrpc_supported_simple(target, &problem_server)) 1724 { 1725 rpc_error_fmt(source, request, JSON_RPC_ERROR_REMOTE_SERVER_NO_RPC, "Server %s does not support remote JSON-RPC", problem_server); 1726 return; 1727 } 1728 1729 /* Add the request to the "Outstanding RRPC list" */ 1730 r = safe_alloc(sizeof(OutstandingRRPC)); 1731 r->sent = TStime(); 1732 strlcpy(r->source, source->id, sizeof(r->source)); 1733 strlcpy(r->destination, target->id, sizeof(r->destination)); 1734 safe_strdup(r->requestid, requestid); 1735 AddListItem(r, outstanding_rrpc_list); 1736 1737 /* And send it! */ 1738 rpc_send_generic_to_remote(source, target, "REQ", request); 1739 } 1740 1741 /** Send a remote RPC (RRPC) request 'request' to server 'target'. */ 1742 void _rpc_send_response_to_remote(Client *source, Client *target, json_t *response) 1743 { 1744 rpc_send_generic_to_remote(source, target, "RES", response); 1745 } 1746 1747 const char *rrpc_md_serialize(ModData *m) 1748 { 1749 static char buf[512]; 1750 char tmp[128]; 1751 NameValuePrioList *nv; 1752 1753 if (m->ptr == NULL) 1754 return NULL; 1755 1756 *buf = '\0'; 1757 for (nv = m->ptr; nv; nv = nv->next) 1758 { 1759 snprintf(tmp, sizeof(tmp), "%s:%s,", nv->name, nv->value); 1760 strlcat(buf, tmp, sizeof(buf)); 1761 } 1762 if (*buf) 1763 buf[strlen(buf)-1] = '\0'; // strip last comma 1764 1765 return buf; 1766 } 1767 1768 void rrpc_md_unserialize(const char *str, ModData *m) 1769 { 1770 char buf[1024], *p, *name, *value; 1771 1772 /* First free everything if needed */ 1773 if (m->ptr) 1774 { 1775 free_nvplist(m->ptr); 1776 m->ptr = NULL; 1777 } 1778 1779 if (BadPtr(str)) 1780 return; /* Done already */ 1781 1782 strlcpy(buf, str, sizeof(buf)); 1783 for (name = strtoken(&p, buf, ","); name; name = strtoken(&p, NULL, ",")) 1784 { 1785 value = strchr(name, ':'); 1786 if (!value) 1787 continue; 1788 *value++ = '\0'; 1789 add_nvplist((NameValuePrioList **)&m->ptr, 0, name, value); 1790 } 1791 } 1792 1793 void rrpc_md_free(ModData *m) 1794 { 1795 if (m->ptr) 1796 { 1797 free_nvplist(m->ptr); 1798 m->ptr = NULL; 1799 } 1800 } 1801 1802 int rpc_json_expand_client_server(Client *client, int detail, json_t *j, json_t *child) 1803 { 1804 NameValuePrioList *nv = RRPCMODULES(client); 1805 json_t *rpc_modules; 1806 1807 if (!nv || (detail < 2)) 1808 return 0; 1809 1810 /* All this belongs under 'features' */ 1811 child = json_object_get(child, "features"); 1812 if (!child) 1813 return 0; 1814 1815 rpc_modules = json_array(); 1816 json_object_set_new(child, "rpc_modules", rpc_modules); 1817 for (; nv; nv = nv->next) 1818 { 1819 json_t *e = json_object(); 1820 json_object_set_new(e, "name", json_string_unreal(nv->name)); 1821 json_object_set_new(e, "version", json_string_unreal(nv->value)); 1822 json_array_append_new(rpc_modules, e); 1823 } 1824 return 0; 1825 } 1826 1827 RPC_CALL_FUNC(rpc_rpc_add_timer) 1828 { 1829 json_t *result; 1830 json_t *subrequest; 1831 long every_msec; 1832 const char *timer_id; 1833 const char *method; 1834 RPCHandler *handler; 1835 RPCTimer *timer; 1836 1837 REQUIRE_PARAM_INTEGER("every_msec", every_msec); 1838 REQUIRE_PARAM_STRING("timer_id", timer_id); 1839 1840 subrequest = json_object_get(params, "request"); 1841 if (!subrequest) 1842 { 1843 rpc_error_fmt(client, request, JSON_RPC_ERROR_INVALID_PARAMS, "Missing parameter: '%s'", "request"); 1844 return; 1845 } 1846 1847 if (every_msec < RPC_MINIMUM_TIMER_MSEC) 1848 { 1849 rpc_error_fmt(client, request, JSON_RPC_ERROR_INVALID_PARAMS, 1850 "Value for every_msec may not be less than %d", 1851 (int)RPC_MINIMUM_TIMER_MSEC); 1852 return; 1853 } 1854 1855 /* Do some validation on the name */ 1856 if (!parse_rpc_call(client, request, subrequest, &method, &handler)) 1857 return; /* Error already returned to caller */ 1858 1859 /* We don't validate 'params' here, but do so at runtime */ 1860 1861 /* Check if a timer with that same name already exists FOR THIS CLIENT */ 1862 if (find_rpc_timer(client, timer_id)) 1863 { 1864 rpc_error_fmt(client, request, JSON_RPC_ERROR_ALREADY_EXISTS, "Timer already exists with timer_id '%s'", timer_id); 1865 return; 1866 } 1867 1868 timer = safe_alloc(sizeof(RPCTimer)); 1869 timer->every_msec = every_msec; 1870 timer->client = client; 1871 safe_strdup(timer->timer_id, timer_id); 1872 json_incref(subrequest); 1873 timer->request = subrequest; 1874 AddListItem(timer, rpc_timer_list); 1875 result = json_boolean(1); 1876 rpc_response(client, request, result); 1877 json_decref(result); 1878 } 1879 1880 EVENT(rpc_do_timers) 1881 { 1882 RPCTimer *e, *e_next; 1883 1884 for (e = rpc_timer_list; e; e = e_next) 1885 { 1886 e_next = e->next; 1887 if (minimum_msec_since_last_run(&e->last_run, e->every_msec)) 1888 { 1889 rpc_call(e->client, e->request); 1890 } 1891 // TODO: maybe do counts as well? 1892 } 1893 } 1894 1895 /** Client being freed? If RPC then cancel timers, if any */ 1896 int rpc_handle_free_client(Client *client) 1897 { 1898 if (IsRPC(client)) 1899 free_rpc_timers_for_user(client); 1900 return 0; 1901 } 1902 1903 RPC_CALL_FUNC(rpc_rpc_del_timer) 1904 { 1905 const char *timer_id; 1906 RPCTimer *r; 1907 json_t *result; 1908 1909 REQUIRE_PARAM_STRING("timer_id", timer_id); 1910 1911 r = find_rpc_timer(client, timer_id); 1912 if (!r) 1913 { 1914 rpc_error_fmt(client, request, JSON_RPC_ERROR_NOT_FOUND, "Timer not found with timer_id '%s'", timer_id); 1915 return; 1916 } 1917 free_rpc_timer(r); 1918 1919 result = json_boolean(1); 1920 rpc_response(client, request, result); 1921 json_decref(result); 1922 }