#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <net/if.h>
#include "avahi-core/lookup.h"
#include <avahi-common/simple-watch.h>
#include <avahi-common/malloc.h>
#include <avahi-common/error.h>
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (1)
#endif
#define MAX_STR_LEN (256)
typedef struct ServiceInfo
{
char *pServiceType;
char *pServiceName;
char *pDomainName;
char *pInterface;
char *pProtocolNumber;
char *pAddr;
unsigned short port;
char *pHostName;
char *pText;
struct ServiceInfo *pNext;
}ServiceInfo, ServiceInfoList;
const char unUsedServiceInfoMemHead[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};//the impossible memory location value
static AvahiServer *pServer;
static AvahiSimplePoll *pSimplePoll;
ServiceInfoList *CreateServiceInfoList(void)
{
ServiceInfo *pServiceInfo;
pServiceInfo = (ServiceInfo*)malloc(sizeof(ServiceInfo));
memset(pServiceInfo, (unsigned char)0xff, sizeof(ServiceInfo));
return pServiceInfo;
}/*CreateServiceInfoList*/
#define SAFE_FREE(PTR) if(NULL != (PTR))\
free(PTR);\
PTR = NULL
void DestroyServiceInfo(ServiceInfo *pServiceInfo)
{
if(NULL == pServiceInfo)
return;
SAFE_FREE(pServiceInfo->pServiceType); SAFE_FREE(pServiceInfo->pServiceName);
SAFE_FREE(pServiceInfo->pDomainName);
SAFE_FREE(pServiceInfo->pInterface);SAFE_FREE(pServiceInfo->pProtocolNumber);
SAFE_FREE(pServiceInfo->pAddr);SAFE_FREE(pServiceInfo->pHostName);
if(NULL != pServiceInfo->pText)
avahi_free(pServiceInfo->pText);
pServiceInfo->pText = NULL;
SAFE_FREE(pServiceInfo);
}/*DestoryServiceInfo*/
void DestroyServiceInfoList(ServiceInfoList *pServiceInfoList)
{
ServiceInfo *pCurrent;
pCurrent = pServiceInfoList;
if( 0 == memcmp(pServiceInfoList, &unUsedServiceInfoMemHead[0], sizeof(unUsedServiceInfoMemHead)) )
{
SAFE_FREE(pCurrent);
return;
}/*if unused*/
while(NULL != pCurrent)
{
ServiceInfo *pNext;
pNext = pCurrent->pNext;
DestroyServiceInfo(pCurrent);
pCurrent = pNext;
}/*while*/
}/*CleanServiceInfoList*/
void ServiceResolverCallback(
AvahiSServiceResolver *pResolver,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiResolverEvent event,
const char *pServiceName,
const char *pType,
const char *pDomain,
const char *pHostName,
const AvahiAddress *pAvahiAddress,
uint16_t port,
AvahiStringList *pText,
AvahiLookupResultFlags flags,
void* pUserdata)
{
char addrStr[AVAHI_ADDRESS_STR_MAX];
ServiceInfo *pServiceInfo;
//printf("__FUNCTION__ = %s\n", __FUNCTION__);
if (AVAHI_RESOLVER_FOUND != event)
{
printf("NOT AVAHI_RESOLVER_FOUND\n");
return;
}/* AVAHI_RESOLVER_FOUND != event */
pServiceInfo = (ServiceInfo*)pUserdata;
if( 0 == memcmp(pServiceInfo, &unUsedServiceInfoMemHead[0], sizeof(unUsedServiceInfoMemHead)) )
{
memset(pServiceInfo, 0, sizeof(ServiceInfo));
}
else
{
while(NULL != pServiceInfo->pNext)
pServiceInfo = pServiceInfo->pNext;
pServiceInfo->pNext = (ServiceInfo*)malloc(sizeof(ServiceInfo));
memset(pServiceInfo->pNext, 0, sizeof(ServiceInfo));
pServiceInfo = pServiceInfo->pNext;
}/*if zero*/
avahi_address_snprint(&addrStr[0], AVAHI_ADDRESS_STR_MAX, pAvahiAddress);
pServiceInfo->pServiceType = strndup(pType, MAX_STR_LEN);
pServiceInfo->pServiceName = strndup(pServiceName, MAX_STR_LEN);
pServiceInfo->pDomainName = strndup(pDomain, MAX_STR_LEN);
pServiceInfo->pInterface = strndup(if_indextoname(interface, (char*)pServiceName), MAX_STR_LEN);
pServiceInfo->pProtocolNumber = strndup(avahi_proto_to_string(protocol), MAX_STR_LEN);
pServiceInfo->pAddr = strndup(&addrStr[0], MAX_STR_LEN);
pServiceInfo->pHostName = strndup(pHostName, MAX_STR_LEN);
pServiceInfo->port = port;
if(NULL != pText)
{
char *pAvahiText = avahi_string_list_to_string(pText) ;
pServiceInfo->pText = strndup(pAvahiText, MAX_STR_LEN);
avahi_free(pAvahiText);
}/*if NULL != pText*/
#if(0)
printf(
"Service Type: %s\n"
"Service Name: %s\n"
"Domain Name: %s\n"
"Interface: %s %s\n"
"Address: %s/%s:%u\n"
"TEXT Data: %s\n"
,
pServiceInfo->pServiceType,
pServiceInfo->pServiceName,
pServiceInfo->pDomainName,
pServiceInfo->pInterface, pServiceInfo->pProtocolNumber,
pServiceInfo->pAddr, pServiceInfo->pHostName, pServiceInfo->port,
NULL == pServiceInfo->pText ? "\x1B[1;36m" "NULL" "\x1B[0m": pServiceInfo->pText
);
#endif
if (NULL != pResolver)
avahi_s_service_resolver_free(pResolver);
}/*ServiceResolverCallback*/
void ServiceBrowserCallback(
AvahiSServiceBrowser *pBrower,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char *pServiceName,
const char *pServiceType,
const char *pDomainName,
AvahiLookupResultFlags flags,
void *pUserdata)
{
AvahiSServiceResolver *pResolver;
//printf("__FUNCTION__ = %s\n", __FUNCTION__);
if(NULL == pServiceName || NULL == pServiceType
|| NULL == pDomainName)
{
return;
}/*if NULL*/
/*
TODO:
a service filter should be put in here.
*/
pResolver = avahi_s_service_resolver_new(pServer, interface, protocol, pServiceName,
pServiceType, pDomainName, AVAHI_PROTO_UNSPEC, 0, ServiceResolverCallback,
(void*)pUserdata);
}/*ServiceBrowserCallback*/
void ServiceTypeBrowserCallback(
AvahiSServiceTypeBrowser *pTypeBrower,
AvahiIfIndex interface,
AvahiProtocol protocol,
AvahiBrowserEvent event,
const char *pType,
const char *pDomain,
AvahiLookupResultFlags flags,
void *pUserdata)
{
assert(pTypeBrower);
switch (event)
{
case AVAHI_BROWSER_FAILURE:
printf("AVAHI_BROWSER_FAILURE\n");
avahi_simple_poll_quit(pSimplePoll);
break;
case AVAHI_BROWSER_NEW:
//printf("AVAHI_BROWSER_NEW\n");
{
/* We ignore the returned resolver object. In the callback
function we free it. If the server is terminated before
the callback function is called the server will free
the resolver for us. */
if( NULL == avahi_s_service_browser_new(pServer, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
pType, pDomain, 0, ServiceBrowserCallback, pUserdata))
{
fprintf(stderr, "error in avahi_s_service_browser_new\n");
}/*if NULL*/
}/*local Variables*/
break;
case AVAHI_BROWSER_REMOVE:
//printf("AVAHI_BROWSER_REMOVE\n");
break;
case AVAHI_BROWSER_ALL_FOR_NOW:
//printf("AVAHI_BROWSER_ALL_FOR_NOW\n");
avahi_simple_poll_quit(pSimplePoll);
break;
case AVAHI_BROWSER_CACHE_EXHAUSTED:
//printf("AVAHI_BROWSER_CACHE_EXHAUSTED\n");
break;
}/*switch event*/
}/*service_type_browser_callback*/
void TimeoutCallback(AvahiTimeout *pTimeout, void *pUserdata)
{
printf("__FUNCTION__ = %s\n", __FUNCTION__);
avahi_simple_poll_quit(pSimplePoll);
}/*TimeoutCallback*/
int MulticastDomainNameSystemServiceDiscover(int *pNumber, ServiceInfoList *pServiceInfoList,
int maxTimeoutInMilliSec)
{
AvahiServerConfig config;
AvahiSServiceTypeBrowser *pServiceTypeBrowser;
ServiceInfo *pServiceInfoHead;
int error;
int i;
int ret;
struct timeval tv;
pSimplePoll = NULL;
pServiceTypeBrowser = NULL;
*pNumber = 0;
pServiceInfoHead = pServiceInfoList;
if(NULL == pServiceInfoHead)
{
fprintf(stderr, "ServiceInfo should be initialized \n");
ret = -1;
goto flag_Fail;
}/*if */
avahi_set_allocator(NULL);
pSimplePoll = avahi_simple_poll_new();
if (NULL == pSimplePoll)
{
fprintf(stderr, "Failed to create simple poll object.\n");
ret = -2;
goto flag_Fail;
}/*if*/
avahi_server_config_init(&config);
config.publish_hinfo = config.publish_addresses = FALSE;
config.publish_domain = config.publish_workstation = FALSE;
pServer = avahi_server_new(avahi_simple_poll_get (pSimplePoll), &config, NULL, NULL, &error);
avahi_server_config_free(&config);
if (NULL == pServer)
{
fprintf(stderr, "Failed to create server : %s\n", avahi_strerror(error));
ret = -3;
goto flag_Fail;
}/*if NULL == pServer*/
pServiceTypeBrowser = avahi_s_service_type_browser_new(pServer,
AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, NULL, 0,
ServiceTypeBrowserCallback, pServiceInfoHead);
if(NULL == pServiceTypeBrowser)
{
fprintf(stderr, "Failed to create brower \n");
ret = -4;
goto flag_Fail;
}/*if NULL == pServiceTypeBrowser*/
avahi_simple_poll_get(pSimplePoll)->timeout_new(
avahi_simple_poll_get(pSimplePoll),
avahi_elapse_time(&tv, maxTimeoutInMilliSec, 0),
TimeoutCallback,
NULL);
/* Run the main loop */
avahi_simple_poll_loop(pSimplePoll);
if( 0 == memcmp(pServiceInfoHead, &unUsedServiceInfoMemHead[0],
sizeof(unUsedServiceInfoMemHead)) )
{
goto flag_Fail ;
}/*if no service found*/
{/*count the list length*/
ServiceInfo *pServiceInfo;
pServiceInfo = pServiceInfoHead;
*pNumber = 0;
while(NULL != pServiceInfo)
{
pServiceInfo = pServiceInfo->pNext;
(*pNumber)++;
}/*NULL != pServiceInfo*/
}/*local variable */
ret = *pNumber;
flag_Fail:
if(NULL != pServer)
avahi_server_free(pServer);
pServer = NULL;
if(NULL != pSimplePoll)
avahi_simple_poll_free(pSimplePoll);
pSimplePoll = NULL;
return ret;
}/*MulticastDomainNameSystemServiceDiscover*/
#define TIMEOUT_VALUE (5000)
int main(int argc, char *argv[])
{
int n, i;
ServiceInfoList *pServiceInfoList;
struct timespec timeStart, timeEnd;
pServiceInfoList = CreateServiceInfoList();
clock_gettime(CLOCK_REALTIME, &timeStart);
MulticastDomainNameSystemServiceDiscover(&n, pServiceInfoList, TIMEOUT_VALUE);
clock_gettime(CLOCK_REALTIME, &timeEnd);
printf("%d services have been found\n", n);
if(0 != n )
{
ServiceInfo *pServiceInfo;
pServiceInfo = pServiceInfoList;
while(NULL != pServiceInfo)
{
printf("\n");
printf(
"Service Type: %s\n"
"Service Name: %s\n"
"Domain Name: %s\n"
"Interface: %s %s\n"
"Address: %s/%s:%u\n"
"TEXT Data: %s\n"
,
pServiceInfo->pServiceType,
pServiceInfo->pServiceName,
pServiceInfo->pDomainName,
pServiceInfo->pInterface, pServiceInfo->pProtocolNumber,
pServiceInfo->pAddr, pServiceInfo->pHostName, pServiceInfo->port,
NULL == pServiceInfo->pText ? "\x1B[1;36m" "NULL" "\x1B[0m": pServiceInfo->pText
);
pServiceInfo = pServiceInfo->pNext;
}/*while*/
printf("\n");
}/*if 0 != n*/
{
int elaspeTimeMicroSec;
elaspeTimeMicroSec = (timeEnd.tv_sec - timeStart.tv_sec) *1e6 +
(timeEnd.tv_nsec - timeStart.tv_nsec) /1e3;
printf("discover cast %u msec\n", elaspeTimeMicroSec/1000);
}
DestroyServiceInfoList(pServiceInfoList);
return 0;
}/*main*/