2539 lines
64 KiB
C
2539 lines
64 KiB
C
/*
|
|
* tinysvcmdns - a tiny MDNS implementation for publishing services
|
|
* Copyright (C) 2011 Darell Tan
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/* mdns core and API */
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#define LOG_ERR 3
|
|
#else
|
|
#include <sys/select.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <net/if.h>
|
|
#include <syslog.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <signal.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <unistd.h>
|
|
#include <assert.h>
|
|
#include <pthread.h>
|
|
#include <sys/time.h>
|
|
//#include <tinyara/clock.h>
|
|
#ifndef NSEC_PER_SEC
|
|
#define NSEC_PER_SEC (1000000000L)
|
|
#endif
|
|
#include <semaphore.h>
|
|
#include <errno.h>
|
|
#define get_errno() (errno)
|
|
|
|
/*
|
|
* Define a proper IP socket level if not already done.
|
|
* Required to compile on OS X
|
|
*/
|
|
#ifndef SOL_IP
|
|
#define SOL_IP IPPROTO_IP
|
|
#endif
|
|
|
|
#include "mdns.h"
|
|
#include "mdnsd.h"
|
|
|
|
#if (MDNS_DEBUG_PRINTF == 1) && (MDNS_RR_DEBUG == 1)
|
|
#define MDNSD_RR_DEBUG
|
|
#endif
|
|
|
|
#if (MDNS_DEBUG_PRINTF == 1) && (MDNS_MEMORY_DEBUG == 1)
|
|
#define MDNSD_MEMORY_DEBUG
|
|
#endif
|
|
|
|
#define THREAD_MAINLOOP_NAME "MDNS"
|
|
#define THREAD_MAINLOOP_STACKSIZE 4096
|
|
|
|
#define PIPE_SOCKET_TYPE 0
|
|
#define PIPE_SOCKET_PORT0 65353
|
|
#define PIPE_SOCKET_PORT1 65354
|
|
|
|
#define MDNS_ADDR "224.0.0.251"
|
|
#define MDNS_PORT 5353
|
|
|
|
#define MDNS_SUFFIX_LOCAL ".local"
|
|
#define MDNS_SUFFIX_SITE ".site"
|
|
#define MDNS_CHECK_SUBTYPE_STR "._sub."
|
|
|
|
#define PACKET_SIZE 1536 /* maximum packet size : */
|
|
//#define PACKET_SIZE 65536
|
|
|
|
#define SERVICES_DNS_SD_NLABEL \
|
|
((uint8_t *) "\x09_services\x07_dns-sd\x04_udp\x05local")
|
|
|
|
#define TIME_GET(time) gettimeofday(&time, NULL)
|
|
#define TIME_DIFF_USEC(old_time, cur_time) \
|
|
((cur_time.tv_sec * 1000000 + cur_time.tv_usec) - (old_time.tv_sec * 1000000 + old_time.tv_usec))
|
|
|
|
#define MAX_ECONNRESET_COUNT 5
|
|
|
|
enum mdns_cache_status {
|
|
CACHE_SLEEP = 0,
|
|
CACHE_NORMAL = 1,
|
|
CACHE_RESOLVE_HOSTNAME = 2,
|
|
CACHE_SERVICE_DISCOVERY = 3,
|
|
};
|
|
|
|
enum mdns_domain {
|
|
MDNS_DOMAIN_UNKNOWN = 0,
|
|
MDNS_DOMAIN_LOCAL = 1,
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
MDNS_DOMAIN_SITE = 2,
|
|
#endif
|
|
};
|
|
|
|
struct mdnsd {
|
|
pthread_mutex_t data_lock;
|
|
sem_t sendmsg_sem;
|
|
int sockfd;
|
|
int notify_pipe[2];
|
|
int stop_flag;
|
|
int dump_cache;
|
|
int sendmsg_requested;
|
|
int domain;
|
|
|
|
enum mdns_cache_status c_status;
|
|
char *c_filter;
|
|
struct rr_group *cache;
|
|
struct rr_list *query;
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
struct rr_group *group;
|
|
struct rr_list *announce;
|
|
struct rr_list *services;
|
|
struct rr_list *probe;
|
|
uint8_t *hostname; /* hostname can be changed if name collision occur */
|
|
uint8_t *hostname_org;
|
|
#endif
|
|
};
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
struct mdns_service {
|
|
struct rr_list *entries;
|
|
};
|
|
#endif
|
|
|
|
static void update_cache(struct mdnsd *svr);
|
|
static void clear_service_discovery_result(struct mdns_service_info
|
|
*service_list, int num_of_services);
|
|
static void release_mdns_context_resource(void);
|
|
|
|
static struct mdnsd *g_svr = NULL;
|
|
static struct mdns_service_info *g_service_list = NULL;
|
|
static pthread_mutex_t g_cmd_lock;
|
|
static int g_cmd_lock_initialized = 0;
|
|
|
|
/////////////////////////////////
|
|
static void log_message(int loglevel, char *fmt_str, ...) {
|
|
va_list ap;
|
|
char buf[2048];
|
|
|
|
va_start(ap, fmt_str);
|
|
vsnprintf(buf, 2047, fmt_str, ap);
|
|
va_end(ap);
|
|
buf[2047] = 0;
|
|
|
|
fprintf(stderr, "%s\n", buf);
|
|
}
|
|
|
|
//#ifdef MDNSD_RR_DEBUG
|
|
static void print_rr_entry(struct rr_entry *rr_e)
|
|
{
|
|
char *str1, *str2;
|
|
|
|
if (!rr_e) {
|
|
MDNS_RR_DEBUG_PRINTF("ERROR: No RR Entry\n");
|
|
return;
|
|
}
|
|
|
|
if (rr_e->name) {
|
|
str1 = nlabel_to_str(rr_e->name);
|
|
} else {
|
|
str1 = NULL;
|
|
}
|
|
|
|
if ((rr_e->type == RR_PTR) && rr_e->data.PTR.name) {
|
|
str2 = nlabel_to_str(rr_e->data.PTR.name);
|
|
} else if ((rr_e->type == RR_SRV) && rr_e->data.SRV.target) {
|
|
str2 = nlabel_to_str(rr_e->data.SRV.target);
|
|
} else {
|
|
str2 = NULL;
|
|
}
|
|
|
|
MDNS_RR_DEBUG_PRINTF("type:%s, ttl=%d, time=%d, ca_fl=%d, rr_class=%d, name=[%s]", rr_get_type_name(rr_e->type), rr_e->ttl, (unsigned int)(time(NULL) - rr_e->update_time), (int)rr_e->cache_flush, rr_e->rr_class, str1 ? str1 : "NULL");
|
|
|
|
if (rr_e->type == RR_SRV || rr_e->type == RR_PTR) {
|
|
MDNS_RR_DEBUG_PRINTF(", target=[%s]\n", str2 ? str2 : "NULL");
|
|
} else {
|
|
MDNS_RR_DEBUG_PRINTF("\n");
|
|
}
|
|
|
|
if (str1) {
|
|
MDNS_FREE(str1);
|
|
}
|
|
if (str2) {
|
|
MDNS_FREE(str2);
|
|
}
|
|
}
|
|
|
|
static void print_cache(struct mdnsd *svr)
|
|
{
|
|
MDNS_RR_DEBUG_PRINTF("\n");
|
|
MDNS_RR_DEBUG_PRINTF(" Multicast DNS Cache\n");
|
|
|
|
for (struct rr_group *group = svr->cache; group; group = group->next) {
|
|
char *pname = group->name? nlabel_to_str(group->name): NULL;
|
|
|
|
MDNS_RR_DEBUG_PRINTF(
|
|
"==================================================\n"
|
|
" Group: %s\n"
|
|
"==================================================\n",
|
|
pname ? pname : "Unknown");
|
|
if (pname) {
|
|
MDNS_FREE(pname);
|
|
}
|
|
|
|
for (struct rr_list *list = group->rr; list; list = list->next) {
|
|
struct rr_entry *entry = list->e;
|
|
if (entry) {
|
|
print_rr_entry(entry);
|
|
}
|
|
}
|
|
}
|
|
MDNS_RR_DEBUG_PRINTF("==================================================\n\n");
|
|
}
|
|
//#endif /* MDNSD_RR_DEBUG */
|
|
|
|
static int check_mdns_domain(const char *name)
|
|
{
|
|
char *str = NULL;
|
|
int domain = MDNS_DOMAIN_UNKNOWN;
|
|
|
|
if (name == NULL) {
|
|
goto done;
|
|
}
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
str = strstr(name, MDNS_SUFFIX_SITE);
|
|
if (str && strcmp(str, MDNS_SUFFIX_SITE) == 0) {
|
|
domain = MDNS_DOMAIN_SITE;
|
|
goto done;
|
|
}
|
|
#endif
|
|
|
|
str = strstr(name, MDNS_SUFFIX_LOCAL);
|
|
if (str && strcmp(str, MDNS_SUFFIX_LOCAL) == 0) {
|
|
domain = MDNS_DOMAIN_LOCAL;
|
|
goto done;
|
|
}
|
|
|
|
domain = MDNS_DOMAIN_UNKNOWN;
|
|
|
|
done:
|
|
return domain;
|
|
}
|
|
|
|
static char *get_service_type_without_subtype(char *name)
|
|
{
|
|
char *str = strstr(name, MDNS_CHECK_SUBTYPE_STR);
|
|
if (str) {
|
|
str += strlen(MDNS_CHECK_SUBTYPE_STR);
|
|
} else {
|
|
str = name;
|
|
}
|
|
|
|
return str;
|
|
}
|
|
|
|
#ifndef MDNS__NO_RESPONDER_SUPPORT
|
|
static int lookup_hostname(struct mdnsd *svr, char *hostname)
|
|
{
|
|
int result = -1;
|
|
int b_found = 0;
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
for (struct rr_group *group = svr->cache; group; group = group->next) {
|
|
for (struct rr_list *list = group->rr; list; list = list->next) {
|
|
struct rr_entry *entry = list->e;
|
|
int ret;
|
|
if (entry && entry->name) {
|
|
char *e_name = nlabel_to_str(entry->name);
|
|
ret = strncmp(e_name, hostname, strlen(hostname));
|
|
MDNS_FREE(e_name);
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
b_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (b_found) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
if (b_found) {
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
|
|
static int lookup_hostname_to_addr(struct mdnsd *svr, char *hostname, uint32_t *ipaddr)
|
|
{
|
|
int result = -1;
|
|
int b_found = 0;
|
|
|
|
update_cache(svr);
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
for (struct rr_group *group = svr->cache; group; group = group->next) {
|
|
for (struct rr_list *list = group->rr; list; list = list->next) {
|
|
struct rr_entry *entry = list->e;
|
|
if (entry && (entry->type == RR_A)) { // currently, support only ipv4
|
|
int ret;
|
|
if (entry->name) {
|
|
char *e_name;
|
|
e_name = nlabel_to_str(entry->name);
|
|
ret = strncmp(e_name, hostname, strlen(hostname));
|
|
MDNS_FREE(e_name);
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
*ipaddr = entry->data.A.addr;
|
|
b_found = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (b_found) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
if (b_found) {
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int lookup_service(struct mdnsd *svr, char *type, struct mdns_service_info *service_list, int *num_of_services)
|
|
{
|
|
int result = -1;
|
|
int result_cnt = 0;
|
|
uint8_t *type_nlabel = create_nlabel(type);
|
|
char *type_without_subtype = get_service_type_without_subtype(type);
|
|
|
|
clear_service_discovery_result(service_list, MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT);
|
|
|
|
update_cache(svr);
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
struct rr_group *ptr_grp = rr_group_find(svr->cache, (uint8_t *)type_nlabel);
|
|
|
|
MDNS_FREE(type_nlabel);
|
|
if (ptr_grp) {
|
|
for (struct rr_list *list = ptr_grp->rr; list; list = list->next) {
|
|
struct rr_entry *entry = list->e;
|
|
if (entry && (entry->type == RR_PTR)) {
|
|
if (entry->data.PTR.name) { /* SRV's name */
|
|
struct rr_group *srv_grp = rr_group_find(svr->cache,
|
|
(uint8_t *)entry->data.PTR.name);
|
|
if (srv_grp) {
|
|
/* find service */
|
|
struct rr_entry *srv_e = rr_entry_find(srv_grp->rr, entry->data.PTR.name,
|
|
RR_SRV);
|
|
if (srv_e && srv_e->name) {
|
|
char *name = nlabel_to_str(srv_e->name); /* full service name */
|
|
char *ptr = strstr(name,
|
|
type_without_subtype); /* separate instance name and service type */
|
|
|
|
if (ptr && (ptr > name)) {
|
|
*(ptr - 1) = '\0';
|
|
} else {
|
|
MDNS_FREE(name);
|
|
continue;
|
|
}
|
|
|
|
/* set instance name */
|
|
service_list[result_cnt].instance_name = MDNS_STRDUP(name);
|
|
/* set service type */
|
|
service_list[result_cnt].type = MDNS_STRDUP(ptr);
|
|
|
|
MDNS_FREE(name);
|
|
|
|
/* set hostname */
|
|
if (srv_e->data.SRV.target) {
|
|
struct rr_group *a_grp = NULL;
|
|
name = nlabel_to_str(srv_e->data.SRV.target);
|
|
name[strlen(name) - 1] = '\0';
|
|
service_list[result_cnt].hostname = MDNS_STRDUP(name);
|
|
MDNS_FREE(name);
|
|
|
|
/* ip address */
|
|
a_grp = rr_group_find(svr->cache, (uint8_t *)srv_e->data.SRV.target);
|
|
if (a_grp) {
|
|
struct rr_entry *a_e = rr_entry_find(a_grp->rr, srv_e->data.SRV.target, RR_A);
|
|
if (a_e) {
|
|
service_list[result_cnt].ipaddr = a_e->data.A.addr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/* port */
|
|
service_list[result_cnt].port = srv_e->data.SRV.port;
|
|
|
|
result_cnt++; /* increase result count */
|
|
|
|
if (result_cnt >= MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
*num_of_services = result_cnt;
|
|
|
|
if (*num_of_services > 0) {
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int create_recv_sock(int domain)
|
|
{
|
|
char *addr;
|
|
int port;
|
|
int r;
|
|
|
|
switch (domain) {
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
case MDNS_DOMAIN_SITE:
|
|
addr = CONFIG_NETUTILS_MDNS_XMDNS_MULTICAST_ADDR;
|
|
port = CONFIG_NETUTILS_MDNS_XMDNS_PORT_NUM;
|
|
break;
|
|
#endif
|
|
case MDNS_DOMAIN_LOCAL:
|
|
default:
|
|
addr = MDNS_ADDR;
|
|
port = MDNS_PORT;
|
|
break;
|
|
}
|
|
|
|
int sd = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (sd < 0) {
|
|
log_message(LOG_ERR, "recv socket(): %s\n", strerror(errno));
|
|
return sd;
|
|
}
|
|
|
|
int on = 1;
|
|
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) < 0) {
|
|
close(sd);
|
|
log_message(LOG_ERR, "recv setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
|
|
return r;
|
|
}
|
|
|
|
#if !defined(WIN32) && defined(SO_REUSEPORT)
|
|
if (!getsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, &addrlen)) {
|
|
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on))) < 0) {
|
|
log_message(LOG_ERR, "recv setsockopt(SO_REUSEPORT): %d; %s\n", r, strerror(errno));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// (issue #5 Tinysvcmdns invisible from chrome app mdns-browser)
|
|
// TTL -> 255 in case receivers demand it (unrouted pkt)
|
|
int ttl = 255;
|
|
if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) < 0) {
|
|
log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_IP): %s", strerror(errno));
|
|
return r;
|
|
}
|
|
|
|
/* bind to an address */
|
|
struct sockaddr_in serveraddr = {0};
|
|
serveraddr.sin_family = AF_INET;
|
|
serveraddr.sin_port = htons(port);
|
|
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */
|
|
if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
|
|
log_message(LOG_ERR, "recv bind(): %s\n", strerror(errno));
|
|
}
|
|
|
|
// add membership to receiving socket
|
|
DECL_ZERO_STRUCT(mreq, ip_mreq);
|
|
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
|
|
mreq.imr_multiaddr.s_addr = inet_addr(addr);
|
|
if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq))) < 0) {
|
|
close(sd);
|
|
log_message(LOG_ERR, "recv setsockopt(IP_ADD_MEMBERSHIP): %s\n", strerror(errno));
|
|
return r;
|
|
}
|
|
|
|
#ifdef MDNS_ENABLE_LOOPBACK
|
|
// enable loopback in case someone else needs the data
|
|
int flag = 1;
|
|
#else
|
|
// disable loopback
|
|
int flag = 0;
|
|
#endif
|
|
if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, &flag, sizeof(flag))) < 0) {
|
|
close(sd);
|
|
log_message(LOG_ERR, "recv setsockopt(IP_MULTICAST_LOOP): %s\n", strerror(errno));
|
|
return r;
|
|
}
|
|
|
|
|
|
#ifdef IP_PKTINFO
|
|
if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, &on, sizeof(on))) < 0) {
|
|
close(sd);
|
|
log_message(LOG_ERR, "recv setsockopt(IP_PKTINFO): %s\n", strerror(errno));
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
return sd;
|
|
}
|
|
|
|
static ssize_t send_packet_to(int fd, const void *data, size_t len, struct sockaddr_in *toaddr) {
|
|
|
|
return sendto(fd, data, len, 0, (struct sockaddr *) toaddr, sizeof(struct sockaddr_in));
|
|
}
|
|
|
|
// populate the specified list which matches the RR name and type
|
|
static int populate_query(struct mdnsd *svr, struct rr_list **rr_head)
|
|
{
|
|
int num_qns = 0;
|
|
|
|
// check if we have the records
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
while (svr->query) {
|
|
struct rr_entry *qn_e = rr_list_remove(&svr->query, svr->query->e);
|
|
if (qn_e == NULL) {
|
|
break;
|
|
}
|
|
num_qns += rr_list_append(rr_head, qn_e);
|
|
}
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
return num_qns;
|
|
}
|
|
|
|
static ssize_t send_packet(int fd, const void *data, size_t len, int domain) {
|
|
static struct sockaddr_in toaddr;
|
|
char *addr;
|
|
int port;
|
|
|
|
switch (domain) {
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
case MDNS_DOMAIN_SITE:
|
|
addr = CONFIG_NETUTILS_MDNS_XMDNS_MULTICAST_ADDR;
|
|
port = CONFIG_NETUTILS_MDNS_XMDNS_PORT_NUM;
|
|
break;
|
|
#endif
|
|
case MDNS_DOMAIN_LOCAL:
|
|
default:
|
|
addr = MDNS_ADDR;
|
|
port = MDNS_PORT;
|
|
break;
|
|
}
|
|
|
|
memset(&toaddr, 0, sizeof(struct sockaddr_in));
|
|
toaddr.sin_family = AF_INET;
|
|
toaddr.sin_port = htons(port);
|
|
toaddr.sin_addr.s_addr = inet_addr(addr);
|
|
|
|
return send_packet_to( fd, data, len, &toaddr);
|
|
}
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
|
|
// populate the specified list which matches the RR name and type
|
|
static int populate_probe(struct mdnsd *svr, struct rr_list **rr_head)
|
|
{
|
|
int num_qns = 0;
|
|
|
|
// check if we have the records
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
while (svr->probe) {
|
|
struct rr_entry *qn_e = rr_list_remove(&svr->probe, svr->probe->e);
|
|
if (qn_e == NULL) {
|
|
break;
|
|
}
|
|
num_qns += rr_list_append(rr_head, qn_e);
|
|
}
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
return num_qns;
|
|
}
|
|
|
|
// populate the specified list which matches the RR name and type
|
|
// type can be RR_ANY, which populates all entries EXCEPT RR_NSEC
|
|
static int populate_answers(struct mdnsd *svr, struct rr_list **rr_head, uint8_t *name, enum rr_type type) {
|
|
int num_ans = 0;
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
if (type == RR_PTR && 0 == cmp_nlabel(SERVICES_DNS_SD_NLABEL, name)) {
|
|
|
|
// add answers for all services
|
|
for (struct rr_list *n = svr->services; n; n = n->next) {
|
|
// if (s->service->service && s->service->proto)
|
|
num_ans += rr_list_append(rr_head, n->e);
|
|
}
|
|
|
|
} else {
|
|
|
|
// check if we have the records
|
|
struct rr_group *ans_grp = rr_group_find(svr->group, name);
|
|
if (ans_grp != NULL) {
|
|
// decide which records should go into answers
|
|
for (struct rr_list *n = ans_grp->rr; n; n = n->next) {
|
|
// exclude NSEC for RR_ANY
|
|
if (type == RR_ANY && n->e->type == RR_NSEC)
|
|
continue;
|
|
|
|
if ((type == n->e->type || type == RR_ANY) && cmp_nlabel(name, n->e->name) == 0) {
|
|
num_ans += rr_list_append(rr_head, n->e);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
return num_ans;
|
|
}
|
|
|
|
// given a list of RRs, look up related records and add them
|
|
static void add_related_rr(struct mdnsd *svr, struct rr_list *list, struct mdns_pkt *reply) {
|
|
for (; list; list = list->next) {
|
|
struct rr_entry *ans = list->e;
|
|
|
|
switch (ans->type) {
|
|
case RR_PTR:
|
|
// target host A, AAAA records
|
|
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
|
|
MDNS_RR_GET_PTR_NAME(ans), RR_ANY);
|
|
break;
|
|
|
|
case RR_SRV:
|
|
// target host A, AAAA records
|
|
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
|
|
ans->data.SRV.target, RR_ANY);
|
|
|
|
// perhaps TXT records of the same name?
|
|
// if we use RR_ANY, we risk pulling in the same RR_SRV
|
|
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
|
|
ans->name, RR_TXT);
|
|
break;
|
|
|
|
case RR_A:
|
|
case RR_AAAA:
|
|
reply->num_add_rr += populate_answers(svr, &reply->rr_add,
|
|
ans->name, RR_NSEC);
|
|
break;
|
|
|
|
default:
|
|
// nothing to add
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// creates an announce packet given the type name PTR
|
|
static void announce_srv(struct mdnsd *svr, struct mdns_pkt *reply, uint8_t *name) {
|
|
mdns_init_reply(reply, 0);
|
|
|
|
/* announce hostname and address */
|
|
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_A);
|
|
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_AAAA);
|
|
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, name, RR_PTR);
|
|
|
|
// remember to add the services dns-sd PTR too
|
|
reply->num_ans_rr += populate_answers(svr, &reply->rr_ans, SERVICES_DNS_SD_NLABEL, RR_PTR);
|
|
|
|
// see if we can match additional records for answers
|
|
add_related_rr(svr, reply->rr_ans, reply);
|
|
|
|
// additional records for additional records
|
|
add_related_rr(svr, reply->rr_add, reply);
|
|
}
|
|
|
|
static void process_for_probe(struct mdnsd *svr, struct mdns_pkt *mdns_packet)
|
|
{
|
|
mdns_init_query(mdns_packet, 0);
|
|
|
|
mdns_packet->num_qn += populate_probe(svr, &mdns_packet->rr_qn);
|
|
}
|
|
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
|
|
static void process_for_query(struct mdnsd *svr, struct mdns_pkt *mdns_packet)
|
|
{
|
|
mdns_init_query(mdns_packet, 0);
|
|
|
|
mdns_packet->num_qn += populate_query(svr, &mdns_packet->rr_qn);
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
// advertisement of my address to mdns neighbor
|
|
mdns_packet->num_ans_rr += populate_answers(svr, &mdns_packet->rr_add, svr->hostname, RR_A);
|
|
mdns_packet->num_ans_rr += populate_answers(svr, &mdns_packet->rr_add, svr->hostname, RR_AAAA);
|
|
#endif
|
|
}
|
|
|
|
static void update_cache(struct mdnsd *svr)
|
|
{
|
|
struct rr_list *remove_list = NULL;
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
for (struct rr_group *group = svr->cache; group; group = group->next) {
|
|
for (struct rr_list *list = group->rr; list; list = list->next) {
|
|
struct rr_entry *entry = list->e;
|
|
if (entry) {
|
|
/* if ttl is expired or rr is RR_PTR or RR_SRV, remove rr from cache */
|
|
if (((time(NULL) - entry->update_time) > entry->ttl) || (svr->c_status != CACHE_SERVICE_DISCOVERY && (entry->type == RR_PTR || entry->type == RR_SRV))) {
|
|
rr_list_append(&remove_list, entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* remove ttl expired entry */
|
|
for (struct rr_list *list = remove_list; list; list = list->next) {
|
|
struct rr_entry *entry = list->e;
|
|
if (entry) {
|
|
rr_group_del(&svr->cache, entry);
|
|
}
|
|
}
|
|
rr_list_destroy(remove_list, 0); /* destroy remove list */
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
}
|
|
|
|
static void add_rr_to_cache(struct mdnsd *svr, struct mdns_pkt *pkt)
|
|
{
|
|
struct rr_list *filtered_rr_list = NULL;
|
|
struct rr_list *srv_list = NULL;
|
|
struct rr_entry *a_e = NULL;
|
|
int b_found = 0;
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
if (svr->c_status == CACHE_SLEEP) {
|
|
goto out_with_mutex;
|
|
}
|
|
|
|
/* filtering */
|
|
struct rr_list *rr_set[] = {
|
|
pkt->rr_ans,
|
|
pkt->rr_auth,
|
|
pkt->rr_add
|
|
};
|
|
for (int i = 0; i < sizeof(rr_set) / sizeof(rr_set[0]); i++) {
|
|
for (struct rr_list *rr_l = rr_set[i]; rr_l; rr_l = rr_l->next) {
|
|
struct rr_entry *rr_e = rr_l->e;
|
|
if (rr_e) {
|
|
if (rr_e->type == RR_A || rr_e->type == RR_AAAA) {
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
b_found = 1;
|
|
rr_list_append(&filtered_rr_list, rr_e);
|
|
#else
|
|
if (svr->c_status == CACHE_RESOLVE_HOSTNAME) {
|
|
if (svr->c_filter) {
|
|
char *name = nlabel_to_str(rr_e->name);
|
|
if (strncmp(name, svr->c_filter, strlen(svr->c_filter)) == 0) {
|
|
b_found = 1;
|
|
rr_list_append(&filtered_rr_list, rr_e);
|
|
}
|
|
MDNS_FREE(name);
|
|
}
|
|
} else if (svr->c_status == CACHE_SERVICE_DISCOVERY) {
|
|
rr_list_append(&filtered_rr_list, rr_e);
|
|
}
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
} else if (rr_e->type == RR_PTR) {
|
|
if (svr->c_status == CACHE_SERVICE_DISCOVERY) {
|
|
if (svr->c_filter) {
|
|
char *name = nlabel_to_str(rr_e->name);
|
|
if (strncmp(name, svr->c_filter, strlen(svr->c_filter)) == 0) {
|
|
b_found = 1;
|
|
rr_list_append(&filtered_rr_list, rr_e);
|
|
}
|
|
MDNS_FREE(name);
|
|
}
|
|
}
|
|
} else if (rr_e->type == RR_SRV) {
|
|
if (svr->c_status == CACHE_SERVICE_DISCOVERY) {
|
|
rr_list_append(&filtered_rr_list, rr_e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!b_found) {
|
|
if (filtered_rr_list) {
|
|
rr_list_destroy(filtered_rr_list, 0);
|
|
filtered_rr_list = NULL;
|
|
}
|
|
goto out_with_mutex;
|
|
}
|
|
|
|
for (struct rr_list *rr_l = filtered_rr_list; rr_l; rr_l = rr_l->next) {
|
|
struct rr_entry *rr_e = rr_l->e;
|
|
|
|
if (rr_e) {
|
|
struct rr_entry *cached_rr_e = NULL;
|
|
struct rr_group *group = rr_group_find(svr->cache, rr_e->name);
|
|
if (group) {
|
|
struct rr_entry *rr_e_in_cache = rr_entry_match(group->rr, rr_e);
|
|
if (rr_e_in_cache) {
|
|
rr_group_del(&svr->cache, rr_e_in_cache);
|
|
if (rr_e->ttl > 0) {
|
|
cached_rr_e = rr_duplicate(rr_e);
|
|
rr_group_add(&svr->cache, cached_rr_e);
|
|
}
|
|
} else {
|
|
cached_rr_e = rr_duplicate(rr_e);
|
|
rr_group_add(&svr->cache, cached_rr_e);
|
|
}
|
|
} else {
|
|
cached_rr_e = rr_duplicate(rr_e);
|
|
rr_group_add(&svr->cache, cached_rr_e);
|
|
}
|
|
|
|
/* if SRV's target is null, add RR_A 's hostname to SRV's target */
|
|
if (cached_rr_e) {
|
|
if (cached_rr_e->type == RR_SRV) {
|
|
rr_list_append(&srv_list, cached_rr_e);
|
|
} else if ((a_e == NULL) && (cached_rr_e->type == RR_A || cached_rr_e->type == RR_AAAA)) {
|
|
a_e = cached_rr_e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* if SRV's target is null, add RR_A 's hostname to SRV's target */
|
|
if (srv_list) {
|
|
if (a_e && a_e->name) {
|
|
for (struct rr_list *rr_l = srv_list; rr_l; rr_l = rr_l->next) {
|
|
struct rr_entry *rr_e = rr_l->e;
|
|
if (rr_e && (rr_e->data.SRV.target == NULL)) {
|
|
rr_e->data.SRV.target = dup_nlabel(a_e->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
rr_list_destroy(srv_list, 0);
|
|
srv_list = NULL;
|
|
}
|
|
|
|
if (filtered_rr_list) {
|
|
rr_list_destroy(filtered_rr_list, 0);
|
|
filtered_rr_list = NULL;
|
|
}
|
|
|
|
out_with_mutex:
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
// processes the incoming MDNS packet
|
|
// returns >0 if processed, 0 otherwise
|
|
static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct sockaddr_in *fromtoaddr, struct mdns_pkt *reply) {
|
|
int result;
|
|
|
|
assert(pkt != NULL);
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
// is it standard query?
|
|
if ((pkt->flags & MDNS_FLAG_RESP) == 0 &&
|
|
MDNS_FLAG_GET_OPCODE(pkt->flags) == 0) {
|
|
mdns_init_reply(reply, pkt->id);
|
|
|
|
DEBUG_PRINTF("flags = %04x, qn = %d, ans = %d, add = %d\n",
|
|
pkt->flags,
|
|
pkt->num_qn,
|
|
pkt->num_ans_rr,
|
|
pkt->num_add_rr);
|
|
|
|
int unicast = 0; /* 0, 1, 2, 4 = no, direct unicast, legacy, unicast-response */
|
|
if (fromtoaddr->sin_port != htons(MDNS_PORT)) {
|
|
/* legacy query */
|
|
unicast |= 2;
|
|
} else if (fromtoaddr->sin_addr.s_addr != inet_addr(MDNS_ADDR)) {
|
|
/* direct unicast - send back to same addr:port */
|
|
unicast |= 1;
|
|
}
|
|
|
|
// loop through questions
|
|
struct rr_list *qnl = pkt->rr_qn;
|
|
for (int i = 0; i < pkt->num_qn; i++, qnl = qnl->next) {
|
|
struct rr_entry *qn = qnl->e;
|
|
int num_ans_added = 0;
|
|
|
|
char *namestr = nlabel_to_str(qn->name);
|
|
DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr);
|
|
MDNS_FREE(namestr);
|
|
|
|
// check if it's a unicast query - we don't treat those differently
|
|
if (qn->unicast_query) {
|
|
DEBUG_PRINTF("treating unicast query as multicast\n");
|
|
unicast |= 4;
|
|
}
|
|
|
|
num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type);
|
|
reply->num_ans_rr += num_ans_added;
|
|
|
|
DEBUG_PRINTF("added %d answers\n", num_ans_added);
|
|
}
|
|
|
|
// remove our replies if they were already in their answers
|
|
struct rr_list *prev_ans = NULL;
|
|
for (struct rr_list *ans = reply->rr_ans; ans;) {
|
|
struct rr_list *next_ans = ans->next;
|
|
struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e);
|
|
|
|
// discard answers that have at least half of the actual TTL
|
|
if (known_ans != NULL && known_ans->ttl >= ans->e->ttl / 2) {
|
|
char *namestr = nlabel_to_str(ans->e->name);
|
|
DEBUG_PRINTF("removing answer for %s\n", namestr);
|
|
MDNS_FREE(namestr);
|
|
|
|
// check if list item is head
|
|
if (prev_ans == NULL)
|
|
reply->rr_ans = ans->next;
|
|
else
|
|
prev_ans->next = ans->next;
|
|
MDNS_FREE(ans);
|
|
|
|
ans = prev_ans;
|
|
|
|
// adjust answer count
|
|
reply->num_ans_rr--;
|
|
}
|
|
|
|
prev_ans = ans;
|
|
ans = next_ans;
|
|
}
|
|
|
|
// see if we can match additional records for answers
|
|
add_related_rr(svr, reply->rr_ans, reply);
|
|
|
|
// additional records for additional records
|
|
add_related_rr(svr, reply->rr_add, reply);
|
|
|
|
DEBUG_PRINTF("\n");
|
|
|
|
/* specific processing for unicast, ... or not */
|
|
if (unicast & 2) {
|
|
/* legacy: (rfc6762#section-5.4) send back to same addr:port, no cache flush, TTL -> 10s */
|
|
struct rr_list *rr_grps[] = { reply->rr_ans, reply->rr_add, NULL };
|
|
for (struct rr_list **rrl = rr_grps; *rrl; ++rrl) {
|
|
for (struct rr_list *ans = *rrl; ans; ans = ans->next) {
|
|
ans->e->cache_flush = 0;
|
|
ans->e->ttl = DEFAULT_TTL_LEGACY;
|
|
}
|
|
}
|
|
} else if (unicast & 1) {
|
|
/* direct: (rfc6762#section-5.5) should be sending answers to same addr:5353 if on LAN, then multicast */
|
|
/* let's pretend the first reply got lost ... */
|
|
fromtoaddr->sin_port = 0;
|
|
} else if (unicast & 4) {
|
|
/* unicast-response: (rfc6762#section-5.4) should be sending answers to QUs to same addr:5353 if on LAN, then multicast */
|
|
/* let's pretend the first reply got lost ... */
|
|
fromtoaddr->sin_port = 0;
|
|
} else {
|
|
/* send usual multicast response */
|
|
fromtoaddr->sin_port = 0;
|
|
}
|
|
|
|
return reply->num_ans_rr;
|
|
} else {
|
|
result = 0;
|
|
}
|
|
#else
|
|
result = 0;
|
|
#endif
|
|
|
|
// if there is answer, add rr cache
|
|
if (pkt->num_ans_rr || pkt->num_auth_rr || pkt->num_add_rr) {
|
|
update_cache(svr);
|
|
add_rr_to_cache(svr, pkt);
|
|
}
|
|
#ifdef MDNSD_RR_DEBUG
|
|
print_cache(svr);
|
|
#endif
|
|
#ifdef MDNSD_MEMORY_DEBUG
|
|
mdns_show_meminfo();
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
int create_pipe(int handles[2]) {
|
|
//#ifdef _WIN32
|
|
#if (PIPE_SOCKET_TYPE == 1)
|
|
int result = -1;
|
|
struct sockaddr_in serv_addr;
|
|
|
|
handles[0] = -1;
|
|
handles[1] = -1;
|
|
|
|
/* create 1st socket */
|
|
handles[0] = socket(AF_INET, SOCK_DGRAM, 0);
|
|
if (handles[0] == -1)
|
|
goto errout;
|
|
|
|
memset(&serv_addr, 0, sizeof(serv_addr));
|
|
serv_addr.sin_family = AF_INET;
|
|
serv_addr.sin_port = htons(PIPE_SOCKET_PORT0);
|
|
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
if (bind(handles[0], (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
|
|
goto errout;
|
|
|
|
if ((handles[1] = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
goto errout;
|
|
#if 0 /* this can be enabled for supporting bi-directional pipe */
|
|
/* create 2nd socket */
|
|
memset(&serv_addr, 0, sizeof(serv_addr));
|
|
serv_addr.sin_family = AF_INET;
|
|
serv_addr.sin_port = htons(PIPE_SOCKET_PORT1);
|
|
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
if (bind(handles[1], (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
|
|
goto errout;
|
|
#endif
|
|
|
|
/* success */
|
|
result = 0;
|
|
return result;
|
|
errout:
|
|
for (int i = 0; i < 2; ++i) {
|
|
if (handles[i] != -1) {
|
|
close(handles[i]);
|
|
handles[i] = -1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
#else
|
|
return pipe(handles);
|
|
#endif
|
|
}
|
|
|
|
int read_pipe(int s, char* buf, int len) {
|
|
//#ifdef _WIN32
|
|
#if (PIPE_SOCKET_TYPE == 1)
|
|
struct sockaddr_in fromaddr;
|
|
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
|
|
return recvfrom(s, buf, len, 0, (struct sockaddr *)&fromaddr, &sockaddr_size);
|
|
#else
|
|
return read(s, buf, len);
|
|
#endif
|
|
}
|
|
|
|
int write_pipe(int s, char *buf, int len, int port) {
|
|
//#ifdef _WIN32
|
|
#if (PIPE_SOCKET_TYPE == 1)
|
|
static struct sockaddr_in toaddr;
|
|
|
|
memset(&toaddr, 0, sizeof(struct sockaddr_in));
|
|
toaddr.sin_family = AF_INET;
|
|
toaddr.sin_port = htons(port);
|
|
toaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
|
|
return sendto(s, buf, len, 0, (struct sockaddr *)&toaddr, sizeof(struct sockaddr_in));
|
|
#else
|
|
return write(s, buf, len);
|
|
#endif
|
|
}
|
|
|
|
int close_pipe(int s) {
|
|
return close(s);
|
|
}
|
|
|
|
static int request_sendmsg(struct mdnsd *svr)
|
|
{
|
|
int ret;
|
|
struct timespec abstime;
|
|
const int wait_sec = 1;
|
|
const int wait_nsec = 0;
|
|
const int max_try_count = 3;
|
|
int try_count = 0;
|
|
int timeout = 0;
|
|
|
|
do {
|
|
ret = write_pipe(svr->notify_pipe[1], ".", 1, PIPE_SOCKET_PORT0);
|
|
if (ret == -1) {
|
|
log_message(LOG_ERR, "write_pipe() failed. (ret=%d)\n", ret);
|
|
continue;
|
|
}
|
|
|
|
(void)clock_gettime(CLOCK_REALTIME, &abstime);
|
|
abstime.tv_sec += wait_sec;
|
|
abstime.tv_nsec += wait_nsec;
|
|
if (abstime.tv_nsec >= NSEC_PER_SEC) {
|
|
abstime.tv_sec++;
|
|
abstime.tv_nsec -= NSEC_PER_SEC;
|
|
}
|
|
|
|
timeout = 0;
|
|
while (sem_timedwait(&svr->sendmsg_sem, &abstime) != 0) {
|
|
int err = get_errno();
|
|
assert(err == EINTR || err == ETIMEDOUT);
|
|
|
|
if (err == ETIMEDOUT) {
|
|
timeout = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (timeout) {
|
|
log_message(LOG_ERR, "sem_timedwait() timeout.\n");
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
} while (++try_count < max_try_count);
|
|
|
|
if (try_count == max_try_count) {
|
|
log_message(LOG_ERR, "request_sendmsg() failed.\n");
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// main loop to receive, process and send out MDNS replies
|
|
// also handles MDNS service announces
|
|
static void main_loop(struct mdnsd *svr) {
|
|
fd_set sockfd_set;
|
|
int max_fd = 0;
|
|
char notify_buf[2]; // buffer for reading of notify_pipe
|
|
int ret;
|
|
int econnreset_count = 0;
|
|
struct mdns_pkt *mdns_packet = NULL;
|
|
|
|
void *pkt_buffer = MDNS_MALLOC(PACKET_SIZE);
|
|
if (pkt_buffer == NULL) {
|
|
log_message(LOG_ERR, "memory allocation : pkt_buffer\n");
|
|
goto out;
|
|
}
|
|
|
|
mdns_packet = MDNS_MALLOC(sizeof(struct mdns_pkt));
|
|
if (mdns_packet == NULL) {
|
|
log_message(LOG_ERR, "memory allocation : mdns_packet\n");
|
|
goto out;
|
|
}
|
|
memset(mdns_packet, 0, sizeof(struct mdns_pkt));
|
|
|
|
max_fd = svr->sockfd;
|
|
if (svr->notify_pipe[0] > max_fd)
|
|
max_fd = svr->notify_pipe[0];
|
|
|
|
while (1) {
|
|
struct sockaddr_in fromaddr;
|
|
socklen_t sockaddr_size = sizeof(struct sockaddr_in);
|
|
|
|
svr->sendmsg_requested = 0;
|
|
|
|
FD_ZERO(&sockfd_set);
|
|
FD_SET(svr->sockfd, &sockfd_set);
|
|
FD_SET(svr->notify_pipe[0], &sockfd_set);
|
|
ret = select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
|
|
|
|
if (ret > 0) {
|
|
if (FD_ISSET(svr->notify_pipe[0], &sockfd_set)) {
|
|
svr->sendmsg_requested = 1;
|
|
// flush the notify_pipe
|
|
if (read_pipe(svr->notify_pipe[0], (char *)¬ify_buf, 1) == -1)
|
|
log_message(LOG_ERR, "read_pipe() failed; %s\n", strerror(errno));
|
|
} else if (FD_ISSET(svr->sockfd, &sockfd_set)) {
|
|
ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0,
|
|
(struct sockaddr *) &fromaddr, &sockaddr_size);
|
|
if (recvsize < 0) {
|
|
int errval = errno;
|
|
log_message(LOG_ERR, "recv failed(): recvsize: %d; %s\n", recvsize, strerror(errno));
|
|
if (errval == ECONNRESET) {
|
|
econnreset_count++;
|
|
if (econnreset_count >= MAX_ECONNRESET_COUNT) {
|
|
int remaining_cnt;
|
|
log_message(LOG_ERR, "ECONNRESET occurred %d times. recv socket will be recreated.\n", econnreset_count);
|
|
|
|
/* close and recreate recv socket */
|
|
close(svr->sockfd);
|
|
remaining_cnt = 3;
|
|
while (remaining_cnt) {
|
|
svr->sockfd = create_recv_sock(svr->domain);
|
|
if (svr->sockfd != -1) {
|
|
/* succeed to create recv socket */
|
|
break;
|
|
}
|
|
remaining_cnt--;
|
|
}
|
|
if (svr->sockfd == -1) {
|
|
log_message(LOG_ERR, "fail to recreate recv socket. mdnsd main_loop will be terminated.\n");
|
|
break; /* exit loop */
|
|
}
|
|
|
|
if (svr->sockfd > max_fd) {
|
|
max_fd = svr->sockfd;
|
|
}
|
|
econnreset_count = 0;
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
DEBUG_PRINTF("data from=%s:%d size=%ld\n", inet_ntoa(fromaddr.sin_addr), ntohl(fromaddr.sin_port), (long) recvsize);
|
|
struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize);
|
|
if (mdns != NULL) {
|
|
struct sockaddr_in toaddr = fromaddr;
|
|
if (process_mdns_pkt(svr, mdns, &toaddr, mdns_packet)) {
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
size_t replylen = mdns_encode_pkt(mdns_packet, pkt_buffer, PACKET_SIZE);
|
|
ssize_t ret = -1;
|
|
if (toaddr.sin_port != 0) {
|
|
ret = send_packet_to(svr->sockfd, pkt_buffer, replylen, &toaddr);
|
|
} else {
|
|
ret = send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain);
|
|
}
|
|
if (ret == -1)
|
|
log_message(LOG_ERR, "send_packet() failed; %s\n", strerror(errno));
|
|
#endif
|
|
} else if (mdns->num_qn == 0) {
|
|
DEBUG_PRINTF("(no questions in packet)\n\n");
|
|
}
|
|
mdns_pkt_destroy(mdns);
|
|
}
|
|
}
|
|
} else {
|
|
log_message(LOG_ERR, "select() failed (ret: %d); %s\n", ret, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
// send out query
|
|
while (1) {
|
|
if (!svr->query) {
|
|
break;
|
|
}
|
|
|
|
process_for_query(svr, mdns_packet);
|
|
if (mdns_packet->num_qn > 0) {
|
|
|
|
DEBUG_PRINTF("sending query : (num of qn = %d)\n", mdns_packet->num_qn);
|
|
|
|
size_t replylen = mdns_encode_pkt(mdns_packet, pkt_buffer, PACKET_SIZE);
|
|
if (send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain) == -1) {
|
|
log_message(LOG_ERR, "send_packet() failed. (errno: %d)\n", errno);
|
|
}
|
|
|
|
if (mdns_packet->rr_qn) {
|
|
rr_list_destroy(mdns_packet->rr_qn, 1);
|
|
mdns_packet->rr_qn = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
// send out probe
|
|
while (1) {
|
|
if (!svr->probe) {
|
|
break;
|
|
}
|
|
|
|
process_for_probe(svr, mdns_packet);
|
|
if (mdns_packet->num_qn > 0) {
|
|
DEBUG_PRINTF("sending query for probe : (num of qn = %d)\n", mdns_packet->num_qn);
|
|
size_t replylen = mdns_encode_pkt(mdns_packet, pkt_buffer, PACKET_SIZE);
|
|
if (send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain) == -1) {
|
|
log_message(LOG_ERR, "send_packet() failed; %s\n", strerror(errno));
|
|
}
|
|
|
|
if (mdns_packet->rr_qn) {
|
|
rr_list_destroy(mdns_packet->rr_qn, 1);
|
|
mdns_packet->rr_qn = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// send out announces
|
|
while (1) {
|
|
struct rr_entry *ann_e = NULL;
|
|
|
|
// extract from head of list
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
if (svr->announce)
|
|
ann_e = rr_list_remove(&svr->announce, svr->announce->e);
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
if (! ann_e)
|
|
break;
|
|
|
|
char *namestr = nlabel_to_str(ann_e->name);
|
|
DEBUG_PRINTF("sending announce for %s\n", namestr);
|
|
MDNS_FREE(namestr);
|
|
|
|
announce_srv(svr, mdns_packet, ann_e->name);
|
|
|
|
if (mdns_packet->num_ans_rr > 0) {
|
|
size_t replylen = mdns_encode_pkt(mdns_packet, pkt_buffer, PACKET_SIZE);
|
|
if (send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain) == -1)
|
|
log_message(LOG_ERR, "send_packet() failed; %s\n", strerror(errno));
|
|
}
|
|
}
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
|
|
if (svr->sendmsg_requested) {
|
|
if (svr->stop_flag)
|
|
break; /* exit main_loop */
|
|
sem_post(&svr->sendmsg_sem);
|
|
}
|
|
|
|
if (svr->dump_cache) {
|
|
svr->dump_cache = 0;
|
|
print_cache(svr);
|
|
}
|
|
}
|
|
|
|
if (svr->sendmsg_requested && svr->stop_flag) {
|
|
// sem_post() should be executed in order to awaken mdnsd_stop(),
|
|
// because main_loop is terminated before executing sem_post()
|
|
sem_post(&svr->sendmsg_sem);
|
|
}
|
|
// main thread terminating. send out "goodbye packets" for services
|
|
mdns_init_reply(mdns_packet, 0);
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
for (struct rr_list *svc_le = svr->services; svc_le; svc_le = svc_le->next) {
|
|
// set TTL to zero
|
|
svc_le->e->ttl = 0;
|
|
mdns_packet->num_ans_rr += rr_list_append(&mdns_packet->rr_ans, svc_le->e);
|
|
}
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
#endif
|
|
|
|
// send out packet
|
|
if (mdns_packet->num_ans_rr > 0) {
|
|
if (svr->sockfd != -1) {
|
|
size_t replylen = mdns_encode_pkt(mdns_packet, pkt_buffer, PACKET_SIZE);
|
|
if (send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain) == -1) {
|
|
log_message(LOG_ERR, "send_packet() failed; %s\n", strerror(errno));
|
|
}
|
|
}
|
|
}
|
|
|
|
out:
|
|
// destroy packet
|
|
if (mdns_packet) {
|
|
mdns_init_reply(mdns_packet, 0);
|
|
MDNS_FREE(mdns_packet);
|
|
mdns_packet = NULL;
|
|
}
|
|
|
|
if (pkt_buffer) {
|
|
MDNS_FREE(pkt_buffer);
|
|
pkt_buffer = NULL;
|
|
}
|
|
|
|
if (svr->sockfd != -1) {
|
|
close(svr->sockfd);
|
|
svr->sockfd = -1;
|
|
}
|
|
|
|
svr->stop_flag = 2;
|
|
}
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
static int probe_hostname(struct mdnsd *svr, char *hostname)
|
|
{
|
|
int result = 1;
|
|
int i;
|
|
|
|
struct rr_entry *probe_e = NULL;
|
|
uint8_t *name;
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
svr->c_status = CACHE_RESOLVE_HOSTNAME;
|
|
if (svr->c_filter) {
|
|
MDNS_FREE(svr->c_filter);
|
|
svr->c_filter = MDNS_STRDUP(hostname);
|
|
}
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
/* make query with RR_ANY for probe hostname */
|
|
name = create_nlabel(hostname);
|
|
probe_e = qn_create(name, RR_ANY, 0);
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
|
|
rr_list_append(&svr->probe, probe_e);
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
request_sendmsg(svr);
|
|
|
|
usleep(250 * 1000); // 250ms delay
|
|
|
|
if (lookup_hostname(svr, hostname) == 0) {
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
svr->c_status = CACHE_NORMAL;
|
|
if (svr->c_filter) {
|
|
MDNS_FREE(svr->c_filter);
|
|
svr->c_filter = NULL;
|
|
}
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static void init_service_discovery_result(struct mdns_service_info
|
|
*service_list, int num_of_services)
|
|
{
|
|
if (service_list == NULL) {
|
|
log_message(LOG_ERR, "mdns_service_info is null.\n");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < num_of_services; i++) {
|
|
service_list[i].type = NULL;
|
|
service_list[i].instance_name = NULL;
|
|
service_list[i].hostname = NULL;
|
|
service_list[i].ipaddr = 0;
|
|
service_list[i].port = 0;
|
|
}
|
|
}
|
|
|
|
static void clear_service_discovery_result(struct mdns_service_info
|
|
*service_list, int num_of_services)
|
|
{
|
|
if (service_list == NULL) {
|
|
log_message(LOG_ERR, "service_list is null.\n");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < num_of_services; i++) {
|
|
if (service_list[i].type) {
|
|
MDNS_FREE(service_list[i].type);
|
|
service_list[i].type = NULL;
|
|
}
|
|
|
|
if (service_list[i].instance_name) {
|
|
MDNS_FREE(service_list[i].instance_name);
|
|
service_list[i].instance_name = NULL;
|
|
}
|
|
|
|
if (service_list[i].hostname) {
|
|
MDNS_FREE(service_list[i].hostname);
|
|
service_list[i].hostname = NULL;
|
|
}
|
|
|
|
service_list[i].ipaddr = 0;
|
|
service_list[i].port = 0;
|
|
}
|
|
}
|
|
|
|
static void destroy_service_discovery_result(struct mdns_service_info
|
|
*service_list, int num_of_services)
|
|
{
|
|
if (service_list == NULL) {
|
|
log_message(LOG_ERR, "service_list is null.\n");
|
|
return;
|
|
}
|
|
|
|
clear_service_discovery_result(service_list, num_of_services);
|
|
|
|
MDNS_FREE(service_list);
|
|
}
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
static int mdnsd_set_host_info(struct mdnsd *svr, const char *hostname, uint32_t ipaddr)
|
|
{
|
|
int result = -1;
|
|
struct rr_entry *a_e = NULL, *nsec_e = NULL;
|
|
int domain;
|
|
char mdns_suffix[16];
|
|
|
|
if (svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd instance handle is null.\n");
|
|
goto done;
|
|
}
|
|
|
|
domain = check_mdns_domain(hostname);
|
|
if (domain == MDNS_DOMAIN_LOCAL) {
|
|
snprintf(mdns_suffix, sizeof(mdns_suffix)-1, MDNS_SUFFIX_LOCAL);
|
|
}
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
else if (domain == MDNS_DOMAIN_SITE) {
|
|
snprintf(mdns_suffix, sizeof(mdns_suffix)-1, MDNS_SUFFIX_SITE);
|
|
}
|
|
#endif
|
|
else {
|
|
log_message(LOG_ERR, "mdnsd hostname is invalid.\n");
|
|
goto done;
|
|
}
|
|
|
|
if (ipaddr == 0) {
|
|
log_message(LOG_ERR, "mdnsd ip address is not set.\n");
|
|
goto done;
|
|
}
|
|
|
|
int need_probe = 1;
|
|
uint8_t *hname_nlabel = create_nlabel(hostname);
|
|
if (svr->hostname && svr->hostname_org) {
|
|
if ((strcmp((char *)hname_nlabel, (char *)svr->hostname_org) == 0) || (strcmp((char *)hname_nlabel, (char *)svr->hostname) == 0)) {
|
|
need_probe = 0;
|
|
}
|
|
}
|
|
MDNS_FREE(hname_nlabel);
|
|
|
|
char hname[128];
|
|
if (need_probe) {
|
|
// probe hostname collision
|
|
char *str;
|
|
snprintf(hname, sizeof(hname), hostname);
|
|
int alternative_hostname = 0;
|
|
int no = 2;
|
|
while (probe_hostname(svr, hname) == 0) {
|
|
/* when name conflict occurs */
|
|
snprintf(hname, sizeof(hname), hostname);
|
|
str = strstr(hname, mdns_suffix);
|
|
if (str) {
|
|
snprintf(str, sizeof(hname), "(%d)%s", no++, mdns_suffix);
|
|
alternative_hostname = 1;
|
|
} else {
|
|
log_message(LOG_ERR, "cannot set an alternative hostname.\n");
|
|
}
|
|
}
|
|
|
|
// if there is previous hostname information, delete it
|
|
if (svr->hostname) {
|
|
if (svr->hostname == svr->hostname_org) {
|
|
MDNS_FREE(svr->hostname);
|
|
svr->hostname = NULL;
|
|
svr->hostname_org = NULL;
|
|
} else {
|
|
MDNS_FREE(svr->hostname);
|
|
svr->hostname = NULL;
|
|
|
|
MDNS_FREE(svr->hostname_org);
|
|
svr->hostname_org = NULL;
|
|
}
|
|
}
|
|
// set hostname information
|
|
if (alternative_hostname) {
|
|
svr->hostname = create_nlabel(hname);
|
|
svr->hostname_org = create_nlabel(hostname);
|
|
} else {
|
|
svr->hostname = create_nlabel(hname);
|
|
svr->hostname_org = svr->hostname;
|
|
}
|
|
}
|
|
|
|
char *hname_str = nlabel_to_str(svr->hostname);
|
|
int hname_strlen = strlen(hname_str);
|
|
hname_str[hname_strlen - 1] = '\0';
|
|
|
|
a_e = rr_create_a(create_nlabel(hname_str), ipaddr);
|
|
nsec_e = rr_create(create_nlabel(hname_str), RR_NSEC);
|
|
rr_set_nsec(nsec_e, RR_A);
|
|
MDNS_FREE(hname_str);
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
rr_group_add(&svr->group, a_e);
|
|
rr_group_add(&svr->group, nsec_e);
|
|
|
|
// append RR_A entry to announce list
|
|
rr_list_append(&svr->announce, a_e);
|
|
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
|
|
request_sendmsg(svr);
|
|
|
|
/* success */
|
|
result = 0;
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/ioctl.h>
|
|
#include <net/if.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
|
|
static const char * flags(int sd, const char * name)
|
|
{
|
|
static char buf[1024];
|
|
|
|
static struct ifreq ifreq;
|
|
strcpy(ifreq.ifr_name, name);
|
|
|
|
int r = ioctl(sd, SIOCGIFFLAGS, (char *)&ifreq);
|
|
assert(r == 0);
|
|
|
|
int l = 0;
|
|
#define FLAG(b) if(ifreq.ifr_flags & b) l += snprintf(buf + l, sizeof(buf) - l, #b " ")
|
|
FLAG(IFF_UP);
|
|
FLAG(IFF_BROADCAST);
|
|
FLAG(IFF_DEBUG);
|
|
FLAG(IFF_LOOPBACK);
|
|
FLAG(IFF_POINTOPOINT);
|
|
FLAG(IFF_RUNNING);
|
|
FLAG(IFF_NOARP);
|
|
FLAG(IFF_PROMISC);
|
|
FLAG(IFF_NOTRAILERS);
|
|
FLAG(IFF_ALLMULTI);
|
|
FLAG(IFF_MASTER);
|
|
FLAG(IFF_SLAVE);
|
|
FLAG(IFF_MULTICAST);
|
|
FLAG(IFF_PORTSEL);
|
|
FLAG(IFF_AUTOMEDIA);
|
|
FLAG(IFF_DYNAMIC);
|
|
#undef FLAG
|
|
|
|
return buf;
|
|
}
|
|
*/
|
|
|
|
#ifndef HAVE_NETLIB_GET_IPV4ADDR
|
|
int netlib_get_ipv4addr(const char *ifname, struct in_addr *addr) {
|
|
int result = -1;
|
|
int sd;
|
|
|
|
struct ifreq ifreqs[8];
|
|
struct ifconf ifconf = {0};
|
|
|
|
ifconf.ifc_req = ifreqs;
|
|
ifconf.ifc_len = sizeof(ifreqs);
|
|
|
|
sd = socket(PF_INET, SOCK_STREAM, 0);
|
|
do {
|
|
if (sd < 0) break;
|
|
|
|
int r = ioctl(sd, SIOCGIFCONF, (char *)&ifconf);
|
|
if (r != 0) break;
|
|
|
|
for (int i = 0; i < ifconf.ifc_len/sizeof(struct ifreq); ++i)
|
|
{
|
|
/* printf("%s: %s\n", ifreqs[i].ifr_name, inet_ntoa(((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr));
|
|
printf(" flags: %s\n", flags(sd, ifreqs[i].ifr_name));
|
|
*/
|
|
if (0 == strcmp( ifname, ifreqs[i].ifr_name)) {
|
|
*addr = ((struct sockaddr_in *)&ifreqs[i].ifr_addr)->sin_addr;
|
|
result = 0;
|
|
break;
|
|
}
|
|
}
|
|
} while (0);
|
|
|
|
close(sd);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static int mdnsd_set_host_info_by_netif(struct mdnsd *svr, const char *hostname, const char *netif_nameOrAddress)
|
|
{
|
|
int result = -1;
|
|
struct in_addr ipaddr;
|
|
|
|
if (svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd instance handle is null.\n");
|
|
goto done;
|
|
}
|
|
|
|
if ((in_addr_t)(-1) == (ipaddr.s_addr = inet_addr(netif_nameOrAddress))) {
|
|
result = netlib_get_ipv4addr(netif_nameOrAddress, &ipaddr);
|
|
if (result < 0)
|
|
return result;
|
|
}
|
|
|
|
result = mdnsd_set_host_info(svr, hostname, ipaddr.s_addr);
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
|
|
static int init_mdns_context(int domain)
|
|
{
|
|
int result = -1;
|
|
pthread_t tid;
|
|
pthread_attr_t attr;
|
|
|
|
if (g_svr) {
|
|
log_message(LOG_ERR, "mdnsd is running.\n");
|
|
goto out;
|
|
}
|
|
|
|
g_svr = MDNS_MALLOC(sizeof(struct mdnsd));
|
|
if (g_svr == NULL) {
|
|
log_message(LOG_ERR, "memory allocation failed.\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* initialize struct mdnsd instance */
|
|
memset(g_svr, 0, sizeof(struct mdnsd));
|
|
g_svr->stop_flag = 0;
|
|
g_svr->sockfd = -1;
|
|
g_svr->notify_pipe[0] = -1;
|
|
g_svr->notify_pipe[1] = -1;
|
|
|
|
switch (domain) {
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
case MDNS_DOMAIN_SITE:
|
|
#endif
|
|
case MDNS_DOMAIN_LOCAL:
|
|
g_svr->domain = domain;
|
|
break;
|
|
case MDNS_DOMAIN_UNKNOWN:
|
|
default:
|
|
log_message(LOG_ERR, "invalid domain type.\n");
|
|
goto errout;
|
|
}
|
|
|
|
if (create_pipe(g_svr->notify_pipe) != 0) {
|
|
log_message(LOG_ERR, "create_pipe() failed.\n");
|
|
goto errout;
|
|
}
|
|
|
|
g_svr->sockfd = create_recv_sock(domain);
|
|
if (g_svr->sockfd < 0) {
|
|
log_message(LOG_ERR, "create_recv_sock() failed.\n");
|
|
goto errout;
|
|
}
|
|
|
|
/* memory allocation for service discovery */
|
|
if (g_service_list == NULL) {
|
|
g_service_list = (struct mdns_service_info *)MDNS_MALLOC(sizeof(struct mdns_service_info) * MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT);
|
|
if (g_service_list == NULL) {
|
|
goto errout;
|
|
}
|
|
init_service_discovery_result(g_service_list, MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT);
|
|
}
|
|
|
|
pthread_mutex_init(&g_svr->data_lock, NULL);
|
|
sem_init(&g_svr->sendmsg_sem, 0, 0);
|
|
g_svr->sendmsg_requested = 1;
|
|
g_svr->dump_cache= 0;
|
|
|
|
/* init thread */
|
|
if (pthread_attr_init(&attr) != 0) {
|
|
log_message(LOG_ERR, "pthread_attr_init() failed.\n");
|
|
goto errout_with_mutex;
|
|
}
|
|
#ifdef PTHREAD_CREATE_DETACHED_SUPPORTED /* PTHREAD_CREATE_DETACHED is not supported in tinyara */
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
|
#endif
|
|
|
|
size_t stksiz;
|
|
if (pthread_attr_getstacksize(&attr, &stksiz) == 0 && stksiz < THREAD_MAINLOOP_STACKSIZE) {
|
|
if (pthread_attr_setstacksize(&attr, THREAD_MAINLOOP_STACKSIZE) != 0) {
|
|
log_message(LOG_ERR, "pthread_attr_setstacksize() failed.\n");
|
|
goto errout_with_mutex;
|
|
}
|
|
}
|
|
|
|
/* create main_loop thread */
|
|
if ((pthread_create(&tid, &attr, (void * (*)(void *))main_loop, (void *)g_svr) != 0)) {
|
|
log_message(LOG_ERR, "pthread_create() failed.\n");
|
|
goto errout_with_mutex;
|
|
}
|
|
|
|
#ifdef PTHREAD_SETNAME_SUPPORTED
|
|
pthread_setname_np(tid, THREAD_MAINLOOP_NAME);
|
|
#endif
|
|
#ifndef PTHREAD_CREATE_DETACHED_SUPPORTED
|
|
if (pthread_detach(tid) != 0) {
|
|
log_message(LOG_ERR, "pthread_detach() failed.\n");
|
|
}
|
|
#endif
|
|
|
|
/* wait until main_loop starts */
|
|
while (g_svr->sendmsg_requested == 1) {
|
|
if (g_svr->stop_flag) {
|
|
goto errout_with_mutex;
|
|
}
|
|
|
|
usleep(10 * 1000);
|
|
}
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
g_svr->c_status = CACHE_NORMAL;
|
|
#else
|
|
g_svr->c_status = CACHE_SLEEP;
|
|
#endif
|
|
|
|
/* success */
|
|
result = 0;
|
|
|
|
out:
|
|
return result;
|
|
|
|
errout_with_mutex:
|
|
if (g_svr) {
|
|
pthread_mutex_destroy(&g_svr->data_lock);
|
|
sem_destroy(&g_svr->sendmsg_sem);
|
|
}
|
|
|
|
errout:
|
|
if (g_svr) {
|
|
if (g_svr->sockfd != -1) {
|
|
close(g_svr->sockfd);
|
|
g_svr->sockfd = -1;
|
|
}
|
|
|
|
if (g_svr->notify_pipe[0] != -1) {
|
|
close_pipe(g_svr->notify_pipe[0]);
|
|
g_svr->notify_pipe[0] = -1;
|
|
}
|
|
|
|
if (g_svr->notify_pipe[1] != -1) {
|
|
close_pipe(g_svr->notify_pipe[1]);
|
|
g_svr->notify_pipe[1] = -1;
|
|
}
|
|
|
|
MDNS_FREE(g_svr);
|
|
g_svr = NULL;
|
|
}
|
|
|
|
if (g_service_list) {
|
|
destroy_service_discovery_result(g_service_list, MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT);
|
|
g_service_list = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void release_mdns_context_resource(void)
|
|
{
|
|
if (g_svr->notify_pipe[0] != -1) {
|
|
close_pipe(g_svr->notify_pipe[0]);
|
|
g_svr->notify_pipe[0] = -1;
|
|
}
|
|
if (g_svr->notify_pipe[1] != -1) {
|
|
close_pipe(g_svr->notify_pipe[1]);
|
|
g_svr->notify_pipe[1] = -1;
|
|
}
|
|
|
|
pthread_mutex_destroy(&g_svr->data_lock);
|
|
sem_destroy(&g_svr->sendmsg_sem);
|
|
|
|
rr_group_destroy(g_svr->cache);
|
|
g_svr->cache = NULL;
|
|
|
|
rr_list_destroy(g_svr->query, 0);
|
|
g_svr->query = NULL;
|
|
|
|
if (g_svr->c_filter) {
|
|
MDNS_FREE(g_svr->c_filter);
|
|
g_svr->c_filter = NULL;
|
|
}
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
rr_group_destroy(g_svr->group);
|
|
g_svr->group = NULL;
|
|
|
|
rr_list_destroy(g_svr->announce, 0);
|
|
g_svr->announce = NULL;
|
|
|
|
rr_list_destroy(g_svr->services, 0);
|
|
g_svr->services = NULL;
|
|
|
|
rr_list_destroy(g_svr->probe, 0);
|
|
g_svr->probe = NULL;
|
|
|
|
if (g_svr->hostname == g_svr->hostname_org) {
|
|
if (g_svr->hostname) {
|
|
MDNS_FREE(g_svr->hostname);
|
|
g_svr->hostname = NULL;
|
|
g_svr->hostname_org = NULL;
|
|
}
|
|
} else {
|
|
if (g_svr->hostname) {
|
|
MDNS_FREE(g_svr->hostname);
|
|
g_svr->hostname = NULL;
|
|
}
|
|
if (g_svr->hostname_org) {
|
|
MDNS_FREE(g_svr->hostname_org);
|
|
g_svr->hostname_org = NULL;
|
|
}
|
|
}
|
|
|
|
if (g_service_list) {
|
|
/* free memory for service discovery */
|
|
destroy_service_discovery_result(g_service_list, MAX_NUMBER_OF_SERVICE_DISCOVERY_RESULT);
|
|
g_service_list = NULL;
|
|
}
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
|
|
g_svr->domain = MDNS_DOMAIN_UNKNOWN;
|
|
|
|
MDNS_FREE(g_svr);
|
|
g_svr = NULL;
|
|
}
|
|
|
|
static int destroy_mdns_context(void)
|
|
{
|
|
int result = -1;
|
|
|
|
if (g_svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd is not running.\n");
|
|
goto done;
|
|
}
|
|
|
|
g_svr->stop_flag = 1;
|
|
|
|
request_sendmsg(g_svr);
|
|
|
|
while (g_svr->stop_flag != 2) {
|
|
usleep(500 * 1000);
|
|
}
|
|
|
|
release_mdns_context_resource();
|
|
|
|
/* success */
|
|
result = 0;
|
|
|
|
done:
|
|
#ifdef MDNSD_MEMORY_DEBUG
|
|
mdns_show_meminfo();
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
static void mdns_cmd_mutex_lock(void)
|
|
{
|
|
if (!g_cmd_lock_initialized) {
|
|
if (pthread_mutex_init(&g_cmd_lock, NULL) != 0) {
|
|
log_message(LOG_ERR, "pthread_mutex_init() failed.\n");
|
|
return;
|
|
}
|
|
g_cmd_lock_initialized = 1;
|
|
}
|
|
|
|
if (g_cmd_lock_initialized) {
|
|
pthread_mutex_lock(&g_cmd_lock);
|
|
}
|
|
}
|
|
|
|
static void mdns_cmd_mutex_unlock(void)
|
|
{
|
|
if (g_cmd_lock_initialized) {
|
|
pthread_mutex_unlock(&g_cmd_lock);
|
|
}
|
|
}
|
|
|
|
static void handler_dump_cache(int sig) {
|
|
(void)sig;
|
|
g_svr->dump_cache = 1;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////
|
|
// Public Functions
|
|
/////////////////////////////////////////////////////
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
|
|
/****************************************************************************
|
|
* Name: mdnsd_start
|
|
*
|
|
* Description:
|
|
* Starts the MDNS daemon.
|
|
*
|
|
* Parameters:
|
|
* desired_hostname : the desired host name as string type. if same name is in the network,
|
|
* an alternative name will be set as hostname.
|
|
* netif_name : network interface name as string type
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned. On failure, a negative value is returned.
|
|
*
|
|
****************************************************************************/
|
|
int mdnsd_start(const char *desired_hostname, const char *netif_nameOrAddress)
|
|
{
|
|
int result = -1;
|
|
int domain;
|
|
|
|
mdns_cmd_mutex_lock();
|
|
|
|
domain = check_mdns_domain(desired_hostname);
|
|
if (init_mdns_context(domain) != 0) {
|
|
goto out_with_mutex;
|
|
}
|
|
|
|
/* set hostname and ip address */
|
|
if (mdnsd_set_host_info_by_netif(g_svr, desired_hostname, netif_nameOrAddress) != 0) {
|
|
if (destroy_mdns_context() != 0) {
|
|
log_message(LOG_ERR, "mdnsd_set_host_info_by_netif() and destroy_mdns_context() failed.\n");
|
|
}
|
|
|
|
goto out_with_mutex;
|
|
}
|
|
|
|
signal(SIGUSR1, handler_dump_cache);
|
|
|
|
/* success */
|
|
result = 0;
|
|
|
|
out_with_mutex:
|
|
mdns_cmd_mutex_unlock();
|
|
return result;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mdnsd_stop
|
|
*
|
|
* Description:
|
|
* Stops the MDNS daemon.
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned. On failure, a negative value is returned.
|
|
*
|
|
****************************************************************************/
|
|
int mdnsd_stop(void)
|
|
{
|
|
int result = -1;
|
|
|
|
mdns_cmd_mutex_lock();
|
|
|
|
if (destroy_mdns_context() != 0) {
|
|
goto out_with_mutex;
|
|
}
|
|
|
|
/* success */
|
|
result = 0;
|
|
|
|
out_with_mutex:
|
|
mdns_cmd_mutex_unlock();
|
|
return result;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mdnsd_get_hostname
|
|
*
|
|
* Description:
|
|
* Gets the current host name as MDNS type.
|
|
*
|
|
* Parameters:
|
|
* hostname_result : 32-bytes string buffer for the host name result
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned. On failure, a negative value is returned.
|
|
*
|
|
****************************************************************************/
|
|
int mdnsd_get_hostname(char *hostname_result)
|
|
{
|
|
int result = -1;
|
|
if (g_svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd is not running.\n");
|
|
goto out;
|
|
}
|
|
|
|
mdns_cmd_mutex_lock();
|
|
|
|
char *hname = nlabel_to_str(g_svr->hostname);
|
|
int hname_len = strlen(hname);
|
|
hname_len = (hname_len > 32) ? 32 : hname_len;
|
|
hname[hname_len - 1] = '\0';
|
|
strncpy(hostname_result, hname, 32);
|
|
MDNS_FREE(hname);
|
|
|
|
/* success */
|
|
result = 0;
|
|
|
|
mdns_cmd_mutex_unlock();
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
static void mdns_service_destroy(struct mdns_service *srv)
|
|
{
|
|
assert(srv != NULL);
|
|
rr_list_destroy(srv->entries, 0);
|
|
MDNS_FREE(srv);
|
|
}
|
|
|
|
int mdnsd_register_service(const char *instance_name,
|
|
const char *type, uint16_t port, const char *hostname, const char *txt[]) {
|
|
struct rr_entry *txt_e = NULL,
|
|
*srv_e = NULL,
|
|
*ptr_e = NULL,
|
|
*bptr_e = NULL;
|
|
uint8_t *target;
|
|
uint8_t *inst_nlabel, *type_nlabel, *nlabel;
|
|
struct mdns_service *service = NULL;
|
|
if (g_svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd is not running.\n");
|
|
goto out;
|
|
}
|
|
|
|
if (check_mdns_domain(type) == MDNS_DOMAIN_UNKNOWN) {
|
|
log_message(LOG_ERR, "service type is invalid. service type should be "
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
"%s or %s domain.\n", MDNS_SUFFIX_LOCAL, MDNS_SUFFIX_SITE);
|
|
#else
|
|
"%s domain.\n", MDNS_SUFFIX_LOCAL);
|
|
#endif
|
|
goto out;
|
|
}
|
|
|
|
mdns_cmd_mutex_lock();
|
|
|
|
service = (struct mdns_service *)MDNS_MALLOC(sizeof(struct mdns_service));
|
|
if (service == NULL) {
|
|
log_message(LOG_ERR, "memory allocation failed.\n");
|
|
goto out_with_mutex;
|
|
}
|
|
memset(service, 0, sizeof(struct mdns_service));
|
|
|
|
// combine service name
|
|
type_nlabel = create_nlabel(type);
|
|
inst_nlabel = create_nlabel(instance_name);
|
|
nlabel = join_nlabel(inst_nlabel, type_nlabel);
|
|
|
|
// create TXT record
|
|
if (txt && *txt) {
|
|
txt_e = rr_create(dup_nlabel(nlabel), RR_TXT);
|
|
rr_list_append(&service->entries, txt_e);
|
|
|
|
// add TXTs
|
|
for (; *txt; txt++)
|
|
rr_add_txt(txt_e, *txt);
|
|
}
|
|
|
|
// create SRV record
|
|
assert(hostname || g_svr->hostname); // either one as target
|
|
target = hostname ?
|
|
create_nlabel(hostname) :
|
|
dup_nlabel(g_svr->hostname);
|
|
|
|
srv_e = rr_create_srv(dup_nlabel(nlabel), port, target);
|
|
rr_list_append(&service->entries, srv_e);
|
|
|
|
// create PTR record for type
|
|
ptr_e = rr_create_ptr(type_nlabel, srv_e);
|
|
|
|
// create services PTR record for type
|
|
// this enables the type to show up as a "service"
|
|
bptr_e = rr_create_ptr(dup_nlabel(SERVICES_DNS_SD_NLABEL), ptr_e);
|
|
|
|
// modify lists here
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
|
|
if (txt_e)
|
|
rr_group_add(&g_svr->group, txt_e);
|
|
rr_group_add(&g_svr->group, srv_e);
|
|
rr_group_add(&g_svr->group, ptr_e);
|
|
rr_group_add(&g_svr->group, bptr_e);
|
|
|
|
// append PTR entry to announce list
|
|
rr_list_append(&g_svr->announce, ptr_e);
|
|
rr_list_append(&g_svr->services, ptr_e);
|
|
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
|
|
// don't free type_nlabel - it's with the PTR record
|
|
MDNS_FREE(nlabel);
|
|
MDNS_FREE(inst_nlabel);
|
|
|
|
// notify server
|
|
request_sendmsg(g_svr);
|
|
|
|
mdns_service_destroy(service);
|
|
|
|
/* result is success */
|
|
int result = 0;
|
|
|
|
out_with_mutex:
|
|
mdns_cmd_mutex_unlock();
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
void mdnsd_set_hostname(struct mdnsd *svr, const char *hostname, uint32_t ip) {
|
|
struct rr_entry *a_e = NULL,
|
|
*nsec_e = NULL;
|
|
|
|
// currently can't be called twice
|
|
// dont ask me what happens if the IP changes
|
|
assert(svr->hostname == NULL);
|
|
|
|
a_e = rr_create_a(create_nlabel(hostname), ip);
|
|
|
|
nsec_e = rr_create(create_nlabel(hostname), RR_NSEC);
|
|
rr_set_nsec(nsec_e, RR_A);
|
|
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
svr->hostname = create_nlabel(hostname);
|
|
rr_group_add(&svr->group, a_e);
|
|
rr_group_add(&svr->group, nsec_e);
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
}
|
|
|
|
void mdnsd_add_rr(struct mdnsd *svr, struct rr_entry *rr) {
|
|
pthread_mutex_lock(&svr->data_lock);
|
|
rr_group_add(&svr->group, rr);
|
|
pthread_mutex_unlock(&svr->data_lock);
|
|
}
|
|
#endif /* !defined MDNS_NO_RESPONDER_SUPPORT */
|
|
|
|
/****************************************************************************
|
|
* Name: mdnsd_resolve_hostname
|
|
*
|
|
* Description:
|
|
* Gets ip address with the given hostname.
|
|
*
|
|
* Parameters:
|
|
* hostname : host name as string type
|
|
* ipaddr : the pointer of ip address result
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned. On failure, a negative value is returned.
|
|
*
|
|
****************************************************************************/
|
|
int mdnsd_resolve_hostname(char *hostname, uint32_t *ipaddr)
|
|
{
|
|
int result = -1;
|
|
struct timeval old_time, cur_time;
|
|
int try_count;
|
|
int domain;
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
if (g_svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd is not running.\n");
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
domain = check_mdns_domain(hostname);
|
|
if (domain == MDNS_DOMAIN_UNKNOWN) {
|
|
log_message(LOG_ERR, "hostname is invalid. hostname should be "
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
"%s or %s domain.\n", MDNS_SUFFIX_LOCAL, MDNS_SUFFIX_SITE);
|
|
#else
|
|
"%s domain.\n", MDNS_SUFFIX_LOCAL);
|
|
#endif
|
|
goto out;
|
|
}
|
|
|
|
mdns_cmd_mutex_lock();
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
if (g_svr->stop_flag == 2) {
|
|
/* mdnsd main_loop has terminated by itself */
|
|
log_message(LOG_ERR, "main_loop has been terminated. mdnsd will stop.\n");
|
|
release_mdns_context_resource();
|
|
goto out_with_mutex;
|
|
}
|
|
#else
|
|
if (init_mdns_context(domain) != 0) {
|
|
log_message(LOG_ERR, "init_mdns_context() failed.\n");
|
|
goto out_with_mutex;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
g_svr->c_status = CACHE_RESOLVE_HOSTNAME;
|
|
if (g_svr->c_filter) {
|
|
MDNS_FREE(g_svr->c_filter);
|
|
g_svr->c_filter = NULL;
|
|
}
|
|
g_svr->c_filter = MDNS_STRDUP(hostname);
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
#endif
|
|
|
|
if (lookup_hostname_to_addr(g_svr, hostname, ipaddr) == 0) {
|
|
result = 0;
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
goto out_with_mutex;
|
|
#else
|
|
goto out_with_context;
|
|
#endif
|
|
}
|
|
|
|
try_count = 0;
|
|
TIME_GET(old_time);
|
|
do {
|
|
if (try_count < MDNS_HOSTNAME_RESOLVER_MAX_TRY_COUNT) {
|
|
struct rr_entry *a_e = NULL;
|
|
struct rr_entry *aaaa_e = NULL;
|
|
|
|
a_e = qn_create(create_nlabel(hostname), RR_A, 0);
|
|
aaaa_e = qn_create(create_nlabel(hostname), RR_AAAA, 0);
|
|
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
|
|
rr_list_append(&g_svr->query, a_e);
|
|
rr_list_append(&g_svr->query, aaaa_e);
|
|
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
|
|
request_sendmsg(g_svr);
|
|
|
|
try_count++;
|
|
}
|
|
|
|
usleep(MDNS_HOSTNAME_RESOLVER_WAIT_TIME_MSEC * 1000);
|
|
|
|
if (lookup_hostname_to_addr(g_svr, hostname, ipaddr) == 0) {
|
|
result = 0;
|
|
break;
|
|
}
|
|
} while ((TIME_GET(cur_time),TIME_DIFF_USEC(old_time, cur_time)) <= (MDNS_HOSTNAME_RESOLVER_TIMEOUT_MSEC * 1000));
|
|
|
|
|
|
#ifdef MDNS_NO_RESPONDER_SUPPORT
|
|
out_with_context:
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
g_svr->c_status = CACHE_SLEEP;
|
|
if (g_svr->c_filter) {
|
|
MDNS_FREE(g_svr->c_filter);
|
|
g_svr->c_filter = NULL;
|
|
}
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
|
|
if (destroy_mdns_context() != 0) {
|
|
log_message(LOG_ERR, "destroy_mdns_context() failed.\n");
|
|
}
|
|
#endif
|
|
|
|
out_with_mutex:
|
|
mdns_cmd_mutex_unlock();
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|
|
/****************************************************************************
|
|
* Name: mdnsd_discover_service
|
|
*
|
|
* Description:
|
|
* Discovers services with the given service type string
|
|
*
|
|
* Parameters:
|
|
* service_type : mdns service type string
|
|
* discover_time_msec : time in milliseconds for discovering service
|
|
* service_list : the array of service list
|
|
* num_of_services : number of services
|
|
*
|
|
* Returned Value:
|
|
* On success, 0 is returned. On failure, a negative value is returned.
|
|
*
|
|
****************************************************************************/
|
|
int mdnsd_discover_service(char *service_type, int discover_time_msec, struct mdns_service_info **service_list, int *num_of_services)
|
|
{
|
|
int result = -1;
|
|
struct timeval old_time, cur_time;
|
|
int try_count;
|
|
int domain;
|
|
char service_type_str[128];
|
|
|
|
if (discover_time_msec <= 0 || discover_time_msec > MAX_SERVICE_DISCOVERY_TIME_MS) {
|
|
log_message(LOG_ERR, "discover_time_msec (%d) is invalid. (valid range : 0 < x <= %d)\n", discover_time_msec, MAX_SERVICE_DISCOVERY_TIME_MS);
|
|
goto out;
|
|
}
|
|
|
|
domain = check_mdns_domain(service_type);
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
if (g_svr == NULL) {
|
|
log_message(LOG_ERR, "mdnsd is not running.\n");
|
|
goto out;
|
|
}
|
|
|
|
if (domain == MDNS_DOMAIN_UNKNOWN) {
|
|
switch (g_svr->domain) {
|
|
case MDNS_DOMAIN_LOCAL:
|
|
domain = MDNS_DOMAIN_LOCAL;
|
|
snprintf(service_type_str, sizeof(service_type_str), "%s%s", service_type, MDNS_SUFFIX_LOCAL);
|
|
break;
|
|
#if defined(CONFIG_NETUTILS_MDNS_XMDNS)
|
|
case MDNS_DOMAIN_SITE:
|
|
domain = MDNS_DOMAIN_SITE;
|
|
snprintf(service_type_str, sizeof(service_type_str), "%s%s", service_type, MDNS_SUFFIX_SITE);
|
|
break;
|
|
#endif
|
|
default:
|
|
log_message(LOG_ERR, "current mdns domain is invalid.\n");
|
|
goto out;
|
|
}
|
|
} else {
|
|
snprintf(service_type_str, sizeof(service_type_str), "%s", service_type);
|
|
}
|
|
|
|
#else
|
|
if (domain == MDNS_DOMAIN_UNKNOWN) {
|
|
domain = MDNS_DOMAIN_LOCAL;
|
|
snprintf(service_type_str, sizeof(service_type_str), "%s%s", service_type, MDNS_SUFFIX_LOCAL);
|
|
} else {
|
|
snprintf(service_type_str, sizeof(service_type_str), "%s", service_type);
|
|
}
|
|
#endif
|
|
|
|
mdns_cmd_mutex_lock();
|
|
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
if (g_svr->stop_flag == 2) {
|
|
/* mdnsd main_loop has terminated by itself */
|
|
log_message(LOG_ERR, "main_loop has terminated. mdnsd will stop.\n");
|
|
release_mdns_context_resource();
|
|
goto out_with_mutex;
|
|
}
|
|
#else
|
|
if (init_mdns_context(domain) != 0) {
|
|
log_message(LOG_ERR, "init_mdns_context() failed.\n");
|
|
goto out_with_mutex;
|
|
}
|
|
#endif
|
|
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
g_svr->c_status = CACHE_SERVICE_DISCOVERY;
|
|
if (g_svr->c_filter) {
|
|
MDNS_FREE(g_svr->c_filter);
|
|
g_svr->c_filter = NULL;
|
|
}
|
|
g_svr->c_filter = MDNS_STRDUP(service_type_str);
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
|
|
/* query PTR for service discovery */
|
|
struct rr_entry *ptr_e = NULL;
|
|
|
|
try_count = 0;
|
|
TIME_GET(old_time);
|
|
do {
|
|
if (try_count < MDNS_HOSTNAME_RESOLVER_MAX_TRY_COUNT) {
|
|
|
|
ptr_e = qn_create(create_nlabel(service_type_str), RR_PTR, 0);
|
|
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
rr_list_append(&g_svr->query, ptr_e);
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
|
|
request_sendmsg(g_svr);
|
|
|
|
try_count++;
|
|
}
|
|
|
|
usleep(MDNS_SERVICE_DISCOVERY_WAIT_TIME_MSEC * 1000);
|
|
} while ((TIME_GET(cur_time),TIME_DIFF_USEC(old_time, cur_time)) <= (discover_time_msec * 1000));
|
|
|
|
if (lookup_service(g_svr, service_type_str, g_service_list, num_of_services) == 0) {
|
|
*service_list = g_service_list;
|
|
result = 0;
|
|
} else {
|
|
*service_list = NULL;
|
|
}
|
|
|
|
pthread_mutex_lock(&g_svr->data_lock);
|
|
if (g_svr->c_filter) {
|
|
MDNS_FREE(g_svr->c_filter);
|
|
g_svr->c_filter = NULL;
|
|
}
|
|
#ifndef MDNS_NO_RESPONDER_SUPPORT
|
|
g_svr->c_status = CACHE_NORMAL;
|
|
#else
|
|
g_svr->c_status = CACHE_SLEEP;
|
|
#endif
|
|
pthread_mutex_unlock(&g_svr->data_lock);
|
|
|
|
#ifdef MDNS_NO_RESPONDER_SUPPORT
|
|
if (destroy_mdns_context() != 0) {
|
|
log_message(LOG_ERR, "destroy_mdns_context() failed.\n");
|
|
}
|
|
#endif
|
|
|
|
out_with_mutex:
|
|
mdns_cmd_mutex_unlock();
|
|
|
|
out:
|
|
return result;
|
|
}
|
|
|