Root/package/network/services/uhttpd/src/uhttpd-ubus.c

1/*
2 * uhttpd - Tiny single-threaded httpd - ubus handler
3 *
4 * Copyright (C) 2012 Jo-Philipp Wich <xm@subsignal.org>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include "uhttpd.h"
20#include "uhttpd-utils.h"
21#include "uhttpd-ubus.h"
22
23
24enum {
25    UH_UBUS_SN_TIMEOUT,
26    __UH_UBUS_SN_MAX,
27};
28
29static const struct blobmsg_policy new_policy[__UH_UBUS_SN_MAX] = {
30    [UH_UBUS_SN_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
31};
32
33
34enum {
35    UH_UBUS_SI_SID,
36    __UH_UBUS_SI_MAX,
37};
38
39static const struct blobmsg_policy sid_policy[__UH_UBUS_SI_MAX] = {
40    [UH_UBUS_SI_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
41};
42
43
44enum {
45    UH_UBUS_SS_SID,
46    UH_UBUS_SS_VALUES,
47    __UH_UBUS_SS_MAX,
48};
49
50static const struct blobmsg_policy set_policy[__UH_UBUS_SS_MAX] = {
51    [UH_UBUS_SS_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
52    [UH_UBUS_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
53};
54
55
56enum {
57    UH_UBUS_SG_SID,
58    UH_UBUS_SG_KEYS,
59    __UH_UBUS_SG_MAX,
60};
61
62static const struct blobmsg_policy get_policy[__UH_UBUS_SG_MAX] = {
63    [UH_UBUS_SG_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
64    [UH_UBUS_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY },
65};
66
67
68enum {
69    UH_UBUS_SA_SID,
70    UH_UBUS_SA_OBJECTS,
71    __UH_UBUS_SA_MAX,
72};
73
74static const struct blobmsg_policy acl_policy[__UH_UBUS_SA_MAX] = {
75    [UH_UBUS_SA_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
76    [UH_UBUS_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY },
77};
78
79
80static bool
81uh_ubus_strmatch(const char *str, const char *pat)
82{
83    while (*pat)
84    {
85        if (*pat == '?')
86        {
87            if (!*str)
88                return false;
89
90            str++;
91            pat++;
92        }
93        else if (*pat == '*')
94        {
95            if (uh_ubus_strmatch(str, pat+1))
96                return true;
97
98            if (*str && uh_ubus_strmatch(str+1, pat))
99                return true;
100
101            return false;
102        }
103        else if (*str++ != *pat++)
104        {
105            return false;
106        }
107    }
108
109    return (!*str && !*pat);
110}
111
112static int
113uh_ubus_avlcmp(const void *k1, const void *k2, void *ptr)
114{
115    return strcmp((char *)k1, (char *)k2);
116}
117
118static void
119uh_ubus_random(char *dest)
120{
121    int i;
122    unsigned char buf[16] = { 0 };
123    FILE *f;
124
125    if ((f = fopen("/dev/urandom", "r")) != NULL)
126    {
127        fread(buf, 1, sizeof(buf), f);
128        fclose(f);
129    }
130
131    for (i = 0; i < sizeof(buf); i++)
132        sprintf(dest + (i<<1), "%02x", buf[i]);
133}
134
135static void
136uh_ubus_session_dump_data(struct uh_ubus_session *ses, struct blob_buf *b)
137{
138    struct uh_ubus_session_data *d;
139
140    avl_for_each_element(&ses->data, d, avl)
141    {
142        blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr),
143                          blobmsg_data(d->attr), blobmsg_data_len(d->attr));
144    }
145}
146
147static void
148uh_ubus_session_dump_acls(struct uh_ubus_session *ses, struct blob_buf *b)
149{
150    struct uh_ubus_session_acl *acl;
151    const char *lastobj = NULL;
152    void *c = NULL;
153
154    avl_for_each_element(&ses->acls, acl, avl)
155    {
156        if (!lastobj || strcmp(acl->object, lastobj))
157        {
158            if (c) blobmsg_close_array(b, c);
159            c = blobmsg_open_array(b, acl->object);
160        }
161
162        blobmsg_add_string(b, NULL, acl->function);
163        lastobj = acl->object;
164    }
165
166    if (c) blobmsg_close_array(b, c);
167}
168
169static void
170uh_ubus_session_dump(struct uh_ubus_session *ses,
171                     struct ubus_context *ctx,
172                     struct ubus_request_data *req)
173{
174    void *c;
175    struct blob_buf b;
176
177    memset(&b, 0, sizeof(b));
178    blob_buf_init(&b, 0);
179
180    blobmsg_add_string(&b, "sid", ses->id);
181    blobmsg_add_u32(&b, "timeout", ses->timeout);
182    blobmsg_add_u32(&b, "touched", ses->touched.tv_sec);
183
184    c = blobmsg_open_table(&b, "acls");
185    uh_ubus_session_dump_acls(ses, &b);
186    blobmsg_close_table(&b, c);
187
188    c = blobmsg_open_table(&b, "data");
189    uh_ubus_session_dump_data(ses, &b);
190    blobmsg_close_table(&b, c);
191
192    ubus_send_reply(ctx, req, b.head);
193    blob_buf_free(&b);
194}
195
196static struct uh_ubus_session *
197uh_ubus_session_create(struct uh_ubus_state *state, int timeout)
198{
199    struct uh_ubus_session *ses;
200
201    ses = malloc(sizeof(*ses));
202
203    /* failed to allocate memory... */
204    if (!ses)
205        return NULL;
206
207    memset(ses, 0, sizeof(*ses));
208
209    uh_ubus_random(ses->id);
210
211    ses->timeout = timeout;
212    ses->avl.key = ses->id;
213
214    avl_insert(&state->sessions, &ses->avl);
215    avl_init(&ses->acls, uh_ubus_avlcmp, true, NULL);
216    avl_init(&ses->data, uh_ubus_avlcmp, false, NULL);
217    clock_gettime(CLOCK_MONOTONIC, &ses->touched);
218
219    return ses;
220}
221
222
223static struct uh_ubus_session *
224uh_ubus_session_get(struct uh_ubus_state *state, const char *id)
225{
226    struct uh_ubus_session *ses;
227
228    ses = avl_find_element(&state->sessions, id, ses, avl);
229
230    if (ses)
231        clock_gettime(CLOCK_MONOTONIC, &ses->touched);
232
233    return ses;
234}
235
236static void
237uh_ubus_session_destroy(struct uh_ubus_state *state,
238                        struct uh_ubus_session *ses)
239{
240    struct uh_ubus_session_acl *acl, *nacl;
241    struct uh_ubus_session_data *data, *ndata;
242
243    avl_remove_all_elements(&ses->acls, acl, avl, nacl)
244        free(acl);
245
246    avl_remove_all_elements(&ses->data, data, avl, ndata)
247        free(data);
248
249    avl_delete(&state->sessions, &ses->avl);
250    free(ses);
251}
252
253static void
254uh_ubus_session_cleanup(struct uh_ubus_state *state)
255{
256    struct timespec now;
257    struct uh_ubus_session *ses, *nses;
258
259    clock_gettime(CLOCK_MONOTONIC, &now);
260
261    avl_for_each_element_safe(&state->sessions, ses, avl, nses)
262    {
263        if ((now.tv_sec - ses->touched.tv_sec) >= ses->timeout)
264            uh_ubus_session_destroy(state, ses);
265    }
266}
267
268
269static int
270uh_ubus_handle_create(struct ubus_context *ctx, struct ubus_object *obj,
271                      struct ubus_request_data *req, const char *method,
272                      struct blob_attr *msg)
273{
274    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
275    struct uh_ubus_session *ses;
276    struct blob_attr *tb[__UH_UBUS_SN_MAX];
277
278    int timeout = state->timeout;
279
280    blobmsg_parse(new_policy, __UH_UBUS_SN_MAX, tb, blob_data(msg), blob_len(msg));
281
282    /* TODO: make this a uloop timeout */
283    uh_ubus_session_cleanup(state);
284
285    if (tb[UH_UBUS_SN_TIMEOUT])
286        timeout = *(uint32_t *)blobmsg_data(tb[UH_UBUS_SN_TIMEOUT]);
287
288    ses = uh_ubus_session_create(state, timeout);
289
290    if (ses)
291        uh_ubus_session_dump(ses, ctx, req);
292
293    return 0;
294}
295
296static int
297uh_ubus_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
298                    struct ubus_request_data *req, const char *method,
299                    struct blob_attr *msg)
300{
301    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
302    struct uh_ubus_session *ses;
303    struct blob_attr *tb[__UH_UBUS_SI_MAX];
304
305    blobmsg_parse(sid_policy, __UH_UBUS_SI_MAX, tb, blob_data(msg), blob_len(msg));
306
307    /* TODO: make this a uloop timeout */
308    uh_ubus_session_cleanup(state);
309
310    if (!tb[UH_UBUS_SI_SID])
311    {
312        avl_for_each_element(&state->sessions, ses, avl)
313            uh_ubus_session_dump(ses, ctx, req);
314    }
315    else
316    {
317        ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SI_SID]));
318
319        if (!ses)
320            return UBUS_STATUS_NOT_FOUND;
321
322        uh_ubus_session_dump(ses, ctx, req);
323    }
324
325    return 0;
326}
327
328
329static int
330uh_ubus_session_grant(struct uh_ubus_session *ses, struct ubus_context *ctx,
331                      const char *object, const char *function)
332{
333    struct uh_ubus_session_acl *acl, *nacl;
334
335    acl = avl_find_element(&ses->acls, object, acl, avl);
336
337    if (acl)
338    {
339        avl_for_element_to_last(&ses->acls, acl, acl, avl)
340        {
341            if (!strcmp(acl->function, function))
342                return 1;
343        }
344    }
345
346    nacl = malloc(sizeof(*nacl) + strlen(object) + strlen(function) + 2);
347
348    if (nacl)
349    {
350        memset(nacl, 0, sizeof(*nacl));
351        nacl->function = nacl->object + 1;
352        nacl->function += sprintf(nacl->object, "%s", object);
353        sprintf(nacl->function, "%s", function);
354
355        nacl->avl.key = nacl->object;
356        avl_insert(&ses->acls, &nacl->avl);
357    }
358
359    return 0;
360}
361
362static int
363uh_ubus_session_revoke(struct uh_ubus_session *ses, struct ubus_context *ctx,
364                       const char *object, const char *function)
365{
366    struct uh_ubus_session_acl *acl, *nacl;
367
368    if (!object && !function)
369    {
370        avl_remove_all_elements(&ses->acls, acl, avl, nacl)
371            free(acl);
372    }
373    else
374    {
375        avl_for_each_element_safe(&ses->acls, acl, avl, nacl)
376        {
377            if (uh_ubus_strmatch(acl->object, object) &&
378                uh_ubus_strmatch(acl->function, function))
379            {
380                avl_delete(&ses->acls, &acl->avl);
381                free(acl);
382            }
383        }
384    }
385
386    return 0;
387}
388
389
390static int
391uh_ubus_handle_grant(struct ubus_context *ctx, struct ubus_object *obj,
392                     struct ubus_request_data *req, const char *method,
393                     struct blob_attr *msg)
394{
395    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
396    struct uh_ubus_session *ses;
397    struct blob_attr *tb[__UH_UBUS_SA_MAX];
398    struct blob_attr *attr, *sattr;
399    const char *object, *function;
400    int rem1, rem2;
401
402    blobmsg_parse(acl_policy, __UH_UBUS_SA_MAX, tb, blob_data(msg), blob_len(msg));
403
404    if (!tb[UH_UBUS_SA_SID] || !tb[UH_UBUS_SA_OBJECTS])
405        return UBUS_STATUS_INVALID_ARGUMENT;
406
407    ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SA_SID]));
408
409    if (!ses)
410        return UBUS_STATUS_NOT_FOUND;
411
412    blobmsg_for_each_attr(attr, tb[UH_UBUS_SA_OBJECTS], rem1)
413    {
414        if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
415            continue;
416
417        object = NULL;
418        function = NULL;
419
420        blobmsg_for_each_attr(sattr, attr, rem2)
421        {
422            if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
423                continue;
424
425            if (!object)
426                object = blobmsg_data(sattr);
427            else if (!function)
428                function = blobmsg_data(sattr);
429            else
430                break;
431        }
432
433        if (object && function)
434            uh_ubus_session_grant(ses, ctx, object, function);
435    }
436
437    return 0;
438}
439
440static int
441uh_ubus_handle_revoke(struct ubus_context *ctx, struct ubus_object *obj,
442                      struct ubus_request_data *req, const char *method,
443                      struct blob_attr *msg)
444{
445    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
446    struct uh_ubus_session *ses;
447    struct blob_attr *tb[__UH_UBUS_SA_MAX];
448    struct blob_attr *attr, *sattr;
449    const char *object, *function;
450    int rem1, rem2;
451
452    blobmsg_parse(acl_policy, __UH_UBUS_SA_MAX, tb, blob_data(msg), blob_len(msg));
453
454    if (!tb[UH_UBUS_SA_SID])
455        return UBUS_STATUS_INVALID_ARGUMENT;
456
457    ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SA_SID]));
458
459    if (!ses)
460        return UBUS_STATUS_NOT_FOUND;
461
462    if (!tb[UH_UBUS_SA_OBJECTS])
463    {
464        uh_ubus_session_revoke(ses, ctx, NULL, NULL);
465    }
466    else
467    {
468        blobmsg_for_each_attr(attr, tb[UH_UBUS_SA_OBJECTS], rem1)
469        {
470            if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
471                continue;
472
473            object = NULL;
474            function = NULL;
475
476            blobmsg_for_each_attr(sattr, attr, rem2)
477            {
478                if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
479                    continue;
480
481                if (!object)
482                    object = blobmsg_data(sattr);
483                else if (!function)
484                    function = blobmsg_data(sattr);
485                else
486                    break;
487            }
488
489            if (object && function)
490                uh_ubus_session_revoke(ses, ctx, object, function);
491        }
492    }
493
494    return 0;
495}
496
497static int
498uh_ubus_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
499                   struct ubus_request_data *req, const char *method,
500                   struct blob_attr *msg)
501{
502    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
503    struct uh_ubus_session *ses;
504    struct uh_ubus_session_data *data;
505    struct blob_attr *tb[__UH_UBUS_SA_MAX];
506    struct blob_attr *attr;
507    int rem;
508
509    blobmsg_parse(set_policy, __UH_UBUS_SS_MAX, tb, blob_data(msg), blob_len(msg));
510
511    if (!tb[UH_UBUS_SS_SID] || !tb[UH_UBUS_SS_VALUES])
512        return UBUS_STATUS_INVALID_ARGUMENT;
513
514    ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SS_SID]));
515
516    if (!ses)
517        return UBUS_STATUS_NOT_FOUND;
518
519    blobmsg_for_each_attr(attr, tb[UH_UBUS_SS_VALUES], rem)
520    {
521        if (!blobmsg_name(attr)[0])
522            continue;
523
524        data = avl_find_element(&ses->data, blobmsg_name(attr), data, avl);
525
526        if (data)
527        {
528            avl_delete(&ses->data, &data->avl);
529            free(data);
530        }
531
532        data = malloc(sizeof(*data) + blob_pad_len(attr));
533
534        if (!data)
535            break;
536
537        memset(data, 0, sizeof(*data) + blob_pad_len(attr));
538        memcpy(data->attr, attr, blob_pad_len(attr));
539
540        data->avl.key = blobmsg_name(data->attr);
541        avl_insert(&ses->data, &data->avl);
542    }
543
544    return 0;
545}
546
547static int
548uh_ubus_handle_get(struct ubus_context *ctx, struct ubus_object *obj,
549                   struct ubus_request_data *req, const char *method,
550                   struct blob_attr *msg)
551{
552    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
553    struct uh_ubus_session *ses;
554    struct uh_ubus_session_data *data;
555    struct blob_attr *tb[__UH_UBUS_SA_MAX];
556    struct blob_attr *attr;
557    struct blob_buf b;
558    void *c;
559    int rem;
560
561    blobmsg_parse(get_policy, __UH_UBUS_SG_MAX, tb, blob_data(msg), blob_len(msg));
562
563    if (!tb[UH_UBUS_SG_SID])
564        return UBUS_STATUS_INVALID_ARGUMENT;
565
566    ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SG_SID]));
567
568    if (!ses)
569        return UBUS_STATUS_NOT_FOUND;
570
571    memset(&b, 0, sizeof(b));
572    blob_buf_init(&b, 0);
573    c = blobmsg_open_table(&b, "values");
574
575    if (!tb[UH_UBUS_SG_KEYS])
576    {
577        uh_ubus_session_dump_data(ses, &b);
578    }
579    else
580    {
581        blobmsg_for_each_attr(attr, tb[UH_UBUS_SG_KEYS], rem)
582        {
583            if (blob_id(attr) != BLOBMSG_TYPE_STRING)
584                continue;
585
586            data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
587
588            if (!data)
589                continue;
590
591            blobmsg_add_field(&b, blobmsg_type(data->attr),
592                              blobmsg_name(data->attr),
593                              blobmsg_data(data->attr),
594                              blobmsg_data_len(data->attr));
595        }
596    }
597
598    blobmsg_close_table(&b, c);
599    ubus_send_reply(ctx, req, b.head);
600    blob_buf_free(&b);
601
602    return 0;
603}
604
605static int
606uh_ubus_handle_unset(struct ubus_context *ctx, struct ubus_object *obj,
607                     struct ubus_request_data *req, const char *method,
608                     struct blob_attr *msg)
609{
610    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
611    struct uh_ubus_session *ses;
612    struct uh_ubus_session_data *data, *ndata;
613    struct blob_attr *tb[__UH_UBUS_SA_MAX];
614    struct blob_attr *attr;
615    int rem;
616
617    blobmsg_parse(get_policy, __UH_UBUS_SG_MAX, tb, blob_data(msg), blob_len(msg));
618
619    if (!tb[UH_UBUS_SG_SID])
620        return UBUS_STATUS_INVALID_ARGUMENT;
621
622    ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SG_SID]));
623
624    if (!ses)
625        return UBUS_STATUS_NOT_FOUND;
626
627    if (!tb[UH_UBUS_SG_KEYS])
628    {
629        avl_remove_all_elements(&ses->data, data, avl, ndata)
630            free(data);
631    }
632    else
633    {
634        blobmsg_for_each_attr(attr, tb[UH_UBUS_SG_KEYS], rem)
635        {
636            if (blob_id(attr) != BLOBMSG_TYPE_STRING)
637                continue;
638
639            data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
640
641            if (!data)
642                continue;
643
644            avl_delete(&ses->data, &data->avl);
645            free(data);
646        }
647    }
648
649    return 0;
650}
651
652static int
653uh_ubus_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj,
654                       struct ubus_request_data *req, const char *method,
655                       struct blob_attr *msg)
656{
657    struct uh_ubus_state *state = container_of(obj, struct uh_ubus_state, ubus);
658    struct uh_ubus_session *ses;
659    struct blob_attr *tb[__UH_UBUS_SA_MAX];
660
661    blobmsg_parse(sid_policy, __UH_UBUS_SI_MAX, tb, blob_data(msg), blob_len(msg));
662
663    if (!tb[UH_UBUS_SI_SID])
664        return UBUS_STATUS_INVALID_ARGUMENT;
665
666    ses = uh_ubus_session_get(state, blobmsg_data(tb[UH_UBUS_SI_SID]));
667
668    if (!ses)
669        return UBUS_STATUS_NOT_FOUND;
670
671    uh_ubus_session_destroy(state, ses);
672
673    return 0;
674}
675
676
677struct uh_ubus_state *
678uh_ubus_init(const struct config *conf)
679{
680    int rv;
681    struct uh_ubus_state *state;
682    struct ubus_object *session_object;
683
684    static struct ubus_method session_methods[] = {
685        UBUS_METHOD("create", uh_ubus_handle_create, new_policy),
686        UBUS_METHOD("list", uh_ubus_handle_list, sid_policy),
687        UBUS_METHOD("grant", uh_ubus_handle_grant, acl_policy),
688        UBUS_METHOD("revoke", uh_ubus_handle_revoke, acl_policy),
689        UBUS_METHOD("set", uh_ubus_handle_set, set_policy),
690        UBUS_METHOD("get", uh_ubus_handle_get, get_policy),
691        UBUS_METHOD("unset", uh_ubus_handle_unset, get_policy),
692        UBUS_METHOD("destroy", uh_ubus_handle_destroy, sid_policy),
693    };
694
695    static struct ubus_object_type session_type =
696        UBUS_OBJECT_TYPE("uhttpd", session_methods);
697
698    state = malloc(sizeof(*state));
699
700    if (!state)
701    {
702        fprintf(stderr, "Unable to allocate memory for ubus state\n");
703        exit(1);
704    }
705
706    memset(state, 0, sizeof(*state));
707    state->ctx = ubus_connect(conf->ubus_socket);
708    state->timeout = conf->script_timeout;
709
710    if (!state->ctx)
711    {
712        fprintf(stderr, "Unable to connect to ubus socket\n");
713        exit(1);
714    }
715
716    ubus_add_uloop(state->ctx);
717
718    session_object = &state->ubus;
719    session_object->name = "session";
720    session_object->type = &session_type;
721    session_object->methods = session_methods;
722    session_object->n_methods = ARRAY_SIZE(session_methods);
723
724    rv = ubus_add_object(state->ctx, &state->ubus);
725
726    if (rv)
727    {
728        fprintf(stderr, "Unable to publish ubus object: %s\n",
729                ubus_strerror(rv));
730        exit(1);
731    }
732
733    blob_buf_init(&state->buf, 0);
734    avl_init(&state->sessions, uh_ubus_avlcmp, false, NULL);
735
736    return state;
737}
738
739
740static bool
741uh_ubus_request_parse_url(struct client *cl, char **sid, char **obj, char **fun)
742{
743    char *url = cl->request.url + strlen(cl->server->conf->ubus_prefix);
744
745    for (; url && *url == '/'; *url++ = 0);
746    *sid = url;
747
748    for (url = url ? strchr(url, '/') : NULL; url && *url == '/'; *url++ = 0);
749    *obj = url;
750
751    for (url = url ? strchr(url, '/') : NULL; url && *url == '/'; *url++ = 0);
752    *fun = url;
753
754    for (url = url ? strchr(url, '/') : NULL; url && *url == '/'; *url++ = 0);
755    return (*sid && *obj && *fun);
756}
757
758static bool
759uh_ubus_request_parse_post(struct client *cl, int len, struct blob_buf *b)
760{
761    int rlen;
762    bool rv = false;
763    char buf[UH_LIMIT_MSGHEAD];
764
765    struct json_object *obj = NULL;
766    struct json_tokener *tok = NULL;
767
768    if (!len)
769        return NULL;
770
771    memset(b, 0, sizeof(*b));
772    blob_buf_init(b, 0);
773
774    tok = json_tokener_new();
775
776    while (len > 0)
777    {
778        /* remaining data in http head buffer ... */
779        if (cl->httpbuf.len > 0)
780        {
781            rlen = min(len, cl->httpbuf.len);
782
783            D("ubus: feed %d HTTP buffer bytes\n", rlen);
784
785            memcpy(buf, cl->httpbuf.ptr, rlen);
786
787            cl->httpbuf.len -= rlen;
788            cl->httpbuf.ptr += rlen;
789        }
790
791        /* read it from socket ... */
792        else
793        {
794            ensure_out(rlen = uh_tcp_recv(cl, buf, min(len, sizeof(buf))));
795
796            if ((rlen < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
797                break;
798
799            D("ubus: feed %d/%d TCP socket bytes\n",
800              rlen, min(len, sizeof(buf)));
801        }
802
803        obj = json_tokener_parse_ex(tok, buf, rlen);
804        len -= rlen;
805
806        if (tok->err != json_tokener_continue && !is_error(obj))
807            break;
808    }
809
810out:
811    if (!is_error(obj))
812    {
813        if (json_object_get_type(obj) == json_type_object)
814        {
815            rv = true;
816            json_object_object_foreach(obj, key, val)
817            {
818                if (!blobmsg_add_json_element(b, key, val))
819                {
820                    rv = false;
821                    break;
822                }
823            }
824        }
825
826        json_object_put(obj);
827    }
828
829    json_tokener_free(tok);
830
831    if (!rv)
832        blob_buf_free(b);
833
834    return rv;
835}
836
837static void
838uh_ubus_request_cb(struct ubus_request *req, int type, struct blob_attr *msg)
839{
840    int len;
841    char *str;
842    struct client *cl = (struct client *)req->priv;
843
844    if (!msg)
845    {
846        uh_http_sendhf(cl, 204, "No content", "Function did not return data\n");
847        return;
848    }
849
850    str = blobmsg_format_json_indent(msg, true, 0);
851    len = strlen(str);
852
853    ensure_out(uh_http_sendf(cl, NULL, "HTTP/1.0 200 OK\r\n"));
854    ensure_out(uh_http_sendf(cl, NULL, "Content-Type: application/json\r\n"));
855    ensure_out(uh_http_sendf(cl, NULL, "Content-Length: %i\r\n\r\n", len));
856    ensure_out(uh_http_send(cl, NULL, str, len));
857
858out:
859    free(str);
860}
861
862bool
863uh_ubus_request(struct client *cl, struct uh_ubus_state *state)
864{
865    int i, len = 0;
866    bool access = false;
867    char *sid, *obj, *fun;
868
869    struct blob_buf buf;
870    struct uh_ubus_session *ses;
871    struct uh_ubus_session_acl *acl;
872
873    uint32_t obj_id;
874
875
876    memset(&buf, 0, sizeof(buf));
877    blob_buf_init(&buf, 0);
878
879    if (!uh_ubus_request_parse_url(cl, &sid, &obj, &fun))
880    {
881        uh_http_sendhf(cl, 400, "Bad Request", "Invalid Request\n");
882        goto out;
883    }
884
885    if (!(ses = uh_ubus_session_get(state, sid)))
886    {
887        uh_http_sendhf(cl, 404, "Not Found", "No such session\n");
888        goto out;
889    }
890
891    avl_for_each_element(&ses->acls, acl, avl)
892    {
893        if (uh_ubus_strmatch(obj, acl->object) &&
894            uh_ubus_strmatch(fun, acl->function))
895        {
896            access = true;
897            break;
898        }
899    }
900
901    if (!access)
902    {
903        uh_http_sendhf(cl, 403, "Denied", "Access to object denied\n");
904        goto out;
905    }
906
907    /* find content length */
908    if (cl->request.method == UH_HTTP_MSG_POST)
909    {
910        foreach_header(i, cl->request.headers)
911        {
912            if (!strcasecmp(cl->request.headers[i], "Content-Length"))
913            {
914                len = atoi(cl->request.headers[i+1]);
915                break;
916            }
917        }
918    }
919
920    if (len > UH_UBUS_MAX_POST_SIZE)
921    {
922        uh_http_sendhf(cl, 413, "Too Large", "Message too big\n");
923        goto out;
924    }
925
926    if (len && !uh_ubus_request_parse_post(cl, len, &buf))
927    {
928        uh_http_sendhf(cl, 400, "Bad Request", "Invalid JSON data\n");
929        goto out;
930    }
931
932    if (ubus_lookup_id(state->ctx, obj, &obj_id))
933    {
934        uh_http_sendhf(cl, 500, "Internal Error", "Unable to lookup object\n");
935        goto out;
936    }
937
938    if (ubus_invoke(state->ctx, obj_id, fun, buf.head,
939                    uh_ubus_request_cb, cl, state->timeout * 1000))
940    {
941        uh_http_sendhf(cl, 500, "Internal Error", "Unable to invoke function\n");
942        goto out;
943    }
944
945out:
946    blob_buf_free(&buf);
947    return false;
948}
949
950void
951uh_ubus_close(struct uh_ubus_state *state)
952{
953    if (state->ctx)
954        ubus_free(state->ctx);
955
956    free(state);
957}
958

Archive Download this file



interactive