luci2: implement password_set call
[project/rpcd.git] / luci2.c
1 /*
2  * luci-rpcd - LuCI UBUS RPC server
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include <fcntl.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <sys/wait.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <arpa/inet.h>
30 #include <signal.h>
31
32 #include "luci2.h"
33
34 static struct blob_buf buf;
35 static struct uci_context *cursor;
36
37 enum {
38         RPC_S_PID,
39         RPC_S_SIGNAL,
40         __RPC_S_MAX,
41 };
42
43 static const struct blobmsg_policy rpc_signal_policy[__RPC_S_MAX] = {
44         [RPC_S_PID]    = { .name = "pid",    .type = BLOBMSG_TYPE_INT32 },
45         [RPC_S_SIGNAL] = { .name = "signal", .type = BLOBMSG_TYPE_INT32 },
46 };
47
48 enum {
49         RPC_I_NAME,
50         RPC_I_ACTION,
51         __RPC_I_MAX,
52 };
53
54 static const struct blobmsg_policy rpc_init_policy[__RPC_I_MAX] = {
55         [RPC_I_NAME]   = { .name = "name",   .type = BLOBMSG_TYPE_STRING },
56         [RPC_I_ACTION] = { .name = "action", .type = BLOBMSG_TYPE_STRING },
57 };
58
59 enum {
60         RPC_K_KEYS,
61         __RPC_K_MAX
62 };
63
64 static const struct blobmsg_policy rpc_sshkey_policy[__RPC_K_MAX] = {
65         [RPC_K_KEYS]   = { .name = "keys",   .type = BLOBMSG_TYPE_ARRAY },
66 };
67
68 enum {
69         RPC_P_USER,
70         RPC_P_PASSWORD,
71         __RPC_P_MAX
72 };
73
74 static const struct blobmsg_policy rpc_password_policy[__RPC_P_MAX] = {
75         [RPC_P_USER]     = { .name = "user",     .type = BLOBMSG_TYPE_STRING },
76         [RPC_P_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING },
77 };
78
79
80 static int
81 rpc_errno_status(void)
82 {
83         switch (errno)
84         {
85         case EACCES:
86                 return UBUS_STATUS_PERMISSION_DENIED;
87
88         case ENOTDIR:
89                 return UBUS_STATUS_INVALID_ARGUMENT;
90
91         case ENOENT:
92                 return UBUS_STATUS_NOT_FOUND;
93
94         case EINVAL:
95                 return UBUS_STATUS_INVALID_ARGUMENT;
96
97         default:
98                 return UBUS_STATUS_UNKNOWN_ERROR;
99         }
100 }
101
102 static void
103 log_read(FILE *log, int logsize)
104 {
105         int len;
106         char *logbuf;
107
108         if (logsize == 0)
109                 logsize = RPC_LUCI2_DEF_LOGSIZE;
110
111         len = (logsize > RPC_LUCI2_MAX_LOGSIZE) ? RPC_LUCI2_MAX_LOGSIZE : logsize;
112         logbuf = blobmsg_alloc_string_buffer(&buf, "log", len + 1);
113
114         if (!logbuf)
115                 return;
116
117         while (logsize > RPC_LUCI2_MAX_LOGSIZE)
118         {
119                 len = logsize % RPC_LUCI2_MAX_LOGSIZE;
120
121                 if (len == 0)
122                         len = RPC_LUCI2_MAX_LOGSIZE;
123
124                 fread(logbuf, 1, len, log);
125                 logsize -= len;
126         }
127
128         len = fread(logbuf, 1, logsize, log);
129         *(logbuf + len) = 0;
130
131         blobmsg_add_string_buffer(&buf);
132 }
133
134 static int
135 rpc_luci2_system_log(struct ubus_context *ctx, struct ubus_object *obj,
136                      struct ubus_request_data *req, const char *method,
137                      struct blob_attr *msg)
138 {
139         FILE *log;
140         int logsize = 0;
141         const char *logfile = NULL;
142         struct stat st;
143         struct uci_package *p;
144         struct uci_element *e;
145         struct uci_section *s;
146         struct uci_ptr ptr = { .package = "system" };
147
148         uci_load(cursor, ptr.package, &p);
149
150         if (!p)
151                 return UBUS_STATUS_NOT_FOUND;
152
153         uci_foreach_element(&p->sections, e)
154         {
155                 s = uci_to_section(e);
156
157                 if (strcmp(s->type, "system"))
158                         continue;
159
160                 ptr.o = NULL;
161                 ptr.option = "log_type";
162                 ptr.section = e->name;
163                 uci_lookup_ptr(cursor, &ptr, NULL, true);
164                 break;
165         }
166
167         if (ptr.o && ptr.o->type == UCI_TYPE_STRING &&
168             !strcmp(ptr.o->v.string, "file"))
169         {
170                 ptr.o = NULL;
171                 ptr.option = "log_file";
172                 uci_lookup_ptr(cursor, &ptr, NULL, true);
173
174                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
175                         logfile = ptr.o->v.string;
176                 else
177                         logfile = "/var/log/messages";
178
179                 if (stat(logfile, &st) || !(log = fopen(logfile, "r")))
180                         goto fail;
181
182                 logsize = st.st_size;
183         }
184         else
185         {
186                 ptr.o = NULL;
187                 ptr.option = "log_size";
188                 uci_lookup_ptr(cursor, &ptr, NULL, true);
189
190                 if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
191                         logsize = atoi(ptr.o->v.string) * 1024;
192
193                 if (!(log = popen("logread", "r")))
194                         goto fail;
195         }
196
197         blob_buf_init(&buf, 0);
198
199         log_read(log, logsize);
200         fclose(log);
201
202         uci_unload(cursor, p);
203         ubus_send_reply(ctx, req, buf.head);
204         return 0;
205
206 fail:
207         uci_unload(cursor, p);
208         return rpc_errno_status();
209 }
210
211 static int
212 rpc_luci2_system_dmesg(struct ubus_context *ctx, struct ubus_object *obj,
213                        struct ubus_request_data *req, const char *method,
214                        struct blob_attr *msg)
215 {
216         FILE *log;
217
218         if (!(log = popen("dmesg", "r")))
219                 return rpc_errno_status();
220
221         blob_buf_init(&buf, 0);
222
223         log_read(log, RPC_LUCI2_MAX_LOGSIZE);
224         fclose(log);
225
226         ubus_send_reply(ctx, req, buf.head);
227         return 0;
228 }
229
230 static int
231 rpc_luci2_process_list(struct ubus_context *ctx, struct ubus_object *obj,
232                        struct ubus_request_data *req, const char *method,
233                        struct blob_attr *msg)
234 {
235         FILE *top;
236         void *c, *d;
237         char line[1024];
238         char *pid, *ppid, *user, *stat, *vsz, *pvsz, *pcpu, *cmd;
239
240         if (!(top = popen("/bin/busybox top -bn1", "r")))
241                 return rpc_errno_status();
242
243         blob_buf_init(&buf, 0);
244         c = blobmsg_open_array(&buf, "processes");
245
246         while (fgets(line, sizeof(line) - 1, top))
247         {
248                 pid  = strtok(line, " ");
249
250                 if (*pid < '0' || *pid > '9')
251                         continue;
252
253                 ppid = strtok(NULL, " ");
254                 user = strtok(NULL, " ");
255                 stat = strtok(NULL, " ");
256
257                 if (!stat)
258                         continue;
259
260                 if (!*(stat + 1))
261                         *(stat + 1) = ' ';
262
263                 if (!*(stat + 2))
264                         *(stat + 2) = ' ';
265
266                 *(stat + 3) = 0;
267
268                 vsz  = strtok(stat + 4, " ");
269                 pvsz = strtok(NULL, " ");
270                 pcpu = strtok(NULL, " ");
271                 cmd  = strtok(NULL, "\n");
272
273                 if (!cmd)
274                         continue;
275
276                 d = blobmsg_open_table(&buf, NULL);
277
278                 blobmsg_add_u32(&buf, "pid", atoi(pid));
279                 blobmsg_add_u32(&buf, "ppid", atoi(ppid));
280                 blobmsg_add_string(&buf, "user", user);
281                 blobmsg_add_string(&buf, "stat", stat);
282                 blobmsg_add_u32(&buf, "vsize", atoi(vsz) * 1024);
283                 blobmsg_add_u32(&buf, "vsize_percent", atoi(pvsz));
284                 blobmsg_add_u32(&buf, "cpu_percent", atoi(pcpu));
285                 blobmsg_add_string(&buf, "command", cmd);
286
287                 blobmsg_close_table(&buf, d);
288         }
289
290         fclose(top);
291         blobmsg_close_array(&buf, c);
292
293         ubus_send_reply(ctx, req, buf.head);
294         return 0;
295 }
296
297 static int
298 rpc_luci2_process_signal(struct ubus_context *ctx, struct ubus_object *obj,
299                          struct ubus_request_data *req, const char *method,
300                          struct blob_attr *msg)
301 {
302         int pid, sig;
303         struct blob_attr *tb[__RPC_S_MAX];
304
305         blobmsg_parse(rpc_signal_policy, __RPC_S_MAX, tb,
306                       blob_data(msg), blob_len(msg));
307
308         if (!tb[RPC_S_SIGNAL] || !tb[RPC_S_PID])
309         {
310                 errno = EINVAL;
311                 return rpc_errno_status();
312         }
313
314         pid = blobmsg_get_u32(tb[RPC_S_PID]);
315         sig = blobmsg_get_u32(tb[RPC_S_SIGNAL]);
316
317         if (kill(pid, sig))
318                 return rpc_errno_status();
319
320         return 0;
321 }
322
323 static int
324 rpc_luci2_init_list(struct ubus_context *ctx, struct ubus_object *obj,
325                     struct ubus_request_data *req, const char *method,
326                     struct blob_attr *msg)
327 {
328         int n;
329         void *c, *t;
330         char *p, path[PATH_MAX];
331         struct stat s;
332         struct dirent *e;
333         FILE *f;
334         DIR *d;
335
336         if (!(d = opendir("/etc/init.d")))
337                 return rpc_errno_status();
338
339         blob_buf_init(&buf, 0);
340         c = blobmsg_open_array(&buf, "initscripts");
341
342         while ((e = readdir(d)) != NULL)
343         {
344                 snprintf(path, sizeof(path) - 1, "/etc/init.d/%s", e->d_name);
345
346                 if (stat(path, &s) || !S_ISREG(s.st_mode) || !(s.st_mode & S_IXUSR))
347                         continue;
348
349                 if ((f = fopen(path, "r")) != NULL)
350                 {
351                         n = -1;
352                         p = fgets(path, sizeof(path) - 1, f);
353
354                         if (!p || !strstr(p, "/etc/rc.common"))
355                                 goto skip;
356
357                         t = blobmsg_open_table(&buf, NULL);
358
359                         blobmsg_add_string(&buf, "name", e->d_name);
360
361                         while (fgets(path, sizeof(path) - 1, f))
362                         {
363                                 p = strtok(path, "= \t");
364
365                                 if (!strcmp(p, "START") && !!(p = strtok(NULL, "= \t\n")))
366                                 {
367                                         n = atoi(p);
368                                         blobmsg_add_u32(&buf, "start", n);
369                                 }
370                                 else if (!strcmp(p, "STOP") && !!(p = strtok(NULL, "= \t\n")))
371                                 {
372                                         blobmsg_add_u32(&buf, "stop", atoi(p));
373                                         break;
374                                 }
375                         }
376
377                         if (n > -1)
378                         {
379                                 snprintf(path, sizeof(path) - 1, "/etc/rc.d/S%02d%s",
380                                          n, e->d_name);
381
382                                 blobmsg_add_u8(&buf, "enabled",
383                                                (!stat(path, &s) && (s.st_mode & S_IXUSR)));
384                         }
385                         else
386                         {
387                                 blobmsg_add_u8(&buf, "enabled", 0);
388                         }
389
390                         blobmsg_close_table(&buf, t);
391
392 skip:
393                         fclose(f);
394                 }
395         }
396
397         closedir(d);
398         blobmsg_close_array(&buf, c);
399
400         ubus_send_reply(ctx, req, buf.head);
401         return 0;
402 }
403
404 static int
405 rpc_luci2_init_action(struct ubus_context *ctx, struct ubus_object *obj,
406                       struct ubus_request_data *req, const char *method,
407                       struct blob_attr *msg)
408 {
409         int fd;
410         pid_t pid;
411         struct stat s;
412         char path[PATH_MAX];
413         const char *action;
414         struct blob_attr *tb[__RPC_I_MAX];
415
416         blobmsg_parse(rpc_init_policy, __RPC_I_MAX, tb,
417                       blob_data(msg), blob_len(msg));
418
419         if (!tb[RPC_I_NAME] || !tb[RPC_I_ACTION])
420                 return UBUS_STATUS_INVALID_ARGUMENT;
421
422         action = blobmsg_data(tb[RPC_I_ACTION]);
423
424         if (strcmp(action, "start") && strcmp(action, "stop") &&
425             strcmp(action, "reload") && strcmp(action, "restart") &&
426             strcmp(action, "enable") && strcmp(action, "disable"))
427                 return UBUS_STATUS_INVALID_ARGUMENT;
428
429         snprintf(path, sizeof(path) - 1, "/etc/init.d/%s",
430                  (char *)blobmsg_data(tb[RPC_I_NAME]));
431
432         if (stat(path, &s))
433                 return rpc_errno_status();
434
435         if (!(s.st_mode & S_IXUSR))
436                 return UBUS_STATUS_PERMISSION_DENIED;
437
438         switch ((pid = fork()))
439         {
440         case -1:
441                 return rpc_errno_status();
442
443         case 0:
444                 uloop_done();
445
446                 if ((fd = open("/dev/null", O_RDWR)) > -1)
447                 {
448                         dup2(fd, 0);
449                         dup2(fd, 1);
450                         dup2(fd, 2);
451
452                         close(fd);
453                 }
454
455                 chdir("/");
456
457                 if (execl(path, path, action, NULL))
458                         return rpc_errno_status();
459
460         default:
461                 return 0;
462         }
463 }
464
465 static int
466 rpc_luci2_sshkeys_get(struct ubus_context *ctx, struct ubus_object *obj,
467                       struct ubus_request_data *req, const char *method,
468                       struct blob_attr *msg)
469 {
470         FILE *f;
471         void *c;
472         char *p, line[4096];
473
474         if (!(f = fopen("/etc/dropbear/authorized_keys", "r")))
475                 return rpc_errno_status();
476
477         blob_buf_init(&buf, 0);
478         c = blobmsg_open_array(&buf, "keys");
479
480         while (fgets(line, sizeof(line) - 1, f))
481         {
482                 for (p = line + strlen(line) - 1; (p > line) && isspace(*p); p--)
483                         *p = 0;
484
485                 for (p = line; isspace(*p); p++)
486                         *p = 0;
487
488                 if (*p)
489                         blobmsg_add_string(&buf, NULL, p);
490         }
491
492         blobmsg_close_array(&buf, c);
493         fclose(f);
494
495         ubus_send_reply(ctx, req, buf.head);
496         return 0;
497 }
498
499 static int
500 rpc_luci2_sshkeys_set(struct ubus_context *ctx, struct ubus_object *obj,
501                       struct ubus_request_data *req, const char *method,
502                       struct blob_attr *msg)
503 {
504         FILE *f;
505         int rem;
506         struct blob_attr *cur, *tb[__RPC_K_MAX];
507
508         blobmsg_parse(rpc_sshkey_policy, __RPC_K_MAX, tb,
509                       blob_data(msg), blob_len(msg));
510
511         if (!tb[RPC_K_KEYS])
512                 return UBUS_STATUS_INVALID_ARGUMENT;
513
514         if (!(f = fopen("/etc/dropbear/authorized_keys", "w")))
515                 return rpc_errno_status();
516
517         blobmsg_for_each_attr(cur, tb[RPC_K_KEYS], rem)
518         {
519                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
520                         continue;
521
522                 fwrite(blobmsg_data(cur), blobmsg_data_len(cur) - 1, 1, f);
523                 fwrite("\n", 1, 1, f);
524         }
525
526         fclose(f);
527         return 0;
528 }
529
530 static int
531 rpc_luci2_password_set(struct ubus_context *ctx, struct ubus_object *obj,
532                        struct ubus_request_data *req, const char *method,
533                        struct blob_attr *msg)
534 {
535         pid_t pid;
536         int fd, fds[2];
537         struct stat s;
538         struct blob_attr *tb[__RPC_P_MAX];
539
540         blobmsg_parse(rpc_password_policy, __RPC_P_MAX, tb,
541                       blob_data(msg), blob_len(msg));
542
543         if (!tb[RPC_P_USER] || !tb[RPC_P_PASSWORD])
544                 return UBUS_STATUS_INVALID_ARGUMENT;
545
546         if (stat("/usr/bin/passwd", &s))
547                 return UBUS_STATUS_NOT_FOUND;
548
549         if (!(s.st_mode & S_IXUSR))
550                 return UBUS_STATUS_PERMISSION_DENIED;
551
552         if (pipe(fds))
553                 return rpc_errno_status();
554
555         switch ((pid = fork()))
556         {
557         case -1:
558                 close(fds[0]);
559                 close(fds[1]);
560                 return rpc_errno_status();
561
562         case 0:
563                 uloop_done();
564
565                 dup2(fds[0], 0);
566                 close(fds[0]);
567                 close(fds[1]);
568
569                 if ((fd = open("/dev/null", O_RDWR)) > -1)
570                 {
571                         dup2(fd, 1);
572                         dup2(fd, 2);
573                         close(fd);
574                 }
575
576                 chdir("/");
577
578                 if (execl("/usr/bin/passwd", "/usr/bin/passwd",
579                           blobmsg_data(tb[RPC_P_USER]), NULL))
580                         return rpc_errno_status();
581
582         default:
583                 close(fds[0]);
584
585                 write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]),
586                               blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1);
587                 write(fds[1], "\n", 1);
588
589                 usleep(100 * 1000);
590
591                 write(fds[1], blobmsg_data(tb[RPC_P_PASSWORD]),
592                               blobmsg_data_len(tb[RPC_P_PASSWORD]) - 1);
593                 write(fds[1], "\n", 1);
594
595                 close(fds[1]);
596
597                 waitpid(pid, NULL, 0);
598
599                 return 0;
600         }
601 }
602
603
604 static FILE *
605 dnsmasq_leasefile(void)
606 {
607         FILE *leases = NULL;
608         struct uci_package *p;
609         struct uci_element *e;
610         struct uci_section *s;
611         struct uci_ptr ptr = {
612                 .package = "dhcp",
613                 .section = NULL,
614                 .option  = "leasefile"
615         };
616
617         uci_load(cursor, ptr.package, &p);
618
619         if (!p)
620                 return NULL;
621
622         uci_foreach_element(&p->sections, e)
623         {
624                 s = uci_to_section(e);
625
626                 if (strcmp(s->type, "dnsmasq"))
627                         continue;
628
629                 ptr.section = e->name;
630                 uci_lookup_ptr(cursor, &ptr, NULL, true);
631                 break;
632         }
633
634         if (ptr.o && ptr.o->type == UCI_TYPE_STRING)
635                 leases = fopen(ptr.o->v.string, "r");
636
637         uci_unload(cursor, p);
638
639         return leases;
640 }
641
642 static int
643 rpc_luci2_network_leases(struct ubus_context *ctx, struct ubus_object *obj,
644                          struct ubus_request_data *req, const char *method,
645                          struct blob_attr *msg)
646 {
647         FILE *leases;
648         void *c, *d;
649         char line[128];
650         char *ts, *mac, *addr, *name;
651         time_t now = time(NULL);
652
653         blob_buf_init(&buf, 0);
654         c = blobmsg_open_array(&buf, "leases");
655
656         leases = dnsmasq_leasefile();
657
658         if (!leases)
659                 goto out;
660
661         while (fgets(line, sizeof(line) - 1, leases))
662         {
663                 ts   = strtok(line, " \t");
664                 mac  = strtok(NULL, " \t");
665                 addr = strtok(NULL, " \t");
666                 name = strtok(NULL, " \t");
667
668                 if (!ts || !mac || !addr || !name)
669                         continue;
670
671                 if (strchr(addr, ':'))
672                         continue;
673
674                 d = blobmsg_open_table(&buf, NULL);
675
676                 blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
677                 blobmsg_add_string(&buf, "macaddr", mac);
678                 blobmsg_add_string(&buf, "ipaddr", addr);
679
680                 if (strcmp(name, "*"))
681                         blobmsg_add_string(&buf, "hostname", name);
682
683                 blobmsg_close_table(&buf, d);
684         }
685
686         fclose(leases);
687
688 out:
689         blobmsg_close_array(&buf, c);
690         ubus_send_reply(ctx, req, buf.head);
691
692         return 0;
693 }
694
695 static int
696 rpc_luci2_network_leases6(struct ubus_context *ctx, struct ubus_object *obj,
697                           struct ubus_request_data *req, const char *method,
698                           struct blob_attr *msg)
699 {
700         FILE *leases;
701         void *c, *d;
702         char line[128];
703         char *ts, *mac, *addr, *name, *duid;
704         time_t now = time(NULL);
705
706         blob_buf_init(&buf, 0);
707         c = blobmsg_open_array(&buf, "leases");
708
709         leases = fopen("/tmp/hosts/6relayd", "r");
710
711         if (leases)
712         {
713                 while (fgets(line, sizeof(line) - 1, leases))
714                 {
715                         if (strncmp(line, "# ", 2))
716                                 continue;
717
718                         strtok(line + 2, " \t"); /* iface */
719
720                         duid = strtok(NULL, " \t");
721
722                         strtok(NULL, " \t"); /* iaid */
723
724                         name = strtok(NULL, " \t");
725                         ts   = strtok(NULL, " \t");
726
727                         strtok(NULL, " \t"); /* id */
728                         strtok(NULL, " \t"); /* length */
729
730                         addr = strtok(NULL, " \t\n");
731
732                         if (!addr)
733                                 continue;
734
735                         d = blobmsg_open_table(&buf, NULL);
736
737                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
738                         blobmsg_add_string(&buf, "duid", duid);
739                         blobmsg_add_string(&buf, "ip6addr", addr);
740
741                         if (strcmp(name, "-"))
742                                 blobmsg_add_string(&buf, "hostname", name);
743
744                         blobmsg_close_array(&buf, d);
745                 }
746
747                 fclose(leases);
748         }
749         else
750         {
751                 leases = dnsmasq_leasefile();
752
753                 if (!leases)
754                         goto out;
755
756                 while (fgets(line, sizeof(line) - 1, leases))
757                 {
758                         ts   = strtok(line, " \t");
759                         mac  = strtok(NULL, " \t");
760                         addr = strtok(NULL, " \t");
761                         name = strtok(NULL, " \t");
762                         duid = strtok(NULL, " \t\n");
763
764                         if (!ts || !mac || !addr || !duid)
765                                 continue;
766
767                         if (!strchr(addr, ':'))
768                                 continue;
769
770                         d = blobmsg_open_table(&buf, NULL);
771
772                         blobmsg_add_u32(&buf, "expires", atoi(ts) - now);
773                         blobmsg_add_string(&buf, "macaddr", mac);
774                         blobmsg_add_string(&buf, "ip6addr", addr);
775
776                         if (strcmp(name, "*"))
777                                 blobmsg_add_string(&buf, "hostname", name);
778
779                         if (strcmp(duid, "*"))
780                                 blobmsg_add_string(&buf, "duid", name);
781
782                         blobmsg_close_table(&buf, d);
783                 }
784
785                 fclose(leases);
786         }
787
788 out:
789         blobmsg_close_array(&buf, c);
790         ubus_send_reply(ctx, req, buf.head);
791
792         return 0;
793 }
794
795 static int
796 rpc_luci2_network_ct_count(struct ubus_context *ctx, struct ubus_object *obj,
797                            struct ubus_request_data *req, const char *method,
798                            struct blob_attr *msg)
799 {
800         FILE *f;
801         char line[128];
802
803         blob_buf_init(&buf, 0);
804
805         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_count", "r")) != NULL)
806         {
807                 if (fgets(line, sizeof(line) - 1, f))
808                         blobmsg_add_u32(&buf, "count", atoi(line));
809
810                 fclose(f);
811         }
812
813         if ((f = fopen("/proc/sys/net/netfilter/nf_conntrack_max", "r")) != NULL)
814         {
815                 if (fgets(line, sizeof(line) - 1, f))
816                         blobmsg_add_u32(&buf, "limit", atoi(line));
817
818                 fclose(f);
819         }
820
821         ubus_send_reply(ctx, req, buf.head);
822
823         return 0;
824 }
825
826 static int
827 rpc_luci2_network_ct_table(struct ubus_context *ctx, struct ubus_object *obj,
828                            struct ubus_request_data *req, const char *method,
829                            struct blob_attr *msg)
830 {
831         FILE *f;
832         int i;
833         void *c, *d;
834         char *p, line[512];
835         bool seen[6];
836
837         blob_buf_init(&buf, 0);
838         c = blobmsg_open_array(&buf, "entries");
839
840         if ((f = fopen("/proc/net/nf_conntrack", "r")) != NULL)
841         {
842                 while (fgets(line, sizeof(line) - 1, f))
843                 {
844                         d = blobmsg_open_table(&buf, NULL);
845                         memset(seen, 0, sizeof(seen));
846
847                         for (i = 0, p = strtok(line, " "); p; i++, p = strtok(NULL, " "))
848                         {
849                                 if (i == 0)
850                                         blobmsg_add_u8(&buf, "ipv6", !strcmp(p, "ipv6"));
851                                 else if (i == 3)
852                                         blobmsg_add_u32(&buf, "protocol", atoi(p));
853                                 else if (i == 4)
854                                         blobmsg_add_u32(&buf, "expires", atoi(p));
855                                 else if (i >= 5)
856                                 {
857                                         if (*p == '[')
858                                                 continue;
859
860                                         if (!seen[0] && !strncmp(p, "src=", 4))
861                                         {
862                                                 blobmsg_add_string(&buf, "src", p + 4);
863                                                 seen[0] = true;
864                                         }
865                                         else if (!seen[1] && !strncmp(p, "dst=", 4))
866                                         {
867                                                 blobmsg_add_string(&buf, "dest", p + 4);
868                                                 seen[1] = true;
869                                         }
870                                         else if (!seen[2] && !strncmp(p, "sport=", 6))
871                                         {
872                                                 blobmsg_add_u32(&buf, "sport", atoi(p + 6));
873                                                 seen[2] = true;
874                                         }
875                                         else if (!seen[3] && !strncmp(p, "dport=", 6))
876                                         {
877                                                 blobmsg_add_u32(&buf, "dport", atoi(p + 6));
878                                                 seen[3] = true;
879                                         }
880                                         else if (!strncmp(p, "packets=", 8))
881                                         {
882                                                 blobmsg_add_u32(&buf,
883                                                                 seen[4] ? "tx_packets" : "rx_packets",
884                                                                 atoi(p + 8));
885                                                 seen[4] = true;
886                                         }
887                                         else if (!strncmp(p, "bytes=", 6))
888                                         {
889                                                 blobmsg_add_u32(&buf,
890                                                                                 seen[5] ? "tx_bytes" : "rx_bytes",
891                                                                 atoi(p + 6));
892                                                 seen[5] = true;
893                                         }
894                                 }
895                         }
896
897                         blobmsg_close_table(&buf, d);
898                 }
899
900                 fclose(f);
901         }
902
903         blobmsg_close_array(&buf, c);
904         ubus_send_reply(ctx, req, buf.head);
905
906         return 0;
907 }
908
909 static int
910 rpc_luci2_network_arp_table(struct ubus_context *ctx, struct ubus_object *obj,
911                             struct ubus_request_data *req, const char *method,
912                             struct blob_attr *msg)
913 {
914         FILE *f;
915         void *c, *d;
916         char *addr, *mac, *dev, line[128];
917
918         blob_buf_init(&buf, 0);
919         c = blobmsg_open_array(&buf, "entries");
920
921         if ((f = fopen("/proc/net/arp", "r")) != NULL)
922         {
923                 /* skip header line */
924                 fgets(line, sizeof(line) - 1, f);
925
926                 while (fgets(line, sizeof(line) - 1, f))
927                 {
928                         addr = strtok(line, " \t");
929
930                         strtok(NULL, " \t"); /* HW type */
931                         strtok(NULL, " \t"); /* Flags */
932
933                         mac = strtok(NULL, " \t");
934
935                         strtok(NULL, " \t"); /* Mask */
936
937                         dev = strtok(NULL, " \t\n");
938
939                         if (!dev)
940                                 continue;
941
942                         d = blobmsg_open_table(&buf, NULL);
943                         blobmsg_add_string(&buf, "ipaddr", addr);
944                         blobmsg_add_string(&buf, "macaddr", mac);
945                         blobmsg_add_string(&buf, "device", dev);
946                         blobmsg_close_table(&buf, d);
947                 }
948
949                 fclose(f);
950         }
951
952         blobmsg_close_array(&buf, c);
953         ubus_send_reply(ctx, req, buf.head);
954
955         return 0;
956 }
957
958 static void
959 put_hexaddr(const char *name, const char *s, const char *m)
960 {
961         int bits;
962         struct in_addr a;
963         char as[sizeof("255.255.255.255/32\0")];
964
965         a.s_addr = strtoul(s, NULL, 16);
966         inet_ntop(AF_INET, &a, as, sizeof(as));
967
968         if (m)
969         {
970                 for (a.s_addr = ntohl(strtoul(m, NULL, 16)), bits = 0;
971                      a.s_addr & 0x80000000;
972                      a.s_addr <<= 1)
973                         bits++;
974
975                 sprintf(as + strlen(as), "/%u", bits);
976         }
977
978         blobmsg_add_string(&buf, name, as);
979 }
980
981 static int
982 rpc_luci2_network_routes(struct ubus_context *ctx, struct ubus_object *obj,
983                          struct ubus_request_data *req, const char *method,
984                          struct blob_attr *msg)
985 {
986         FILE *routes;
987         void *c, *d;
988         char *dst, *dmask, *next, *metric, *device;
989         char line[256];
990         unsigned int n;
991
992         if (!(routes = fopen("/proc/net/route", "r")))
993                 return rpc_errno_status();
994
995         blob_buf_init(&buf, 0);
996         c = blobmsg_open_array(&buf, "routes");
997
998         /* skip header line */
999         fgets(line, sizeof(line) - 1, routes);
1000
1001         while (fgets(line, sizeof(line) - 1, routes))
1002         {
1003                 device = strtok(line, "\t ");
1004                 dst    = strtok(NULL, "\t ");
1005                 next   = strtok(NULL, "\t ");
1006
1007                 strtok(NULL, "\t "); /* flags */
1008                 strtok(NULL, "\t "); /* refcount */
1009                 strtok(NULL, "\t "); /* usecount */
1010
1011                 metric = strtok(NULL, "\t ");
1012                 dmask  = strtok(NULL, "\t ");
1013
1014                 if (!dmask)
1015                         continue;
1016
1017                 d = blobmsg_open_table(&buf, NULL);
1018
1019                 put_hexaddr("target", dst, dmask);
1020                 put_hexaddr("nexthop", next, NULL);
1021
1022                 n = strtoul(metric, NULL, 10);
1023                 blobmsg_add_u32(&buf, "metric", n);
1024
1025                 blobmsg_add_string(&buf, "device", device);
1026
1027                 blobmsg_close_table(&buf, d);
1028         }
1029
1030         blobmsg_close_array(&buf, c);
1031         fclose(routes);
1032
1033         ubus_send_reply(ctx, req, buf.head);
1034         return 0;
1035 }
1036
1037 static void
1038 put_hex6addr(const char *name, const char *s, const char *m)
1039 {
1040         int i;
1041         struct in6_addr a;
1042         char as[INET6_ADDRSTRLEN + sizeof("/128")];
1043
1044 #define hex(x) \
1045         (((x) <= '9') ? ((x) - '0') : \
1046                 (((x) <= 'F') ? ((x) - 'A' + 10) : \
1047                         ((x) - 'a' + 10)))
1048
1049         for (i = 0; i < 16; i++, s += 2)
1050                 a.s6_addr[i] = (16 * hex(*s)) + hex(*(s+1));
1051
1052         inet_ntop(AF_INET6, &a, as, sizeof(as));
1053
1054         if (m)
1055                 sprintf(as + strlen(as), "/%lu", strtoul(m, NULL, 16));
1056
1057         blobmsg_add_string(&buf, name, as);
1058 }
1059
1060 static int
1061 rpc_luci2_network_routes6(struct ubus_context *ctx, struct ubus_object *obj,
1062                           struct ubus_request_data *req, const char *method,
1063                           struct blob_attr *msg)
1064 {
1065         FILE *routes;
1066         void *c, *d;
1067         char *src, *smask, *dst, *dmask, *next, *metric, *flags, *device;
1068         char line[256];
1069         unsigned int n;
1070
1071         if (!(routes = fopen("/proc/net/ipv6_route", "r")))
1072                 return rpc_errno_status();
1073
1074         blob_buf_init(&buf, 0);
1075         c = blobmsg_open_array(&buf, "routes");
1076
1077         while (fgets(line, sizeof(line) - 1, routes))
1078         {
1079                 dst    = strtok(line, " ");
1080                 dmask  = strtok(NULL, " ");
1081                 src    = strtok(NULL, " ");
1082                 smask  = strtok(NULL, " ");
1083                 next   = strtok(NULL, " ");
1084                 metric = strtok(NULL, " ");
1085
1086                 strtok(NULL, " "); /* refcount */
1087                 strtok(NULL, " "); /* usecount */
1088
1089                 flags  = strtok(NULL, " ");
1090                 device = strtok(NULL, " \n");
1091
1092                 if (!device)
1093                         continue;
1094
1095                 n = strtoul(flags, NULL, 16);
1096
1097                 if (!(n & 1))
1098                         continue;
1099
1100                 d = blobmsg_open_table(&buf, NULL);
1101
1102                 put_hex6addr("target", dst, dmask);
1103                 put_hex6addr("source", src, smask);
1104                 put_hex6addr("nexthop", next, NULL);
1105
1106                 n = strtoul(metric, NULL, 16);
1107                 blobmsg_add_u32(&buf, "metric", n);
1108
1109                 blobmsg_add_string(&buf, "device", device);
1110
1111                 blobmsg_close_table(&buf, d);
1112         }
1113
1114         blobmsg_close_array(&buf, c);
1115         fclose(routes);
1116
1117         ubus_send_reply(ctx, req, buf.head);
1118         return 0;
1119 }
1120
1121
1122 int rpc_luci2_api_init(struct ubus_context *ctx)
1123 {
1124         int rv = 0;
1125
1126         static const struct ubus_method luci2_system_methods[] = {
1127                 UBUS_METHOD_NOARG("syslog",       rpc_luci2_system_log),
1128                 UBUS_METHOD_NOARG("dmesg",        rpc_luci2_system_dmesg),
1129                 UBUS_METHOD_NOARG("process_list", rpc_luci2_process_list),
1130                 UBUS_METHOD("process_signal",     rpc_luci2_process_signal,
1131                                                   rpc_signal_policy),
1132                 UBUS_METHOD_NOARG("init_list",    rpc_luci2_init_list),
1133                 UBUS_METHOD("init_action",        rpc_luci2_init_action,
1134                                                   rpc_init_policy),
1135                 UBUS_METHOD_NOARG("sshkeys_get",  rpc_luci2_sshkeys_get),
1136                 UBUS_METHOD("sshkeys_set",        rpc_luci2_sshkeys_set,
1137                                                   rpc_sshkey_policy),
1138                 UBUS_METHOD("password_set",       rpc_luci2_password_set,
1139                                                   rpc_password_policy)
1140         };
1141
1142         static struct ubus_object_type luci2_system_type =
1143                 UBUS_OBJECT_TYPE("luci-rpc-luci2-system", luci2_system_methods);
1144
1145         static struct ubus_object system_obj = {
1146                 .name = "luci2.system",
1147                 .type = &luci2_system_type,
1148                 .methods = luci2_system_methods,
1149                 .n_methods = ARRAY_SIZE(luci2_system_methods),
1150         };
1151
1152
1153         static const struct ubus_method luci2_network_methods[] = {
1154                 UBUS_METHOD_NOARG("conntrack_count", rpc_luci2_network_ct_count),
1155                 UBUS_METHOD_NOARG("conntrack_table", rpc_luci2_network_ct_table),
1156                 UBUS_METHOD_NOARG("arp_table",       rpc_luci2_network_arp_table),
1157                 UBUS_METHOD_NOARG("dhcp_leases",     rpc_luci2_network_leases),
1158                 UBUS_METHOD_NOARG("dhcp6_leases",    rpc_luci2_network_leases6),
1159                 UBUS_METHOD_NOARG("routes",          rpc_luci2_network_routes),
1160                 UBUS_METHOD_NOARG("routes6",         rpc_luci2_network_routes6),
1161         };
1162
1163         static struct ubus_object_type luci2_network_type =
1164                 UBUS_OBJECT_TYPE("luci-rpc-luci2-network", luci2_network_methods);
1165
1166         static struct ubus_object network_obj = {
1167                 .name = "luci2.network",
1168                 .type = &luci2_network_type,
1169                 .methods = luci2_network_methods,
1170                 .n_methods = ARRAY_SIZE(luci2_network_methods),
1171         };
1172
1173         cursor = uci_alloc_context();
1174
1175         if (!cursor)
1176                 return UBUS_STATUS_UNKNOWN_ERROR;
1177
1178         rv |= ubus_add_object(ctx, &system_obj);
1179         rv |= ubus_add_object(ctx, &network_obj);
1180
1181         return rv;
1182 }