ubus: assume that the service iface can be NULL
[project/mdnsd.git] / ubus.c
1 /*
2 * Copyright (C) 2014 John Crispin <blogic@openwrt.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14 #include <sys/types.h>
15 #include <arpa/inet.h>
16
17 #include <stdio.h>
18
19 #include <libubus.h>
20 #include <libubox/vlist.h>
21 #include <libubox/uloop.h>
22
23 #include "util.h"
24 #include "ubus.h"
25 #include "cache.h"
26 #include "service.h"
27 #include "interface.h"
28
29 static struct ubus_auto_conn conn;
30 static struct blob_buf b;
31 static struct ubus_subscriber udebug_sub;
32
33 static int
34 umdns_reload(struct ubus_context *ctx, struct ubus_object *obj,
35 struct ubus_request_data *req, const char *method,
36 struct blob_attr *msg)
37 {
38 service_init(1);
39 return 0;
40 }
41
42 static int
43 umdns_update(struct ubus_context *ctx, struct ubus_object *obj,
44 struct ubus_request_data *req, const char *method,
45 struct blob_attr *msg)
46 {
47 cache_update();
48 return 0;
49 }
50
51 enum {
52 BROWSE_SERVICE,
53 BROWSE_ARRAY,
54 BROWSE_ADDRESS,
55 BROWSE_MAX
56 };
57
58 static const struct blobmsg_policy browse_policy[] = {
59 [BROWSE_SERVICE] = { "service", BLOBMSG_TYPE_STRING },
60 [BROWSE_ARRAY] = { "array", BLOBMSG_TYPE_BOOL },
61 [BROWSE_ADDRESS] = { "address", BLOBMSG_TYPE_BOOL },
62 };
63
64 static int
65 umdns_browse(struct ubus_context *ctx, struct ubus_object *obj,
66 struct ubus_request_data *req, const char *method,
67 struct blob_attr *msg)
68 {
69 struct cache_service *s, *q;
70 char *buffer = (char *) mdns_buf;
71 struct blob_attr *data[BROWSE_MAX];
72 void *c1 = NULL, *c2;
73 char *service = NULL;
74 int array = 0;
75 bool address = true;
76
77 blobmsg_parse(browse_policy, BROWSE_MAX, data, blob_data(msg), blob_len(msg));
78 if (data[BROWSE_SERVICE])
79 service = blobmsg_get_string(data[BROWSE_SERVICE]);
80 if (data[BROWSE_ARRAY])
81 array = blobmsg_get_u8(data[BROWSE_ARRAY]);
82 if (data[BROWSE_ADDRESS])
83 address = blobmsg_get_bool(data[BROWSE_ADDRESS]);
84
85 blob_buf_init(&b, 0);
86 avl_for_each_element(&services, s, avl) {
87 const char *hostname = buffer;
88 char *local;
89
90 snprintf(buffer, MAX_NAME_LEN, "%s", (const char *) s->avl.key);
91 local = strstr(buffer, ".local");
92 if (local)
93 *local = '\0';
94 if (!strcmp(buffer, "_tcp") || !strcmp(buffer, "_udp"))
95 continue;
96 if (service && strcmp(buffer, service))
97 continue;
98 if (!c1) {
99 c1 = blobmsg_open_table(&b, buffer);
100 }
101 snprintf(buffer, MAX_NAME_LEN, "%s", s->entry);
102 local = strstr(buffer, "._");
103 if (local)
104 *local = '\0';
105 c2 = blobmsg_open_table(&b, buffer);
106 strncat(buffer, ".local", MAX_NAME_LEN);
107 if (s->iface)
108 blobmsg_add_string(&b, "iface", s->iface->name);
109 cache_dump_records(&b, s->entry, array, &hostname);
110 if (address)
111 cache_dump_records(&b, hostname, array, NULL);
112 blobmsg_close_table(&b, c2);
113 q = avl_next_element(s, avl);
114 if (!q || avl_is_last(&services, &s->avl) || strcmp(s->avl.key, q->avl.key)) {
115 blobmsg_close_table(&b, c1);
116 c1 = NULL;
117 }
118 }
119 ubus_send_reply(ctx, req, b.head);
120
121 return UBUS_STATUS_OK;
122 }
123
124 enum {
125 HOSTS_ARRAY,
126 __HOSTS_MAX
127 };
128 static const struct blobmsg_policy hosts_policy[] = {
129 [HOSTS_ARRAY] = { "array", BLOBMSG_TYPE_BOOL }
130 };
131
132 static int
133 umdns_hosts(struct ubus_context *ctx, struct ubus_object *obj,
134 struct ubus_request_data *req, const char *method,
135 struct blob_attr *msg)
136 {
137 struct cache_record *prev = NULL;
138 struct blob_attr *tb[__HOSTS_MAX];
139 struct cache_record *r;
140 bool array = false;
141 void *c;
142
143 blobmsg_parse(hosts_policy, __HOSTS_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
144 if (tb[HOSTS_ARRAY])
145 array = blobmsg_get_bool(tb[HOSTS_ARRAY]);
146
147 blob_buf_init(&b, 0);
148 avl_for_each_element(&records, r, avl) {
149 if (r->type != TYPE_A && r->type != TYPE_AAAA)
150 continue;
151 /* Query each domain just once */
152 if (!prev || strcmp(r->record, prev->record)) {
153 c = blobmsg_open_table(&b, r->record);
154 cache_dump_records(&b, r->record, array, NULL);
155 blobmsg_close_table(&b, c);
156 }
157 prev = r;
158 }
159 ubus_send_reply(ctx, req, b.head);
160
161 return UBUS_STATUS_OK;
162 }
163
164 enum {
165 CFG_INTERFACES,
166 CFG_KEEP,
167 CFG_MAX
168 };
169
170 static const struct blobmsg_policy config_policy[] = {
171 [CFG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_ARRAY },
172 [CFG_KEEP] = { "keep", BLOBMSG_TYPE_BOOL },
173 };
174
175 static int
176 umdns_set_config(struct ubus_context *ctx, struct ubus_object *obj,
177 struct ubus_request_data *req, const char *method,
178 struct blob_attr *msg)
179 {
180 struct blob_attr *data[CFG_MAX], *cur;
181 int rem, keep = false;
182
183 blobmsg_parse(config_policy, CFG_MAX, data, blob_data(msg), blob_len(msg));
184 if (!data[CFG_INTERFACES])
185 return UBUS_STATUS_INVALID_ARGUMENT;
186
187 if (!blobmsg_check_attr_list(data[CFG_INTERFACES], BLOBMSG_TYPE_STRING))
188 return UBUS_STATUS_INVALID_ARGUMENT;
189
190 keep = data[CFG_KEEP] && blobmsg_get_bool(data[CFG_KEEP]);
191 if (!keep) {
192 vlist_update(&interfaces);
193 ubus_notify(ctx, obj, "set_config", NULL, 1000);
194 }
195
196 blobmsg_for_each_attr(cur, data[CFG_INTERFACES], rem)
197 interface_add(blobmsg_data(cur));
198
199 if (!keep)
200 vlist_flush(&interfaces);
201
202 return 0;
203 }
204
205 enum query_attr {
206 QUERY_QUESTION,
207 QUERY_IFACE,
208 QUERY_TYPE,
209 QUERY_MAX
210 };
211
212 static const struct blobmsg_policy query_policy[QUERY_MAX] = {
213 [QUERY_QUESTION]= { "question", BLOBMSG_TYPE_STRING },
214 [QUERY_IFACE] = { "interface", BLOBMSG_TYPE_STRING },
215 [QUERY_TYPE] = { "type", BLOBMSG_TYPE_INT32 },
216 };
217
218 static int
219 umdns_query(struct ubus_context *ctx, struct ubus_object *obj,
220 struct ubus_request_data *req, const char *method,
221 struct blob_attr *msg)
222 {
223 struct blob_attr *tb[QUERY_MAX], *c;
224 const char *question = C_DNS_SD;
225 const char *ifname;
226 int type = TYPE_ANY;
227
228 blobmsg_parse(query_policy, QUERY_MAX, tb, blob_data(msg), blob_len(msg));
229
230 if (!(c = tb[QUERY_IFACE]))
231 return UBUS_STATUS_INVALID_ARGUMENT;
232
233 ifname = blobmsg_get_string(c);
234
235 if ((c = tb[QUERY_QUESTION]))
236 question = blobmsg_get_string(c);
237
238 if ((c = tb[QUERY_TYPE]))
239 type = blobmsg_get_u32(c);
240
241 struct interface *iface_v4 = interface_get(ifname, SOCK_MC_IPV4);
242 struct interface *iface_v6 = interface_get(ifname, SOCK_MC_IPV6);
243
244 if (!iface_v4 && !iface_v6)
245 return UBUS_STATUS_NOT_FOUND;
246
247 if (!strcmp(method, "query")) {
248 if (iface_v4)
249 dns_send_question(iface_v4, NULL, question, type, 1);
250
251 if (iface_v6)
252 dns_send_question(iface_v6, NULL, question, type, 1);
253
254 return UBUS_STATUS_OK;
255 } else if (!strcmp(method, "fetch")) {
256 blob_buf_init(&b, 0);
257 void *k = blobmsg_open_array(&b, "records");
258 cache_dump_recursive(&b, question, type, iface_v4 ? iface_v4 : iface_v6);
259 blobmsg_close_array(&b, k);
260 ubus_send_reply(ctx, req, b.head);
261 return UBUS_STATUS_OK;
262 } else {
263 return UBUS_STATUS_INVALID_ARGUMENT;
264 }
265 }
266
267
268 static const struct ubus_method umdns_methods[] = {
269 UBUS_METHOD("set_config", umdns_set_config, config_policy),
270 UBUS_METHOD("query", umdns_query, query_policy),
271 UBUS_METHOD("fetch", umdns_query, query_policy),
272 UBUS_METHOD("browse", umdns_browse, browse_policy),
273 UBUS_METHOD_NOARG("update", umdns_update),
274 UBUS_METHOD("hosts", umdns_hosts, hosts_policy),
275 UBUS_METHOD_NOARG("reload", umdns_reload),
276 };
277
278 static struct ubus_object_type umdns_object_type =
279 UBUS_OBJECT_TYPE("umdns", umdns_methods);
280
281 static struct ubus_object umdns_object = {
282 .name = "umdns",
283 .type = &umdns_object_type,
284 .methods = umdns_methods,
285 .n_methods = ARRAY_SIZE(umdns_methods),
286 };
287
288 static struct blob_attr *
289 find_attr(struct blob_attr *attr, const char *name, enum blobmsg_type type)
290 {
291 struct blobmsg_policy policy = { name, type };
292 struct blob_attr *ret;
293
294 if (!attr)
295 return NULL;
296
297 blobmsg_parse_attr(&policy, 1, &ret, attr);
298
299 return ret;
300 }
301
302 static void
303 umdns_udebug_config_cb(struct blob_attr *data)
304 {
305 enum {
306 CFG_ATTR_ENABLED,
307 __CFG_ATTR_MAX
308 };
309 static const struct blobmsg_policy policy[__CFG_ATTR_MAX] = {
310 [CFG_ATTR_ENABLED] = { "enabled", BLOBMSG_TYPE_STRING },
311 };
312 struct blob_attr *tb[__CFG_ATTR_MAX];
313 bool en;
314
315 data = find_attr(data, "service", BLOBMSG_TYPE_TABLE);
316 data = find_attr(data, "umdns", BLOBMSG_TYPE_TABLE);
317 if (!data)
318 return;
319
320 blobmsg_parse_attr(policy, __CFG_ATTR_MAX, tb, data);
321 if (!tb[CFG_ATTR_ENABLED])
322 return;
323
324 en = !!atoi(blobmsg_get_string(tb[CFG_ATTR_ENABLED]));
325 umdns_udebug_set_enabled(en);
326 }
327
328 static int
329 umdns_udebug_notify_cb(struct ubus_context *ctx, struct ubus_object *obj,
330 struct ubus_request_data *req, const char *method,
331 struct blob_attr *msg)
332 {
333 umdns_udebug_config_cb(msg);
334
335 return 0;
336 }
337
338 static void
339 umdns_udebug_req_cb(struct ubus_request *req, int type, struct blob_attr *msg)
340 {
341 umdns_udebug_config_cb(msg);
342 }
343
344 static bool
345 umdns_udebug_sub_cb(struct ubus_context *ctx, struct ubus_subscriber *sub,
346 const char *path)
347 {
348 return !strcmp(path, "udebug");
349 }
350
351
352 static void
353 ubus_connect_handler(struct ubus_context *ctx)
354 {
355 uint32_t id;
356 int ret;
357
358 ret = ubus_add_object(ctx, &umdns_object);
359 if (ret)
360 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
361
362 udebug_sub.cb = umdns_udebug_notify_cb;
363 udebug_sub.new_obj_cb = umdns_udebug_sub_cb;
364 ubus_register_subscriber(&conn.ctx, &udebug_sub);
365 if (ubus_lookup_id(&conn.ctx, "udebug", &id) == 0) {
366 ubus_subscribe(&conn.ctx, &udebug_sub, id);
367 ubus_invoke(&conn.ctx, id, "get_config", NULL, umdns_udebug_req_cb, NULL, 1000);
368 }
369 }
370
371 void
372 ubus_startup(void)
373 {
374 conn.cb = ubus_connect_handler;
375 ubus_auto_connect(&conn);
376 }
377
378 int ubus_service_list(ubus_data_handler_t cb)
379 {
380 uint32_t id;
381 int ret;
382
383 blob_buf_init(&b, 0);
384 ret = ubus_lookup_id(&conn.ctx, "service", &id);
385 if (ret)
386 return ret;
387
388 return ubus_invoke(&conn.ctx, id, "list", b.head, cb, NULL, 5 * 1000);
389 }