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 } 390
This page was automatically generated by LXR 0.3.1. • OpenWrt