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 ucv_array_set(registry, ucl->idx + 1, NULL); 62 uclient_free(cl); 63 free(ucl); 64 } 65 66 static uc_value_t * 67 uc_uclient_free(uc_vm_t *vm, size_t nargs) 68 { 69 struct uclient **cl = uc_fn_this("uclient"); 70 71 free_uclient(*cl); 72 *cl = NULL; 73 74 return NULL; 75 } 76 77 static uc_value_t * 78 uc_uclient_ssl_init(uc_vm_t *vm, size_t nargs) 79 { 80 struct uclient *cl = uc_fn_thisval("uclient"); 81 const struct ustream_ssl_ops *ops; 82 struct ustream_ssl_ctx *ctx; 83 struct uc_uclient_priv *ucl; 84 uc_value_t *args = uc_fn_arg(0); 85 bool verify = false; 86 uc_value_t *cur; 87 88 if (!cl) 89 return NULL; 90 91 ucl = cl->priv; 92 if (ucl->ssl_ctx) { 93 uclient_http_set_ssl_ctx(cl, NULL, NULL, false); 94 ucl->ssl_ctx = NULL; 95 ucl->ssl_ops = NULL; 96 } 97 98 ctx = uclient_new_ssl_context(&ops); 99 if (!ctx) 100 return NULL; 101 102 ucl->ssl_ops = ops; 103 ucl->ssl_ctx = ctx; 104 105 if ((cur = ucv_object_get(args, "cert_file", NULL)) != NULL) { 106 const char *str = ucv_string_get(cur); 107 if (!str || ops->context_set_crt_file(ctx, str)) 108 goto err; 109 } 110 111 if ((cur = ucv_object_get(args, "key_file", NULL)) != NULL) { 112 const char *str = ucv_string_get(cur); 113 if (!str || ops->context_set_key_file(ctx, str)) 114 goto err; 115 } 116 117 if ((cur = ucv_object_get(args, "ca_files", NULL)) != NULL) { 118 size_t len; 119 120 if (ucv_type(cur) != UC_ARRAY) 121 goto err; 122 123 len = ucv_array_length(cur); 124 for (size_t i = 0; i < len; i++) { 125 uc_value_t *c = ucv_array_get(cur, i); 126 const char *str; 127 128 if (!c) 129 continue; 130 131 str = ucv_string_get(c); 132 if (!str) 133 goto err; 134 135 ops->context_add_ca_crt_file(ctx, str); 136 } 137 138 verify = true; 139 } 140 141 if ((cur = ucv_object_get(args, "verify", NULL)) != NULL) 142 verify = ucv_is_truish(cur); 143 144 ops->context_set_require_validation(ctx, verify); 145 uclient_http_set_ssl_ctx(cl, ops, ctx, verify); 146 147 return ucv_boolean_new(true); 148 149 err: 150 ops->context_free(ctx); 151 return NULL; 152 } 153 154 static uc_value_t * 155 uc_uclient_set_timeout(uc_vm_t *vm, size_t nargs) 156 { 157 struct uclient *cl = uc_fn_thisval("uclient"); 158 uc_value_t *val = uc_fn_arg(0); 159 160 if (!cl || ucv_type(val) != UC_INTEGER) 161 return NULL; 162 163 if (uclient_set_timeout(cl, ucv_int64_get(val))) 164 return NULL; 165 166 return ucv_boolean_new(true); 167 } 168 169 static uc_value_t * 170 uc_uclient_set_url(uc_vm_t *vm, size_t nargs) 171 { 172 struct uclient *cl = uc_fn_thisval("uclient"); 173 uc_value_t *url = uc_fn_arg(0); 174 uc_value_t *auth_str = uc_fn_arg(1); 175 176 if (!cl || ucv_type(url) != UC_STRING || 177 (auth_str && ucv_type(auth_str) != UC_STRING)) 178 return NULL; 179 180 if (uclient_set_url(cl, ucv_string_get(url), ucv_string_get(auth_str))) 181 return NULL; 182 183 return ucv_boolean_new(true); 184 } 185 186 static uc_value_t * 187 uc_uclient_set_proxy_url(uc_vm_t *vm, size_t nargs) 188 { 189 struct uclient *cl = uc_fn_thisval("uclient"); 190 uc_value_t *url = uc_fn_arg(0); 191 uc_value_t *auth_str = uc_fn_arg(1); 192 193 if (!cl || ucv_type(url) != UC_STRING || 194 (auth_str && ucv_type(auth_str) != UC_STRING)) 195 return NULL; 196 197 if (uclient_set_proxy_url(cl, ucv_string_get(url), ucv_string_get(auth_str))) 198 return NULL; 199 200 return ucv_boolean_new(true); 201 } 202 203 static uc_value_t * 204 uc_uclient_get_headers(uc_vm_t *vm, size_t nargs) 205 { 206 struct uclient *cl = uc_fn_thisval("uclient"); 207 struct blob_attr *cur; 208 uc_value_t *ret; 209 size_t rem; 210 211 if (!cl) 212 return NULL; 213 214 ret = ucv_object_new(uc_vm); 215 blobmsg_for_each_attr(cur, cl->meta, rem) { 216 uc_value_t *str; 217 218 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) 219 continue; 220 221 str = ucv_string_new(blobmsg_get_string(cur)); 222 ucv_object_add(ret, blobmsg_name(cur), ucv_get(str)); 223 } 224 225 return ret; 226 } 227 228 static uc_value_t * 229 uc_uclient_connect(uc_vm_t *vm, size_t nargs) 230 { 231 struct uclient *cl = uc_fn_thisval("uclient"); 232 233 if (!cl || uclient_connect(cl)) 234 return NULL; 235 236 return ucv_boolean_new(true); 237 } 238 239 static uc_value_t * 240 uc_uclient_disconnect(uc_vm_t *vm, size_t nargs) 241 { 242 struct uclient *cl = uc_fn_thisval("uclient"); 243 244 if (!cl) 245 return NULL; 246 247 uclient_disconnect(cl); 248 249 return ucv_boolean_new(true); 250 } 251 252 static uc_value_t * 253 __uc_uclient_cb(struct uclient *cl, const char *name, uc_value_t *arg) 254 { 255 struct uc_uclient_priv *ucl = cl->priv; 256 uc_vm_t *vm = uc_vm; 257 uc_value_t *cb, *cb_obj; 258 259 cb_obj = ucv_array_get(registry, ucl->idx); 260 if (!cb_obj) 261 return NULL; 262 263 cb = ucv_property_get(cb_obj, name); 264 if (!cb) 265 return NULL; 266 267 if (!ucv_is_callable(cb)) 268 return NULL; 269 270 uc_vm_stack_push(vm, ucv_get(ucl->resource)); 271 uc_vm_stack_push(vm, ucv_get(cb)); 272 uc_vm_stack_push(vm, ucv_get(cb_obj)); 273 if (arg) 274 uc_vm_stack_push(vm, ucv_get(arg)); 275 276 if (uc_vm_call(vm, true, !!arg + 1) != EXCEPTION_NONE) { 277 if (vm->exhandler) 278 vm->exhandler(vm, &vm->exception); 279 return NULL; 280 } 281 282 return uc_vm_stack_pop(vm); 283 } 284 285 static void 286 uc_write_str(struct uclient *cl, uc_value_t *val) 287 { 288 uclient_write(cl, ucv_string_get(val), ucv_string_length(val)); 289 } 290 291 static bool uc_cb_data_write(struct uclient *cl) 292 { 293 struct uc_uclient_priv *ucl = cl->priv; 294 bool ret = false; 295 uc_value_t *val; 296 size_t len; 297 298 val = __uc_uclient_cb(cl, "get_post_data", ucv_int64_new(ucl->offset)); 299 if (ucv_type(val) != UC_STRING) 300 goto out; 301 302 len = ucv_string_length(val); 303 if (!len) 304 goto out; 305 306 ucl->offset += len; 307 uc_write_str(cl, val); 308 ret = true; 309 310 out: 311 ucv_put(val); 312 return ret; 313 } 314 315 static uc_value_t * 316 uc_uclient_request(uc_vm_t *vm, size_t nargs) 317 { 318 struct uclient *cl = uc_fn_thisval("uclient"); 319 struct uc_uclient_priv *ucl; 320 uc_value_t *type = uc_fn_arg(0); 321 uc_value_t *arg = uc_fn_arg(1); 322 uc_value_t *cur; 323 const char *type_str = ucv_string_get(type); 324 325 if (!cl || !type_str) 326 return NULL; 327 328 ucl = cl->priv; 329 ucl->offset = 0; 330 331 if (uclient_http_set_request_type(cl, type_str)) 332 return NULL; 333 334 uclient_http_reset_headers(cl); 335 336 if ((cur = ucv_property_get(arg, "headers")) != NULL) { 337 if (ucv_type(cur) != UC_OBJECT) 338 return NULL; 339 340 ucv_object_foreach(cur, key, val) { 341 char *str; 342 343 if (!val) 344 continue; 345 346 if (ucv_type(val) == UC_STRING) { 347 uclient_http_set_header(cl, key, ucv_string_get(val)); 348 continue; 349 } 350 351 str = ucv_to_string(uc_vm, val); 352 uclient_http_set_header(cl, key, str); 353 free(str); 354 } 355 } 356 357 if ((cur = ucv_property_get(arg, "post_data")) != NULL) { 358 if (ucv_type(cur) != UC_STRING) 359 return NULL; 360 361 uc_write_str(cl, cur); 362 } 363 364 while (uc_cb_data_write(cl)) 365 if (uclient_pending_bytes(cl, true)) 366 return ucv_boolean_new(true); 367 368 ucl->offset = -1; 369 if (uclient_request(cl)) 370 return NULL; 371 372 return ucv_boolean_new(true); 373 } 374 375 static uc_value_t * 376 uc_uclient_redirect(uc_vm_t *vm, size_t nargs) 377 { 378 struct uclient *cl = uc_fn_thisval("uclient"); 379 380 if (!cl || uclient_http_redirect(cl)) 381 return NULL; 382 383 return ucv_boolean_new(true); 384 } 385 386 static uc_value_t * 387 uc_uclient_status(uc_vm_t *vm, size_t nargs) 388 { 389 struct uclient *cl = uc_fn_thisval("uclient"); 390 char addr[INET6_ADDRSTRLEN]; 391 uc_value_t *ret; 392 int port; 393 394 if (!cl) 395 return NULL; 396 397 ret = ucv_object_new(vm); 398 ucv_object_add(ret, "eof", ucv_boolean_new(cl->eof)); 399 ucv_object_add(ret, "data_eof", ucv_boolean_new(cl->data_eof)); 400 ucv_object_add(ret, "status", ucv_int64_new(cl->status_code)); 401 ucv_object_add(ret, "redirect", ucv_boolean_new(uclient_http_status_redirect(cl))); 402 403 uclient_get_addr(addr, &port, &cl->local_addr); 404 ucv_object_add(ret, "local_addr", ucv_get(ucv_string_new(addr))); 405 ucv_object_add(ret, "local_port", ucv_get(ucv_int64_new(port))); 406 407 uclient_get_addr(addr, &port, &cl->remote_addr); 408 ucv_object_add(ret, "remote_addr", ucv_get(ucv_string_new(addr))); 409 ucv_object_add(ret, "remote_port", ucv_get(ucv_int64_new(port))); 410 411 return ret; 412 } 413 414 static uc_value_t * 415 uc_uclient_read(uc_vm_t *vm, size_t nargs) 416 { 417 struct uclient *cl = uc_fn_thisval("uclient"); 418 size_t len = ucv_int64_get(uc_fn_arg(0)); 419 uc_stringbuf_t *strbuf = NULL; 420 static char buf[4096]; 421 int cur; 422 423 if (!cl) 424 return NULL; 425 426 if (!len) 427 len = sizeof(buf); 428 429 while (len > 0) { 430 cur = uclient_read(cl, buf, len); 431 if (cur <= 0) 432 break; 433 434 if (!strbuf) 435 strbuf = ucv_stringbuf_new(); 436 437 ucv_stringbuf_addstr(strbuf, buf, cur); 438 len -= cur; 439 } 440 441 if (!strbuf) 442 return NULL; 443 444 return ucv_stringbuf_finish(strbuf); 445 } 446 447 static void 448 uc_uclient_cb(struct uclient *cl, const char *name, uc_value_t *arg) 449 { 450 ucv_put(__uc_uclient_cb(cl, name, arg)); 451 } 452 453 static void uc_cb_data_read(struct uclient *cl) 454 { 455 uc_uclient_cb(cl, "data_read", NULL); 456 } 457 458 static void uc_cb_data_sent(struct uclient *cl) 459 { 460 struct uc_uclient_priv *ucl = cl->priv; 461 462 if (ucl->offset < 0 || uclient_pending_bytes(cl, true)) 463 return; 464 465 while (uc_cb_data_write(cl)) 466 if (uclient_pending_bytes(cl, true)) 467 return; 468 469 ucl->offset = -1; 470 uclient_request(cl); 471 } 472 473 static void uc_cb_data_eof(struct uclient *cl) 474 { 475 uc_uclient_cb(cl, "data_eof", NULL); 476 } 477 478 static void uc_cb_header_done(struct uclient *cl) 479 { 480 uc_uclient_cb(cl, "header_done", NULL); 481 } 482 483 static void uc_cb_error(struct uclient *cl, int code) 484 { 485 uc_uclient_cb(cl, "error", ucv_int64_new(code)); 486 } 487 488 static uc_value_t * 489 uc_uclient_new(uc_vm_t *vm, size_t nargs) 490 { 491 struct uc_uclient_priv *ucl; 492 uc_value_t *url = uc_fn_arg(0); 493 uc_value_t *auth_str = uc_fn_arg(1); 494 uc_value_t *cb = uc_fn_arg(2); 495 static bool _init_done; 496 struct uclient *cl; 497 498 if (!_init_done) { 499 uloop_init(); 500 _init_done = true; 501 } 502 503 uc_vm = vm; 504 505 if (ucv_type(url) != UC_STRING || 506 (auth_str && ucv_type(auth_str) != UC_STRING) || 507 ucv_type(cb) != UC_OBJECT) 508 return NULL; 509 510 ucl = calloc(1, sizeof(*ucl)); 511 if (ucv_property_get(cb, "data_read")) 512 ucl->cb.data_read = uc_cb_data_read; 513 if (ucv_property_get(cb, "get_post_data")) 514 ucl->cb.data_sent = uc_cb_data_sent; 515 if (ucv_property_get(cb, "data_eof")) 516 ucl->cb.data_eof = uc_cb_data_eof; 517 if (ucv_property_get(cb, "header_done")) 518 ucl->cb.header_done = uc_cb_header_done; 519 if (ucv_property_get(cb, "error")) 520 ucl->cb.error = uc_cb_error; 521 522 cl = uclient_new(ucv_string_get(url), ucv_string_get(auth_str), &ucl->cb); 523 if (!cl) { 524 free(ucl); 525 return NULL; 526 } 527 528 cl->priv = ucl; 529 uc_uclient_register(ucl, cb); 530 ucl->resource = ucv_resource_new(uc_uclient_type, cl); 531 532 return ucl->resource; 533 } 534 static const uc_function_list_t uclient_fns[] = { 535 { "free", uc_uclient_free }, 536 { "ssl_init", uc_uclient_ssl_init }, 537 { "set_url", uc_uclient_set_url }, 538 { "set_proxy_url", uc_uclient_set_proxy_url }, 539 { "set_timeout", uc_uclient_set_timeout }, 540 { "get_headers", uc_uclient_get_headers }, 541 542 { "connect", uc_uclient_connect }, 543 { "disconnect", uc_uclient_disconnect }, 544 { "request", uc_uclient_request }, 545 { "redirect", uc_uclient_redirect }, 546 { "status", uc_uclient_status }, 547 548 { "read", uc_uclient_read }, 549 }; 550 551 static const uc_function_list_t global_fns[] = { 552 { "new", uc_uclient_new }, 553 }; 554 555 void uc_module_init(uc_vm_t *vm, uc_value_t *scope) 556 { 557 uc_uclient_type = uc_type_declare(vm, "uclient", uclient_fns, free_uclient); 558 registry = ucv_array_new(vm); 559 uc_vm_registry_set(vm, "uclient.registry", registry); 560 uc_function_list_register(scope, global_fns); 561 } 562
This page was automatically generated by LXR 0.3.1. • OpenWrt