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 #include <ctype.h> 19 20 #include <libubus.h> 21 #include <libubox/vlist.h> 22 #include <libubox/uloop.h> 23 24 #include "util.h" 25 #include "ubus.h" 26 #include "cache.h" 27 #include "service.h" 28 #include "interface.h" 29 30 static struct ubus_auto_conn conn; 31 static struct blob_buf b; 32 static struct ubus_subscriber udebug_sub; 33 34 static int 35 umdns_reload(struct ubus_context *ctx, struct ubus_object *obj, 36 struct ubus_request_data *req, const char *method, 37 struct blob_attr *msg) 38 { 39 service_init(1); 40 return 0; 41 } 42 43 static int 44 umdns_update(struct ubus_context *ctx, struct ubus_object *obj, 45 struct ubus_request_data *req, const char *method, 46 struct blob_attr *msg) 47 { 48 cache_update(); 49 return 0; 50 } 51 52 static int 53 umdns_announcements(struct ubus_context *ctx, struct ubus_object *obj, 54 struct ubus_request_data *req, const char *method, 55 struct blob_attr *msg) 56 { 57 struct service *s; 58 void *c1 = NULL, *c2 = NULL; 59 60 blob_buf_init(&b, 0); 61 vlist_for_each_element(&announced_services, s, node) { 62 // is something there? 63 if (!s->id || !strlen(s->id)) 64 continue; 65 66 if (!s->service || !strlen(s->service)) 67 continue; 68 69 if (!s->port) 70 continue; 71 72 if (!c1) { 73 c1 = blobmsg_open_table(&b, (const char *) s->service); 74 } 75 76 c2 = blobmsg_open_table(&b, s->id); 77 78 blobmsg_add_u32(&b, "port", s->port); 79 80 // check if there are any text entries 81 if (s->txt_len) { 82 void *c_txt = NULL; 83 int i; 84 85 // this string will hold text records 86 char *txt_str = (char *) calloc(s->txt_len, sizeof(char)); 87 88 // we get some weird characters like \u000b, so get don't copy them 89 for (i=0; i<s->txt_len; i++) { 90 if ((ispunct(s->txt[i])) || (isalnum(s->txt[i]))) 91 txt_str[i] = (char) s->txt[i]; 92 else 93 txt_str[i] = ' '; 94 } 95 96 txt_str[s->txt_len] = '\0'; 97 98 // a table of txt json objects 99 c_txt = blobmsg_open_array(&b, "txt"); 100 101 // split based on space and add each token to output 102 char *pch = NULL, *pchr = NULL; 103 104 for (pch = strtok_r(txt_str, " ", &pchr); pch != NULL; pch = strtok_r(NULL, " ", &pchr)) { 105 // add it to array 106 blobmsg_add_string(&b, "txt", pch); 107 } 108 109 // close the array 110 blobmsg_close_array(&b, c_txt); 111 112 // free the calloced memory 113 free(txt_str); 114 } 115 116 blobmsg_close_table(&b, c2); 117 blobmsg_close_table(&b, c1); 118 c1 = NULL; 119 } 120 121 ubus_send_reply(ctx, req, b.head); 122 123 return UBUS_STATUS_OK; 124 } 125 126 enum { 127 BROWSE_SERVICE, 128 BROWSE_ARRAY, 129 BROWSE_ADDRESS, 130 BROWSE_MAX 131 }; 132 133 static const struct blobmsg_policy browse_policy[] = { 134 [BROWSE_SERVICE] = { "service", BLOBMSG_TYPE_STRING }, 135 [BROWSE_ARRAY] = { "array", BLOBMSG_TYPE_BOOL }, 136 [BROWSE_ADDRESS] = { "address", BLOBMSG_TYPE_BOOL }, 137 }; 138 139 static int 140 umdns_browse(struct ubus_context *ctx, struct ubus_object *obj, 141 struct ubus_request_data *req, const char *method, 142 struct blob_attr *msg) 143 { 144 struct cache_service *s, *q; 145 char *buffer = (char *) mdns_buf; 146 struct blob_attr *data[BROWSE_MAX]; 147 void *c1 = NULL, *c2; 148 char *service = NULL; 149 int array = 0; 150 bool address = true; 151 152 blobmsg_parse(browse_policy, BROWSE_MAX, data, blob_data(msg), blob_len(msg)); 153 if (data[BROWSE_SERVICE]) 154 service = blobmsg_get_string(data[BROWSE_SERVICE]); 155 if (data[BROWSE_ARRAY]) 156 array = blobmsg_get_u8(data[BROWSE_ARRAY]); 157 if (data[BROWSE_ADDRESS]) 158 address = blobmsg_get_bool(data[BROWSE_ADDRESS]); 159 160 blob_buf_init(&b, 0); 161 162 avl_for_each_element(&services, s, avl) { 163 const char *hostname = buffer; 164 char *local; 165 166 snprintf(buffer, MAX_NAME_LEN, "%s", (const char *) s->avl.key); 167 local = strstr(buffer, ".local"); 168 if (local) 169 *local = '\0'; 170 if (!strcmp(buffer, "_tcp") || !strcmp(buffer, "_udp")) 171 continue; 172 if (service && strcmp(buffer, service)) 173 continue; 174 if (!c1) { 175 c1 = blobmsg_open_table(&b, buffer); 176 } 177 snprintf(buffer, MAX_NAME_LEN, "%s", s->entry); 178 local = strstr(buffer, "._"); 179 if (local) 180 *local = '\0'; 181 c2 = blobmsg_open_table(&b, buffer); 182 strncat(buffer, ".local", MAX_NAME_LEN); 183 if (s->iface) 184 blobmsg_add_string(&b, "iface", s->iface->name); 185 cache_dump_records(&b, s->entry, array, &hostname); 186 if (address) 187 cache_dump_records(&b, hostname, array, NULL); 188 blobmsg_close_table(&b, c2); 189 q = avl_next_element(s, avl); 190 if (!q || avl_is_last(&services, &s->avl) || strcmp(s->avl.key, q->avl.key)) { 191 blobmsg_close_table(&b, c1); 192 c1 = NULL; 193 } 194 } 195 196 ubus_send_reply(ctx, req, b.head); 197 198 return UBUS_STATUS_OK; 199 } 200 201 enum { 202 HOSTS_ARRAY, 203 __HOSTS_MAX 204 }; 205 static const struct blobmsg_policy hosts_policy[] = { 206 [HOSTS_ARRAY] = { "array", BLOBMSG_TYPE_BOOL } 207 }; 208 209 static int 210 umdns_hosts(struct ubus_context *ctx, struct ubus_object *obj, 211 struct ubus_request_data *req, const char *method, 212 struct blob_attr *msg) 213 { 214 struct cache_record *prev = NULL; 215 struct blob_attr *tb[__HOSTS_MAX]; 216 struct cache_record *r; 217 bool array = false; 218 void *c; 219 220 blobmsg_parse(hosts_policy, __HOSTS_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 221 if (tb[HOSTS_ARRAY]) 222 array = blobmsg_get_bool(tb[HOSTS_ARRAY]); 223 224 blob_buf_init(&b, 0); 225 avl_for_each_element(&records, r, avl) { 226 if (r->type != TYPE_A && r->type != TYPE_AAAA) 227 continue; 228 /* Query each domain just once */ 229 if (!prev || strcmp(r->record, prev->record)) { 230 c = blobmsg_open_table(&b, r->record); 231 cache_dump_records(&b, r->record, array, NULL); 232 blobmsg_close_table(&b, c); 233 } 234 prev = r; 235 } 236 ubus_send_reply(ctx, req, b.head); 237 238 return UBUS_STATUS_OK; 239 } 240 241 enum { 242 CFG_INTERFACES, 243 CFG_KEEP, 244 CFG_MAX 245 }; 246 247 static const struct blobmsg_policy config_policy[] = { 248 [CFG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_ARRAY }, 249 [CFG_KEEP] = { "keep", BLOBMSG_TYPE_BOOL }, 250 }; 251 252 static int 253 umdns_set_config(struct ubus_context *ctx, struct ubus_object *obj, 254 struct ubus_request_data *req, const char *method, 255 struct blob_attr *msg) 256 { 257 struct blob_attr *data[CFG_MAX], *cur; 258 int rem, keep = false; 259 260 blobmsg_parse(config_policy, CFG_MAX, data, blob_data(msg), blob_len(msg)); 261 if (!data[CFG_INTERFACES]) 262 return UBUS_STATUS_INVALID_ARGUMENT; 263 264 if (!blobmsg_check_attr_list(data[CFG_INTERFACES], BLOBMSG_TYPE_STRING)) 265 return UBUS_STATUS_INVALID_ARGUMENT; 266 267 keep = data[CFG_KEEP] && blobmsg_get_bool(data[CFG_KEEP]); 268 if (!keep) { 269 vlist_update(&interfaces); 270 ubus_notify(ctx, obj, "set_config", NULL, 1000); 271 } 272 273 blobmsg_for_each_attr(cur, data[CFG_INTERFACES], rem) 274 interface_add(blobmsg_data(cur)); 275 276 if (!keep) 277 vlist_flush(&interfaces); 278 279 return 0; 280 } 281 282 enum query_attr { 283 QUERY_QUESTION, 284 QUERY_IFACE, 285 QUERY_TYPE, 286 QUERY_MAX 287 }; 288 289 static const struct blobmsg_policy query_policy[QUERY_MAX] = { 290 [QUERY_QUESTION]= { "question", BLOBMSG_TYPE_STRING }, 291 [QUERY_IFACE] = { "interface", BLOBMSG_TYPE_STRING }, 292 [QUERY_TYPE] = { "type", BLOBMSG_TYPE_INT32 }, 293 }; 294 295 static int 296 umdns_query(struct ubus_context *ctx, struct ubus_object *obj, 297 struct ubus_request_data *req, const char *method, 298 struct blob_attr *msg) 299 { 300 struct interface *iface_v4 = NULL, *iface_v6 = NULL; 301 struct blob_attr *tb[QUERY_MAX], *c; 302 const char *question = C_DNS_SD; 303 const char *ifname; 304 int type = TYPE_ANY; 305 306 blobmsg_parse(query_policy, QUERY_MAX, tb, blob_data(msg), blob_len(msg)); 307 308 if ((c = tb[QUERY_QUESTION])) 309 question = blobmsg_get_string(c); 310 311 if ((c = tb[QUERY_TYPE])) 312 type = blobmsg_get_u32(c); 313 314 if ((c = tb[QUERY_IFACE]) != NULL) { 315 ifname = blobmsg_get_string(c); 316 iface_v4 = interface_get(ifname, SOCK_MC_IPV4); 317 iface_v6 = interface_get(ifname, SOCK_MC_IPV6); 318 if (!iface_v4 && !iface_v6) 319 return UBUS_STATUS_NOT_FOUND; 320 } 321 322 if (!strcmp(method, "query")) { 323 if (!iface_v4 && !iface_v6) { 324 struct interface *iface; 325 vlist_for_each_element(&interfaces, iface, node) 326 dns_send_question(iface, NULL, question, type, 1); 327 } else { 328 if (iface_v4) 329 dns_send_question(iface_v4, NULL, question, type, 1); 330 if (iface_v6) 331 dns_send_question(iface_v6, NULL, question, type, 1); 332 } 333 334 return UBUS_STATUS_OK; 335 } else if (!strcmp(method, "fetch")) { 336 if (!iface_v4 && !iface_v6) 337 return UBUS_STATUS_INVALID_ARGUMENT; 338 339 blob_buf_init(&b, 0); 340 void *k = blobmsg_open_array(&b, "records"); 341 cache_dump_recursive(&b, question, type, iface_v4 ? iface_v4 : iface_v6); 342 blobmsg_close_array(&b, k); 343 ubus_send_reply(ctx, req, b.head); 344 return UBUS_STATUS_OK; 345 } else { 346 return UBUS_STATUS_INVALID_ARGUMENT; 347 } 348 } 349 350 351 static const struct ubus_method umdns_methods[] = { 352 UBUS_METHOD("set_config", umdns_set_config, config_policy), 353 UBUS_METHOD("query", umdns_query, query_policy), 354 UBUS_METHOD("fetch", umdns_query, query_policy), 355 UBUS_METHOD("browse", umdns_browse, browse_policy), 356 UBUS_METHOD_NOARG("announcements", umdns_announcements), 357 UBUS_METHOD_NOARG("update", umdns_update), 358 UBUS_METHOD("hosts", umdns_hosts, hosts_policy), 359 UBUS_METHOD_NOARG("reload", umdns_reload), 360 }; 361 362 static struct ubus_object_type umdns_object_type = 363 UBUS_OBJECT_TYPE("umdns", umdns_methods); 364 365 static struct ubus_object umdns_object = { 366 .name = "umdns", 367 .type = &umdns_object_type, 368 .methods = umdns_methods, 369 .n_methods = ARRAY_SIZE(umdns_methods), 370 }; 371 372 static struct blob_attr * 373 find_attr(struct blob_attr *attr, const char *name, enum blobmsg_type type) 374 { 375 struct blobmsg_policy policy = { name, type }; 376 struct blob_attr *ret; 377 378 if (!attr) 379 return NULL; 380 381 blobmsg_parse_attr(&policy, 1, &ret, attr); 382 383 return ret; 384 } 385 386 static void 387 umdns_udebug_config_cb(struct blob_attr *data) 388 { 389 enum { 390 CFG_ATTR_ENABLED, 391 __CFG_ATTR_MAX 392 }; 393 static const struct blobmsg_policy policy[__CFG_ATTR_MAX] = { 394 [CFG_ATTR_ENABLED] = { "enabled", BLOBMSG_TYPE_STRING }, 395 }; 396 struct blob_attr *tb[__CFG_ATTR_MAX]; 397 bool en; 398 399 data = find_attr(data, "service", BLOBMSG_TYPE_TABLE); 400 data = find_attr(data, "umdns", BLOBMSG_TYPE_TABLE); 401 if (!data) 402 return; 403 404 blobmsg_parse_attr(policy, __CFG_ATTR_MAX, tb, data); 405 if (!tb[CFG_ATTR_ENABLED]) 406 return; 407 408 en = !!atoi(blobmsg_get_string(tb[CFG_ATTR_ENABLED])); 409 umdns_udebug_set_enabled(en); 410 } 411 412 static int 413 umdns_udebug_notify_cb(struct ubus_context *ctx, struct ubus_object *obj, 414 struct ubus_request_data *req, const char *method, 415 struct blob_attr *msg) 416 { 417 umdns_udebug_config_cb(msg); 418 419 return 0; 420 } 421 422 static void 423 umdns_udebug_req_cb(struct ubus_request *req, int type, struct blob_attr *msg) 424 { 425 umdns_udebug_config_cb(msg); 426 } 427 428 static bool 429 umdns_udebug_sub_cb(struct ubus_context *ctx, struct ubus_subscriber *sub, 430 const char *path) 431 { 432 return !strcmp(path, "udebug"); 433 } 434 435 436 static void 437 ubus_connect_handler(struct ubus_context *ctx) 438 { 439 uint32_t id; 440 int ret; 441 442 ret = ubus_add_object(ctx, &umdns_object); 443 if (ret) 444 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); 445 446 udebug_sub.cb = umdns_udebug_notify_cb; 447 udebug_sub.new_obj_cb = umdns_udebug_sub_cb; 448 ubus_register_subscriber(&conn.ctx, &udebug_sub); 449 if (ubus_lookup_id(&conn.ctx, "udebug", &id) == 0) { 450 ubus_subscribe(&conn.ctx, &udebug_sub, id); 451 ubus_invoke(&conn.ctx, id, "get_config", NULL, umdns_udebug_req_cb, NULL, 1000); 452 } 453 } 454 455 void 456 ubus_startup(void) 457 { 458 conn.cb = ubus_connect_handler; 459 ubus_auto_connect(&conn); 460 } 461 462 int ubus_service_list(ubus_data_handler_t cb) 463 { 464 uint32_t id; 465 int ret; 466 467 blob_buf_init(&b, 0); 468 ret = ubus_lookup_id(&conn.ctx, "service", &id); 469 if (ret) 470 return ret; 471 472 return ubus_invoke(&conn.ctx, id, "list", b.head, cb, NULL, 5 * 1000); 473 } 474
This page was automatically generated by LXR 0.3.1. • OpenWrt