• 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         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