void Browser::updateService(const char *name, const char *host_name, const AvahiAddress *a, uint16_t port, AvahiStringList *txt) { // Find the service in our list Browser::Service s(name); auto it = std::lower_bound(services_.begin(), services_.end(), s); if (it == services_.end() || it->service_name != name) { return; } Browser::Service &svc = *it; auto index = createIndex(it - services_.begin(), 0); svc.host_name = host_name; char addr[AVAHI_ADDRESS_STR_MAX]; avahi_address_snprint(addr, sizeof(addr), a); svc.address = addr; svc.port = port; svc.txt.clear(); for (auto item = txt; item != nullptr; item = avahi_string_list_get_next(item)) { // TODO: this probably isn't actually UTF-8 svc.txt.push_front( QString::fromUtf8( reinterpret_cast<const char*>(avahi_string_list_get_text(item)), avahi_string_list_get_size(item))); } Q_EMIT dataChanged(index, index); }
/* TODO: Docs. Return value is only valid as long as @txt is. Reference: RFC 6763, §6. */ GHashTable * _ostree_txt_records_parse (AvahiStringList *txt) { AvahiStringList *l; g_autoptr(GHashTable) out = NULL; out = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_bytes_unref); for (l = txt; l != NULL; l = avahi_string_list_get_next (l)) { const guint8 *txt; gsize txt_len; const gchar *key; const guint8 *value; gsize key_len, value_len; g_autofree gchar *key_allocated = NULL; g_autoptr(GBytes) value_allocated = NULL; txt = avahi_string_list_get_text (l); txt_len = avahi_string_list_get_size (l); if (!parse_txt_record (txt, txt_len, &key, &key_len, &value, &value_len)) { g_debug ("Ignoring invalid TXT record of length %" G_GSIZE_FORMAT, txt_len); continue; } key_allocated = g_ascii_strdown (key, key_len); if (g_hash_table_lookup_extended (out, key_allocated, NULL, NULL)) { g_debug ("Ignoring duplicate TXT record ‘%s’", key_allocated); continue; } /* Distinguish between the case where the entire record is the key * (value == NULL) and the case where the record is the key + ‘=’ and the * value is empty (value != NULL && value_len == 0). */ if (value != NULL) value_allocated = g_bytes_new_static (value, value_len); g_hash_table_insert (out, g_steal_pointer (&key_allocated), g_steal_pointer (&value_allocated)); } return g_steal_pointer (&out); }
static int spawn_record_browser(AvahiClient *c, AvahiIfIndex intf, AvahiProtocol proto, const char *hostname, const char *domain, uint16_t type, struct mdns_browser *mb, const char *name, uint16_t port, AvahiStringList *txt) { AvahiRecordBrowser *rb; struct mdns_record_browser *rb_data; char *key; char *value; char *ptr; size_t len; int ret; rb_data = (struct mdns_record_browser *)malloc(sizeof(struct mdns_record_browser)); if (!rb_data) { DPRINTF(E_LOG, L_MDNS, "Out of memory for record browser data\n"); return -1; } memset(rb_data, 0, sizeof(struct mdns_record_browser)); rb_data->mb = mb; rb_data->port = port; rb_data->name = strdup(name); if (!rb_data->name) { DPRINTF(E_LOG, L_MDNS, "Out of memory for service name\n"); goto out_free_rb; } rb_data->domain = strdup(domain); if (!rb_data->domain) { DPRINTF(E_LOG, L_MDNS, "Out of memory for service domain\n"); goto out_free_name; } while (txt) { len = avahi_string_list_get_size(txt); key = (char *)avahi_string_list_get_text(txt); ptr = memchr(key, '=', len); if (!ptr) { value = ""; len = 0; } else { *ptr = '\0'; value = ptr + 1; len -= strlen(key) + 1; } ret = keyval_add_size(&rb_data->txt_kv, key, value, len); if (ptr) *ptr = '='; if (ret < 0) { DPRINTF(E_LOG, L_MDNS, "Could not build TXT record keyval\n"); goto out_free_keyval; } txt = avahi_string_list_get_next(txt); } rb = NULL; switch (type) { case AVAHI_DNS_TYPE_A: rb = avahi_record_browser_new(c, intf, proto, hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_A, 0, browse_record_callback_v4, rb_data); if (!rb) DPRINTF(E_LOG, L_MDNS, "Could not create v4 record browser for host %s: %s\n", hostname, avahi_strerror(avahi_client_errno(c))); break; case AVAHI_DNS_TYPE_AAAA: rb = avahi_record_browser_new(c, intf, proto, hostname, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_AAAA, 0, browse_record_callback_v6, rb_data); if (!rb) DPRINTF(E_LOG, L_MDNS, "Could not create v4 record browser for host %s: %s\n", hostname, avahi_strerror(avahi_client_errno(c))); break; } if (!rb) goto out_free_keyval; return 0; out_free_keyval: keyval_clear(&rb_data->txt_kv); free(rb_data->domain); out_free_name: free(rb_data->name); out_free_rb: free(rb_data); return -1; }
static void browse_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex intf, AvahiProtocol proto, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *hostname, const AvahiAddress *addr, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { char address[AVAHI_ADDRESS_STR_MAX + IF_NAMESIZE + 2]; char ifname[IF_NAMESIZE]; struct keyval txt_kv; struct mdns_browser *mb; char *key; char *value; size_t len; int family; int ll; int ret; mb = (struct mdns_browser *)userdata; switch (event) { case AVAHI_RESOLVER_FAILURE: DPRINTF(E_LOG, L_MDNS, "Avahi Resolver failure: service '%s' type '%s': %s\n", name, type, avahi_strerror(avahi_client_errno(mdns_client))); break; case AVAHI_RESOLVER_FOUND: DPRINTF(E_DBG, L_MDNS, "Avahi Resolver: resolved service '%s' type '%s' proto %d\n", name, type, proto); // KK - if I reject 192.168.* my AirTunes get rejected. // How does avahi passes multiple IP addresses? // ll = is_link_local(addr); ll = 0; switch (proto) { case AVAHI_PROTO_INET: avahi_record_browser_new() if (ll) { family = AF_UNSPEC; DPRINTF(E_DBG, L_MDNS, "Discarding IPv4 LL address\n"); break; } family = AF_INET; avahi_address_snprint(address, sizeof(address), addr); break; case AVAHI_PROTO_INET6: avahi_address_snprint(address, sizeof(address), addr); if (ll) { DPRINTF(E_DBG, L_MDNS, "Appending interface name to IPv6 LL address\n"); if (!if_indextoname(intf, ifname)) { DPRINTF(E_LOG, L_MDNS, "Could not map interface index %d to a name\n", intf); family = AF_UNSPEC; break; } len = strlen(address); ret = snprintf(address + len, sizeof(address) - len, "%%%s", ifname); if ((ret < 0) || (ret > sizeof(address) - len)) { DPRINTF(E_LOG, L_MDNS, "Buffer too short for scoped IPv6 LL\n"); family = AF_UNSPEC; break; } DPRINTF(E_DBG, L_MDNS, "Scoped IPv6 LL: %s\n", address); } family = AF_INET6; break; default: DPRINTF(E_INFO, L_MDNS, "Avahi Resolver: unknown protocol %d\n", proto); family = AF_UNSPEC; break; } if (family == AF_UNSPEC) break; memset(&txt_kv, 0, sizeof(struct keyval)); while (txt) { len = avahi_string_list_get_size(txt); key = (char *)avahi_string_list_get_text(txt); value = memchr(key, '=', len); if (!value) { value = ""; len = 0; } else { *value = '\0'; value++; len -= strlen(key) + 1; } ret = keyval_add_size(&txt_kv, key, value, len); if (ret < 0) { DPRINTF(E_LOG, L_MDNS, "Could not build TXT record keyval\n"); goto out_clear_txt_kv; } txt = avahi_string_list_get_next(txt); } /* Execute callback (mb->cb) with all the data */ mb->cb(name, type, domain, hostname, family, address, port, &txt_kv); out_clear_txt_kv: keyval_clear(&txt_kv); break; } avahi_service_resolver_free(r); }
CAMLprim value stub_avahi_string_list_get_text(value l) { CAMLparam1(l); CAMLreturn(caml_copy_string((const char *)avahi_string_list_get_text((AvahiStringList *)l))); }