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