1 /* 2 * uclient - ustream based protocol client library - ucode binding 3 * 4 * Copyright (C) 2024 Felix Fietkau <nbd@openwrt.org> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include <libubox/uloop.h> 19 #include <libubox/blobmsg.h> 20 #include <ucode/module.h> 21 #include "uclient.h" 22 23 static uc_resource_type_t *uc_uclient_type; 24 static uc_value_t *registry; 25 static uc_vm_t *uc_vm; 26 27 struct uc_uclient_priv { 28 struct uclient_cb cb; 29 const struct ustream_ssl_ops *ssl_ops; 30 struct ustream_ssl_ctx *ssl_ctx; 31 uc_value_t *resource; 32 unsigned int idx; 33 int offset; 34 }; 35 36 static void uc_uclient_register(struct uc_uclient_priv *ucl, uc_value_t *cb) 37 { 38 size_t i, len; 39 40 len = ucv_array_length(registry); 41 for (i = 0; i < len; i++) 42 if (!ucv_array_get(registry, i)) 43 break; 44 45 ucv_array_set(registry, i, ucv_get(cb)); 46 ucl->idx = i; 47 } 48 49 static void free_uclient(void *ptr) 50 { 51 struct uclient *cl = ptr; 52 struct uc_uclient_priv *ucl; 53 54 if (!cl) 55 return; 56 57 ucl = cl->priv; 58 if (ucl->ssl_ctx) 59 ucl->ssl_ops->context_free(ucl->ssl_ctx); 60 ucv_array_set(registry, ucl->idx, NULL); 61 uclient_free(cl); 62 free(ucl); 63 } 64 65 static uc_value_t * 66 uc_uclient_free(uc_vm_t *vm, size_t nargs) 67 { 68 struct uclient **cl = uc_fn_this("uclient"); 69 70 free_uclient(*cl); 71 *cl = NULL; 72 73 return NULL; 74 } 75 76 static uc_value_t * 77 uc_uclient_ssl_init(uc_vm_t *vm, size_t nargs) 78 { 79 struct uclient *cl = uc_fn_thisval("uclient"); 80 const struct ustream_ssl_ops *ops; 81 struct ustream_ssl_ctx *ctx; 82 struct uc_uclient_priv *ucl; 83 uc_value_t *args = uc_fn_arg(0); 84 bool verify = false; 85 uc_value_t *cur; 86 87 if (!cl) 88 return NULL; 89 90 ucl = cl->priv; 91 if (ucl->ssl_ctx) { 92 uclient_http_set_ssl_ctx(cl, NULL, NULL, false); 93 ucl->ssl_ctx = NULL; 94 ucl->ssl_ops = NULL; 95 } 96 97 ctx = uclient_new_ssl_context(&ops); 98 if (!ctx) 99 return NULL; 100 101 ucl->ssl_ops = ops; 102 ucl->ssl_ctx = ctx; 103 104 if ((cur = ucv_object_get(args, "cert_file", NULL)) != NULL) { 105 const char *str = ucv_string_get(cur); 106 if (!str || ops->context_set_crt_file(ctx, str)) 107 goto err; 108 } 109 110 if ((cur = ucv_object_get(args, "key_file", NULL)) != NULL) { 111 const char *str = ucv_string_get(cur); 112 if (!str || ops->context_set_key_file(ctx, str)) 113 goto err; 114 } 115 116 if ((cur = ucv_object_get(args, "ca_files", NULL)) != NULL) { 117 size_t len; 118 119 if (ucv_type(cur) != UC_ARRAY) 120 goto err; 121 122 len = ucv_array_length(cur); 123 for (size_t i = 0; i < len; i++) { 124 uc_value_t *c = ucv_array_get(cur, i); 125 const char *str; 126 127 if (!c) 128 continue; 129 130 str = ucv_string_get(c); 131 if (!str) 132 goto err; 133 134 ops->context_add_ca_crt_file(ctx, str); 135 } 136 137 verify = true; 138 } 139 140 if ((cur = ucv_object_get(args, "verify", NULL)) != NULL) 141 verify = ucv_is_truish(cur); 142 143 ops->context_set_require_validation(ctx, verify); 144 uclient_http_set_ssl_ctx(cl, ops, ctx, verify); 145 146 return ucv_boolean_new(true); 147 148 err: 149 ops->context_free(ctx); 150 ucl->ssl_ctx = NULL; 151 ucl->ssl_ops = NULL; 152 return NULL; 153 } 154 155 static uc_value_t * 156 uc_uclient_set_timeout(uc_vm_t *vm, size_t nargs) 157 { 158 struct uclient *cl = uc_fn_thisval("uclient"); 159 uc_value_t *val = uc_fn_arg(0); 160 161 if (!cl || ucv_type(val) != UC_INTEGER) 162 return NULL; 163 164 if (uclient_set_timeout(cl, ucv_int64_get(val))) 165 return NULL; 166 167 return ucv_boolean_new(true); 168 } 169 170 static uc_value_t * 171 uc_uclient_set_url(uc_vm_t *vm, size_t nargs) 172 { 173 struct uclient *cl = uc_fn_thisval("uclient"); 174 uc_value_t *url = uc_fn_arg(0); 175 uc_value_t *auth_str = uc_fn_arg(1); 176 177 if (!cl || ucv_type(url) != UC_STRING || 178 (auth_str && ucv_type(auth_str) != UC_STRING)) 179 return NULL; 180 181 if (uclient_set_url(cl, ucv_string_get(url), ucv_string_get(auth_str))) 182 return NULL; 183 184 return ucv_boolean_new(true); 185 } 186 187 static uc_value_t * 188 uc_uclient_set_proxy_url(uc_vm_t *vm, size_t nargs) 189 { 190 struct uclient *cl = uc_fn_thisval("uclient"); 191 uc_value_t *url = uc_fn_arg(0); 192 uc_value_t *auth_str = uc_fn_arg(1); 193 194 if (!cl || ucv_type(url) != UC_STRING || 195 (auth_str && ucv_type(auth_str) != UC_STRING)) 196 return NULL; 197 198 if (uclient_set_proxy_url(cl, ucv_string_get(url), ucv_string_get(auth_str))) 199 return NULL; 200 201 return ucv_boolean_new(true); 202 } 203 204 static uc_value_t * 205 uc_uclient_get_headers(uc_vm_t *vm, size_t nargs) 206 { 207 struct uclient *cl = uc_fn_thisval("uclient"); 208 struct blob_attr *cur; 209 uc_value_t *ret; 210 size_t rem; 211 212 if (!cl) 213 return NULL; 214 215 ret = ucv_object_new(uc_vm); 216 blobmsg_for_each_attr(cur, cl->meta, rem) { 217 uc_value_t *str; 218 219 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 220 continue; 221 222 str = ucv_string_new(blobmsg_get_string(cur)); 223 ucv_object_add(ret, blobmsg_name(cur), str); 224 } 225 226 return ret; 227 } 228 229 static uc_value_t * 230 uc_uclient_connect(uc_vm_t *vm, size_t nargs) 231 { 232 struct uclient *cl = uc_fn_thisval("uclient"); 233 234 if (!cl || uclient_connect(cl)) 235 return NULL; 236 237 return ucv_boolean_new(true); 238 } 239 240 static uc_value_t * 241 uc_uclient_disconnect(uc_vm_t *vm, size_t nargs) 242 { 243 struct uclient *cl = uc_fn_thisval("uclient"); 244 245 if (!cl) 246 return NULL; 247 248 uclient_disconnect(cl); 249 250 return ucv_boolean_new(true); 251 } 252 253 static uc_value_t * 254 __uc_uclient_cb(struct uclient *cl, const char *name, uc_value_t *arg) 255 { 256 struct uc_uclient_priv *ucl = cl->priv; 257 uc_vm_t *vm = uc_vm; 258 uc_value_t *cb, *cb_obj, *ret = NULL; 259 260 cb_obj = ucv_array_get(registry, ucl->idx); 261 if (!cb_obj) 262 goto out; 263 264 cb = ucv_property_get(cb_obj, name); 265 if (!cb) 266 goto out; 267 268 if (!ucv_is_callable(cb)) 269 goto out; 270 271 uc_vm_stack_push(vm, ucv_get(ucl->resource)); 272 uc_vm_stack_push(vm, ucv_get(cb)); 273 uc_vm_stack_push(vm, ucv_get(cb_obj)); 274 if (arg) 275 uc_vm_stack_push(vm, ucv_get(arg)); 276 277 if (uc_vm_call(vm, true, !!arg + 1) != EXCEPTION_NONE) { 278 if (vm->exhandler) 279 vm->exhandler(vm, &vm->exception); 280 goto out; 281 } 282 283 ret = uc_vm_stack_pop(vm); 284 285 out: 286 /* the callers hand over their reference on arg */ 287 if (arg) 288 ucv_put(arg); 289 290 return ret; 291 } 292 293 static void 294 uc_write_str(struct uclient *cl, uc_value_t *val) 295 { 296 uclient_write(cl, ucv_string_get(val), ucv_string_length(val)); 297 } 298 299 static bool uc_cb_data_write(struct uclient *cl) 300 { 301 struct uc_uclient_priv *ucl = cl->priv; 302 bool ret = false; 303 uc_value_t *val; 304 size_t len; 305 306 val = __uc_uclient_cb(cl, "get_post_data", ucv_int64_new(ucl->offset)); 307 if (ucv_type(val) != UC_STRING) 308 goto out; 309 310 len = ucv_string_length(val); 311 if (!len) 312 goto out; 313 314 ucl->offset += len; 315 uc_write_str(cl, val); 316 ret = true; 317 318 out: 319 ucv_put(val); 320 return ret; 321 } 322 323 static uc_value_t * 324 uc_uclient_request(uc_vm_t *vm, size_t nargs) 325 { 326 struct uclient *cl = uc_fn_thisval("uclient"); 327 struct uc_uclient_priv *ucl; 328 uc_value_t *type = uc_fn_arg(0); 329 uc_value_t *arg = uc_fn_arg(1); 330 uc_value_t *cur; 331 const char *type_str = ucv_string_get(type); 332 333 if (!cl || !type_str) 334 return NULL; 335 336 ucl = cl->priv; 337 ucl->offset = 0; 338 339 if (uclient_http_set_request_type(cl, type_str)) 340 return NULL; 341 342 uclient_http_reset_headers(cl); 343 344 if ((cur = ucv_property_get(arg, "headers")) != NULL) { 345 if (ucv_type(cur) != UC_OBJECT) 346 return NULL; 347 348 ucv_object_foreach(cur, key, val) { 349 char *str; 350 351 if (!val) 352 continue; 353 354 if (ucv_type(val) == UC_STRING) { 355 uclient_http_set_header(cl, key, ucv_string_get(val)); 356 continue; 357 } 358 359 str = ucv_to_string(uc_vm, val); 360 uclient_http_set_header(cl, key, str); 361 free(str); 362 } 363 } 364 365 if ((cur = ucv_property_get(arg, "post_data")) != NULL) { 366 if (ucv_type(cur) != UC_STRING) 367 return NULL; 368 369 uc_write_str(cl, cur); 370 } 371 372 while (uc_cb_data_write(cl)) 373 if (uclient_pending_bytes(cl, true)) 374 return ucv_boolean_new(true); 375 376 ucl->offset = -1; 377 if (uclient_request(cl)) 378 return NULL; 379 380 return ucv_boolean_new(true); 381 } 382 383 static uc_value_t * 384 uc_uclient_redirect(uc_vm_t *vm, size_t nargs) 385 { 386 struct uclient *cl = uc_fn_thisval("uclient"); 387 388 if (!cl || uclient_http_redirect(cl)) 389 return NULL; 390 391 return ucv_boolean_new(true); 392 } 393 394 static uc_value_t * 395 uc_uclient_status(uc_vm_t *vm, size_t nargs) 396 { 397 struct uclient *cl = uc_fn_thisval("uclient"); 398 char addr[INET6_ADDRSTRLEN]; 399 uc_value_t *ret; 400 int port; 401 402 if (!cl) 403 return NULL; 404 405 ret = ucv_object_new(vm); 406 ucv_object_add(ret, "eof", ucv_boolean_new(cl->eof)); 407 ucv_object_add(ret, "data_eof", ucv_boolean_new(cl->data_eof)); 408 ucv_object_add(ret, "status", ucv_int64_new(cl->status_code)); 409 ucv_object_add(ret, "redirect", ucv_boolean_new(uclient_http_status_redirect(cl))); 410 411 uclient_get_addr(addr, &port, &cl->local_addr); 412 ucv_object_add(ret, "local_addr", ucv_string_new(addr)); 413 ucv_object_add(ret, "local_port", ucv_int64_new(port)); 414 415 uclient_get_addr(addr, &port, &cl->remote_addr); 416 ucv_object_add(ret, "remote_addr", ucv_string_new(addr)); 417 ucv_object_add(ret, "remote_port", ucv_int64_new(port)); 418 419 return ret; 420 } 421 422 static uc_value_t * 423 uc_uclient_read(uc_vm_t *vm, size_t nargs) 424 { 425 struct uclient *cl = uc_fn_thisval("uclient"); 426 size_t len = ucv_int64_get(uc_fn_arg(0)); 427 uc_stringbuf_t *strbuf = NULL; 428 static char buf[4096]; 429 int cur; 430 431 if (!cl) 432 return NULL; 433 434 if (!len) 435 len = sizeof(buf); 436 437 while (len > 0) { 438 cur = uclient_read(cl, buf, len > sizeof(buf) ? sizeof(buf) : len); 439 if (cur <= 0) 440 break; 441 442 if (!strbuf) 443 strbuf = ucv_stringbuf_new(); 444 445 ucv_stringbuf_addstr(strbuf, buf, cur); 446 len -= cur; 447 } 448 449 if (!strbuf) 450 return NULL; 451 452 return ucv_stringbuf_finish(strbuf); 453 } 454 455 static void 456 uc_uclient_cb(struct uclient *cl, const char *name, uc_value_t *arg) 457 { 458 ucv_put(__uc_uclient_cb(cl, name, arg)); 459 } 460 461 static void uc_cb_data_read(struct uclient *cl) 462 { 463 uc_uclient_cb(cl, "data_read", NULL); 464 } 465 466 static void uc_cb_data_sent(struct uclient *cl) 467 { 468 struct uc_uclient_priv *ucl = cl->priv; 469 470 if (ucl->offset < 0 || uclient_pending_bytes(cl, true)) 471 return; 472 473 while (uc_cb_data_write(cl)) 474 if (uclient_pending_bytes(cl, true)) 475 return; 476 477 ucl->offset = -1; 478 uclient_request(cl); 479 } 480 481 static void uc_cb_data_eof(struct uclient *cl) 482 { 483 uc_uclient_cb(cl, "data_eof", NULL); 484 } 485 486 static void uc_cb_header_done(struct uclient *cl) 487 { 488 uc_uclient_cb(cl, "header_done", NULL); 489 } 490 491 static void uc_cb_error(struct uclient *cl, int code) 492 { 493 uc_uclient_cb(cl, "error", ucv_int64_new(code)); 494 } 495 496 static uc_value_t * 497 uc_uclient_new(uc_vm_t *vm, size_t nargs) 498 { 499 struct uc_uclient_priv *ucl; 500 uc_value_t *url = uc_fn_arg(0); 501 uc_value_t *auth_str = uc_fn_arg(1); 502 uc_value_t *cb = uc_fn_arg(2); 503 static bool _init_done; 504 struct uclient *cl; 505 506 if (!_init_done) { 507 uloop_init(); 508 _init_done = true; 509 } 510 511 uc_vm = vm; 512 513 if (ucv_type(url) != UC_STRING || 514 (auth_str && ucv_type(auth_str) != UC_STRING) || 515 ucv_type(cb) != UC_OBJECT) 516 return NULL; 517 518 ucl = calloc(1, sizeof(*ucl)); 519 if (!ucl) 520 return NULL; 521 522 if (ucv_property_get(cb, "data_read")) 523 ucl->cb.data_read = uc_cb_data_read; 524 if (ucv_property_get(cb, "get_post_data")) 525 ucl->cb.data_sent = uc_cb_data_sent; 526 if (ucv_property_get(cb, "data_eof")) 527 ucl->cb.data_eof = uc_cb_data_eof; 528 if (ucv_property_get(cb, "header_done")) 529 ucl->cb.header_done = uc_cb_header_done; 530 if (ucv_property_get(cb, "error")) 531 ucl->cb.error = uc_cb_error; 532 533 cl = uclient_new(ucv_string_get(url), ucv_string_get(auth_str), &ucl->cb); 534 if (!cl) { 535 free(ucl); 536 return NULL; 537 } 538 539 cl->priv = ucl; 540 uc_uclient_register(ucl, cb); 541 ucl->resource = ucv_resource_new(uc_uclient_type, cl); 542 543 return ucl->resource; 544 } 545 static const uc_function_list_t uclient_fns[] = { 546 { "free", uc_uclient_free }, 547 { "ssl_init", uc_uclient_ssl_init }, 548 { "set_url", uc_uclient_set_url }, 549 { "set_proxy_url", uc_uclient_set_proxy_url }, 550 { "set_timeout", uc_uclient_set_timeout }, 551 { "get_headers", uc_uclient_get_headers }, 552 553 { "connect", uc_uclient_connect }, 554 { "disconnect", uc_uclient_disconnect }, 555 { "request", uc_uclient_request }, 556 { "redirect", uc_uclient_redirect }, 557 { "status", uc_uclient_status }, 558 559 { "read", uc_uclient_read }, 560 }; 561 562 static const uc_function_list_t global_fns[] = { 563 { "new", uc_uclient_new }, 564 }; 565 566 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 567 { 568 uc_uclient_type = uc_type_declare(vm, "uclient", uclient_fns, free_uclient); 569 registry = ucv_array_new(vm); 570 uc_vm_registry_set(vm, "uclient.registry", registry); 571 uc_function_list_register(scope, global_fns); 572 } 573
This page was automatically generated by LXR 0.3.1. • OpenWrt