• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/uclient/ucode.c

  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