1062 lines
41 KiB
C
1062 lines
41 KiB
C
#include <stdio.h>
|
||
|
||
#ifdef ENABLE_DNSSD
|
||
#ifdef ENABLE_BONJOUR
|
||
#include "dns_sd.h"
|
||
|
||
|
||
typedef uint32_t t_svc_iface_idx;
|
||
typedef struct {
|
||
uint16_t txtLen;
|
||
const unsigned char *txtStr;
|
||
} t_svc_txt_record;
|
||
#elif defined ENABLE_AVAHI
|
||
#include <netinet/in.h>
|
||
#include <avahi-client/client.h>
|
||
#include <avahi-client/lookup.h>
|
||
|
||
#include <avahi-common/simple-watch.h>
|
||
#include <avahi-common/malloc.h>
|
||
#include <avahi-common/error.h>
|
||
|
||
typedef AvahiIfIndex t_svc_iface_idx;
|
||
typedef AvahiStringList* t_svc_txt_record;
|
||
#else
|
||
#error Invalid DNSSD backend
|
||
#endif
|
||
#include "ltimer.h"
|
||
#include "e502api_tcp_private.h"
|
||
|
||
|
||
|
||
#include <stdlib.h>
|
||
|
||
#define E502_SVC_REC_SIGN 0xE502CCA5
|
||
#define E502_SVC_BROWSE_SIGN 0xE502BBA5
|
||
|
||
#define SERVICE_CONTEXT_MAX_CNT 1024
|
||
|
||
|
||
#define E502_TCP_SERVICE_NAME "_lcard_acqdev._tcp"
|
||
|
||
|
||
#define E502_CHECK_SVC_REC(svc) ((svc != NULL) ? (svc)->sign == E502_SVC_REC_SIGN ? X502_ERR_OK \
|
||
: X502_ERR_INVALID_SVC_RECORD_HANDLE : X502_ERR_INVALID_SVC_RECORD_HANDLE)
|
||
|
||
#define E502_CHECK_BROWSE_CONTEXT(svc) ((svc != NULL) ? (svc)->sign == E502_SVC_BROWSE_SIGN ? X502_ERR_OK \
|
||
: X502_ERR_INVALID_SVC_BROWSE_HANDLE : X502_ERR_INVALID_SVC_BROWSE_HANDLE)
|
||
|
||
typedef struct st_e502_eth_svc_record {
|
||
uint32_t sign;
|
||
char devname[X502_DEVNAME_SIZE];
|
||
char serial[X502_SERIAL_SIZE];
|
||
char service_name[X502_INSTANCE_NAME_SIZE];
|
||
char *domain;
|
||
char *hosttarget;
|
||
uint16_t port;
|
||
t_svc_iface_idx iface_idx;
|
||
uint32_t ipv4_addr;
|
||
int op_in_progr;
|
||
int32_t op_err;
|
||
} t_svc_record;
|
||
|
||
|
||
|
||
typedef struct {
|
||
#if defined ENABLE_BONJOUR
|
||
DNSServiceRef sdref;
|
||
DNSServiceRef sdref_addr;
|
||
#elif defined ENABLE_AVAHI
|
||
AvahiServiceResolver *resolver;
|
||
#endif
|
||
t_e502_eth_svc_event event;
|
||
int init_done;
|
||
int fail;
|
||
t_e502_eth_svc_record_hnd rec;
|
||
} t_service_context;
|
||
|
||
typedef struct st_e502_eth_svc_browse_context {
|
||
uint32_t sign;
|
||
#if defined ENABLE_BONJOUR
|
||
DNSServiceRef main_ref;
|
||
DNSServiceRef browse_ref;
|
||
int socket;
|
||
#elif defined ENABLE_AVAHI
|
||
AvahiSimplePoll *poll;
|
||
AvahiClient *client;
|
||
AvahiServiceBrowser *sb;
|
||
#endif
|
||
int32_t err;
|
||
unsigned svc_cnt;
|
||
t_service_context services[SERVICE_CONTEXT_MAX_CNT];
|
||
} t_e502_service_browse_context;
|
||
|
||
|
||
|
||
#if defined ENABLE_BONJOUR
|
||
void DNSSD_API f_cb_resolve (DNSServiceRef sdRef, DNSServiceFlags flags,
|
||
uint32_t interfaceIndex,
|
||
DNSServiceErrorType errorCode,
|
||
const char *fullname, const char *hosttarget,
|
||
uint16_t port, /* In network byte order */
|
||
uint16_t txtLen,
|
||
const unsigned char *txtRecord,
|
||
void *context);
|
||
|
||
#elif defined ENABLE_AVAHI
|
||
|
||
static void f_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
|
||
AvahiProtocol protocol, AvahiResolverEvent event,
|
||
const char *name, const char *type,
|
||
const char *domain, const char *host_name,
|
||
const AvahiAddress *address, uint16_t port,
|
||
AvahiStringList *txt, AvahiLookupResultFlags flags,
|
||
void* userdata);
|
||
#endif
|
||
|
||
|
||
|
||
static LINLINE void f_set_timeval_left(t_ltimer *tmr, struct timeval *tval) {
|
||
t_lclock_ticks left = ltimer_expiration(tmr);
|
||
tval->tv_sec = left / LCLOCK_TICKS_PER_SECOND;
|
||
tval->tv_usec = (left % LCLOCK_TICKS_PER_SECOND) * 1000000/LCLOCK_TICKS_PER_SECOND;
|
||
}
|
||
|
||
|
||
|
||
static t_e502_eth_svc_record_hnd f_service_record_create(void) {
|
||
t_e502_eth_svc_record_hnd svc = calloc(1, sizeof(t_svc_record));
|
||
if (svc != NULL) {
|
||
svc->sign = E502_SVC_REC_SIGN;
|
||
}
|
||
return svc;
|
||
}
|
||
|
||
static t_e502_eth_svc_record_hnd f_service_record_create_copy(t_e502_eth_svc_record_hnd rec) {
|
||
t_e502_eth_svc_record_hnd ret = NULL;
|
||
if (E502_CHECK_SVC_REC(rec) == X502_ERR_OK) {
|
||
ret = f_service_record_create();
|
||
memcpy(ret, rec, sizeof(t_svc_record));
|
||
if (rec->domain != NULL) {
|
||
size_t len = strlen(rec->domain) + 1;
|
||
ret->domain = malloc(len);
|
||
if (ret->domain != NULL) {
|
||
strcpy(ret->domain, rec->domain);
|
||
} else {
|
||
E502_EthSvcRecordFree(ret);
|
||
ret = NULL;
|
||
}
|
||
}
|
||
ret->hosttarget = NULL;
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordFree(t_e502_eth_svc_record_hnd rec) {
|
||
int32_t err = E502_CHECK_SVC_REC(rec);
|
||
if (err == X502_ERR_OK) {
|
||
if (rec->domain != NULL)
|
||
free(rec->domain);
|
||
if (rec->hosttarget)
|
||
free(rec->hosttarget);
|
||
memset(rec, 0, sizeof(t_svc_record));
|
||
free(rec);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
static void f_svc_context_free(t_service_context *svc_context) {
|
||
#if defined ENABLE_BONJOUR
|
||
if (svc_context->sdref != NULL)
|
||
DNSServiceRefDeallocate(svc_context->sdref);
|
||
if (svc_context->sdref_addr)
|
||
DNSServiceRefDeallocate(svc_context->sdref_addr);
|
||
#elif defined ENABLE_AVAHI
|
||
if (svc_context->resolver)
|
||
avahi_service_resolver_free(svc_context->resolver);
|
||
#endif
|
||
if (svc_context->rec != NULL)
|
||
E502_EthSvcRecordFree(svc_context->rec);
|
||
svc_context->event = E502_ETH_SVC_EVENT_NONE;
|
||
svc_context->init_done = 0;
|
||
}
|
||
|
||
static int32_t f_browse_context_free(t_e502_eth_svc_browse_hnd context) {
|
||
int32_t err = E502_CHECK_BROWSE_CONTEXT(context);
|
||
if (err == X502_ERR_OK) {
|
||
unsigned i;
|
||
for (i=0; i < context->svc_cnt; i++) {
|
||
f_svc_context_free(&context->services[i]);
|
||
}
|
||
#if defined ENABLE_BONJOUR
|
||
if (context->browse_ref != NULL)
|
||
DNSServiceRefDeallocate(context->browse_ref);
|
||
if (context->main_ref != NULL)
|
||
DNSServiceRefDeallocate(context->main_ref);
|
||
#elif defined ENABLE_AVAHI
|
||
if (context->sb != NULL)
|
||
avahi_service_browser_free(context->sb);
|
||
if (context->client != NULL)
|
||
avahi_client_free(context->client);
|
||
if (context->poll != NULL) {
|
||
avahi_simple_poll_quit(context->poll);
|
||
avahi_simple_poll_free(context->poll);
|
||
}
|
||
#endif
|
||
memset(context, 0, sizeof(t_e502_service_browse_context));
|
||
free(context);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
#if defined ENABLE_BONJOUR
|
||
static const char* f_get_txt_rec(t_svc_txt_record txt_record, const char *name, uint8_t *len) {
|
||
return TXTRecordGetValuePtr(txt_record.txtLen, txt_record.txtStr, name, len);
|
||
}
|
||
#elif defined ENABLE_AVAHI
|
||
static const char* f_get_txt_rec(t_svc_txt_record txt_record, const char *name, uint8_t *len) {
|
||
const char *ret = NULL;
|
||
int out = 0;
|
||
int name_len = strlen(name);
|
||
while (!out) {
|
||
const char *label = (const char*)txt_record->text;
|
||
int rec_len = strlen(label);
|
||
const char *label_end = strchr(label, '=');
|
||
int label_len = (label_end != NULL) ? label_end - label : rec_len;
|
||
if ((name_len == label_len) && !memcmp(name, label, label_len)) {
|
||
int val_len = label_end == NULL ? 0 : rec_len - label_len;
|
||
if (val_len != 0) {
|
||
ret = label_end+1;
|
||
} else {
|
||
ret = NULL;
|
||
}
|
||
*len = val_len;
|
||
out = 1;
|
||
}
|
||
|
||
if (!out) {
|
||
if (txt_record->next != NULL) {
|
||
txt_record = txt_record->next;
|
||
} else {
|
||
out = 1;
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
#endif
|
||
|
||
/* Анализ текстовой информации о найденном сервисе и проверка, подходит ли он */
|
||
static int32_t f_svc_check_resolved(t_service_context *svc_context,
|
||
t_svc_txt_record txt_record) {
|
||
uint8_t len;
|
||
int32_t err = X502_ERR_OK;
|
||
const char *devname_ptr = f_get_txt_rec(txt_record, "devname", &len);
|
||
const char *serial_ptr;
|
||
if ((devname_ptr != NULL) &&
|
||
(!strncmp(devname_ptr, E502_DEVICE_NAME, len) || !strncmp(devname_ptr, E16_DEVICE_NAME, len))) {
|
||
memcpy(svc_context->rec->devname, devname_ptr, len);
|
||
serial_ptr = f_get_txt_rec(txt_record, "serial", &len);
|
||
if (serial_ptr != NULL) {
|
||
if (len >= X502_SERIAL_SIZE)
|
||
len = X502_SERIAL_SIZE-1;
|
||
memcpy(svc_context->rec->serial, serial_ptr, len);
|
||
svc_context->rec->serial[len] = 0;
|
||
}
|
||
} else {
|
||
err = X502_ERR_INVALID_DEVICE;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
static void f_add_new_svc(t_e502_eth_svc_browse_hnd browse_context, t_svc_iface_idx iface_idx,
|
||
const char *svc_name, const char *domain, const char *regtype) {
|
||
if (browse_context->svc_cnt < SERVICE_CONTEXT_MAX_CNT) {
|
||
t_service_context *svc_context = &browse_context->services[browse_context->svc_cnt];
|
||
size_t domain_len = strlen(domain) + 1;
|
||
|
||
memset(svc_context, 0, sizeof(t_service_context));
|
||
svc_context->rec = f_service_record_create();
|
||
if (svc_context->rec != NULL) {
|
||
strncpy(svc_context->rec->service_name, svc_name, X502_INSTANCE_NAME_SIZE-1);
|
||
svc_context->rec->service_name[X502_INSTANCE_NAME_SIZE-1] = '\0';
|
||
svc_context->rec->domain = malloc(domain_len);
|
||
if (svc_context->rec->domain != NULL) {
|
||
strcpy(svc_context->rec->domain, domain);
|
||
|
||
svc_context->rec->iface_idx = iface_idx;
|
||
#ifdef ENABLE_BONJOUR
|
||
svc_context->sdref = browse_context->main_ref;
|
||
if (DNSServiceResolve(&svc_context->sdref, kDNSServiceFlagsShareConnection,
|
||
iface_idx, svc_name, regtype, domain,
|
||
f_cb_resolve, browse_context) == kDNSServiceErr_NoError) {
|
||
browse_context->svc_cnt++;
|
||
} else {
|
||
svc_context->sdref = NULL;
|
||
E502_EthSvcRecordFree(svc_context->rec);
|
||
}
|
||
#elif defined ENABLE_AVAHI
|
||
svc_context->resolver = avahi_service_resolver_new(browse_context->client,
|
||
iface_idx, AVAHI_PROTO_INET,
|
||
svc_name, regtype, domain,
|
||
AVAHI_PROTO_UNSPEC, 0, f_resolve_callback, browse_context);
|
||
if (svc_context->resolver != NULL) {
|
||
browse_context->svc_cnt++;
|
||
} else {
|
||
E502_EthSvcRecordFree(svc_context->rec);
|
||
}
|
||
#endif
|
||
} else {
|
||
E502_EthSvcRecordFree(svc_context->rec);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void f_remove_service_idx(t_e502_eth_svc_browse_hnd browse_context, unsigned i) {
|
||
t_service_context *svc_context = &browse_context->services[i];
|
||
if (svc_context->init_done && !svc_context->fail) {
|
||
svc_context->event = E502_ETH_SVC_EVENT_REMOVE;
|
||
} else {
|
||
f_svc_context_free(svc_context);
|
||
if (i != (browse_context->svc_cnt-1)) {
|
||
memmove(&browse_context->services[i], &browse_context->services[i+1],
|
||
(browse_context->svc_cnt-i-1)*sizeof(browse_context->services[0]));
|
||
}
|
||
browse_context->svc_cnt--;
|
||
}
|
||
}
|
||
|
||
|
||
static void f_svc_fail(t_e502_eth_svc_browse_hnd browse_context, unsigned i) {
|
||
t_service_context *svc_context = &browse_context->services[i];
|
||
if (svc_context->init_done) {
|
||
svc_context->event = E502_ETH_SVC_EVENT_REMOVE;
|
||
}
|
||
svc_context->fail = 1;
|
||
svc_context->init_done = 0;
|
||
}
|
||
|
||
|
||
|
||
static void f_remove_service(t_e502_eth_svc_browse_hnd browse_context, const char *svc_name,
|
||
const char *domain) {
|
||
unsigned i;
|
||
for (i = 0; i < browse_context->svc_cnt; i++) {
|
||
t_service_context *svc_context = &browse_context->services[i];
|
||
if (!strcmp(svc_name, svc_context->rec->service_name) &&
|
||
!strcmp(domain, svc_context->rec->domain)) {
|
||
f_remove_service_idx(browse_context, i);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
#ifdef ENABLE_BONJOUR
|
||
static uint32_t f_get_addr(const struct sockaddr *address) {
|
||
uint32_t ipv4_addr = 0;
|
||
if (address && address->sa_family == AF_INET) {
|
||
const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr;
|
||
ipv4_addr = ((uint32_t)b[0] << 24) |
|
||
((uint32_t)b[1] << 16) |
|
||
((uint32_t)b[2] << 8) |
|
||
b[3];
|
||
}
|
||
return ipv4_addr;
|
||
}
|
||
|
||
|
||
|
||
static void DNSSD_API f_get_addr_info_reply(
|
||
DNSServiceRef sdRef,
|
||
DNSServiceFlags flags,
|
||
uint32_t interfaceIndex,
|
||
DNSServiceErrorType errorCode,
|
||
const char *hostname,
|
||
const struct sockaddr *address,
|
||
uint32_t ttl,
|
||
void *context
|
||
) {
|
||
t_e502_eth_svc_record_hnd rec = (t_e502_eth_svc_record_hnd)context;
|
||
if (errorCode != kDNSServiceErr_NoError) {
|
||
rec->op_err = X502_ERR_DNSSD_COMMUNICATION;
|
||
rec->op_in_progr = 0;
|
||
} else {
|
||
if (address && address->sa_family == AF_INET) {
|
||
rec->ipv4_addr = f_get_addr(address);
|
||
rec->op_err = X502_ERR_OK;
|
||
rec->op_in_progr = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
void DNSSD_API f_cb_gethosttarget(DNSServiceRef sdRef, DNSServiceFlags flags,
|
||
uint32_t interfaceIndex,
|
||
DNSServiceErrorType errorCode,
|
||
const char *fullname, const char *hosttarget,
|
||
uint16_t port, /* In network byte order */
|
||
uint16_t txtLen,
|
||
const unsigned char *txtRecord,
|
||
void *context) {
|
||
|
||
t_e502_eth_svc_record_hnd rec = (t_e502_eth_svc_record_hnd)context;
|
||
if (errorCode != kDNSServiceErr_NoError) {
|
||
rec->op_err = X502_ERR_DNSSD_COMMUNICATION;
|
||
} else {
|
||
size_t hostlen = strlen(hosttarget)+1;
|
||
rec->port = ntohs(port);
|
||
if (rec->hosttarget != NULL)
|
||
free(rec->hosttarget);
|
||
rec->hosttarget = malloc(hostlen);
|
||
if (rec->hosttarget != NULL) {
|
||
strcpy(rec->hosttarget, hosttarget);
|
||
rec->op_err = X502_ERR_OK;
|
||
} else {
|
||
rec->op_err = X502_ERR_MEMORY_ALLOC;
|
||
}
|
||
}
|
||
rec->op_in_progr = 0;
|
||
}
|
||
|
||
|
||
static void DNSSD_API f_cb_browse_addr(
|
||
DNSServiceRef sdRef,
|
||
DNSServiceFlags flags,
|
||
uint32_t interfaceIndex,
|
||
DNSServiceErrorType errorCode,
|
||
const char *hostname,
|
||
const struct sockaddr *address,
|
||
uint32_t ttl,
|
||
void *context
|
||
) {
|
||
t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)context;
|
||
|
||
unsigned i;
|
||
|
||
for (i=0; i < browse_context->svc_cnt; i++) {
|
||
t_service_context *svc_context = &browse_context->services[i];
|
||
if (svc_context->sdref_addr == sdRef) {
|
||
if (errorCode == kDNSServiceErr_NoError) {
|
||
if (address && address->sa_family == AF_INET) {
|
||
uint32_t new_addr = f_get_addr(address);
|
||
if (svc_context->init_done == 0) {
|
||
svc_context->rec->ipv4_addr = new_addr;
|
||
svc_context->init_done = 1;
|
||
svc_context->event = E502_ETH_SVC_EVENT_ADD;
|
||
} else if (svc_context->event == E502_ETH_SVC_EVENT_NONE) {
|
||
if (new_addr != svc_context->rec->ipv4_addr) {
|
||
svc_context->rec->ipv4_addr = new_addr;
|
||
svc_context->event = E502_ETH_SVC_EVENT_CHANGED;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void DNSSD_API f_cb_resolve (DNSServiceRef sdRef, DNSServiceFlags flags,
|
||
uint32_t interfaceIndex,
|
||
DNSServiceErrorType errorCode,
|
||
const char *fullname, const char *hosttarget,
|
||
uint16_t port, /* In network byte order */
|
||
uint16_t txtLen,
|
||
const unsigned char *txtRecord,
|
||
void *context) {
|
||
t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)context;
|
||
|
||
unsigned i;
|
||
|
||
for (i=0; i < browse_context->svc_cnt; i++) {
|
||
t_service_context *svc_context = &browse_context->services[i];
|
||
if (svc_context->sdref == sdRef) {
|
||
t_svc_txt_record txt_rec;
|
||
txt_rec.txtLen = txtLen;
|
||
txt_rec.txtStr = txtRecord;
|
||
if (f_svc_check_resolved(svc_context, txt_rec) != X502_ERR_OK) {
|
||
f_remove_service_idx(browse_context, i);
|
||
} else {
|
||
if (svc_context->init_done) {
|
||
if (svc_context->event == E502_ETH_SVC_EVENT_NONE)
|
||
svc_context->event = E502_ETH_SVC_EVENT_CHANGED;
|
||
} else {
|
||
svc_context->sdref_addr = browse_context->main_ref;
|
||
if (DNSServiceGetAddrInfo(&svc_context->sdref_addr, kDNSServiceFlagsShareConnection,
|
||
interfaceIndex,
|
||
kDNSServiceProtocol_IPv4,
|
||
hosttarget, f_cb_browse_addr,
|
||
browse_context)
|
||
!= kDNSServiceErr_NoError) {
|
||
svc_context->sdref_addr = NULL;
|
||
f_remove_service_idx(browse_context, i);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static void DNSSD_API f_browse_replay (DNSServiceRef sdRef, DNSServiceFlags flags,
|
||
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
|
||
const char *serviceName, const char *regtype,
|
||
const char *replyDomain, void *context) {
|
||
t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)context;
|
||
if (errorCode != kDNSServiceErr_NoError) {
|
||
browse_context->err = X502_ERR_DNSSD_COMMUNICATION;
|
||
} else {
|
||
if (flags & kDNSServiceFlagsAdd) {
|
||
f_add_new_svc(browse_context, interfaceIndex, serviceName, replyDomain, regtype);
|
||
} else {
|
||
f_remove_service(browse_context, serviceName, replyDomain);
|
||
}
|
||
}
|
||
}
|
||
#elif defined ENABLE_AVAHI
|
||
static void f_resolve_addr_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
|
||
AvahiProtocol protocol, AvahiResolverEvent event,
|
||
const char *name, const char *type,
|
||
const char *domain, const char *host_name,
|
||
const AvahiAddress *address, uint16_t port,
|
||
AvahiStringList *txt, AvahiLookupResultFlags flags,
|
||
void* userdata) {
|
||
t_e502_eth_svc_record_hnd rec = (t_e502_eth_svc_record_hnd) userdata;
|
||
switch (event) {
|
||
case AVAHI_RESOLVER_FAILURE:
|
||
rec->op_err = X502_ERR_DNSSD_COMMUNICATION;
|
||
rec->op_in_progr = 0;
|
||
break;
|
||
case AVAHI_RESOLVER_FOUND: {
|
||
size_t hostlen = strlen(host_name) + 1;
|
||
rec->port = port;
|
||
if (rec->hosttarget != NULL)
|
||
free(rec->hosttarget);
|
||
rec->hosttarget = malloc(hostlen);
|
||
if (rec->hosttarget != NULL) {
|
||
strcpy(rec->hosttarget, host_name);
|
||
rec->op_err = X502_ERR_OK;
|
||
rec->ipv4_addr = ntohl(address->data.ipv4.address);
|
||
} else {
|
||
rec->op_err = X502_ERR_MEMORY_ALLOC;
|
||
}
|
||
rec->op_in_progr = 0;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void f_resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface,
|
||
AvahiProtocol protocol, AvahiResolverEvent event,
|
||
const char *name, const char *type,
|
||
const char *domain, const char *host_name,
|
||
const AvahiAddress *address, uint16_t port,
|
||
AvahiStringList *txt, AvahiLookupResultFlags flags,
|
||
void* userdata) {
|
||
t_e502_eth_svc_browse_hnd browse_context = (t_e502_eth_svc_browse_hnd)userdata;
|
||
unsigned i;
|
||
for (i=0; i < browse_context->svc_cnt; i++) {
|
||
t_service_context *svc_context = &browse_context->services[i];
|
||
if (svc_context->resolver == r) {
|
||
/* Called whenever a service has been resolved successfully or timed out */
|
||
switch (event) {
|
||
case AVAHI_RESOLVER_FAILURE:
|
||
f_svc_fail(browse_context, i);
|
||
break;
|
||
case AVAHI_RESOLVER_FOUND: {
|
||
int32_t err = f_svc_check_resolved(svc_context, txt);
|
||
if (err != X502_ERR_OK) {
|
||
f_remove_service_idx(browse_context, i);
|
||
} else {
|
||
if (svc_context->init_done == 0) {
|
||
svc_context->init_done = 1;
|
||
svc_context->fail = 0;
|
||
svc_context->event = E502_ETH_SVC_EVENT_ADD;
|
||
} else if (svc_context->event == E502_ETH_SVC_EVENT_NONE) {
|
||
svc_context->event = E502_ETH_SVC_EVENT_CHANGED;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void f_browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface,
|
||
AvahiProtocol protocol, AvahiBrowserEvent event,
|
||
const char *name, const char *type,
|
||
const char *domain, AvahiLookupResultFlags flags,
|
||
void* userdata) {
|
||
t_e502_eth_svc_browse_hnd context = (t_e502_eth_svc_browse_hnd)userdata;
|
||
|
||
switch (event) {
|
||
case AVAHI_BROWSER_FAILURE:
|
||
context->err = X502_ERR_DNSSD_COMMUNICATION;
|
||
avahi_simple_poll_quit(context->poll);
|
||
return;
|
||
|
||
case AVAHI_BROWSER_NEW:
|
||
f_add_new_svc(context, interface, name, domain, type);
|
||
break;
|
||
|
||
case AVAHI_BROWSER_REMOVE:
|
||
f_remove_service(context, name, domain);
|
||
break;
|
||
|
||
case AVAHI_BROWSER_ALL_FOR_NOW:
|
||
case AVAHI_BROWSER_CACHE_EXHAUSTED:
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
static void f_client_callback(AvahiClient *c, AvahiClientState state, void * userdata) {
|
||
|
||
/* Called whenever the client or server state changes */
|
||
|
||
if (state == AVAHI_CLIENT_FAILURE) {
|
||
t_e502_eth_svc_browse_hnd context = (t_e502_eth_svc_browse_hnd)userdata;
|
||
context->err = X502_ERR_DNSSD_NOT_RUNNING;
|
||
avahi_simple_poll_quit(context->poll);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
#endif
|
||
|
||
|
||
static t_e502_eth_svc_event f_get_event(t_e502_eth_svc_browse_hnd browse_context,
|
||
t_e502_eth_svc_record_hnd *svc_hnd, uint32_t *flags) {
|
||
t_e502_eth_svc_event ret = E502_ETH_SVC_EVENT_NONE;
|
||
unsigned i;
|
||
|
||
for (i=0; (i < browse_context->svc_cnt) && (ret == E502_ETH_SVC_EVENT_NONE); i++) {
|
||
t_service_context *svc = &browse_context->services[i];
|
||
if (svc->event != E502_ETH_SVC_EVENT_NONE) {
|
||
ret = svc->event;
|
||
|
||
if ((svc->event == E502_ETH_SVC_EVENT_REMOVE) && !svc->fail) {
|
||
/* так при удалении запись нам больше не нужна, то можно ее
|
||
* передать пользователю, без создания копии */
|
||
*svc_hnd = svc->rec;
|
||
svc->rec = NULL;
|
||
f_svc_context_free(svc);
|
||
if (i != (browse_context->svc_cnt-1)) {
|
||
memmove(&browse_context->services[i], &browse_context->services[i+1],
|
||
(browse_context->svc_cnt-i-1)*sizeof(browse_context->services[0]));
|
||
}
|
||
browse_context->svc_cnt--;
|
||
} else {
|
||
/* если событие ADD/CHANGE, то сбрасываем флаг события */
|
||
svc->event = E502_ETH_SVC_EVENT_NONE;
|
||
/* далаем копию записи, чтобы пользователь мог ее сохранить и
|
||
после завершения */
|
||
*svc_hnd = f_service_record_create_copy(svc->rec);
|
||
}
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordGetInstanceName(t_e502_eth_svc_record_hnd rec, char *name) {
|
||
int32_t err = name == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
|
||
if (err == X502_ERR_OK) {
|
||
strcpy(name, rec->service_name);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordGetDevSerial(t_e502_eth_svc_record_hnd rec, char *serial) {
|
||
int32_t err = serial == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
|
||
if (err == X502_ERR_OK) {
|
||
strcpy(serial, rec->serial);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordGetDevName(t_e502_eth_svc_record_hnd rec, char *devname) {
|
||
int32_t err = devname == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
|
||
if (err == X502_ERR_OK) {
|
||
strcpy(devname, rec->devname);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
#ifdef ENABLE_BONJOUR
|
||
static int32_t f_wait_result(DNSServiceRef ref, t_e502_eth_svc_record_hnd rec, t_ltimer *ptmr) {
|
||
int32_t err = X502_ERR_OK;
|
||
int sock = DNSServiceRefSockFD(ref);
|
||
int nfds = sock + 1;
|
||
int out = 0;
|
||
do {
|
||
fd_set readfds;
|
||
struct timeval tv;
|
||
int result;
|
||
|
||
FD_ZERO(&readfds);
|
||
FD_SET(sock, &readfds);
|
||
f_set_timeval_left(ptmr, &tv);
|
||
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
|
||
if (result > 0) {
|
||
DNSServiceErrorType dnssd_err = DNSServiceProcessResult(ref);
|
||
if (dnssd_err == kDNSServiceErr_NoError) {
|
||
if (!rec->op_in_progr) {
|
||
err = rec->op_err;
|
||
out = 1;
|
||
}
|
||
} else {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
} else if (ltimer_expired(ptmr) && rec->op_in_progr) {
|
||
err = X502_ERR_SVC_RESOLVE_TIMEOUT;
|
||
rec->op_in_progr = 0;
|
||
}
|
||
} while ((err == X502_ERR_OK) && !out);
|
||
DNSServiceRefDeallocate(ref);
|
||
return err;
|
||
}
|
||
#endif
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordResolveIPv4Addr(t_e502_eth_svc_record_hnd rec,
|
||
uint32_t *addr, uint32_t tout) {
|
||
int32_t err = addr == NULL ? X502_ERR_INVALID_POINTER : E502_CHECK_SVC_REC(rec);
|
||
if (err == X502_ERR_OK) {
|
||
#if defined ENABLE_BONJOUR
|
||
DNSServiceRef ref;
|
||
t_ltimer tmr;
|
||
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
|
||
|
||
|
||
rec->op_in_progr = 1;
|
||
if (DNSServiceResolve(&ref, 0, rec->iface_idx, rec->service_name,
|
||
E502_TCP_SERVICE_NAME, rec->domain,
|
||
f_cb_gethosttarget, rec) == kDNSServiceErr_NoError) {
|
||
err = f_wait_result(ref, rec, &tmr);
|
||
} else {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
|
||
if (err == X502_ERR_OK) {
|
||
rec->op_in_progr = 1;
|
||
if (DNSServiceGetAddrInfo(&ref, 0, rec->iface_idx, kDNSServiceProtocol_IPv4,
|
||
rec->hosttarget, f_get_addr_info_reply, rec)
|
||
== kDNSServiceErr_NoError) {;
|
||
err = f_wait_result(ref, rec, &tmr);
|
||
} else {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
}
|
||
#elif defined ENABLE_AVAHI
|
||
AvahiSimplePoll *poll = NULL;
|
||
AvahiClient *client = NULL;
|
||
AvahiServiceResolver *resolver = NULL;
|
||
poll = avahi_simple_poll_new();
|
||
if (poll == NULL) {
|
||
err = X502_ERR_MEMORY_ALLOC;
|
||
} else {
|
||
client = avahi_client_new(avahi_simple_poll_get(poll), 0,
|
||
NULL, NULL, NULL);
|
||
if (client == NULL) {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
}
|
||
|
||
if (err == X502_ERR_OK) {
|
||
t_ltimer tmr;
|
||
int out = 0;
|
||
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
|
||
resolver = avahi_service_resolver_new(client, rec->iface_idx, AVAHI_PROTO_INET,
|
||
rec->service_name, E502_TCP_SERVICE_NAME,
|
||
rec->domain, AVAHI_PROTO_UNSPEC, 0,
|
||
f_resolve_addr_callback, rec);
|
||
do {
|
||
rec->op_in_progr = 1;
|
||
avahi_simple_poll_iterate(poll, LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
|
||
if (rec->op_in_progr == 0) {
|
||
err = rec->op_err;
|
||
out = 1;
|
||
} else if (ltimer_expired(&tmr)) {
|
||
err = X502_ERR_SVC_RESOLVE_TIMEOUT;
|
||
}
|
||
} while ((err == X502_ERR_OK) && !out);
|
||
}
|
||
|
||
if (resolver != NULL)
|
||
avahi_service_resolver_free(resolver);
|
||
if (client != NULL)
|
||
avahi_client_free(client);
|
||
if (poll != NULL) {
|
||
avahi_simple_poll_quit(poll);
|
||
avahi_simple_poll_free(poll);
|
||
}
|
||
#endif
|
||
if (err == X502_ERR_OK) {
|
||
*addr = rec->ipv4_addr;
|
||
}
|
||
}
|
||
return err;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordIsSameInstance(t_e502_eth_svc_record_hnd svc1,
|
||
t_e502_eth_svc_record_hnd svc2) {
|
||
int32_t err = E502_CHECK_SVC_REC(svc1);
|
||
if (err == X502_ERR_OK)
|
||
err = E502_CHECK_SVC_REC(svc2);
|
||
if (err == X502_ERR_OK) {
|
||
if (strcmp(svc1->service_name, svc2->service_name) || strcmp(svc1->domain, svc2->domain)) {
|
||
err = X502_ERR_INSTANCE_MISMATCH;
|
||
}
|
||
}
|
||
return err;
|
||
}
|
||
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcBrowseStart(t_e502_eth_svc_browse_hnd *pcontext, uint32_t flags) {
|
||
int32_t err = X502_ERR_OK;
|
||
t_e502_eth_svc_browse_hnd context = calloc(1, sizeof(t_e502_service_browse_context));
|
||
|
||
if (context != NULL) {
|
||
context->sign = E502_SVC_BROWSE_SIGN;
|
||
} else {
|
||
err = X502_ERR_MEMORY_ALLOC;
|
||
}
|
||
|
||
#ifdef ENABLE_BONJOUR
|
||
if (err == X502_ERR_OK) {
|
||
DNSServiceErrorType dnssd_err = DNSServiceCreateConnection(&context->main_ref);
|
||
if (dnssd_err == kDNSServiceErr_NoError) {
|
||
context->browse_ref = context->main_ref;
|
||
dnssd_err = DNSServiceBrowse(&context->browse_ref, kDNSServiceFlagsShareConnection, 0,
|
||
E502_TCP_SERVICE_NAME, NULL, f_browse_replay, context);
|
||
|
||
if (dnssd_err == kDNSServiceErr_NoError) {
|
||
context->socket = DNSServiceRefSockFD(context->main_ref);
|
||
}
|
||
}
|
||
|
||
if (dnssd_err != kDNSServiceErr_NoError) {
|
||
if (dnssd_err == kDNSServiceErr_ServiceNotRunning) {
|
||
err = X502_ERR_DNSSD_NOT_RUNNING;
|
||
} else {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
}
|
||
}
|
||
#elif defined ENABLE_AVAHI
|
||
if (err == X502_ERR_OK) {
|
||
context->poll = avahi_simple_poll_new();
|
||
if (context->poll == NULL) {
|
||
err = X502_ERR_MEMORY_ALLOC;
|
||
} else {
|
||
context->client = avahi_client_new(avahi_simple_poll_get(context->poll), 0,
|
||
f_client_callback, context, NULL);
|
||
if (context->client == NULL) {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
} else if (context->err != X502_ERR_OK) {
|
||
err = context->err;
|
||
}
|
||
|
||
if (err == X502_ERR_OK) {
|
||
context->sb = avahi_service_browser_new(context->client, AVAHI_IF_UNSPEC,
|
||
AVAHI_PROTO_INET,
|
||
E502_TCP_SERVICE_NAME, NULL, 0,
|
||
f_browse_callback, context);
|
||
if (context->sb == NULL)
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (err != X502_ERR_OK) {
|
||
if (context != NULL)
|
||
f_browse_context_free(context);
|
||
} else {
|
||
*pcontext = context;
|
||
}
|
||
|
||
return err;
|
||
}
|
||
|
||
|
||
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcBrowseGetEvent(t_e502_eth_svc_browse_hnd context,
|
||
t_e502_eth_svc_record_hnd *svc, uint32_t *pevent,
|
||
uint32_t *flags, uint32_t tout) {
|
||
int32_t err = (svc == NULL) || (pevent == NULL) ? X502_ERR_INVALID_POINTER :
|
||
E502_CHECK_BROWSE_CONTEXT(context);
|
||
|
||
if (err == X502_ERR_OK) {
|
||
t_ltimer tmr;
|
||
t_e502_eth_svc_event evt = f_get_event(context, svc, flags);
|
||
ltimer_set(&tmr, LTIMER_MS_TO_CLOCK_TICKS(tout));
|
||
context->err = X502_ERR_OK;
|
||
|
||
while ((evt == E502_ETH_SVC_EVENT_NONE) && !ltimer_expired(&tmr) && (err == X502_ERR_OK)) {
|
||
#ifdef ENABLE_BONJOUR
|
||
fd_set readfds;
|
||
struct timeval tv;
|
||
int result;
|
||
|
||
FD_ZERO(&readfds);
|
||
FD_SET(context->socket, &readfds);
|
||
f_set_timeval_left(&tmr, &tv);
|
||
|
||
result = select(context->socket + 1, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
|
||
if (result > 0) {
|
||
DNSServiceErrorType dnssd_err = DNSServiceProcessResult(context->main_ref);
|
||
if (dnssd_err == kDNSServiceErr_NoError) {
|
||
evt = f_get_event(context, svc, flags);
|
||
} else {
|
||
err = X502_ERR_DNSSD_COMMUNICATION;
|
||
}
|
||
}
|
||
#elif defined ENABLE_AVAHI
|
||
avahi_simple_poll_iterate(context->poll, LTIMER_CLOCK_TICKS_TO_MS(ltimer_expiration(&tmr)));
|
||
if (context->err != X502_ERR_OK) {
|
||
err = context->err;
|
||
} else {
|
||
evt = f_get_event(context, svc, flags);
|
||
}
|
||
#endif
|
||
}
|
||
*pevent = evt;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcBrowseStop(t_e502_eth_svc_browse_hnd context) {
|
||
return f_browse_context_free(context);
|
||
}
|
||
|
||
|
||
X502_EXPORT(int32_t) E502_MakeDevRecordByEthSvc(t_x502_devrec *devrec, t_e502_eth_svc_record_hnd svc,
|
||
uint32_t flags, uint32_t tout) {
|
||
|
||
int32_t err = e502_make_tcp_rec(devrec, flags, tout, svc->devname);
|
||
if (err==X502_ERR_OK) {
|
||
t_tcp_devinfo_data *devinfo_data = (t_tcp_devinfo_data *)devrec->internal->iface_data;
|
||
devinfo_data->svc_rec = f_service_record_create_copy(svc);
|
||
|
||
strcpy(devrec->location, svc->service_name);
|
||
devrec->location_type = X502_LOCATION_TYPE_INSTANCE_NAME;
|
||
strcpy(devrec->serial, svc->serial);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
int32_t e502_svc_fill_devinfo(t_tcp_devinfo_data *data) {
|
||
uint32_t addr;
|
||
int32_t err = E502_EthSvcRecordResolveIPv4Addr(data->svc_rec, &addr, data->open_tout);
|
||
if (err == X502_ERR_OK) {
|
||
data->ip_addr = addr;
|
||
data->cmd_port = data->svc_rec->port;
|
||
}
|
||
return err;
|
||
}
|
||
|
||
#else
|
||
#include "e502api.h"
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordFree(t_e502_eth_svc_record_hnd rec) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordGetInstanceName(t_e502_eth_svc_record_hnd rec, char *name) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordGetDevSerial(t_e502_eth_svc_record_hnd rec, char *serial) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordResolveIPv4Addr(t_e502_eth_svc_record_hnd rec,
|
||
uint32_t *addr, uint32_t tout) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcRecordIsSameInstance(t_e502_eth_svc_record_hnd svc1,
|
||
t_e502_eth_svc_record_hnd svc2) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcBrowseStart(t_e502_eth_svc_browse_hnd *pcontext, uint32_t flags) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcBrowseGetEvent(t_e502_eth_svc_browse_hnd context,
|
||
t_e502_eth_svc_record_hnd *svc, uint32_t *pevent,
|
||
uint32_t *flags, uint32_t tout) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_EthSvcBrowseStop(t_e502_eth_svc_browse_hnd context) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
X502_EXPORT(int32_t) E502_MakeDevRecordByEthSvc(t_x502_devrec *devrec, t_e502_eth_svc_record_hnd svc,
|
||
uint32_t flags, uint32_t tout) {
|
||
return X502_ERR_NOT_IMPLEMENTED;
|
||
}
|
||
#endif
|
||
|
||
X502_EXPORT(int32_t) E502_SearchEthForDevicesIPv4Addr(t_x502_devrec* rec_list, uint32_t flags, uint32_t size,
|
||
uint32_t *devcount, uint32_t event_tout, uint32_t tcp_tout){
|
||
|
||
uint32_t count = 0;
|
||
int32_t err = X502_ERR_OK;
|
||
|
||
t_e502_eth_svc_browse_hnd browse_hnd;
|
||
err = E502_EthSvcBrowseStart(&browse_hnd, 0);
|
||
|
||
if (!err){
|
||
int run = 1;
|
||
while (run) {
|
||
t_e502_eth_svc_record_hnd svc_hnd;
|
||
uint32_t event;
|
||
// обязательно вернет значение после tout мс
|
||
err = E502_EthSvcBrowseGetEvent(browse_hnd, &svc_hnd, &event, &flags, event_tout);
|
||
if (!err){
|
||
if (E502_ETH_SVC_EVENT_NONE == event) {
|
||
run = 0;
|
||
} else {
|
||
if ((NULL != rec_list) && (count < size)) {
|
||
int32_t res = E502_MakeDevRecordByEthSvc(&rec_list[count], svc_hnd, flags, tcp_tout);
|
||
if (X502_ERR_OK != res){
|
||
break;
|
||
}
|
||
uint32_t ip_addr;
|
||
E502_EthSvcRecordResolveIPv4Addr(svc_hnd, &ip_addr ,tcp_tout);
|
||
|
||
sprintf(rec_list[count].location, "%d.%d.%d.%d",
|
||
(ip_addr>>24) & 0xFF,
|
||
(ip_addr>>16) & 0xFF,
|
||
(ip_addr>>8) & 0xFF,
|
||
(ip_addr>>0) & 0xFF);
|
||
rec_list[count].location_type = X502_LOCATION_TYPE_ADDR;
|
||
}
|
||
count++;
|
||
}
|
||
} else {
|
||
run = 0;
|
||
}
|
||
}
|
||
}
|
||
err = E502_EthSvcBrowseStop(browse_hnd);
|
||
|
||
if (NULL != devcount){
|
||
*devcount = count;
|
||
}
|
||
|
||
return err != X502_ERR_OK ? err : count > (uint32_t)size ? (int32_t)size : (int32_t)count ;
|
||
}
|