X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fmdnsd.git;a=blobdiff_plain;f=service.c;h=e375fced85ffb8f1a13a8d0b4cd8933a06c79f56;hp=763c89979d1463d0cf2979b3750db9e7a359bc98;hb=9879d5fcafed84a912baa8e2e2d3e73df1a9236d;hpb=a0f1b43caf06e13067ccadffdb85b0b57a436e24 diff --git a/service.c b/service.c index 763c899..e375fce 100644 --- a/service.c +++ b/service.c @@ -12,312 +12,338 @@ */ #include -#include #include #include -#include -#include #include #include #include #include -#include -#include - -#include +#include +#include #include #include #include +#include "ubus.h" #include "dns.h" #include "service.h" #include "util.h" +#include "interface.h" +#include "announce.h" enum { + SERVICE_SERVICE, SERVICE_PORT, SERVICE_TXT, __SERVICE_MAX, }; struct service { - struct avl_node avl; + struct vlist_node node; time_t t; - char *service; - char *daemon; - char *txt; + const char *id; + const char *service; + const uint8_t *txt; int txt_len; int port; int active; }; static const struct blobmsg_policy service_policy[__SERVICE_MAX] = { + [SERVICE_SERVICE] = { .name = "service", .type = BLOBMSG_TYPE_STRING }, [SERVICE_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT32 }, [SERVICE_TXT] = { .name = "txt", .type = BLOBMSG_TYPE_ARRAY }, }; -static const struct uci_blob_param_list service_attr_list = { - .n_params = __SERVICE_MAX, - .params = service_policy, -}; +static void +service_update(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old); static struct blob_buf b; -static struct avl_tree services; -char *hostname = NULL; -static char *sdudp = "_services._dns-sd._udp.local"; -static char *sdtcp = "_services._dns-sd._tcp.local"; +static VLIST_TREE(services, avl_strcmp, service_update, false, false); +char *sdudp = "_services._dns-sd._udp.local"; +char *sdtcp = "_services._dns-sd._tcp.local"; +static int service_init_announce; -char* -service_name(char *domain) +static const char * +service_name(const char *domain) { static char buffer[256]; - snprintf(buffer, sizeof(buffer), "%s.%s", hostname, domain); + snprintf(buffer, sizeof(buffer), "%s.%s", mdns_hostname, domain); return buffer; } static void -service_send_ptr(struct uloop_fd *u, struct service *s) +service_add_ptr(const char *host, int ttl) { - unsigned char buffer[MAX_NAME_LEN]; - char *host = service_name(s->service); - int len = dn_comp(host, buffer, MAX_NAME_LEN, NULL, NULL); + int len = dn_comp(host, mdns_buf, sizeof(mdns_buf), NULL, NULL); if (len < 1) return; - dns_add_answer(TYPE_PTR, buffer, len); + dns_add_answer(TYPE_PTR, mdns_buf, len, ttl); } static void -service_send_ptr_c(struct uloop_fd *u, char *host) +service_add_srv(struct service *s, int ttl) { - unsigned char buffer[MAX_NAME_LEN]; - int len = dn_comp(host, buffer, MAX_NAME_LEN, NULL, NULL); + struct dns_srv_data *sd = (struct dns_srv_data *) mdns_buf; + int len = sizeof(*sd); - if (len < 1) + len += dn_comp(mdns_hostname_local, mdns_buf + len, sizeof(mdns_buf) - len, NULL, NULL); + if (len <= sizeof(*sd)) return; - dns_add_answer(TYPE_PTR, buffer, len); + sd->port = cpu_to_be16(s->port); + dns_add_answer(TYPE_SRV, mdns_buf, len, ttl); } -static void -service_send_a(struct uloop_fd *u) -{ - unsigned char buffer[MAX_NAME_LEN]; - char *host = service_name("local"); - int len = dn_comp(host, buffer, MAX_NAME_LEN, NULL, NULL); - struct in_addr in; +#define TOUT_LOOKUP 60 - if (!inet_aton(iface_ip, &in)) { - fprintf(stderr, "%s is not valid\n", iface_ip); - return; - } +static time_t +service_timeout(struct service *s) +{ + time_t t = monotonic_time(); - if (len < 1) - return; + if (t - s->t <= TOUT_LOOKUP) + return 0; - dns_add_answer(TYPE_A, (uint8_t *) &in.s_addr, 4); + return t; } static void -service_send_srv(struct uloop_fd *u, struct service *s) +service_reply_single(struct interface *iface, struct service *s, int ttl, int force) { - unsigned char buffer[MAX_NAME_LEN]; - struct dns_srv_data *sd; - char *host = service_name("local"); - int len = dn_comp(host, buffer, MAX_NAME_LEN, NULL, NULL); + const char *host = service_name(s->service); + char *service = strstr(host, "._"); + time_t t = service_timeout(s); - if (len < 1) - return; - sd = calloc(1, len + sizeof(struct dns_srv_data)); - if (!sd) + if (!force && (!s->active || !service || !t)) return; - sd->port = cpu_to_be16(s->port); - memcpy(&sd[1], buffer, len); - host = service_name(s->service); - dns_add_answer(TYPE_SRV, (uint8_t *) sd, len + sizeof(struct dns_srv_data)); - free(sd); -} - -#define TOUT_LOOKUP 60 - -static int -service_timeout(struct service *s) -{ - time_t t = time(NULL); - - if (t - s->t <= TOUT_LOOKUP) - return 0; + service++; s->t = t; - return 1; + dns_init_answer(); + service_add_ptr(service_name(s->service), ttl); + dns_send_answer(iface, service); + + dns_init_answer(); + service_add_srv(s, ttl); + if (s->txt && s->txt_len) + dns_add_answer(TYPE_TXT, (uint8_t *) s->txt, s->txt_len, ttl); + dns_send_answer(iface, host); } void -service_reply_a(struct uloop_fd *u, int type) +service_reply(struct interface *iface, const char *match, int ttl) { - if (type != TYPE_A) - return; + struct service *s; - dns_init_answer(); - service_send_a(u); - dns_send_answer(u, service_name("local")); + vlist_for_each_element(&services, s, node) { + if (!match || !strcmp(s->service, match)) + service_reply_single(iface, s, ttl, 0); + } } void -service_reply(struct uloop_fd *u, char *match) +service_announce_services(struct interface *iface, int tcp, int ttl) { struct service *s; - avl_for_each_element(&services, s, avl) { - char *host = service_name(s->service); - char *service = strstr(host, "._"); - - if (!s->active || !service || !service_timeout(s)) + vlist_for_each_element(&services, s, node) { + if (!strstr(s->service, "._tcp") && tcp) continue; - - service++; - - if (match && strcmp(match, s->service)) + if (!strstr(s->service, "._udp") && !tcp) continue; + s->t = 0; + if (ttl) { + dns_init_answer(); + service_add_ptr(s->service, ttl); + if (tcp) + dns_send_answer(iface, sdtcp); + else + dns_send_answer(iface, sdudp); + } + service_reply_single(iface, s, ttl, 0); + } +} - dns_init_answer(); - service_send_ptr(u, s); - dns_send_answer(u, service); +void +service_announce(struct interface *iface, int ttl) +{ + service_announce_services(iface, 0, ttl); + service_announce_services(iface, 1, ttl); +} - dns_init_answer(); - service_send_srv(u, s); - if (s->txt && s->txt_len) - dns_add_answer(TYPE_TXT, (uint8_t *) s->txt, s->txt_len); - dns_send_answer(u, host); - } +static void +service_update(struct vlist_tree *tree, struct vlist_node *node_new, + struct vlist_node *node_old) +{ + struct interface *iface; + struct service *s; - if (match) + if (!node_old) { + s = container_of(node_new, struct service, node); + if (service_init_announce) + vlist_for_each_element(&interfaces, iface, node) { + s->t = 0; + service_reply_single(iface, s, announce_ttl, 1); + } return; + } - dns_init_answer(); - service_send_a(u); - dns_send_answer(u, service_name("local")); + s = container_of(node_old, struct service, node); + if (!node_new && service_init_announce) + vlist_for_each_element(&interfaces, iface, node) + service_reply_single(iface, s, 0, 1); + free(s); } -void -service_announce_services(struct uloop_fd *u, char *service) +static void +service_load_blob(struct blob_attr *b) { + struct blob_attr *txt, *_tb[__SERVICE_MAX]; struct service *s; - int tcp = 1; + char *d_service, *d_id; + uint8_t *d_txt; + int rem2; + int txt_len = 0; + + blobmsg_parse(service_policy, ARRAY_SIZE(service_policy), + _tb, blobmsg_data(b), blobmsg_data_len(b)); + if (!_tb[SERVICE_PORT] || !_tb[SERVICE_SERVICE]) + return; - if (!strcmp(service, sdudp)) - tcp = 0; - else if (strcmp(service, sdtcp)) + if (_tb[SERVICE_TXT]) + blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) + txt_len += 1 + strlen(blobmsg_get_string(txt)); + + s = calloc_a(sizeof(*s), + &d_id, strlen(blobmsg_name(b)) + 1, + &d_service, strlen(blobmsg_get_string(_tb[SERVICE_SERVICE])) + 1, + &d_txt, txt_len); + if (!s) return; - avl_for_each_element(&services, s, avl) { - if (!strstr(s->service, "._tcp") && tcp) - continue; - if (!strstr(s->service, "._udp") && !tcp) - continue; - s->t = 0; - dns_init_answer(); - service_send_ptr_c(u, s->service); - if (tcp) - dns_send_answer(u, sdtcp); - else - dns_send_answer(u, sdudp); - service_reply(u, s->service); - } -} + s->port = blobmsg_get_u32(_tb[SERVICE_PORT]); + s->id = strcpy(d_id, blobmsg_name(b)); + s->service = strcpy(d_service, blobmsg_get_string(_tb[SERVICE_SERVICE])); + s->active = 1; + s->t = 0; + s->txt_len = txt_len; + s->txt = d_txt; + + if (_tb[SERVICE_TXT]) + blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) { + int len = strlen(blobmsg_get_string(txt)); + if (!len) + return; + if (len > 0xff) + len = 0xff; + *d_txt = len; + d_txt++; + memcpy(d_txt, blobmsg_get_string(txt), len); + d_txt += len; + } -void -service_announce(struct uloop_fd *u) -{ - service_announce_services(u, sdudp); - service_announce_services(u, sdtcp); + vlist_add(&services, &s->node, s->id); } static void service_load(char *path) { - struct blob_attr *txt, *cur, *_tb[__SERVICE_MAX]; - int rem, i; + struct blob_attr *cur; glob_t gl; + int i, rem; if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl)) return; for (i = 0; i < gl.gl_pathc; i++) { blob_buf_init(&b, 0); + if (blobmsg_add_json_from_file(&b, gl.gl_pathv[i])) + blob_for_each_attr(cur, b.head, rem) + service_load_blob(cur); + } + globfree(&gl); +} - if (!blobmsg_add_json_from_file(&b, gl.gl_pathv[i])) - continue; - blob_for_each_attr(cur, b.head, rem) { - struct service *s; - char *d_service, *d_txt, *d_daemon; - int rem2; - int txt_len = 0; - - blobmsg_parse(service_policy, ARRAY_SIZE(service_policy), - _tb, blobmsg_data(cur), blobmsg_data_len(cur)); - if (!_tb[SERVICE_PORT] || !_tb[SERVICE_TXT]) - continue; +static void +service_init_cb(struct ubus_request *req, int type, struct blob_attr *msg) +{ + struct blob_attr *cur; + int rem; - blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) - txt_len += 1 + strlen(blobmsg_get_string(txt)); + get_hostname(); - s = calloc_a(sizeof(*s), - &d_daemon, strlen(gl.gl_pathv[i]) + 1, - &d_service, strlen(blobmsg_name(cur)) + 1, - &d_txt, txt_len); - if (!s) - continue; + vlist_update(&services); + service_load("/tmp/run/mdns/*"); + + blob_for_each_attr(cur, msg, rem) { + struct blob_attr *cur2; + int rem2; - s->port = blobmsg_get_u32(_tb[SERVICE_PORT]); - s->service = d_service; - s->daemon = d_daemon; - strcpy(s->service, blobmsg_name(cur)); - strcpy(s->daemon, gl.gl_pathv[i]); - s->avl.key = s->service; - s->active = 1; - s->t = 0; - s->txt_len = txt_len; - s->txt = d_txt; - avl_insert(&services, &s->avl); - - if (!s->txt_len) + blobmsg_for_each_attr(cur2, cur, rem2) { + struct blob_attr *cur3; + int rem3; + + if (strcmp(blobmsg_name(cur2), "instances")) continue; - blobmsg_for_each_attr(txt, _tb[SERVICE_TXT], rem2) { - int len = strlen(blobmsg_get_string(txt)); - if (!len) - continue; - if (len > 0xff) - len = 0xff; - *d_txt = len; - d_txt++; - memcpy(d_txt, blobmsg_get_string(txt), len); - d_txt += len; + blobmsg_for_each_attr(cur3, cur2, rem3) { + struct blob_attr *cur4; + int rem4; + int running = 0; + + blobmsg_for_each_attr(cur4, cur3, rem4) { + const char *name = blobmsg_name(cur4); + + if (!strcmp(name, "running")) { + running = blobmsg_get_bool(cur4); + } else if (running && !strcmp(name, "data")) { + struct blob_attr *cur5; + int rem5; + + blobmsg_for_each_attr(cur5, cur4, rem5) { + struct blob_attr *cur6; + int rem6; + + if (strcmp(blobmsg_name(cur5), "mdns")) + continue; + + blobmsg_for_each_attr(cur6, cur5, rem6) + service_load_blob(cur6); + } + break; + } + } } } } + vlist_flush(&services); } void -service_init(void) +service_init(int announce) { - if (!hostname) - hostname = get_hostname(); - avl_init(&services, avl_strcmp, true, NULL); - service_load("/tmp/run/mdnsd/*"); + get_hostname(); + + service_init_announce = announce; + ubus_service_list(service_init_cb); } void service_cleanup(void) { + vlist_flush(&services); + blob_buf_free(&b); }