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