• 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_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