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/nameser.h> 16 #include <sys/socket.h> 17 18 #include <resolv.h> 19 #include <glob.h> 20 #include <inttypes.h> 21 #include <stdio.h> 22 #include <time.h> 23 24 #include <libubus.h> 25 #include <libubox/uloop.h> 26 #include <libubox/blobmsg_json.h> 27 28 #include "ubus.h" 29 #include "dns.h" 30 #include "service.h" 31 #include "util.h" 32 #include "interface.h" 33 #include "announce.h" 34 35 enum { 36 SERVICE_INSTANCE, 37 SERVICE_SERVICE, 38 SERVICE_PORT, 39 SERVICE_TXT, 40 SERVICE_HOSTNAME, 41 __SERVICE_MAX, 42 }; 43 44 static const struct blobmsg_policy service_policy[__SERVICE_MAX] = { 45 [SERVICE_INSTANCE] = { .name = "instance", .type = BLOBMSG_TYPE_STRING }, 46 [SERVICE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING }, 47 [SERVICE_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 }, 48 [SERVICE_TXT] = { .name = "txt", .type = BLOBMSG_TYPE_ARRAY }, 49 [SERVICE_HOSTNAME] = { .name = "hostname", .type = BLOBMSG_TYPE_STRING }, 50 }; 51 52 static void 53 hostname_update(struct vlist_tree *tree, struct vlist_node *node_new, 54 struct vlist_node *node_old); 55 56 static struct blob_buf b; 57 VLIST_TREE(announced_services, avl_strcmp, service_update, false, false); 58 VLIST_TREE(hostnames, avl_strcmp, hostname_update, false, false); 59 static int service_init_announce; 60 61 /** 62 * service_instance_name - construct Service Instance Name as in RFC 6763 63 * 64 * RFC 6763 specifies Service Instance Names in the following way: 65 * 66 * Service Instance Name = <Instance> . <Service> . <Domain> 67 * 68 * @s: service to generate service instance name for 69 */ 70 static const char * 71 service_instance_name(struct service *s) 72 { 73 static char buffer[256]; 74 75 snprintf(buffer, sizeof(buffer), "%s.%s", s->instance, s->service); 76 77 return buffer; 78 } 79 80 static void 81 service_add_ptr(const char *name, const char *host, int ttl) 82 { 83 int len = dn_comp(host, mdns_buf, sizeof(mdns_buf), NULL, NULL); 84 85 if (len < 1) 86 return; 87 88 dns_packet_answer(name, TYPE_PTR, mdns_buf, len, ttl); 89 } 90 91 static void 92 service_add_srv(const char *name, struct service *s, int ttl) 93 { 94 struct dns_srv_data *sd = (struct dns_srv_data *) mdns_buf; 95 int len = sizeof(*sd); 96 97 len += dn_comp(s->hostname, mdns_buf + len, sizeof(mdns_buf) - len, NULL, NULL); 98 if (len <= sizeof(*sd)) 99 return; 100 101 sd->port = cpu_to_be16(s->port); 102 dns_packet_answer(name, TYPE_SRV, mdns_buf, len, ttl); 103 } 104 105 #define TOUT_LOOKUP 60 106 107 static time_t 108 service_timeout(struct service *s) 109 { 110 time_t t = monotonic_time(); 111 112 if (t - s->t <= TOUT_LOOKUP) { 113 DBG(2, "t=%" PRId64 ", s->t=%" PRId64 ", t - s->t = %" PRId64 "\n", (int64_t)t, (int64_t)s->t, (int64_t)(t - s->t)); 114 return 0; 115 } 116 117 return t; 118 } 119 120 static void 121 service_reply_single(struct interface *iface, struct sockaddr *to, struct service *s, int ttl, int force) 122 { 123 const char *host = service_instance_name(s); 124 char *service = strstr(host, "._"); 125 time_t t = service_timeout(s); 126 127 if (!force && (!s->active || !service || !t)) 128 return; 129 130 service++; 131 132 s->t = t; 133 134 dns_packet_init(); 135 service_add_ptr(service, service_instance_name(s), ttl); 136 service_add_srv(host, s, ttl); 137 if (s->txt && s->txt_len) 138 dns_packet_answer(host, TYPE_TXT, (uint8_t *) s->txt, s->txt_len, ttl); 139 dns_packet_send(iface, to, 0, 0); 140 } 141 142 void 143 service_reply(struct interface *iface, struct sockaddr *to, const char *instance, const char *service_domain, int ttl, int force) 144 { 145 struct service *s; 146 147 vlist_for_each_element(&announced_services, s, node) { 148 if (instance && strcmp(s->instance, instance)) 149 continue; 150 if (service_domain && strcmp(s->service, service_domain)) 151 continue; 152 service_reply_single(iface, to, s, ttl, force); 153 } 154 } 155 156 void 157 service_announce_services(struct interface *iface, struct sockaddr *to, int ttl) 158 { 159 struct service *s; 160 int count = 0; 161 162 dns_packet_init(); 163 vlist_for_each_element(&announced_services, s, node) { 164 s->t = 0; 165 if (ttl) { 166 service_add_ptr(C_DNS_SD, s->service, ttl); 167 count++; 168 } 169 } 170 if (count) 171 dns_packet_send(iface, to, 0, 0); 172 } 173 174 void 175 service_update(struct vlist_tree *tree, struct vlist_node *node_new, 176 struct vlist_node *node_old) 177 { 178 struct interface *iface; 179 struct service *s; 180 181 if (!node_old) { 182 s = container_of(node_new, struct service, node); 183 if (service_init_announce) 184 vlist_for_each_element(&interfaces, iface, node) { 185 s->t = 0; 186 service_reply_single(iface, NULL, s, announce_ttl, 1); 187 } 188 return; 189 } 190 191 s = container_of(node_old, struct service, node); 192 if (!node_new && service_init_announce) 193 vlist_for_each_element(&interfaces, iface, node) 194 service_reply_single(iface, NULL, s, 0, 1); 195 free(s); 196 } 197 198 static void 199 hostname_update(struct vlist_tree *tree, struct vlist_node *node_new, 200 struct vlist_node *node_old) 201 { 202 struct interface *iface; 203 struct hostname *h; 204 205 if (!node_old) { 206 h = container_of(node_new, struct hostname, node); 207 vlist_for_each_element(&interfaces, iface, node) 208 dns_reply_a(iface, NULL, announce_ttl, h->hostname); 209 return; 210 } 211 212 h = container_of(node_old, struct hostname, node); 213 if (!node_new) 214 vlist_for_each_element(&interfaces, iface, node) 215 dns_reply_a(iface, NULL, 0, h->hostname); 216 217 free(h); 218 } 219 220 static void 221 service_load_hostname(struct blob_attr *b) 222 { 223 struct hostname *h; 224 char *hostname, *d_hostname; 225 226 hostname = blobmsg_get_string(b); 227 h = calloc_a(sizeof(*h), &d_hostname, strlen(hostname) + 1); 228 if (!h) 229 return; 230 231 h->hostname = strcpy(d_hostname, hostname); 232 233 vlist_add(&hostnames, &h->node, h->hostname); 234 } 235 236 static void 237 service_load_blob(struct blob_attr *b) 238 { 239 struct blob_attr *txt, *_tb[__SERVICE_MAX]; 240 struct service *s; 241 char *d_instance, *d_hostname, *d_service, *d_id; 242 uint8_t *d_txt; 243 int rem2; 244 int txt_len = 0; 245 unsigned int n; 246 247 blobmsg_parse(service_policy, ARRAY_SIZE(service_policy), 248 _tb, blobmsg_data(b), blobmsg_data_len(b)); 249 250 if (_tb[SERVICE_HOSTNAME]) 251 service_load_hostname(_tb[SERVICE_HOSTNAME]); 252 253 if (!_tb[SERVICE_PORT] || !_tb[SERVICE_SERVICE]) 254 return; 255 256 if (_tb[SERVICE_TXT]) 257 blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) 258 txt_len += 1 + strlen(blobmsg_get_string(txt)); 259 260 n = strlen(blobmsg_name(b)); 261 s = calloc_a(sizeof(*s), 262 &d_id, n + 1, 263 &d_hostname, _tb[SERVICE_HOSTNAME] ? strlen(blobmsg_get_string(_tb[SERVICE_HOSTNAME])) + 1 : 0, 264 &d_instance, _tb[SERVICE_INSTANCE] ? strlen(blobmsg_get_string(_tb[SERVICE_INSTANCE])) + 1 : 0, 265 &d_service, strlen(blobmsg_get_string(_tb[SERVICE_SERVICE])) + 1, 266 &d_txt, txt_len); 267 if (!s) 268 return; 269 270 s->port = blobmsg_get_u32(_tb[SERVICE_PORT]); 271 s->id = strncpy(d_id, blobmsg_name(b), n); 272 if (_tb[SERVICE_HOSTNAME]) 273 s->hostname = strcpy(d_hostname, blobmsg_get_string(_tb[SERVICE_HOSTNAME])); 274 else 275 s->hostname = mdns_hostname_local; 276 if (_tb[SERVICE_INSTANCE]) 277 s->instance = strcpy(d_instance, blobmsg_get_string(_tb[SERVICE_INSTANCE])); 278 else 279 s->instance = umdns_host_label; 280 s->service = strcpy(d_service, blobmsg_get_string(_tb[SERVICE_SERVICE])); 281 s->active = 1; 282 s->t = 0; 283 s->txt_len = txt_len; 284 s->txt = d_txt; 285 286 if (_tb[SERVICE_TXT]) 287 blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) { 288 int len = strlen(blobmsg_get_string(txt)); 289 if (!len) 290 return; 291 if (len > 0xff) 292 len = 0xff; 293 *d_txt = len; 294 d_txt++; 295 memcpy(d_txt, blobmsg_get_string(txt), len); 296 d_txt += len; 297 } 298 299 vlist_add(&announced_services, &s->node, s->id); 300 } 301 302 static void 303 service_load(char *path) 304 { 305 struct blob_attr *cur; 306 glob_t gl; 307 int i, rem; 308 309 if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) 310 return; 311 312 for (i = 0; i < gl.gl_pathc; i++) { 313 blob_buf_init(&b, 0); 314 if (blobmsg_add_json_from_file(&b, gl.gl_pathv[i])) { 315 blob_for_each_attr(cur, b.head, rem) 316 service_load_blob(cur); 317 } else { 318 fprintf(stderr, "Error reading %s JSON\n", gl.gl_pathv[i]); 319 } 320 } 321 globfree(&gl); 322 } 323 324 static void 325 service_init_cb(struct ubus_request *req, int type, struct blob_attr *msg) 326 { 327 struct blob_attr *cur; 328 int rem; 329 330 get_hostname(); 331 332 vlist_update(&announced_services); 333 vlist_update(&hostnames); 334 service_load("/etc/umdns/*"); 335 336 blob_for_each_attr(cur, msg, rem) { 337 struct blob_attr *cur2; 338 int rem2; 339 340 blobmsg_for_each_attr(cur2, cur, rem2) { 341 struct blob_attr *cur3; 342 int rem3; 343 344 if (strcmp(blobmsg_name(cur2), "instances")) 345 continue; 346 347 blobmsg_for_each_attr(cur3, cur2, rem3) { 348 struct blob_attr *cur4; 349 int rem4; 350 int running = 0; 351 352 blobmsg_for_each_attr(cur4, cur3, rem4) { 353 const char *name = blobmsg_name(cur4); 354 355 if (!strcmp(name, "running")) { 356 running = blobmsg_get_bool(cur4); 357 } else if (running && !strcmp(name, "data")) { 358 struct blob_attr *cur5; 359 int rem5; 360 361 blobmsg_for_each_attr(cur5, cur4, rem5) { 362 struct blob_attr *cur6; 363 int rem6; 364 365 if (strcmp(blobmsg_name(cur5), "mdns")) 366 continue; 367 368 blobmsg_for_each_attr(cur6, cur5, rem6) 369 service_load_blob(cur6); 370 } 371 break; 372 } 373 } 374 } 375 } 376 } 377 vlist_flush(&announced_services); 378 vlist_flush(&hostnames); 379 } 380 381 void 382 service_init(int announce) 383 { 384 get_hostname(); 385 386 service_init_announce = announce; 387 ubus_service_list(service_init_cb); 388 } 389 390 void 391 service_cleanup(void) 392 { 393 vlist_flush(&announced_services); 394 blob_buf_free(&b); 395 } 396
This page was automatically generated by LXR 0.3.1. • OpenWrt