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