From 8d9e1fce9f89d07c41e8c4984c6583babfc9709f Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Thu, 23 Jul 2020 07:38:07 +0200 Subject: ubus: parse "call" method params only for relevant call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no point in parsing "call" specific params for other ("list") method calls. This is a minor cleanup that doesn't change uhttpd ubus behaviour. Signed-off-by: Rafał Miłecki Acked-by: Jo-Philipp Wich --- ubus.c | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 8578631..97cc1e2 100644 --- a/ubus.c +++ b/ubus.c @@ -409,14 +409,7 @@ static void uh_ubus_send_list(struct client *cl, json_object *obj, struct blob_a static bool parse_json_rpc(struct rpc_data *d, struct blob_attr *data) { - const struct blobmsg_policy data_policy[] = { - { .type = BLOBMSG_TYPE_STRING }, - { .type = BLOBMSG_TYPE_STRING }, - { .type = BLOBMSG_TYPE_STRING }, - { .type = BLOBMSG_TYPE_TABLE }, - }; struct blob_attr *tb[__RPC_MAX]; - struct blob_attr *tb2[4]; struct blob_attr *cur; blobmsg_parse(rpc_policy, __RPC_MAX, tb, blob_data(data), blob_len(data)); @@ -440,24 +433,35 @@ static bool parse_json_rpc(struct rpc_data *d, struct blob_attr *data) if (!d->params) return false; - blobmsg_parse_array(data_policy, ARRAY_SIZE(data_policy), tb2, + return true; +} + +static void parse_call_params(struct rpc_data *d) +{ + const struct blobmsg_policy data_policy[] = { + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_STRING }, + { .type = BLOBMSG_TYPE_TABLE }, + }; + struct blob_attr *tb[4]; + + blobmsg_parse_array(data_policy, ARRAY_SIZE(data_policy), tb, blobmsg_data(d->params), blobmsg_data_len(d->params)); - if (tb2[0]) - d->sid = blobmsg_data(tb2[0]); + if (tb[0]) + d->sid = blobmsg_data(tb[0]); if (conf.ubus_noauth && (!d->sid || !*d->sid)) d->sid = UH_UBUS_DEFAULT_SID; - if (tb2[1]) - d->object = blobmsg_data(tb2[1]); - - if (tb2[2]) - d->function = blobmsg_data(tb2[2]); + if (tb[1]) + d->object = blobmsg_data(tb[1]); - d->data = tb2[3]; + if (tb[2]) + d->function = blobmsg_data(tb[2]); - return true; + d->data = tb[3]; } static void uh_ubus_init_batch(struct client *cl) @@ -528,6 +532,8 @@ static void uh_ubus_handle_request_object(struct client *cl, struct json_object goto error; if (!strcmp(data.method, "call")) { + parse_call_params(&data); + if (!data.sid || !data.object || !data.function || !data.data) goto error; -- cgit v1.2.3 From 77d345e52ea941d968d5ae5702dd2b69405097af Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sat, 25 Jul 2020 10:18:49 +0200 Subject: ubus: drop unused "obj" arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both: uh_ubus_send_request() and uh_ubus_send_list() don't use it. Signed-off-by: Rafał Miłecki --- ubus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 97cc1e2..51c58b5 100644 --- a/ubus.c +++ b/ubus.c @@ -288,7 +288,7 @@ static void uh_ubus_single_error(struct client *cl, enum rpc_error type) ops->request_done(cl); } -static void uh_ubus_send_request(struct client *cl, json_object *obj, const char *sid, struct blob_attr *args) +static void uh_ubus_send_request(struct client *cl, const char *sid, struct blob_attr *args) { struct dispatch *d = &cl->dispatch; struct dispatch_ubus *du = &d->ubus; @@ -370,7 +370,7 @@ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *o blobmsg_close_table(data->buf, o); } -static void uh_ubus_send_list(struct client *cl, json_object *obj, struct blob_attr *params) +static void uh_ubus_send_list(struct client *cl, struct blob_attr *params) { struct blob_attr *cur, *dup; struct list_data data = { .buf = &cl->dispatch.ubus.buf, .verbose = false }; @@ -548,11 +548,11 @@ static void uh_ubus_handle_request_object(struct client *cl, struct json_object goto error; } - uh_ubus_send_request(cl, obj, data.sid, data.data); + uh_ubus_send_request(cl, data.sid, data.data); goto out; } else if (!strcmp(data.method, "list")) { - uh_ubus_send_list(cl, obj, data.params); + uh_ubus_send_list(cl, data.params); goto out; } else { -- cgit v1.2.3 From 9d663e76bd3ac91b18633eb8f4f0bfc6996c5875 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Sat, 25 Jul 2020 10:18:50 +0200 Subject: ubus: use BLOBMSG_TYPE_UNSPEC for "params" JSON attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the JSON-RPC 2.0 specification "params" value can be either an Array or Object. This change makes parse_json_rpc() accept both. Type validation should be handled by a function that actually reads "params" depending on expected format. This doesn't change existing behaviour but allows adding more methods (that expect Object) in the future. Signed-off-by: Rafał Miłecki --- ubus.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 51c58b5..ddc2f05 100644 --- a/ubus.c +++ b/ubus.c @@ -48,7 +48,7 @@ enum { static const struct blobmsg_policy rpc_policy[__RPC_MAX] = { [RPC_JSONRPC] = { .name = "jsonrpc", .type = BLOBMSG_TYPE_STRING }, [RPC_METHOD] = { .name = "method", .type = BLOBMSG_TYPE_STRING }, - [RPC_PARAMS] = { .name = "params", .type = BLOBMSG_TYPE_ARRAY }, + [RPC_PARAMS] = { .name = "params", .type = BLOBMSG_TYPE_UNSPEC }, [RPC_ID] = { .name = "id", .type = BLOBMSG_TYPE_UNSPEC }, }; @@ -446,6 +446,9 @@ static void parse_call_params(struct rpc_data *d) }; struct blob_attr *tb[4]; + if (!d->params || blobmsg_type(d->params) != BLOBMSG_TYPE_ARRAY) + return; + blobmsg_parse_array(data_policy, ARRAY_SIZE(data_policy), tb, blobmsg_data(d->params), blobmsg_data_len(d->params)); -- cgit v1.2.3 From 628341fae41230627ff4055b248698bb6ed007a8 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 27 Jul 2020 07:07:34 +0200 Subject: ubus: use local "blob_buf" in uh_ubus_handle_request_object() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This follows two other functions logic: uh_ubus_send_request() and uh_ubus_allowed(). Thanks to this change global "buf" variable is used only for replies and doesn't require state tracking & reinitialization. Signed-off-by: Rafał Miłecki --- ubus.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index ddc2f05..062e973 100644 --- a/ubus.c +++ b/ubus.c @@ -194,7 +194,6 @@ static void uh_ubus_init_response(struct client *cl) struct dispatch_ubus *du = &cl->dispatch.ubus; struct json_object *obj = du->jsobj_cur, *obj2 = NULL; - blob_buf_init(&buf, 0); blobmsg_add_string(&buf, "jsonrpc", "2.0"); if (obj) @@ -520,6 +519,7 @@ static void uh_ubus_handle_request_object(struct client *cl, struct json_object struct dispatch_ubus *du = &cl->dispatch.ubus; struct rpc_data data = {}; enum rpc_error err = ERROR_PARSE; + static struct blob_buf req; uh_client_ref(cl); @@ -527,11 +527,11 @@ static void uh_ubus_handle_request_object(struct client *cl, struct json_object goto error; du->jsobj_cur = obj; - blob_buf_init(&buf, 0); - if (!blobmsg_add_object(&buf, obj)) + blob_buf_init(&req, 0); + if (!blobmsg_add_object(&req, obj)) goto error; - if (!parse_json_rpc(&data, buf.head)) + if (!parse_json_rpc(&data, req.head)) goto error; if (!strcmp(data.method, "call")) { -- cgit v1.2.3 From 212f8364d49c1bc27dd8bdc394fc3615ea9b7ba3 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Wed, 5 Aug 2020 08:58:45 +0200 Subject: ubus: rename JSON-RPC format related functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use "_json_rpc_" in their names so it's clear they are related to the JSON-RPC format. This cleans up code a bit and will allow adding more formats in the future. Signed-off-by: Rafał Miłecki --- ubus.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 062e973..c22e07a 100644 --- a/ubus.c +++ b/ubus.c @@ -189,7 +189,7 @@ static void uh_ubus_send_response(struct client *cl) return ops->request_done(cl); } -static void uh_ubus_init_response(struct client *cl) +static void uh_ubus_init_json_rpc_response(struct client *cl) { struct dispatch_ubus *du = &cl->dispatch.ubus; struct json_object *obj = du->jsobj_cur, *obj2 = NULL; @@ -205,11 +205,11 @@ static void uh_ubus_init_response(struct client *cl) blobmsg_add_field(&buf, BLOBMSG_TYPE_UNSPEC, "id", NULL, 0); } -static void uh_ubus_json_error(struct client *cl, enum rpc_error type) +static void uh_ubus_json_rpc_error(struct client *cl, enum rpc_error type) { void *c; - uh_ubus_init_response(cl); + uh_ubus_init_json_rpc_response(cl); c = blobmsg_open_table(&buf, "error"); blobmsg_add_u32(&buf, "code", json_errors[type].code); blobmsg_add_string(&buf, "message", json_errors[type].msg); @@ -235,7 +235,7 @@ uh_ubus_request_cb(struct ubus_request *req, int ret) int rem; uloop_timeout_cancel(&du->timeout); - uh_ubus_init_response(cl); + uh_ubus_init_json_rpc_response(cl); r = blobmsg_open_array(&buf, "result"); blobmsg_add_u32(&buf, "", ret); blob_for_each_attr(cur, du->buf.head, rem) @@ -251,7 +251,7 @@ uh_ubus_timeout_cb(struct uloop_timeout *timeout) struct client *cl = container_of(du, struct client, dispatch.ubus); ubus_abort_request(ctx, &du->req); - uh_ubus_json_error(cl, ERROR_TIMEOUT); + uh_ubus_json_rpc_error(cl, ERROR_TIMEOUT); } static void uh_ubus_close_fds(struct client *cl) @@ -283,7 +283,7 @@ static void uh_ubus_request_free(struct client *cl) static void uh_ubus_single_error(struct client *cl, enum rpc_error type) { uh_ubus_send_header(cl); - uh_ubus_json_error(cl, type); + uh_ubus_json_rpc_error(cl, type); ops->request_done(cl); } @@ -298,7 +298,7 @@ static void uh_ubus_send_request(struct client *cl, const char *sid, struct blob blob_buf_init(&req, 0); blobmsg_for_each_attr(cur, args, rem) { if (!strcmp(blobmsg_name(cur), "ubus_rpc_session")) - return uh_ubus_json_error(cl, ERROR_PARAMS); + return uh_ubus_json_rpc_error(cl, ERROR_PARAMS); blobmsg_add_blob(&req, cur); } @@ -308,7 +308,7 @@ static void uh_ubus_send_request(struct client *cl, const char *sid, struct blob memset(&du->req, 0, sizeof(du->req)); ret = ubus_invoke_async(ctx, du->obj, du->func, req.head, &du->req); if (ret) - return uh_ubus_json_error(cl, ERROR_INTERNAL); + return uh_ubus_json_rpc_error(cl, ERROR_INTERNAL); du->req.data_cb = uh_ubus_request_data_cb; du->req.complete_cb = uh_ubus_request_cb; @@ -401,7 +401,7 @@ static void uh_ubus_send_list(struct client *cl, struct blob_attr *params) uh_client_unref(cl); - uh_ubus_init_response(cl); + uh_ubus_init_json_rpc_response(cl); blobmsg_add_blob(&buf, blob_data(data.buf->head)); uh_ubus_send_response(cl); } @@ -564,7 +564,7 @@ static void uh_ubus_handle_request_object(struct client *cl, struct json_object } error: - uh_ubus_json_error(cl, err); + uh_ubus_json_rpc_error(cl, err); out: if (data.params) free(data.params); -- cgit v1.2.3 From fe1888f19e7f8ee4237409b8616c82926190c2f8 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 14 Sep 2020 16:04:13 +0200 Subject: ubus: fix blob_buf initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initializing buffer in the uh_ubus_handle_request() didn't handle batched requests correctly. It resulted in reusing buffer and generating malformed replies. Call blob_buf_init() before every usage of the global buf variable. While at it make two functions take blob_buf pointer as argument: 1. uh_ubus_send_response() 2. uh_ubus_init_json_rpc_response() This helps following global "buf" variable usage and will help avoiding similar bugs in the future. Fixes: 628341fae412 ("ubus: use local "blob_buf" in uh_ubus_handle_request_object()") Signed-off-by: Rafał Miłecki --- ubus.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index c22e07a..a458f4c 100644 --- a/ubus.c +++ b/ubus.c @@ -169,7 +169,7 @@ static void uh_ubus_send_header(struct client *cl) ustream_printf(cl->us, "\r\n"); } -static void uh_ubus_send_response(struct client *cl) +static void uh_ubus_send_response(struct client *cl, struct blob_buf *buf) { struct dispatch_ubus *du = &cl->dispatch.ubus; const char *sep = ""; @@ -178,7 +178,7 @@ static void uh_ubus_send_response(struct client *cl) if (du->array && du->array_idx > 1) sep = ","; - str = blobmsg_format_json(buf.head, true); + str = blobmsg_format_json(buf->head, true); ops->chunk_printf(cl, "%s%s", sep, str); free(str); @@ -189,32 +189,34 @@ static void uh_ubus_send_response(struct client *cl) return ops->request_done(cl); } -static void uh_ubus_init_json_rpc_response(struct client *cl) +static void uh_ubus_init_json_rpc_response(struct client *cl, struct blob_buf *buf) { struct dispatch_ubus *du = &cl->dispatch.ubus; struct json_object *obj = du->jsobj_cur, *obj2 = NULL; - blobmsg_add_string(&buf, "jsonrpc", "2.0"); + blobmsg_add_string(buf, "jsonrpc", "2.0"); if (obj) json_object_object_get_ex(obj, "id", &obj2); if (obj2) - blobmsg_add_json_element(&buf, "id", obj2); + blobmsg_add_json_element(buf, "id", obj2); else - blobmsg_add_field(&buf, BLOBMSG_TYPE_UNSPEC, "id", NULL, 0); + blobmsg_add_field(buf, BLOBMSG_TYPE_UNSPEC, "id", NULL, 0); } static void uh_ubus_json_rpc_error(struct client *cl, enum rpc_error type) { void *c; - uh_ubus_init_json_rpc_response(cl); + blob_buf_init(&buf, 0); + + uh_ubus_init_json_rpc_response(cl, &buf); c = blobmsg_open_table(&buf, "error"); blobmsg_add_u32(&buf, "code", json_errors[type].code); blobmsg_add_string(&buf, "message", json_errors[type].msg); blobmsg_close_table(&buf, c); - uh_ubus_send_response(cl); + uh_ubus_send_response(cl, &buf); } static void @@ -234,14 +236,16 @@ uh_ubus_request_cb(struct ubus_request *req, int ret) void *r; int rem; + blob_buf_init(&buf, 0); + uloop_timeout_cancel(&du->timeout); - uh_ubus_init_json_rpc_response(cl); + uh_ubus_init_json_rpc_response(cl, &buf); r = blobmsg_open_array(&buf, "result"); blobmsg_add_u32(&buf, "", ret); blob_for_each_attr(cur, du->buf.head, rem) blobmsg_add_blob(&buf, cur); blobmsg_close_array(&buf, r); - uh_ubus_send_response(cl); + uh_ubus_send_response(cl, &buf); } static void @@ -401,9 +405,10 @@ static void uh_ubus_send_list(struct client *cl, struct blob_attr *params) uh_client_unref(cl); - uh_ubus_init_json_rpc_response(cl); + blob_buf_init(&buf, 0); + uh_ubus_init_json_rpc_response(cl, &buf); blobmsg_add_blob(&buf, blob_data(data.buf->head)); - uh_ubus_send_response(cl); + uh_ubus_send_response(cl, &buf); } static bool parse_json_rpc(struct rpc_data *d, struct blob_attr *data) @@ -627,8 +632,6 @@ static void uh_ubus_handle_request(struct client *cl, char *url, struct path_inf { struct dispatch *d = &cl->dispatch; - blob_buf_init(&buf, 0); - switch (cl->request.method) { case UH_HTTP_MSG_POST: -- cgit v1.2.3 From 11723570af9cb7bd87842e79c85ee99530be9902 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 14 Sep 2020 17:15:23 +0200 Subject: ubus: add new RESTful API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial uhttpd ubus API was fully based on JSON-RPC. That restricted it from supporting ubus notifications that don't fit its model. Notifications require protocol that allows server to send data without being polled. There are two candidates for that: 1. Server-sent events 2. WebSocket The later one is overcomplex for this simple task so ideally uhttps ubus should support text-based server-sent events. It's not possible with JSON-RPC without violating it. Specification requires server to reply with Response object. Replying with text/event-stream is not allowed. All above led to designing new API that: 1. Uses GET and POST requests 2. Makes use of RESTful URLs 3. Uses JSON-RPC in cleaner form and only for calling ubus methods This new API allows: 1. Listing all ubus objects and their methods using GET /list 2. Listing object methods using GET /list/ 3. Listening to object notifications with GET /subscribe/ 4. Calling ubus methods using POST /call/ JSON-RPC custom protocol was also simplified to: 1. Use "method" member for ubus object method name It was possible thanks to using RESTful URLs. Previously "method" had to be "list" or "call". 2. Reply with Error object on ubus method call error This simplified "result" member format as it doesn't need to contain ubus result code anymore. This patch doesn't break or change the old API. The biggest downside of the new API is no support for batch requests. It's cost of using RESTful URLs. It should not matter much as uhttpd supports keep alive. Example usages: 1. Getting all objects and their methods: $ curl http://192.168.1.1/ubus/list { "dhcp": { "ipv4leases": { }, "ipv6leases": { } }, "log": { "read": { "lines": "number", "stream": "boolean", "oneshot": "boolean" }, "write": { "event": "string" } } } 2. Getting object methods: $ curl http://192.168.1.1/ubus/list/log { "read": { "lines": "number", "stream": "boolean", "oneshot": "boolean" }, "write": { "event": "string" } } 3. Subscribing to notifications: $ curl http://192.168.1.1/ubus/subscribe/foo event: status data: {"count":5} 4. Calling ubus object method: $ curl -d '{ "jsonrpc": "2.0", "id": 1, "method": "login", "params": {"username": "root", "password": "password" } }' http://192.168.1.1/ubus/call/session { "jsonrpc": "2.0", "id": 1, "result": { "ubus_rpc_session": "01234567890123456789012345678901", (...) } } $ curl -H 'Authorization: Bearer 01234567890123456789012345678901' -d '{ "jsonrpc": "2.0", "id": 1, "method": "write", "params": {"event": "Hello world" } }' http://192.168.1.1/ubus/call/log { "jsonrpc": "2.0", "id": 1, "result": null } Signed-off-by: Rafał Miłecki --- main.c | 8 +- ubus.c | 348 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- uhttpd.h | 5 + 3 files changed, 339 insertions(+), 22 deletions(-) (limited to 'ubus.c') diff --git a/main.c b/main.c index 26e74ec..73e3d42 100644 --- a/main.c +++ b/main.c @@ -159,6 +159,7 @@ static int usage(const char *name) " -U file Override ubus socket path\n" " -a Do not authenticate JSON-RPC requests against UBUS session api\n" " -X Enable CORS HTTP headers on JSON-RPC api\n" + " -e Events subscription reconnection time (retry value)\n" #endif " -x string URL prefix for CGI handler, default is '/cgi-bin'\n" " -y alias[=path] URL alias handle\n" @@ -262,7 +263,7 @@ int main(int argc, char **argv) init_defaults_pre(); signal(SIGPIPE, SIG_IGN); - while ((ch = getopt(argc, argv, "A:aC:c:Dd:E:fh:H:I:i:K:k:L:l:m:N:n:P:p:qRr:Ss:T:t:U:u:Xx:y:")) != -1) { + while ((ch = getopt(argc, argv, "A:aC:c:Dd:E:e:fh:H:I:i:K:k:L:l:m:N:n:P:p:qRr:Ss:T:t:U:u:Xx:y:")) != -1) { switch(ch) { #ifdef HAVE_TLS case 'C': @@ -490,11 +491,16 @@ int main(int argc, char **argv) case 'X': conf.ubus_cors = 1; break; + + case 'e': + conf.events_retry = atoi(optarg); + break; #else case 'a': case 'u': case 'U': case 'X': + case 'e': fprintf(stderr, "uhttpd: UBUS support not compiled, " "ignoring -%c\n", ch); break; diff --git a/ubus.c b/ubus.c index a458f4c..ccddbbd 100644 --- a/ubus.c +++ b/ubus.c @@ -73,6 +73,7 @@ struct rpc_data { struct list_data { bool verbose; + bool add_object; struct blob_buf *buf; }; @@ -154,14 +155,14 @@ static void uh_ubus_add_cors_headers(struct client *cl) ustream_printf(cl->us, "Access-Control-Allow-Credentials: true\r\n"); } -static void uh_ubus_send_header(struct client *cl) +static void uh_ubus_send_header(struct client *cl, int code, const char *summary, const char *content_type) { - ops->http_header(cl, 200, "OK"); + ops->http_header(cl, code, summary); if (conf.ubus_cors) uh_ubus_add_cors_headers(cl); - ustream_printf(cl->us, "Content-Type: application/json\r\n"); + ustream_printf(cl->us, "Content-Type: %s\r\n", content_type); if (cl->request.method == UH_HTTP_MSG_OPTIONS) ustream_printf(cl->us, "Content-Length: 0\r\n"); @@ -219,12 +220,169 @@ static void uh_ubus_json_rpc_error(struct client *cl, enum rpc_error type) uh_ubus_send_response(cl, &buf); } +static void uh_ubus_error(struct client *cl, int code, const char *message) +{ + blob_buf_init(&buf, 0); + + blobmsg_add_u32(&buf, "code", code); + blobmsg_add_string(&buf, "message", message); + uh_ubus_send_response(cl, &buf); +} + +static void uh_ubus_posix_error(struct client *cl, int err) +{ + uh_ubus_error(cl, -err, strerror(err)); +} + +static void uh_ubus_ubus_error(struct client *cl, int err) +{ + uh_ubus_error(cl, err, ubus_strerror(err)); +} + +/* GET requests handling */ + +static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv); + +static void uh_ubus_handle_get_list(struct client *cl, const char *path) +{ + static struct blob_buf tmp; + struct list_data data = { .verbose = true, .add_object = !path, .buf = &tmp}; + struct blob_attr *cur; + int rem; + int err; + + blob_buf_init(&tmp, 0); + + err = ubus_lookup(ctx, path, uh_ubus_list_cb, &data); + if (err) { + uh_ubus_send_header(cl, 500, "Ubus Protocol Error", "application/json"); + uh_ubus_ubus_error(cl, err); + return; + } + + blob_buf_init(&buf, 0); + blob_for_each_attr(cur, tmp.head, rem) + blobmsg_add_blob(&buf, cur); + + uh_ubus_send_header(cl, 200, "OK", "application/json"); + uh_ubus_send_response(cl, &buf); +} + +static int uh_ubus_subscription_notification_cb(struct ubus_context *ctx, + struct ubus_object *obj, + struct ubus_request_data *req, + const char *method, + struct blob_attr *msg) +{ + struct ubus_subscriber *s; + struct dispatch_ubus *du; + struct client *cl; + char *json; + + s = container_of(obj, struct ubus_subscriber, obj); + du = container_of(s, struct dispatch_ubus, sub); + cl = container_of(du, struct client, dispatch.ubus); + + json = blobmsg_format_json(msg, true); + if (json) { + ops->chunk_printf(cl, "event: %s\ndata: %s\n\n", method, json); + free(json); + } + + return 0; +} + +static void uh_ubus_subscription_notification_remove_cb(struct ubus_context *ctx, struct ubus_subscriber *s, uint32_t id) +{ + struct dispatch_ubus *du; + struct client *cl; + + du = container_of(s, struct dispatch_ubus, sub); + cl = container_of(du, struct client, dispatch.ubus); + + ops->request_done(cl); +} + +static void uh_ubus_handle_get_subscribe(struct client *cl, const char *sid, const char *path) +{ + struct dispatch_ubus *du = &cl->dispatch.ubus; + uint32_t id; + int err; + + /* TODO: add ACL support */ + if (!conf.ubus_noauth) { + uh_ubus_send_header(cl, 200, "OK", "application/json"); + uh_ubus_posix_error(cl, EACCES); + return; + } + + du->sub.cb = uh_ubus_subscription_notification_cb; + du->sub.remove_cb = uh_ubus_subscription_notification_remove_cb; + + uh_client_ref(cl); + + err = ubus_register_subscriber(ctx, &du->sub); + if (err) + goto err_unref; + + err = ubus_lookup_id(ctx, path, &id); + if (err) + goto err_unregister; + + err = ubus_subscribe(ctx, &du->sub, id); + if (err) + goto err_unregister; + + uh_ubus_send_header(cl, 200, "OK", "text/event-stream"); + + if (conf.events_retry) + ops->chunk_printf(cl, "retry: %d\n", conf.events_retry); + + return; + +err_unregister: + ubus_unregister_subscriber(ctx, &du->sub); +err_unref: + uh_client_unref(cl); + if (err) { + uh_ubus_send_header(cl, 200, "OK", "application/json"); + uh_ubus_ubus_error(cl, err); + } +} + +static void uh_ubus_handle_get(struct client *cl) +{ + struct dispatch_ubus *du = &cl->dispatch.ubus; + const char *url = du->url_path; + + url += strlen(conf.ubus_prefix); + + if (!strcmp(url, "/list") || !strncmp(url, "/list/", strlen("/list/"))) { + url += strlen("/list"); + + uh_ubus_handle_get_list(cl, *url ? url + 1 : NULL); + } else if (!strncmp(url, "/subscribe/", strlen("/subscribe/"))) { + url += strlen("/subscribe"); + + uh_ubus_handle_get_subscribe(cl, NULL, url + 1); + } else { + ops->http_header(cl, 404, "Not Found"); + ustream_printf(cl->us, "\r\n"); + ops->request_done(cl); + } +} + +/* POST requests handling */ + static void uh_ubus_request_data_cb(struct ubus_request *req, int type, struct blob_attr *msg) { struct dispatch_ubus *du = container_of(req, struct dispatch_ubus, req); + struct blob_attr *cur; + int len; - blobmsg_add_field(&du->buf, BLOBMSG_TYPE_TABLE, "", blob_data(msg), blob_len(msg)); + blob_for_each_attr(cur, msg, len) + blobmsg_add_blob(&du->buf, cur); } static void @@ -239,13 +397,46 @@ uh_ubus_request_cb(struct ubus_request *req, int ret) blob_buf_init(&buf, 0); uloop_timeout_cancel(&du->timeout); - uh_ubus_init_json_rpc_response(cl, &buf); - r = blobmsg_open_array(&buf, "result"); - blobmsg_add_u32(&buf, "", ret); - blob_for_each_attr(cur, du->buf.head, rem) - blobmsg_add_blob(&buf, cur); - blobmsg_close_array(&buf, r); - uh_ubus_send_response(cl, &buf); + + /* Legacy format always uses "result" array - even for errors and empty + * results. */ + if (du->legacy) { + void *c; + + uh_ubus_init_json_rpc_response(cl, &buf); + r = blobmsg_open_array(&buf, "result"); + blobmsg_add_u32(&buf, "", ret); + c = blobmsg_open_table(&buf, NULL); + blob_for_each_attr(cur, du->buf.head, rem) + blobmsg_add_blob(&buf, cur); + blobmsg_close_table(&buf, c); + blobmsg_close_array(&buf, r); + uh_ubus_send_response(cl, &buf); + return; + } + + if (ret) { + void *c; + + uh_ubus_init_json_rpc_response(cl, &buf); + c = blobmsg_open_table(&buf, "error"); + blobmsg_add_u32(&buf, "code", ret); + blobmsg_add_string(&buf, "message", ubus_strerror(ret)); + blobmsg_close_table(&buf, c); + uh_ubus_send_response(cl, &buf); + } else { + uh_ubus_init_json_rpc_response(cl, &buf); + if (blob_len(du->buf.head)) { + r = blobmsg_open_table(&buf, "result"); + blob_for_each_attr(cur, du->buf.head, rem) + blobmsg_add_blob(&buf, cur); + blobmsg_close_table(&buf, r); + } else { + blobmsg_add_field(&buf, BLOBMSG_TYPE_UNSPEC, "result", NULL, 0); + } + uh_ubus_send_response(cl, &buf); + } + } static void @@ -282,11 +473,14 @@ static void uh_ubus_request_free(struct client *cl) if (du->req_pending) ubus_abort_request(ctx, &du->req); + + free(du->url_path); + du->url_path = NULL; } static void uh_ubus_single_error(struct client *cl, enum rpc_error type) { - uh_ubus_send_header(cl); + uh_ubus_send_header(cl, 200, "OK", "application/json"); uh_ubus_json_rpc_error(cl, type); ops->request_done(cl); } @@ -339,7 +533,8 @@ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *o if (!obj->signature) return; - o = blobmsg_open_table(data->buf, obj->path); + if (data->add_object) + o = blobmsg_open_table(data->buf, obj->path); blob_for_each_attr(sig, obj->signature, rem) { t = blobmsg_open_table(data->buf, blobmsg_name(sig)); rem2 = blobmsg_data_len(sig); @@ -370,13 +565,14 @@ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *o } blobmsg_close_table(data->buf, t); } - blobmsg_close_table(data->buf, o); + if (data->add_object) + blobmsg_close_table(data->buf, o); } static void uh_ubus_send_list(struct client *cl, struct blob_attr *params) { struct blob_attr *cur, *dup; - struct list_data data = { .buf = &cl->dispatch.ubus.buf, .verbose = false }; + struct list_data data = { .buf = &cl->dispatch.ubus.buf, .verbose = false, .add_object = true }; void *r; int rem; @@ -476,7 +672,7 @@ static void uh_ubus_init_batch(struct client *cl) struct dispatch_ubus *du = &cl->dispatch.ubus; du->array = true; - uh_ubus_send_header(cl); + uh_ubus_send_header(cl, 200, "OK", "application/json"); ops->chunk_printf(cl, "["); } @@ -599,7 +795,7 @@ static void uh_ubus_data_done(struct client *cl) switch (obj ? json_object_get_type(obj) : json_type_null) { case json_type_object: - uh_ubus_send_header(cl); + uh_ubus_send_header(cl, 200, "OK", "application/json"); return uh_ubus_handle_request_object(cl, obj); case json_type_array: uh_ubus_init_batch(cl); @@ -609,6 +805,97 @@ static void uh_ubus_data_done(struct client *cl) } } +static void uh_ubus_call(struct client *cl, const char *path, const char *sid) +{ + struct dispatch_ubus *du = &cl->dispatch.ubus; + struct json_object *obj = du->jsobj; + struct rpc_data data = {}; + enum rpc_error err = ERROR_PARSE; + static struct blob_buf req; + + uh_client_ref(cl); + + if (!obj || json_object_get_type(obj) != json_type_object) + goto error; + + uh_ubus_send_header(cl, 200, "OK", "application/json"); + + du->jsobj_cur = obj; + blob_buf_init(&req, 0); + if (!blobmsg_add_object(&req, obj)) + goto error; + + if (!parse_json_rpc(&data, req.head)) + goto error; + + du->func = data.method; + if (ubus_lookup_id(ctx, path, &du->obj)) { + err = ERROR_OBJECT; + goto error; + } + + if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, data.method)) { + err = ERROR_ACCESS; + goto error; + } + + uh_ubus_send_request(cl, sid, data.params); + goto out; + +error: + uh_ubus_json_rpc_error(cl, err); +out: + if (data.params) + free(data.params); + + uh_client_unref(cl); +} + +enum ubus_hdr { + HDR_AUTHORIZATION, + __HDR_UBUS_MAX +}; + +static void uh_ubus_handle_post(struct client *cl) +{ + static const struct blobmsg_policy hdr_policy[__HDR_UBUS_MAX] = { + [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING }, + }; + struct dispatch_ubus *du = &cl->dispatch.ubus; + struct blob_attr *tb[__HDR_UBUS_MAX]; + const char *url = du->url_path; + const char *auth; + + /* Treat both: /foo AND /foo/ as legacy requests. */ + if (ops->path_match(conf.ubus_prefix, url) && strlen(url) - strlen(conf.ubus_prefix) <= 1) { + du->legacy = true; + uh_ubus_data_done(cl); + return; + } + + blobmsg_parse(hdr_policy, __HDR_UBUS_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head)); + + auth = UH_UBUS_DEFAULT_SID; + if (tb[HDR_AUTHORIZATION]) { + const char *tmp = blobmsg_get_string(tb[HDR_AUTHORIZATION]); + + if (!strncasecmp(tmp, "Bearer ", 7)) + auth = tmp + 7; + } + + url += strlen(conf.ubus_prefix); + + if (!strncmp(url, "/call/", strlen("/call/"))) { + url += strlen("/call/"); + + uh_ubus_call(cl, url, auth); + } else { + ops->http_header(cl, 404, "Not Found"); + ustream_printf(cl->us, "\r\n"); + ops->request_done(cl); + } +} + static int uh_ubus_data_send(struct client *cl, const char *data, int len) { struct dispatch_ubus *du = &cl->dispatch.ubus; @@ -631,25 +918,44 @@ error: static void uh_ubus_handle_request(struct client *cl, char *url, struct path_info *pi) { struct dispatch *d = &cl->dispatch; + struct dispatch_ubus *du = &d->ubus; + char *chr; + + du->url_path = strdup(url); + if (!du->url_path) { + ops->client_error(cl, 500, "Internal Server Error", "Failed to allocate resources"); + return; + } + chr = strchr(du->url_path, '?'); + if (chr) + chr[0] = '\0'; + + du->legacy = false; switch (cl->request.method) { + case UH_HTTP_MSG_GET: + uh_ubus_handle_get(cl); + break; case UH_HTTP_MSG_POST: d->data_send = uh_ubus_data_send; - d->data_done = uh_ubus_data_done; + d->data_done = uh_ubus_handle_post; d->close_fds = uh_ubus_close_fds; d->free = uh_ubus_request_free; - d->ubus.jstok = json_tokener_new(); - break; + du->jstok = json_tokener_new(); + return; case UH_HTTP_MSG_OPTIONS: - uh_ubus_send_header(cl); + uh_ubus_send_header(cl, 200, "OK", "application/json"); ops->request_done(cl); break; default: ops->client_error(cl, 400, "Bad Request", "Invalid Request"); } + + free(du->url_path); + du->url_path = NULL; } static bool diff --git a/uhttpd.h b/uhttpd.h index f77718e..e61e176 100644 --- a/uhttpd.h +++ b/uhttpd.h @@ -82,6 +82,7 @@ struct config { int ubus_noauth; int ubus_cors; int cgi_prefix_len; + int events_retry; struct list_head cgi_alias; struct list_head lua_prefix; }; @@ -209,6 +210,7 @@ struct dispatch_ubus { struct json_tokener *jstok; struct json_object *jsobj; struct json_object *jsobj_cur; + char *url_path; int post_len; uint32_t obj; @@ -218,6 +220,9 @@ struct dispatch_ubus { bool req_pending; bool array; int array_idx; + bool legacy; /* Got legacy request => use legacy reply */ + + struct ubus_subscriber sub; }; #endif -- cgit v1.2.3 From 47c34bd6ad49cae408b8d7c150c6f9f324aaddf5 Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Tue, 15 Sep 2020 20:05:11 +0200 Subject: ubus: add ACL support for "subscribe" request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change ubus will allow users with access to the object pseudo method ":subscribe" to subscribe for notifications. 1. Move uh_ubus_allowed() up in the code 2. Export "Authorization" parsing code to the uh_ubus_get_auth() 3. Check for ":subscribe" method access Right now this depends on "Authorization" HTTP header which browsers don't allow setting for the EventSource. An alternative method of submitting session token remains to be implemented. Signed-off-by: Rafał Miłecki --- ubus.c | 119 +++++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 64 insertions(+), 55 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index ccddbbd..1cf5c5f 100644 --- a/ubus.c +++ b/ubus.c @@ -112,6 +112,30 @@ enum cors_hdr { __HDR_MAX }; +enum ubus_hdr { + HDR_AUTHORIZATION, + __HDR_UBUS_MAX +}; + +static const char *uh_ubus_get_auth(const struct blob_attr *attr) +{ + static const struct blobmsg_policy hdr_policy[__HDR_UBUS_MAX] = { + [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING }, + }; + struct blob_attr *tb[__HDR_UBUS_MAX]; + + blobmsg_parse(hdr_policy, __HDR_UBUS_MAX, tb, blob_data(attr), blob_len(attr)); + + if (tb[HDR_AUTHORIZATION]) { + const char *tmp = blobmsg_get_string(tb[HDR_AUTHORIZATION]); + + if (!strncasecmp(tmp, "Bearer ", 7)) + return tmp + 7; + } + + return UH_UBUS_DEFAULT_SID; +} + static void __uh_ubus_next_batched_request(struct uloop_timeout *timeout); static void uh_ubus_next_batched_request(struct client *cl) @@ -239,6 +263,39 @@ static void uh_ubus_ubus_error(struct client *cl, int err) uh_ubus_error(cl, err, ubus_strerror(err)); } +static void uh_ubus_allowed_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr *tb[__SES_MAX]; + bool *allow = (bool *)req->priv; + + if (!msg) + return; + + blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg)); + + if (tb[SES_ACCESS]) + *allow = blobmsg_get_bool(tb[SES_ACCESS]); +} + +static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun) +{ + uint32_t id; + bool allow = false; + static struct blob_buf req; + + if (ubus_lookup_id(ctx, "session", &id)) + return false; + + blob_buf_init(&req, 0); + blobmsg_add_string(&req, "ubus_rpc_session", sid); + blobmsg_add_string(&req, "object", obj); + blobmsg_add_string(&req, "function", fun); + + ubus_invoke(ctx, id, "access", req.head, uh_ubus_allowed_cb, &allow, conf.script_timeout * 500); + + return allow; +} + /* GET requests handling */ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *obj, void *priv); @@ -303,14 +360,16 @@ static void uh_ubus_subscription_notification_remove_cb(struct ubus_context *ctx ops->request_done(cl); } -static void uh_ubus_handle_get_subscribe(struct client *cl, const char *sid, const char *path) +static void uh_ubus_handle_get_subscribe(struct client *cl, const char *path) { struct dispatch_ubus *du = &cl->dispatch.ubus; + const char *sid; uint32_t id; int err; - /* TODO: add ACL support */ - if (!conf.ubus_noauth) { + sid = uh_ubus_get_auth(cl->hdr.head); + + if (!conf.ubus_noauth && !uh_ubus_allowed(sid, path, ":subscribe")) { uh_ubus_send_header(cl, 200, "OK", "application/json"); uh_ubus_posix_error(cl, EACCES); return; @@ -364,7 +423,7 @@ static void uh_ubus_handle_get(struct client *cl) } else if (!strncmp(url, "/subscribe/", strlen("/subscribe/"))) { url += strlen("/subscribe"); - uh_ubus_handle_get_subscribe(cl, NULL, url + 1); + uh_ubus_handle_get_subscribe(cl, url + 1); } else { ops->http_header(cl, 404, "Not Found"); ustream_printf(cl->us, "\r\n"); @@ -682,39 +741,6 @@ static void uh_ubus_complete_batch(struct client *cl) ops->request_done(cl); } -static void uh_ubus_allowed_cb(struct ubus_request *req, int type, struct blob_attr *msg) -{ - struct blob_attr *tb[__SES_MAX]; - bool *allow = (bool *)req->priv; - - if (!msg) - return; - - blobmsg_parse(ses_policy, __SES_MAX, tb, blob_data(msg), blob_len(msg)); - - if (tb[SES_ACCESS]) - *allow = blobmsg_get_bool(tb[SES_ACCESS]); -} - -static bool uh_ubus_allowed(const char *sid, const char *obj, const char *fun) -{ - uint32_t id; - bool allow = false; - static struct blob_buf req; - - if (ubus_lookup_id(ctx, "session", &id)) - return false; - - blob_buf_init(&req, 0); - blobmsg_add_string(&req, "ubus_rpc_session", sid); - blobmsg_add_string(&req, "object", obj); - blobmsg_add_string(&req, "function", fun); - - ubus_invoke(ctx, id, "access", req.head, uh_ubus_allowed_cb, &allow, conf.script_timeout * 500); - - return allow; -} - static void uh_ubus_handle_request_object(struct client *cl, struct json_object *obj) { struct dispatch_ubus *du = &cl->dispatch.ubus; @@ -851,18 +877,9 @@ out: uh_client_unref(cl); } -enum ubus_hdr { - HDR_AUTHORIZATION, - __HDR_UBUS_MAX -}; - static void uh_ubus_handle_post(struct client *cl) { - static const struct blobmsg_policy hdr_policy[__HDR_UBUS_MAX] = { - [HDR_AUTHORIZATION] = { "authorization", BLOBMSG_TYPE_STRING }, - }; struct dispatch_ubus *du = &cl->dispatch.ubus; - struct blob_attr *tb[__HDR_UBUS_MAX]; const char *url = du->url_path; const char *auth; @@ -873,15 +890,7 @@ static void uh_ubus_handle_post(struct client *cl) return; } - blobmsg_parse(hdr_policy, __HDR_UBUS_MAX, tb, blob_data(cl->hdr.head), blob_len(cl->hdr.head)); - - auth = UH_UBUS_DEFAULT_SID; - if (tb[HDR_AUTHORIZATION]) { - const char *tmp = blobmsg_get_string(tb[HDR_AUTHORIZATION]); - - if (!strncasecmp(tmp, "Bearer ", 7)) - auth = tmp + 7; - } + auth = uh_ubus_get_auth(cl->hdr.head); url += strlen(conf.ubus_prefix); -- cgit v1.2.3 From c186212a3075766717cab396a46242f110ee71bd Mon Sep 17 00:00:00 2001 From: Rafał Miłecki Date: Mon, 21 Sep 2020 16:16:23 +0200 Subject: ubus: support GET method with CORS requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Complex GET requests (e.g. those with custom headers) require browsers to send preflight OPTIONS request with: Access-Control-Request-Method: GET It's important to reply to such requests with the header Access-Control-Allow-Origin (and optionally others) to allow CORS requests. Adding GET to the Access-Control-Allow-Methods is cosmetical as according to the Fetch standard: > If request’s method is not in methods, request’s method is not a > CORS-safelisted method, and request’s credentials mode is "include" or > methods does not contain `*`, then return a network error. It basically means that Access-Control-Allow-Methods value is ignored for GET, HEAD and POST methods. Signed-off-by: Rafał Miłecki --- ubus.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 1cf5c5f..39b38b2 100644 --- a/ubus.c +++ b/ubus.c @@ -164,7 +164,7 @@ static void uh_ubus_add_cors_headers(struct client *cl) { char *hdr = (char *) blobmsg_data(tb[HDR_ACCESS_CONTROL_REQUEST_METHOD]); - if (strcmp(hdr, "POST") && strcmp(hdr, "OPTIONS")) + if (strcmp(hdr, "GET") && strcmp(hdr, "POST") && strcmp(hdr, "OPTIONS")) return; } @@ -175,7 +175,7 @@ static void uh_ubus_add_cors_headers(struct client *cl) ustream_printf(cl->us, "Access-Control-Allow-Headers: %s\r\n", blobmsg_get_string(tb[HDR_ACCESS_CONTROL_REQUEST_HEADERS])); - ustream_printf(cl->us, "Access-Control-Allow-Methods: POST, OPTIONS\r\n"); + ustream_printf(cl->us, "Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"); ustream_printf(cl->us, "Access-Control-Allow-Credentials: true\r\n"); } -- cgit v1.2.3 From 14a3cb42047bd0c0f8d5713332b9b4516a505607 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Thu, 1 Oct 2020 19:58:27 +0200 Subject: ubus: fix legacy empty reply format The legacy ubus protocol must not include an empty object in the result array if the invoked ubus procedure yielded no response. This fixes compatibility with existing legacy ubus api clients that expect this behaviour, LuCI's fs.js in particular. Signed-off-by: Jo-Philipp Wich --- ubus.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 39b38b2..cc7447e 100644 --- a/ubus.c +++ b/ubus.c @@ -465,10 +465,14 @@ uh_ubus_request_cb(struct ubus_request *req, int ret) uh_ubus_init_json_rpc_response(cl, &buf); r = blobmsg_open_array(&buf, "result"); blobmsg_add_u32(&buf, "", ret); - c = blobmsg_open_table(&buf, NULL); - blob_for_each_attr(cur, du->buf.head, rem) - blobmsg_add_blob(&buf, cur); - blobmsg_close_table(&buf, c); + + if (blob_len(du->buf.head)) { + c = blobmsg_open_table(&buf, NULL); + blob_for_each_attr(cur, du->buf.head, rem) + blobmsg_add_blob(&buf, cur); + blobmsg_close_table(&buf, c); + } + blobmsg_close_array(&buf, r); uh_ubus_send_response(cl, &buf); return; -- cgit v1.2.3 From f53a63999784bcb7dc513e221f3f25dd3de2f35e Mon Sep 17 00:00:00 2001 From: Wojciech Jowsa Date: Sun, 15 Nov 2020 10:19:17 +0100 Subject: ubus: fix uhttpd crash Unregister ubus subscriber in notification remove callback. Without this call, uhttpd crashes when client tries to subscribe to the ubus object after the object was unregistred and registered again. It is bacuse the reference to ubus subscriber is not freed but the memory is cleared in the uh_request_done function. Signed-off-by: Wojciech Jowsa --- ubus.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index cc7447e..619135c 100644 --- a/ubus.c +++ b/ubus.c @@ -357,6 +357,8 @@ static void uh_ubus_subscription_notification_remove_cb(struct ubus_context *ctx du = container_of(s, struct dispatch_ubus, sub); cl = container_of(du, struct client, dispatch.ubus); + ubus_unregister_subscriber(ctx, &du->sub); + ops->request_done(cl); } -- cgit v1.2.3 From 51283f9f1df5dedcba35f40367ef5d4ab1a55e0b Mon Sep 17 00:00:00 2001 From: Thomas Huehn Date: Tue, 28 Sep 2021 01:59:39 +0200 Subject: fix compiler uninitialized variable In file included from ubus.c:20: ubus.c: In function 'uh_ubus_list_cb': libubox/blobmsg.h:256:9: error: 'o' may be used uninitialized in this function [-Werror=maybe-uninitialized] 256 | blob_nest_end(buf, cookie); | ^~~~~~~~~~~~~~~~~~~~~~~~~~ ubus.c:591:19: note: 'o' was declared here 591 | void *t, *o; | ^ cc1: all warnings being treated as errors Signed-off-by: Thomas Huehn Acked-by: Alexander Couzens --- ubus.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'ubus.c') diff --git a/ubus.c b/ubus.c index 619135c..99cc400 100644 --- a/ubus.c +++ b/ubus.c @@ -588,7 +588,7 @@ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *o struct blob_attr *sig, *attr; struct list_data *data = priv; int rem, rem2; - void *t, *o; + void *t, *o=NULL; if (!data->verbose) { blobmsg_add_string(data->buf, NULL, obj->path); @@ -598,8 +598,12 @@ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *o if (!obj->signature) return; - if (data->add_object) + if (data->add_object) { o = blobmsg_open_table(data->buf, obj->path); + if (!o) + return; + } + blob_for_each_attr(sig, obj->signature, rem) { t = blobmsg_open_table(data->buf, blobmsg_name(sig)); rem2 = blobmsg_data_len(sig); @@ -630,6 +634,7 @@ static void uh_ubus_list_cb(struct ubus_context *ctx, struct ubus_object_data *o } blobmsg_close_table(data->buf, t); } + if (data->add_object) blobmsg_close_table(data->buf, o); } -- cgit v1.2.3