main: add ucode options to help text
[project/uhttpd.git] / ubus.c
1 /*
2 * uhttpd - Tiny single-threaded httpd
3 *
4 * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
5 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <libubox/blobmsg.h>
21 #include <libubox/blobmsg_json.h>
22 #include <libubox/avl.h>
23 #include <libubox/avl-cmp.h>
24 #include <stdio.h>
25 #include <poll.h>
26
27 #include "uhttpd.h"
28 #include "plugin.h"
29
30 static const struct uhttpd_ops *ops;
31 static struct config *_conf;
32 #define conf (*_conf)
33
34 static struct ubus_context *ctx;
35 static struct blob_buf buf;
36
37 #define UH_UBUS_MAX_POST_SIZE 65536
38 #define UH_UBUS_DEFAULT_SID "00000000000000000000000000000000"
39
40 enum {
41 RPC_JSONRPC,
42 RPC_METHOD,
43 RPC_PARAMS,
44 RPC_ID,
45 __RPC_MAX,
46 };
47
48 static const struct blobmsg_policy rpc_policy[__RPC_MAX] = {
49 [RPC_JSONRPC] = { .name = "jsonrpc", .type = BLOBMSG_TYPE_STRING },
50 [RPC_METHOD] = { .name = "method", .type = BLOBMSG_TYPE_STRING },
51 [RPC_PARAMS] = { .name = "params", .type = BLOBMSG_TYPE_UNSPEC },
52 [RPC_ID] = { .name = "id", .type = BLOBMSG_TYPE_UNSPEC },
53 };
54
55 enum {
56 SES_ACCESS,
57 __SES_MAX,
58 };
59
60 static const struct blobmsg_policy ses_policy[__SES_MAX] = {
61 [SES_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_BOOL },
62 };
63
64 struct rpc_data {
65 struct blob_attr *id;
66 const char *sid;
67 const char *method;
68 const char *object;
69 const char *function;
70 struct blob_attr *data;
71 struct blob_attr *params;
72 };
73
74 struct list_data {
75 bool verbose;
76 bool add_object;
77 struct blob_buf *buf;
78 };
79
80 enum rpc_error {
81 ERROR_PARSE,
82 ERROR_REQUEST,
83 ERROR_METHOD,
84 ERROR_PARAMS,
85 ERROR_INTERNAL,
86 ERROR_OBJECT,
87 ERROR_SESSION,
88 ERROR_ACCESS,
89 ERROR_TIMEOUT,
90 __ERROR_MAX
91 };
92
93 static const struct {
94 int code;
95 const char *msg;
96 } json_errors[__ERROR_MAX] = {
97 [ERROR_PARSE] = { -32700, "Parse error" },
98 [ERROR_REQUEST] = { -32600, "Invalid request" },
99 [ERROR_METHOD] = { -32601, "Method not found" },
100 [ERROR_PARAMS] = { -32602, "Invalid parameters" },
101 [ERROR_INTERNAL] = { -32603, "Internal error" },
102 [ERROR_OBJECT] = { -32000, "Object not found" },
103 [ERROR_SESSION] = { -32001, "Session not found" },
104 [ERROR_ACCESS] = { -32002, "Access denied" },
105 [ERROR_TIMEOUT] = { -32003, "ubus request timed out" },
106 };
107
108 enum cors_hdr {
109 HDR_ORIGIN,
110 HDR_ACCESS_CONTROL_REQUEST_METHOD,
111 HDR_ACCESS_CONTROL_REQUEST_HEADERS,
112 __HDR_MAX
113 };
114
115 enum ubus_hdr {
116 HDR_AUTHORIZATION,
117 __HDR_UBUS_MAX
118 };
119
120 static const char *uh_ubus_get_auth(const struct blob_attr *attr)
121 {
122 static const struct blobmsg_policy hdr_policy[__HDR_UBUS_MAX] = {
123 [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING },
124 };
125 struct blob_attr *tb[__HDR_UBUS_MAX];
126
127 blobmsg_parse(hdr_policy, __HDR_UBUS_MAX, tb, blob_data(attr), blob_len(attr));
128
129 if (tb[HDR_AUTHORIZATION]) {
130 const char *tmp = blobmsg_get_string(tb[HDR_AUTHORIZATION]);
131
132 if (!strncasecmp(tmp, "Bearer ", 7))
133 return tmp + 7;
134 }
135
136 return UH_UBUS_DEFAULT_SID;
137 }
138
139 static void __uh_ubus_next_batched_request(struct uloop_timeout *timeout);
140
141 static void uh_ubus_next_batched_request(struct client *cl)
142 {
143 struct dispatch_ubus *du = &cl->dispatch.ubus;
144
145 du->timeout.cb = __uh_ubus_next_batched_request;
146 uloop_timeout_set(&du->timeout, 1);
147 }
148
149 static void uh_ubus_add_cors_headers(struct client *cl)
150 {
151 struct blob_attr *tb[__HDR_MAX];
152 static const struct blobmsg_policy hdr_policy[__HDR_MAX] = {
153 [HDR_ORIGIN] = { "origin", BLOBMSG_TYPE_STRING },
154 [HDR_ACCESS_CONTROL_REQUEST_METHOD] = { "access-control-request-method", BLOBMSG_TYPE_STRING },
155 [HDR_ACCESS_CONTROL_REQUEST_HEADERS] = { "access-control-request-headers", BLOBMSG_TYPE_STRING },
156 };
157
158 blobmsg_parse(hdr_policy, __HDR_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head));
159
160 if (!tb[HDR_ORIGIN])
161 return;
162
163 if (tb[HDR_ACCESS_CONTROL_REQUEST_METHOD])
164 {
165 char *hdr = (char *) blobmsg_data(tb[HDR_ACCESS_CONTROL_REQUEST_METHOD]);
166
167 if (strcmp(hdr, "GET") && strcmp(hdr, "POST") && strcmp(hdr, "OPTIONS"))
168 return;
169 }
170
171 ustream_printf(cl->us, "Access-Control-Allow-Origin: %s\r\n",
172 blobmsg_get_string(tb[HDR_ORIGIN]));
173
174 if (tb[HDR_ACCESS_CONTROL_REQUEST_HEADERS])
175 ustream_printf(cl->us, "Access-Control-Allow-Headers: %s\r\n",
176 blobmsg_get_string(tb[HDR_ACCESS_CONTROL_REQUEST_HEADERS]));
177
178 ustream_printf(cl->us, "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n");
179 ustream_printf(cl->us, "Access-Control-Allow-Credentials: true\r\n");
180 }
181
182 static void uh_ubus_send_header(struct client *cl, int code, const char *summary, const char *content_type)
183 {
184 ops->http_header(cl, code, summary);
185
186 if (conf.ubus_cors)
187 uh_ubus_add_cors_headers(cl);
188
189 ustream_printf(cl->us, "Content-Type: %s\r\n", content_type);
190
191 if (cl->request.method == UH_HTTP_MSG_OPTIONS)
192 ustream_printf(cl->us, "Content-Length: 0\r\n");
193
194 ustream_printf(cl->us, "\r\n");
195 }
196
197 static void uh_ubus_send_response(struct client *cl, struct blob_buf *buf)
198 {
199 struct dispatch_ubus *du = &cl->dispatch.ubus;
200 const char *sep = "";
201 char *str;
202
203 if (du->array && du->array_idx > 1)
204 sep = ",";
205
206 str = blobmsg_format_json(buf->head, true);
207 ops->chunk_printf(cl, "%s%s", sep, str);
208 free(str);
209
210 du->jsobj_cur = NULL;
211 if (du->array)
212 uh_ubus_next_batched_request(cl);
213 else
214 return ops->request_done(cl);
215 }
216
217 static void uh_ubus_init_json_rpc_response(struct client *cl, struct blob_buf *buf)
218 {
219 struct dispatch_ubus *du = &cl->dispatch.ubus;
220 struct json_object *obj = du->jsobj_cur, *obj2 = NULL;
221
222 blobmsg_add_string(buf, "jsonrpc", "2.0");
223
224 if (obj)
225 json_object_object_get_ex(obj, "id", &obj2);
226
227 if (obj2)
228 blobmsg_add_json_element(buf, "id", obj2);
229 else
230 blobmsg_add_field(buf, BLOBMSG_TYPE_UNSPEC, "id", NULL, 0);
231 }
232
233 static void uh_ubus_json_rpc_error(struct client *cl, enum rpc_error type)
234 {
235 void *c;
236
237 blob_buf_init(&buf, 0);
238
239 uh_ubus_init_json_rpc_response(cl, &buf);
240 c = blobmsg_open_table(&buf, "error");
241 blobmsg_add_u32(&buf, "code", json_errors[type].code);
242 blobmsg_add_string(&buf, "message", json_errors[type].msg);
243 blobmsg_close_table(&buf, c);
244 uh_ubus_send_response(cl, &buf);
245 }
246
247 static void uh_ubus_error(struct client *cl, int code, const char *message)
248 {
249 blob_buf_init(&buf, 0);
250
251 blobmsg_add_u32(&buf, "code", code);
252 blobmsg_add_string(&buf, "message", message);
253 uh_ubus_send_response(cl, &buf);
254 }
255
256 static void uh_ubus_posix_error(struct client *cl, int err)
257 {
258 uh_ubus_error(cl, -err, strerror(err));
259 }
260
261 static void uh_ubus_ubus_error(struct client *cl, int err)
262 {
263 uh_ubus_error(cl, err, ubus_strerror(err));
264 }
265
266 static void uh_ubus_allowed_cb(struct ubus_request *req, int type, struct blob_attr *msg)
267 {
268 struct blob_attr *tb[__SES_MAX];
269 bool *allow = (bool *)req->priv;
270
271 if (!msg)
272 return;
273
274 blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg));
275
276 if (tb[SES_ACCESS])
277 *allow = blobmsg_get_bool(tb[SES_ACCESS]);
278 }
279
280 static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun)
281 {
282 uint32_t id;
283 bool allow = false;
284 static struct blob_buf req;
285
286 if (ubus_lookup_id(ctx, "session", &id))
287 return false;
288
289 blob_buf_init(&req, 0);
290 blobmsg_add_string(&req, "ubus_rpc_session", sid);
291 blobmsg_add_string(&req, "object", obj);
292 blobmsg_add_string(&req, "function", fun);
293
294 ubus_invoke(ctx, id, "access", req.head, uh_ubus_allowed_cb, &allow, conf.script_timeout * 500);
295
296 return allow;
297 }
298
299 /* GET requests handling */
300
301 static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv);
302
303 static void uh_ubus_handle_get_list(struct client *cl, const char *path)
304 {
305 static struct blob_buf tmp;
306 struct list_data data = { .verbose = true, .add_object = !path, .buf = &tmp};
307 struct blob_attr *cur;
308 int rem;
309 int err;
310
311 blob_buf_init(&tmp, 0);
312
313 err = ubus_lookup(ctx, path, uh_ubus_list_cb, &data);
314 if (err) {
315 uh_ubus_send_header(cl, 500, "Ubus Protocol Error", "application/json");
316 uh_ubus_ubus_error(cl, err);
317 return;
318 }
319
320 blob_buf_init(&buf, 0);
321 blob_for_each_attr(cur, tmp.head, rem)
322 blobmsg_add_blob(&buf, cur);
323
324 uh_ubus_send_header(cl, 200, "OK", "application/json");
325 uh_ubus_send_response(cl, &buf);
326 }
327
328 static int uh_ubus_subscription_notification_cb(struct ubus_context *ctx,
329 struct ubus_object *obj,
330 struct ubus_request_data *req,
331 const char *method,
332 struct blob_attr *msg)
333 {
334 struct ubus_subscriber *s;
335 struct dispatch_ubus *du;
336 struct client *cl;
337 char *json;
338
339 s = container_of(obj, struct ubus_subscriber, obj);
340 du = container_of(s, struct dispatch_ubus, sub);
341 cl = container_of(du, struct client, dispatch.ubus);
342
343 json = blobmsg_format_json(msg, true);
344 if (json) {
345 ops->chunk_printf(cl, "event: %s\ndata: %s\n\n", method, json);
346 free(json);
347 }
348
349 return 0;
350 }
351
352 static void uh_ubus_subscription_notification_remove_cb(struct ubus_context *ctx, struct ubus_subscriber *s, uint32_t id)
353 {
354 struct dispatch_ubus *du;
355 struct client *cl;
356
357 du = container_of(s, struct dispatch_ubus, sub);
358 cl = container_of(du, struct client, dispatch.ubus);
359
360 ubus_unregister_subscriber(ctx, &du->sub);
361
362 ops->request_done(cl);
363 }
364
365 static void uh_ubus_handle_get_subscribe(struct client *cl, const char *path)
366 {
367 struct dispatch_ubus *du = &cl->dispatch.ubus;
368 const char *sid;
369 uint32_t id;
370 int err;
371
372 sid = uh_ubus_get_auth(cl->hdr.head);
373
374 if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, ":subscribe")) {
375 uh_ubus_send_header(cl, 200, "OK", "application/json");
376 uh_ubus_posix_error(cl, EACCES);
377 return;
378 }
379
380 du->sub.cb = uh_ubus_subscription_notification_cb;
381 du->sub.remove_cb = uh_ubus_subscription_notification_remove_cb;
382
383 uh_client_ref(cl);
384
385 err = ubus_register_subscriber(ctx, &du->sub);
386 if (err)
387 goto err_unref;
388
389 err = ubus_lookup_id(ctx, path, &id);
390 if (err)
391 goto err_unregister;
392
393 err = ubus_subscribe(ctx, &du->sub, id);
394 if (err)
395 goto err_unregister;
396
397 uh_ubus_send_header(cl, 200, "OK", "text/event-stream");
398
399 if (conf.events_retry)
400 ops->chunk_printf(cl, "retry: %d\n", conf.events_retry);
401
402 return;
403
404 err_unregister:
405 ubus_unregister_subscriber(ctx, &du->sub);
406 err_unref:
407 uh_client_unref(cl);
408 if (err) {
409 uh_ubus_send_header(cl, 200, "OK", "application/json");
410 uh_ubus_ubus_error(cl, err);
411 }
412 }
413
414 static void uh_ubus_handle_get(struct client *cl)
415 {
416 struct dispatch_ubus *du = &cl->dispatch.ubus;
417 const char *url = du->url_path;
418
419 url += strlen(conf.ubus_prefix);
420
421 if (!strcmp(url, "/list") || !strncmp(url, "/list/", strlen("/list/"))) {
422 url += strlen("/list");
423
424 uh_ubus_handle_get_list(cl, *url ? url + 1 : NULL);
425 } else if (!strncmp(url, "/subscribe/", strlen("/subscribe/"))) {
426 url += strlen("/subscribe");
427
428 uh_ubus_handle_get_subscribe(cl, url + 1);
429 } else {
430 ops->http_header(cl, 404, "Not Found");
431 ustream_printf(cl->us, "\r\n");
432 ops->request_done(cl);
433 }
434 }
435
436 /* POST requests handling */
437
438 static void
439 uh_ubus_request_data_cb(struct ubus_request *req, int type, struct blob_attr *msg)
440 {
441 struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req);
442 struct blob_attr *cur;
443 int len;
444
445 blob_for_each_attr(cur, msg, len)
446 blobmsg_add_blob(&du->buf, cur);
447 }
448
449 static void
450 uh_ubus_request_cb(struct ubus_request *req, int ret)
451 {
452 struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req);
453 struct client *cl = container_of(du, struct client, dispatch.ubus);
454 struct blob_attr *cur;
455 void *r;
456 int rem;
457
458 blob_buf_init(&buf, 0);
459
460 uloop_timeout_cancel(&du->timeout);
461
462 /* Legacy format always uses "result" array - even for errors and empty
463 * results. */
464 if (du->legacy) {
465 void *c;
466
467 uh_ubus_init_json_rpc_response(cl, &buf);
468 r = blobmsg_open_array(&buf, "result");
469 blobmsg_add_u32(&buf, "", ret);
470
471 if (blob_len(du->buf.head)) {
472 c = blobmsg_open_table(&buf, NULL);
473 blob_for_each_attr(cur, du->buf.head, rem)
474 blobmsg_add_blob(&buf, cur);
475 blobmsg_close_table(&buf, c);
476 }
477
478 blobmsg_close_array(&buf, r);
479 uh_ubus_send_response(cl, &buf);
480 return;
481 }
482
483 if (ret) {
484 void *c;
485
486 uh_ubus_init_json_rpc_response(cl, &buf);
487 c = blobmsg_open_table(&buf, "error");
488 blobmsg_add_u32(&buf, "code", ret);
489 blobmsg_add_string(&buf, "message", ubus_strerror(ret));
490 blobmsg_close_table(&buf, c);
491 uh_ubus_send_response(cl, &buf);
492 } else {
493 uh_ubus_init_json_rpc_response(cl, &buf);
494 if (blob_len(du->buf.head)) {
495 r = blobmsg_open_table(&buf, "result");
496 blob_for_each_attr(cur, du->buf.head, rem)
497 blobmsg_add_blob(&buf, cur);
498 blobmsg_close_table(&buf, r);
499 } else {
500 blobmsg_add_field(&buf, BLOBMSG_TYPE_UNSPEC, "result", NULL, 0);
501 }
502 uh_ubus_send_response(cl, &buf);
503 }
504
505 }
506
507 static void
508 uh_ubus_timeout_cb(struct uloop_timeout *timeout)
509 {
510 struct dispatch_ubus *du = container_of(timeout, struct dispatch_ubus, timeout);
511 struct client *cl = container_of(du, struct client, dispatch.ubus);
512
513 ubus_abort_request(ctx, &du->req);
514 uh_ubus_json_rpc_error(cl, ERROR_TIMEOUT);
515 }
516
517 static void uh_ubus_close_fds(struct client *cl)
518 {
519 if (ctx->sock.fd < 0)
520 return;
521
522 close(ctx->sock.fd);
523 ctx->sock.fd = -1;
524 }
525
526 static void uh_ubus_request_free(struct client *cl)
527 {
528 struct dispatch_ubus *du = &cl->dispatch.ubus;
529
530 blob_buf_free(&du->buf);
531 uloop_timeout_cancel(&du->timeout);
532
533 if (du->jsobj)
534 json_object_put(du->jsobj);
535
536 if (du->jstok)
537 json_tokener_free(du->jstok);
538
539 if (du->req_pending)
540 ubus_abort_request(ctx, &du->req);
541
542 free(du->url_path);
543 du->url_path = NULL;
544 }
545
546 static void uh_ubus_single_error(struct client *cl, enum rpc_error type)
547 {
548 uh_ubus_send_header(cl, 200, "OK", "application/json");
549 uh_ubus_json_rpc_error(cl, type);
550 ops->request_done(cl);
551 }
552
553 static void uh_ubus_send_request(struct client *cl, const char *sid, struct blob_attr *args)
554 {
555 struct dispatch *d = &cl->dispatch;
556 struct dispatch_ubus *du = &d->ubus;
557 struct blob_attr *cur;
558 static struct blob_buf req;
559 int ret, rem;
560
561 blob_buf_init(&req, 0);
562 blobmsg_for_each_attr(cur, args, rem) {
563 if (!strcmp(blobmsg_name(cur), "ubus_rpc_session"))
564 return uh_ubus_json_rpc_error(cl, ERROR_PARAMS);
565 blobmsg_add_blob(&req, cur);
566 }
567
568 blobmsg_add_string(&req, "ubus_rpc_session", sid);
569
570 blob_buf_init(&du->buf, 0);
571 memset(&du->req, 0, sizeof(du->req));
572 ret = ubus_invoke_async(ctx, du->obj, du->func, req.head, &du->req);
573 if (ret)
574 return uh_ubus_json_rpc_error(cl, ERROR_INTERNAL);
575
576 du->req.data_cb = uh_ubus_request_data_cb;
577 du->req.complete_cb = uh_ubus_request_cb;
578 ubus_complete_request_async(ctx, &du->req);
579
580 du->timeout.cb = uh_ubus_timeout_cb;
581 uloop_timeout_set(&du->timeout, conf.script_timeout * 1000);
582
583 du->req_pending = true;
584 }
585
586 static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv)
587 {
588 struct blob_attr *sig, *attr;
589 struct list_data *data = priv;
590 int rem, rem2;
591 void *t, *o;
592
593 if (!data->verbose) {
594 blobmsg_add_string(data->buf, NULL, obj->path);
595 return;
596 }
597
598 if (!obj->signature)
599 return;
600
601 if (data->add_object)
602 o = blobmsg_open_table(data->buf, obj->path);
603 blob_for_each_attr(sig, obj->signature, rem) {
604 t = blobmsg_open_table(data->buf, blobmsg_name(sig));
605 rem2 = blobmsg_data_len(sig);
606 __blob_for_each_attr(attr, blobmsg_data(sig), rem2) {
607 if (blob_id(attr) != BLOBMSG_TYPE_INT32)
608 continue;
609
610 switch (blobmsg_get_u32(attr)) {
611 case BLOBMSG_TYPE_INT8:
612 blobmsg_add_string(data->buf, blobmsg_name(attr), "boolean");
613 break;
614 case BLOBMSG_TYPE_INT32:
615 blobmsg_add_string(data->buf, blobmsg_name(attr), "number");
616 break;
617 case BLOBMSG_TYPE_STRING:
618 blobmsg_add_string(data->buf, blobmsg_name(attr), "string");
619 break;
620 case BLOBMSG_TYPE_ARRAY:
621 blobmsg_add_string(data->buf, blobmsg_name(attr), "array");
622 break;
623 case BLOBMSG_TYPE_TABLE:
624 blobmsg_add_string(data->buf, blobmsg_name(attr), "object");
625 break;
626 default:
627 blobmsg_add_string(data->buf, blobmsg_name(attr), "unknown");
628 break;
629 }
630 }
631 blobmsg_close_table(data->buf, t);
632 }
633 if (data->add_object)
634 blobmsg_close_table(data->buf, o);
635 }
636
637 static void uh_ubus_send_list(struct client *cl, struct blob_attr *params)
638 {
639 struct blob_attr *cur, *dup;
640 struct list_data data = { .buf = &cl->dispatch.ubus.buf, .verbose = false, .add_object = true };
641 void *r;
642 int rem;
643
644 blob_buf_init(data.buf, 0);
645
646 uh_client_ref(cl);
647
648 if (!params || blob_id(params) != BLOBMSG_TYPE_ARRAY) {
649 r = blobmsg_open_array(data.buf, "result");
650 ubus_lookup(ctx, NULL, uh_ubus_list_cb, &data);
651 blobmsg_close_array(data.buf, r);
652 }
653 else {
654 r = blobmsg_open_table(data.buf, "result");
655 dup = blob_memdup(params);
656 if (dup)
657 {
658 rem = blobmsg_data_len(dup);
659 data.verbose = true;
660 __blob_for_each_attr(cur, blobmsg_data(dup), rem)
661 ubus_lookup(ctx, blobmsg_data(cur), uh_ubus_list_cb, &data);
662 free(dup);
663 }
664 blobmsg_close_table(data.buf, r);
665 }
666
667 uh_client_unref(cl);
668
669 blob_buf_init(&buf, 0);
670 uh_ubus_init_json_rpc_response(cl, &buf);
671 blobmsg_add_blob(&buf, blob_data(data.buf->head));
672 uh_ubus_send_response(cl, &buf);
673 }
674
675 static bool parse_json_rpc(struct rpc_data *d, struct blob_attr *data)
676 {
677 struct blob_attr *tb[__RPC_MAX];
678 struct blob_attr *cur;
679
680 blobmsg_parse(rpc_policy, __RPC_MAX, tb, blob_data(data), blob_len(data));
681
682 cur = tb[RPC_JSONRPC];
683 if (!cur || strcmp(blobmsg_data(cur), "2.0") != 0)
684 return false;
685
686 cur = tb[RPC_METHOD];
687 if (!cur)
688 return false;
689
690 d->id = tb[RPC_ID];
691 d->method = blobmsg_data(cur);
692
693 cur = tb[RPC_PARAMS];
694 if (!cur)
695 return true;
696
697 d->params = blob_memdup(cur);
698 if (!d->params)
699 return false;
700
701 return true;
702 }
703
704 static void parse_call_params(struct rpc_data *d)
705 {
706 const struct blobmsg_policy data_policy[] = {
707 { .type = BLOBMSG_TYPE_STRING },
708 { .type = BLOBMSG_TYPE_STRING },
709 { .type = BLOBMSG_TYPE_STRING },
710 { .type = BLOBMSG_TYPE_TABLE },
711 };
712 struct blob_attr *tb[4];
713
714 if (!d->params || blobmsg_type(d->params) != BLOBMSG_TYPE_ARRAY)
715 return;
716
717 blobmsg_parse_array(data_policy, ARRAY_SIZE(data_policy), tb,
718 blobmsg_data(d->params), blobmsg_data_len(d->params));
719
720 if (tb[0])
721 d->sid = blobmsg_data(tb[0]);
722
723 if (conf.ubus_noauth && (!d->sid || !*d->sid))
724 d->sid = UH_UBUS_DEFAULT_SID;
725
726 if (tb[1])
727 d->object = blobmsg_data(tb[1]);
728
729 if (tb[2])
730 d->function = blobmsg_data(tb[2]);
731
732 d->data = tb[3];
733 }
734
735 static void uh_ubus_init_batch(struct client *cl)
736 {
737 struct dispatch_ubus *du = &cl->dispatch.ubus;
738
739 du->array = true;
740 uh_ubus_send_header(cl, 200, "OK", "application/json");
741 ops->chunk_printf(cl, "[");
742 }
743
744 static void uh_ubus_complete_batch(struct client *cl)
745 {
746 ops->chunk_printf(cl, "]");
747 ops->request_done(cl);
748 }
749
750 static void uh_ubus_handle_request_object(struct client *cl, struct json_object *obj)
751 {
752 struct dispatch_ubus *du = &cl->dispatch.ubus;
753 struct rpc_data data = {};
754 enum rpc_error err = ERROR_PARSE;
755 static struct blob_buf req;
756
757 uh_client_ref(cl);
758
759 if (json_object_get_type(obj) != json_type_object)
760 goto error;
761
762 du->jsobj_cur = obj;
763 blob_buf_init(&req, 0);
764 if (!blobmsg_add_object(&req, obj))
765 goto error;
766
767 if (!parse_json_rpc(&data, req.head))
768 goto error;
769
770 if (!strcmp(data.method, "call")) {
771 parse_call_params(&data);
772
773 if (!data.sid || !data.object || !data.function || !data.data)
774 goto error;
775
776 du->func = data.function;
777 if (ubus_lookup_id(ctx, data.object, &du->obj)) {
778 err = ERROR_OBJECT;
779 goto error;
780 }
781
782 if (!conf.ubus_noauth && !uh_ubus_allowed(data.sid, data.object, data.function)) {
783 err = ERROR_ACCESS;
784 goto error;
785 }
786
787 uh_ubus_send_request(cl, data.sid, data.data);
788 goto out;
789 }
790 else if (!strcmp(data.method, "list")) {
791 uh_ubus_send_list(cl, data.params);
792 goto out;
793 }
794 else {
795 err = ERROR_METHOD;
796 goto error;
797 }
798
799 error:
800 uh_ubus_json_rpc_error(cl, err);
801 out:
802 if (data.params)
803 free(data.params);
804
805 uh_client_unref(cl);
806 }
807
808 static void __uh_ubus_next_batched_request(struct uloop_timeout *timeout)
809 {
810 struct dispatch_ubus *du = container_of(timeout, struct dispatch_ubus, timeout);
811 struct client *cl = container_of(du, struct client, dispatch.ubus);
812 struct json_object *obj = du->jsobj;
813 int len;
814
815 len = json_object_array_length(obj);
816 if (du->array_idx >= len)
817 return uh_ubus_complete_batch(cl);
818
819 obj = json_object_array_get_idx(obj, du->array_idx++);
820 uh_ubus_handle_request_object(cl, obj);
821 }
822
823 static void uh_ubus_data_done(struct client *cl)
824 {
825 struct dispatch_ubus *du = &cl->dispatch.ubus;
826 struct json_object *obj = du->jsobj;
827
828 switch (obj ? json_object_get_type(obj) : json_type_null) {
829 case json_type_object:
830 uh_ubus_send_header(cl, 200, "OK", "application/json");
831 return uh_ubus_handle_request_object(cl, obj);
832 case json_type_array:
833 uh_ubus_init_batch(cl);
834 return uh_ubus_next_batched_request(cl);
835 default:
836 return uh_ubus_single_error(cl, ERROR_PARSE);
837 }
838 }
839
840 static void uh_ubus_call(struct client *cl, const char *path, const char *sid)
841 {
842 struct dispatch_ubus *du = &cl->dispatch.ubus;
843 struct json_object *obj = du->jsobj;
844 struct rpc_data data = {};
845 enum rpc_error err = ERROR_PARSE;
846 static struct blob_buf req;
847
848 uh_client_ref(cl);
849
850 if (!obj || json_object_get_type(obj) != json_type_object)
851 goto error;
852
853 uh_ubus_send_header(cl, 200, "OK", "application/json");
854
855 du->jsobj_cur = obj;
856 blob_buf_init(&req, 0);
857 if (!blobmsg_add_object(&req, obj))
858 goto error;
859
860 if (!parse_json_rpc(&data, req.head))
861 goto error;
862
863 du->func = data.method;
864 if (ubus_lookup_id(ctx, path, &du->obj)) {
865 err = ERROR_OBJECT;
866 goto error;
867 }
868
869 if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, data.method)) {
870 err = ERROR_ACCESS;
871 goto error;
872 }
873
874 uh_ubus_send_request(cl, sid, data.params);
875 goto out;
876
877 error:
878 uh_ubus_json_rpc_error(cl, err);
879 out:
880 if (data.params)
881 free(data.params);
882
883 uh_client_unref(cl);
884 }
885
886 static void uh_ubus_handle_post(struct client *cl)
887 {
888 struct dispatch_ubus *du = &cl->dispatch.ubus;
889 const char *url = du->url_path;
890 const char *auth;
891
892 /* Treat both: /foo AND /foo/ as legacy requests. */
893 if (ops->path_match(conf.ubus_prefix, url) && strlen(url) - strlen(conf.ubus_prefix) <= 1) {
894 du->legacy = true;
895 uh_ubus_data_done(cl);
896 return;
897 }
898
899 auth = uh_ubus_get_auth(cl->hdr.head);
900
901 url += strlen(conf.ubus_prefix);
902
903 if (!strncmp(url, "/call/", strlen("/call/"))) {
904 url += strlen("/call/");
905
906 uh_ubus_call(cl, url, auth);
907 } else {
908 ops->http_header(cl, 404, "Not Found");
909 ustream_printf(cl->us, "\r\n");
910 ops->request_done(cl);
911 }
912 }
913
914 static int uh_ubus_data_send(struct client *cl, const char *data, int len)
915 {
916 struct dispatch_ubus *du = &cl->dispatch.ubus;
917
918 if (du->jsobj || !du->jstok)
919 goto error;
920
921 du->post_len += len;
922 if (du->post_len > UH_UBUS_MAX_POST_SIZE)
923 goto error;
924
925 du->jsobj = json_tokener_parse_ex(du->jstok, data, len);
926 return len;
927
928 error:
929 uh_ubus_single_error(cl, ERROR_PARSE);
930 return 0;
931 }
932
933 static void uh_ubus_handle_request(struct client *cl, char *url, struct path_info *pi)
934 {
935 struct dispatch *d = &cl->dispatch;
936 struct dispatch_ubus *du = &d->ubus;
937 char *chr;
938
939 du->url_path = strdup(url);
940 if (!du->url_path) {
941 ops->client_error(cl, 500, "Internal Server Error", "Failed to allocate resources");
942 return;
943 }
944 chr = strchr(du->url_path, '?');
945 if (chr)
946 chr[0] = '\0';
947
948 du->legacy = false;
949
950 switch (cl->request.method)
951 {
952 case UH_HTTP_MSG_GET:
953 uh_ubus_handle_get(cl);
954 break;
955 case UH_HTTP_MSG_POST:
956 d->data_send = uh_ubus_data_send;
957 d->data_done = uh_ubus_handle_post;
958 d->close_fds = uh_ubus_close_fds;
959 d->free = uh_ubus_request_free;
960 du->jstok = json_tokener_new();
961 return;
962
963 case UH_HTTP_MSG_OPTIONS:
964 uh_ubus_send_header(cl, 200, "OK", "application/json");
965 ops->request_done(cl);
966 break;
967
968 default:
969 ops->client_error(cl, 400, "Bad Request", "Invalid Request");
970 }
971
972 free(du->url_path);
973 du->url_path = NULL;
974 }
975
976 static bool
977 uh_ubus_check_url(const char *url)
978 {
979 return ops->path_match(conf.ubus_prefix, url);
980 }
981
982 static int
983 uh_ubus_init(void)
984 {
985 static struct dispatch_handler ubus_dispatch = {
986 .check_url = uh_ubus_check_url,
987 .handle_request = uh_ubus_handle_request,
988 };
989
990 ctx = ubus_connect(conf.ubus_socket);
991 if (!ctx) {
992 fprintf(stderr, "Unable to connect to ubus socket\n");
993 exit(1);
994 }
995
996 ops->dispatch_add(&ubus_dispatch);
997
998 uloop_done();
999 return 0;
1000 }
1001
1002
1003 static int uh_ubus_plugin_init(const struct uhttpd_ops *o, struct config *c)
1004 {
1005 ops = o;
1006 _conf = c;
1007 return uh_ubus_init();
1008 }
1009
1010 static void uh_ubus_post_init(void)
1011 {
1012 ubus_add_uloop(ctx);
1013 }
1014
1015 struct uhttpd_plugin uhttpd_plugin = {
1016 .init = uh_ubus_plugin_init,
1017 .post_init = uh_ubus_post_init,
1018 };