From b20dbf0e55d65919c82de19484a06fea22f68e68 Mon Sep 17 00:00:00 2001 From: phil <me@filou.se> Date: Wed, 7 Apr 2021 12:49:36 +0200 Subject: [PATCH] mdns minor updates, dnssd adaptions, first avahi working draft --- src/include/aes67/utils/mdns.h | 2 +- src/utils/mdns-avahi.c | 378 ++++++++++++++++++++++++++---- src/utils/mdns-dnssd.c | 9 +- src/utils/rav-lookup/rav-lookup.c | 24 +- 4 files changed, 348 insertions(+), 65 deletions(-) diff --git a/src/include/aes67/utils/mdns.h b/src/include/aes67/utils/mdns.h index 30109ac..e7a0112 100644 --- a/src/include/aes67/utils/mdns.h +++ b/src/include/aes67/utils/mdns.h @@ -75,7 +75,7 @@ aes67_mdns_register_start(aes67_mdns_context_t ctx, const char *fullname, u16_t void aes67_mdns_stop(aes67_mdns_resource_t res); -void aes67_mdns_process(aes67_mdns_context_t ctx, struct timeval *timeout); +void aes67_mdns_process(aes67_mdns_context_t ctx, int timeout_msec); void aes67_mdns_getsockfds(aes67_mdns_context_t ctx, int * fds[], size_t *count); diff --git a/src/utils/mdns-avahi.c b/src/utils/mdns-avahi.c index 1fb13c6..ad1d5e9 100644 --- a/src/utils/mdns-avahi.c +++ b/src/utils/mdns-avahi.c @@ -19,6 +19,7 @@ #include "aes67/utils/mdns.h" #include <assert.h> +#include <string.h> //#include <syslog.h> #include <avahi-client/client.h> @@ -29,15 +30,123 @@ //#error TODO see https://www.avahi.org/doxygen/html/client-browse-services_8c-example.html -typedef struct { - AvahiServiceBrowser *sb; -} browser_t; +// forward decl +struct context_st; -typedef struct { +enum restype { + restype_undefined, + restype_browse, + restype_resolve, + restype_resolve2_browse, + restype_resolve2_resolve, + restype_register_pending, + restype_register_done, + restype_publish_service_pending, + restype_publish_service_done, +}; + +typedef struct resource_st { + struct context_st * context; + void * res; + enum restype type; + + void * callback; + void * user_data; + + struct resource_st * next; +} resource_t; + + +typedef struct context_st { AvahiSimplePoll *simple_poll; AvahiClient *client; -} avahi_context_t; + resource_t * first_resource; +} context_t; + +static resource_t * resource_new(context_t * context, enum restype restype, void *callback, void * user_data) +{ + + resource_t * r = calloc(1, sizeof(resource_t)); + + r->context = context; + r->type = restype; + + r->callback = callback; + r->user_data = user_data; + + + return r; +} + +static void resource_link(context_t * context, resource_t * res) +{ + res->next = context->first_resource; + context->first_resource = res; +} + +static void resource_delete(context_t * context, resource_t * res) +{ + if (context->first_resource == res){ + context->first_resource = res->next; + } else { + resource_t * prev = context->first_resource; + + while(prev != NULL){ + + if (prev->next == res){ + prev->next = res->next; + break; + } + + prev = prev->next; + } + } + + if (res->type == restype_browse && res->res){ + avahi_service_browser_free(res->res); + } + else if (res->type == restype_resolve && res->res){ + avahi_service_resolver_free(res->res); + } + + free(res); +} + +static char* avahi_string_list_to_raw(AvahiStringList *l, uint16_t * len) { + AvahiStringList *n; + size_t s = 0; + char *t, *e; + + *len = 0; + + for (n = l; n; n = n->next) { + s += 1 + n->size; /* +1 for the leading segment length byte */ + } + + if (!(t = e = malloc(s+1))) + return NULL; + + l = avahi_string_list_reverse(l); + + for (n = l; n; n = n->next) { + *(e++) = n->size; + for(int i = 0; i < n->size; i++){ + *(e++) = n->text[i]; + } + + assert(e); + } + + l = avahi_string_list_reverse(l); + +// *e = 0; // actually, this is not needed, but let's play it safe. + + // make sure to tell caller size of total txt + *len = s; + + return t; +} static void resolve_callback( AvahiServiceResolver *r, @@ -53,37 +162,49 @@ static void resolve_callback( AvahiStringList *txt, AvahiLookupResultFlags flags, AVAHI_GCC_UNUSED void* userdata) { + + resource_t * res = userdata; + assert(res); + assert(r); + + char * txtstr = NULL; + uint16_t txtlen = 0; + + enum aes67_mdns_result result = aes67_mdns_result_error; + /* Called whenever a service has been resolved successfully or timed out */ switch (event) { case AVAHI_RESOLVER_FAILURE: - fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); +// fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))); + result = aes67_mdns_result_error; break; + case AVAHI_RESOLVER_FOUND: { - char a[AVAHI_ADDRESS_STR_MAX], *t; - fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n", name, type, domain); - avahi_address_snprint(a, sizeof(a), address); - t = avahi_string_list_to_string(txt); - fprintf(stderr, - "\t%s:%u (%s)\n" - "\tTXT=%s\n" - "\tcookie is %u\n" - "\tis_local: %i\n" - "\tour_own: %i\n" - "\twide_area: %i\n" - "\tmulticast: %i\n" - "\tcached: %i\n", - host_name, port, a, - t, - avahi_string_list_get_service_cookie(txt), - !!(flags & AVAHI_LOOKUP_RESULT_LOCAL), - !!(flags & AVAHI_LOOKUP_RESULT_OUR_OWN), - !!(flags & AVAHI_LOOKUP_RESULT_WIDE_AREA), - !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), - !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); - avahi_free(t); +// fprintf(stderr, "Service '%s' of type '%s' in domain '%s':\n", name, type, domain); + result = aes67_mdns_result_discovered; + txtstr = avahi_string_list_to_raw(txt, &txtlen); } } + + + enum aes67_net_ipver ipver = aes67_net_ipver_undefined; + const uint8_t * ip = NULL; + if (address->proto == AVAHI_PROTO_INET){ + ipver = aes67_net_ipver_4; + ip = (uint8_t*)&address->data.ipv4.address; + } else if (address->proto == AVAHI_PROTO_INET6){ + ipver = aes67_net_ipver_6; + ip = address->data.ipv6.address; + } + + uint16_t ttl = 0; + + ((aes67_mdns_resolve_callback) res->callback)(res, result, type, name, host_name, + port, txtlen, (uint8_t*)txtstr, ipver, ip, ttl, res->user_data); + if (txtstr) + free(txtstr); + avahi_service_resolver_free(r); } static void browse_callback( @@ -96,45 +217,75 @@ static void browse_callback( const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata) { - AvahiClient *c = userdata; - assert(b); + + resource_t * res = userdata; + assert(res); + + enum aes67_mdns_result result; + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ switch (event) { case AVAHI_BROWSER_FAILURE: - fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); - avahi_simple_poll_quit(simple_poll); +// fprintf(stderr, "(Browser) %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); +// avahi_simple_poll_quit(res->context->simple_poll); + result = aes67_mdns_result_error; return; case AVAHI_BROWSER_NEW: - fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); - /* We ignore the returned resolver object. In the callback - function we free it. If the server is terminated before - the callback function is called the server will free - the resolver for us. */ - if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) - fprintf(stderr, "Failed to resolve service '%s': %s\n", name, avahi_strerror(avahi_client_errno(c))); +// fprintf(stderr, "(Browser) NEW: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + result = aes67_mdns_result_discovered; break; case AVAHI_BROWSER_REMOVE: - fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); +// fprintf(stderr, "(Browser) REMOVE: service '%s' of type '%s' in domain '%s'\n", name, type, domain); + result = aes67_mdns_result_terminated; break; + case AVAHI_BROWSER_ALL_FOR_NOW: case AVAHI_BROWSER_CACHE_EXHAUSTED: - fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); - break; +// fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); + return; + + default: + // ignore + return; + } + + + + if (res->type == restype_browse){ + ((aes67_mdns_browse_callback)res->callback)(res, result, type, name, domain, res->user_data); + } + else if (res->type == restype_resolve2_browse){ + + if (result == aes67_mdns_result_error){ + ((aes67_mdns_resolve_callback)res->callback)(res, aes67_mdns_result_error, NULL, NULL, NULL, 0, 0, NULL, aes67_net_ipver_undefined, NULL, 0, res->user_data); + return; + } + + if (!(avahi_service_resolver_new(res->context->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, res))) { + fprintf(stderr, "Failed to resolve service '%s': %s\n", name, + avahi_strerror(avahi_client_errno(res->context->client))); + } } } + static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { assert(c); + + context_t * context = userdata; + /* Called whenever the client or server state changes */ if (state == AVAHI_CLIENT_FAILURE) { - fprintf(stderr, "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(c))); - avahi_simple_poll_quit(simple_poll); + fprintf(stderr, "Server connection failure: %s\n", avahi_strerror(avahi_client_errno(context->client))); + avahi_simple_poll_quit(context->simple_poll); } } aes67_mdns_context_t aes67_mdns_new(void) { - avahi_context_t * context = calloc(1, sizeof(avahi_context_t); + int error; + + context_t * context = calloc(1, sizeof(context_t)); /* Allocate main loop object */ if (!(context->simple_poll = avahi_simple_poll_new())) { @@ -144,7 +295,7 @@ aes67_mdns_context_t aes67_mdns_new(void) } /* Allocate a new client */ - context->client = avahi_client_new(avahi_simple_poll_get(simple_poll), 0, client_callback, NULL, &error); + context->client = avahi_client_new(avahi_simple_poll_get(context->simple_poll), 0, client_callback, context, &error); /* Check wether creating the client object succeeded */ if (!context->client) { @@ -160,9 +311,11 @@ void aes67_mdns_delete(aes67_mdns_context_t ctx) { assert(ctx != NULL); - avahi_context_t * context = ctx; - + context_t * context = ctx; + if (context->client){ + avahi_client_free(context->client); + } if (context->simple_poll){ avahi_simple_poll_free(context->simple_poll); @@ -170,3 +323,132 @@ void aes67_mdns_delete(aes67_mdns_context_t ctx) free(context); } + + +aes67_mdns_resource_t +aes67_mdns_browse_start(aes67_mdns_context_t ctx, const char *type, const char *domain, + aes67_mdns_browse_callback callback, void *user_data) +{ + assert(ctx); + + context_t * context = ctx; + + AvahiIfIndex interface = AVAHI_IF_UNSPEC; + AvahiProtocol protocol = AVAHI_PROTO_UNSPEC; + + AvahiLookupFlags flags = 0; + + resource_t * res = resource_new(context, restype_browse, callback, user_data); + + AvahiServiceBrowser * sb = res->res = avahi_service_browser_new(context->client, interface, protocol, type, domain, flags, browse_callback, res); + + if (!sb){ + free(res); + return NULL; + } + + resource_link(context, res); + + return res; +} + +aes67_mdns_resource_t +aes67_mdns_resolve_start(aes67_mdns_context_t ctx, const char *type, const char *name, const char *domain, + aes67_mdns_resolve_callback callback, void *user_data) +{ + assert(ctx); + + context_t * context = ctx; + + AvahiIfIndex interface = AVAHI_IF_UNSPEC; + AvahiProtocol protocol = AVAHI_PROTO_UNSPEC; + + AvahiLookupFlags flags = 0; + + resource_t * res = resource_new(context, restype_resolve, callback, user_data); + + AvahiServiceResolver * sr = res->res = avahi_service_resolver_new(res->context->client, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, flags, resolve_callback, res); + + if (!sr) { + fprintf(stderr, "Failed to resolve service '%s': %s\n", name, + avahi_strerror(avahi_client_errno(res->context->client))); + free(res); + return NULL; + } + + resource_link(context, res); + + return res; +} + +aes67_mdns_resource_t +aes67_mdns_resolve2_start(aes67_mdns_context_t ctx, const char *type, const char *domain, + aes67_mdns_resolve_callback callback, void *user_data) +{ + assert(ctx); + + context_t * context = ctx; + + AvahiIfIndex interface = AVAHI_IF_UNSPEC; + AvahiProtocol protocol = AVAHI_PROTO_UNSPEC; + + AvahiLookupFlags flags = 0; + + resource_t * res = resource_new(context, restype_resolve2_browse, callback, user_data); + + AvahiServiceBrowser * sb = res->res = avahi_service_browser_new(context->client, interface, protocol, type, domain, flags, browse_callback, res); + + if (!sb){ + free(res); + return NULL; + } + + resource_link(context, res); + + return res; +} + +aes67_mdns_resource_t +aes67_mdns_service_start(aes67_mdns_context_t ctx, const char *type, const char *name, const char *domain, + const char * host, u16_t port, u16_t txtlen, const u8_t * txt, aes67_mdns_service_callback callback, void *user_data) +{ + return NULL; +} + +aes67_mdns_resource_t +aes67_mdns_service_addrecord(aes67_mdns_context_t ctx, aes67_mdns_resource_t service, u16_t rrtype, u16_t rdlen, const u8_t * rdata, u32_t ttl) +{ + return NULL; +} + +aes67_mdns_resource_t +aes67_mdns_register_start(aes67_mdns_context_t ctx, const char *fullname, u16_t rrtype, u16_t rrclass, u16_t rdlen, const u8_t * rdata, u32_t ttl, aes67_mdns_register_callback callback, void *user_data) +{ + return NULL; +} + + +void aes67_mdns_stop(aes67_mdns_resource_t res) +{ + assert(res); + + resource_t * r = res; + + + resource_delete(r->context, r); +} + + +void aes67_mdns_process(aes67_mdns_context_t ctx, int timeout_msec) +{ + assert(ctx != NULL); + + context_t * context = ctx; + + avahi_simple_poll_iterate(context->simple_poll, timeout_msec); +} + +int aes67_mdns_geterrcode(aes67_mdns_resource_t res) +{ + return 0; +} \ No newline at end of file diff --git a/src/utils/mdns-dnssd.c b/src/utils/mdns-dnssd.c index 92643d4..97c2e2c 100644 --- a/src/utils/mdns-dnssd.c +++ b/src/utils/mdns-dnssd.c @@ -403,11 +403,12 @@ static void resolve_callback(DNSServiceRef ref, DNSServiceFlags flags, u32_t int if (hosttarget != NULL){ res2->hostTarget = calloc(1, strlen(hosttarget)+1); strcpy(res2->hostTarget, hosttarget); + res2->hostTarget[strlen(hosttarget)-1] = '\0'; } res2->port = ntohs(port); - res2->txtlen = txtlen; - if (txtlen > 0){ + if (txtlen > 1){ + res2->txtlen = txtlen; res2->txt = calloc(1, txtlen); memcpy(res2->txt, txt, txtlen); } @@ -681,7 +682,7 @@ void aes67_mdns_stop(aes67_mdns_resource_t res) -void aes67_mdns_process(aes67_mdns_context_t ctx, struct timeval *timeout) +void aes67_mdns_process(aes67_mdns_context_t ctx, int timeout_msec) { assert(ctx != NULL); @@ -693,7 +694,7 @@ void aes67_mdns_process(aes67_mdns_context_t ctx, struct timeval *timeout) FD_ZERO(&fds); FD_SET(__ctx->sockfd, &fds); - int retval = select(nfds, &fds, NULL, &fds, timeout); + int retval = select(nfds, &fds, NULL, &fds, NULL); if (retval > 0){ DNSServiceProcessResult(__ctx->sharedRef); } diff --git a/src/utils/rav-lookup/rav-lookup.c b/src/utils/rav-lookup/rav-lookup.c index f188943..4f59ab1 100644 --- a/src/utils/rav-lookup/rav-lookup.c +++ b/src/utils/rav-lookup/rav-lookup.c @@ -96,11 +96,11 @@ void session_lookup_callback(aes67_mdns_resource_t res, enum aes67_mdns_result r fprintf(stderr, "%s %s._session_ravenna._sub._rtsp._tcp @ %s:%hu (%s) [%d](", result == aes67_mdns_result_discovered ? "DISCOVERED" : "TERMINATED", name, hosttarget, port, ipstr, txtlen); - for (int i = 0; i < txtlen-1; i++){ + for (int i = 0; i < txtlen; i++){ if (isprint(txt[i])){ fprintf(stderr, "%c", txt[i]); } else { - fprintf(stderr, " "); + fprintf(stderr, " [%d]", txt[i]); } } fprintf(stderr, ")\n"); @@ -110,7 +110,7 @@ void session_lookup_callback(aes67_mdns_resource_t res, enum aes67_mdns_result r if (result == opts.result_filter){ char host[256]; - size_t hostlen = strlen((char*)hosttarget) - 1; + size_t hostlen = strlen((char*)hosttarget); assert( hostlen < sizeof(host) - 1); @@ -160,11 +160,11 @@ void receiver_lookup_callback(aes67_mdns_resource_t res, enum aes67_mdns_result fprintf(stderr, "DISCOVERED %s._ravenna._http._tcp @ %s:%hu (%s) [%d](", name, hosttarget, port, ipstr, txtlen); - for (int i = 0; i < txtlen-1; i++){ + for (int i = 0; i < txtlen; i++){ if (isprint(txt[i])){ fprintf(stderr, "%c", txt[i]); } else { - fprintf(stderr, " "); + fprintf(stderr, " [%d]", txt[i]); } } fprintf(stderr, ")\n"); @@ -173,9 +173,9 @@ void receiver_lookup_callback(aes67_mdns_resource_t res, enum aes67_mdns_result } if (result == opts.result_filter){ char host[256]; - size_t hostlen = strlen((char*)hosttarget) - 1; + size_t hostlen = strlen((char*)hosttarget); - assert( hostlen < sizeof(host) - 1); + assert( hostlen < sizeof(host)-1); memcpy(host, hosttarget, hostlen); host[hostlen] = '\0'; @@ -200,11 +200,11 @@ void sender_lookup_callback(aes67_mdns_resource_t res, enum aes67_mdns_result re fprintf(stderr, "DISCOVERED %s._ravenna._rtsp._tcp @ %s:%hu (%s) [%d](", name, hosttarget, port, ipstr, txtlen); - for (int i = 0; i < txtlen-1; i++){ + for (int i = 0; i < txtlen; i++){ if (isprint(txt[i])){ fprintf(stderr, "%c", txt[i]); } else { - fprintf(stderr, " "); + fprintf(stderr, " [%d]", txt[i]); } } fprintf(stderr, ")\n"); @@ -214,9 +214,9 @@ void sender_lookup_callback(aes67_mdns_resource_t res, enum aes67_mdns_result re if (result == opts.result_filter){ char host[256]; - size_t hostlen = strlen((char*)hosttarget) - 1; + size_t hostlen = strlen((char*)hosttarget); - assert( hostlen < sizeof(host) - 1); + assert( hostlen < sizeof(host)-1); memcpy(host, hosttarget, hostlen); host[hostlen] = '\0'; @@ -341,7 +341,7 @@ int main(int argc, char * argv[]) signal(SIGINT, sig_int); keep_running = true; while(keep_running){ - aes67_mdns_process(ctx, NULL); + aes67_mdns_process(ctx, 0); } aes67_mdns_delete(ctx); -- GitLab