• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/mdnsd/service.c

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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt