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 udebug_ubus udebug; 32 static struct blob_buf b; 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 uint32_t txt_offset = 0; 84 char *buf; 85 86 // a table of txt json objects 87 c_txt = blobmsg_open_array(&b, "txt"); 88 89 while (txt_offset < s->txt_len) { 90 uint8_t len = s->txt[txt_offset++]; 91 if (!len) 92 break; 93 94 // copy to NUL-terminated string 95 buf = blobmsg_alloc_string_buffer(&b, "txt", len + 1); 96 strlcpy(buf, (const char *) &s->txt[txt_offset], len+1); 97 blobmsg_add_string_buffer(&b); 98 99 txt_offset += len; 100 } 101 102 // close the array 103 blobmsg_close_array(&b, c_txt); 104 } 105 106 blobmsg_close_table(&b, c2); 107 blobmsg_close_table(&b, c1); 108 c1 = NULL; 109 } 110 111 ubus_send_reply(ctx, req, b.head); 112 113 return UBUS_STATUS_OK; 114 } 115 116 enum { 117 BROWSE_SERVICE, 118 BROWSE_ARRAY, 119 BROWSE_ADDRESS, 120 BROWSE_MAX 121 }; 122 123 static const struct blobmsg_policy browse_policy[] = { 124 [BROWSE_SERVICE] = { "service", BLOBMSG_TYPE_STRING }, 125 [BROWSE_ARRAY] = { "array", BLOBMSG_TYPE_BOOL }, 126 [BROWSE_ADDRESS] = { "address", BLOBMSG_TYPE_BOOL }, 127 }; 128 129 static int 130 umdns_browse(struct ubus_context *ctx, struct ubus_object *obj, 131 struct ubus_request_data *req, const char *method, 132 struct blob_attr *msg) 133 { 134 struct cache_service *s, *q; 135 char *buffer = (char *) mdns_buf; 136 struct blob_attr *data[BROWSE_MAX]; 137 void *c1 = NULL, *c2; 138 char *service = NULL; 139 int array = 0; 140 bool address = true; 141 142 blobmsg_parse(browse_policy, BROWSE_MAX, data, blob_data(msg), blob_len(msg)); 143 if (data[BROWSE_SERVICE]) 144 service = blobmsg_get_string(data[BROWSE_SERVICE]); 145 if (data[BROWSE_ARRAY]) 146 array = blobmsg_get_u8(data[BROWSE_ARRAY]); 147 if (data[BROWSE_ADDRESS]) 148 address = blobmsg_get_bool(data[BROWSE_ADDRESS]); 149 150 blob_buf_init(&b, 0); 151 152 avl_for_each_element(&services, s, avl) { 153 const char *hostname = buffer; 154 char *local; 155 156 snprintf(buffer, MAX_NAME_LEN, "%s", (const char *) s->avl.key); 157 local = strstr(buffer, ".local"); 158 if (local) 159 *local = '\0'; 160 if (!strcmp(buffer, "_tcp") || !strcmp(buffer, "_udp")) 161 continue; 162 if (service && strcmp(buffer, service)) 163 continue; 164 if (!c1) { 165 c1 = blobmsg_open_table(&b, buffer); 166 } 167 snprintf(buffer, MAX_NAME_LEN, "%s", s->entry); 168 local = strstr(buffer, "._"); 169 if (local) 170 *local = '\0'; 171 c2 = blobmsg_open_table(&b, buffer); 172 strncat(buffer, ".local", MAX_NAME_LEN); 173 if (s->iface) 174 blobmsg_add_string(&b, "iface", s->iface->name); 175 cache_dump_records(&b, s->entry, array, &hostname); 176 if (address) 177 cache_dump_records(&b, hostname, array, NULL); 178 blobmsg_close_table(&b, c2); 179 q = avl_next_element(s, avl); 180 if (!q || avl_is_last(&services, &s->avl) || strcmp(s->avl.key, q->avl.key)) { 181 blobmsg_close_table(&b, c1); 182 c1 = NULL; 183 } 184 } 185 186 ubus_send_reply(ctx, req, b.head); 187 188 return UBUS_STATUS_OK; 189 } 190 191 enum { 192 HOSTS_ARRAY, 193 __HOSTS_MAX 194 }; 195 static const struct blobmsg_policy hosts_policy[] = { 196 [HOSTS_ARRAY] = { "array", BLOBMSG_TYPE_BOOL } 197 }; 198 199 static int 200 umdns_hosts(struct ubus_context *ctx, struct ubus_object *obj, 201 struct ubus_request_data *req, const char *method, 202 struct blob_attr *msg) 203 { 204 struct cache_record *prev = NULL; 205 struct blob_attr *tb[__HOSTS_MAX]; 206 struct cache_record *r; 207 bool array = false; 208 void *c; 209 210 blobmsg_parse(hosts_policy, __HOSTS_MAX, tb, blobmsg_data(msg), blobmsg_len(msg)); 211 if (tb[HOSTS_ARRAY]) 212 array = blobmsg_get_bool(tb[HOSTS_ARRAY]); 213 214 blob_buf_init(&b, 0); 215 avl_for_each_element(&records, r, avl) { 216 if (r->type != TYPE_A && r->type != TYPE_AAAA) 217 continue; 218 /* Query each domain just once */ 219 if (!prev || strcmp(r->record, prev->record)) { 220 c = blobmsg_open_table(&b, r->record); 221 cache_dump_records(&b, r->record, array, NULL); 222 blobmsg_close_table(&b, c); 223 } 224 prev = r; 225 } 226 ubus_send_reply(ctx, req, b.head); 227 228 return UBUS_STATUS_OK; 229 } 230 231 enum { 232 CFG_INTERFACES, 233 CFG_KEEP, 234 CFG_MAX 235 }; 236 237 static const struct blobmsg_policy config_policy[] = { 238 [CFG_INTERFACES] = { "interfaces", BLOBMSG_TYPE_ARRAY }, 239 [CFG_KEEP] = { "keep", BLOBMSG_TYPE_BOOL }, 240 }; 241 242 static int 243 umdns_set_config(struct ubus_context *ctx, struct ubus_object *obj, 244 struct ubus_request_data *req, const char *method, 245 struct blob_attr *msg) 246 { 247 struct blob_attr *data[CFG_MAX], *cur; 248 int rem, keep = false; 249 250 blobmsg_parse(config_policy, CFG_MAX, data, blob_data(msg), blob_len(msg)); 251 if (!data[CFG_INTERFACES]) 252 return UBUS_STATUS_INVALID_ARGUMENT; 253 254 if (!blobmsg_check_attr_list(data[CFG_INTERFACES], BLOBMSG_TYPE_STRING)) 255 return UBUS_STATUS_INVALID_ARGUMENT; 256 257 keep = data[CFG_KEEP] && blobmsg_get_bool(data[CFG_KEEP]); 258 if (!keep) { 259 vlist_update(&interfaces); 260 ubus_notify(ctx, obj, "set_config", NULL, 1000); 261 } 262 263 blobmsg_for_each_attr(cur, data[CFG_INTERFACES], rem) 264 interface_add(blobmsg_data(cur)); 265 266 if (!keep) 267 vlist_flush(&interfaces); 268 269 return 0; 270 } 271 272 enum query_attr { 273 QUERY_QUESTION, 274 QUERY_IFACE, 275 QUERY_TYPE, 276 QUERY_MAX 277 }; 278 279 static const struct blobmsg_policy query_policy[QUERY_MAX] = { 280 [QUERY_QUESTION]= { "question", BLOBMSG_TYPE_STRING }, 281 [QUERY_IFACE] = { "interface", BLOBMSG_TYPE_STRING }, 282 [QUERY_TYPE] = { "type", BLOBMSG_TYPE_INT32 }, 283 }; 284 285 static int 286 umdns_query(struct ubus_context *ctx, struct ubus_object *obj, 287 struct ubus_request_data *req, const char *method, 288 struct blob_attr *msg) 289 { 290 struct interface *iface_v4 = NULL, *iface_v6 = NULL; 291 struct blob_attr *tb[QUERY_MAX], *c; 292 const char *question = C_DNS_SD; 293 const char *ifname; 294 int type = TYPE_ANY; 295 296 blobmsg_parse(query_policy, QUERY_MAX, tb, blob_data(msg), blob_len(msg)); 297 298 if ((c = tb[QUERY_QUESTION])) 299 question = blobmsg_get_string(c); 300 301 if ((c = tb[QUERY_TYPE])) 302 type = blobmsg_get_u32(c); 303 304 if ((c = tb[QUERY_IFACE]) != NULL) { 305 ifname = blobmsg_get_string(c); 306 iface_v4 = interface_get(ifname, SOCK_MC_IPV4); 307 iface_v6 = interface_get(ifname, SOCK_MC_IPV6); 308 if (!iface_v4 && !iface_v6) 309 return UBUS_STATUS_NOT_FOUND; 310 } 311 312 if (!strcmp(method, "query")) { 313 if (!iface_v4 && !iface_v6) { 314 struct interface *iface; 315 vlist_for_each_element(&interfaces, iface, node) 316 dns_send_question(iface, NULL, question, type, 1); 317 } else { 318 if (iface_v4) 319 dns_send_question(iface_v4, NULL, question, type, 1); 320 if (iface_v6) 321 dns_send_question(iface_v6, NULL, question, type, 1); 322 } 323 324 return UBUS_STATUS_OK; 325 } else if (!strcmp(method, "fetch")) { 326 if (!iface_v4 && !iface_v6) 327 return UBUS_STATUS_INVALID_ARGUMENT; 328 329 blob_buf_init(&b, 0); 330 void *k = blobmsg_open_array(&b, "records"); 331 cache_dump_recursive(&b, question, type, iface_v4 ? iface_v4 : iface_v6); 332 blobmsg_close_array(&b, k); 333 ubus_send_reply(ctx, req, b.head); 334 return UBUS_STATUS_OK; 335 } else { 336 return UBUS_STATUS_INVALID_ARGUMENT; 337 } 338 } 339 340 341 static const struct ubus_method umdns_methods[] = { 342 UBUS_METHOD("set_config", umdns_set_config, config_policy), 343 UBUS_METHOD("query", umdns_query, query_policy), 344 UBUS_METHOD("fetch", umdns_query, query_policy), 345 UBUS_METHOD("browse", umdns_browse, browse_policy), 346 UBUS_METHOD_NOARG("announcements", umdns_announcements), 347 UBUS_METHOD_NOARG("update", umdns_update), 348 UBUS_METHOD("hosts", umdns_hosts, hosts_policy), 349 UBUS_METHOD_NOARG("reload", umdns_reload), 350 }; 351 352 static struct ubus_object_type umdns_object_type = 353 UBUS_OBJECT_TYPE("umdns", umdns_methods); 354 355 static struct ubus_object umdns_object = { 356 .name = "umdns", 357 .type = &umdns_object_type, 358 .methods = umdns_methods, 359 .n_methods = ARRAY_SIZE(umdns_methods), 360 }; 361 362 static void 363 ubus_connect_handler(struct ubus_context *ctx) 364 { 365 int ret; 366 367 ret = ubus_add_object(ctx, &umdns_object); 368 if (ret) 369 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret)); 370 371 udebug_ubus_init(&udebug, ctx, "umdns", umdns_udebug_config); 372 } 373 374 void 375 ubus_startup(void) 376 { 377 conn.cb = ubus_connect_handler; 378 ubus_auto_connect(&conn); 379 } 380 381 int ubus_service_list(ubus_data_handler_t cb) 382 { 383 uint32_t id; 384 int ret; 385 386 blob_buf_init(&b, 0); 387 ret = ubus_lookup_id(&conn.ctx, "service", &id); 388 if (ret) 389 return ret; 390 391 return ubus_invoke(&conn.ctx, id, "list", b.head, cb, NULL, 5 * 1000); 392 } 393
This page was automatically generated by LXR 0.3.1. • OpenWrt