How to use Bonjour SDK for mDNS is rut, but I found that in the website, there is no simple example WITHOUT window to demonstrate how exploit this library. In the post, I would fill the gap.
零. Download the Bonjour Library.
. The apple id is requisite. After you have installed the SDK, check if your Bonjour Service is running or not.
一. Create a Visual Studio Project, Copy the Bonjour SDK folders: include and LIB (by default, they locates in C:\Program Files\Bonjour SDK) into the project folder, and add include path and library path refering to them:
Of course, you should add dnssd.lib as your library reference.
二.
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "IPHLPAPI.lib")
#include <iphlpapi.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "dns_sd.h"
#pragma comment(lib, "Ws2_32.lib")
#define MAX_STR_LEN (256)
#ifdef _DEBUG
#define LOG(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
#else
#define LOG(fmt, ...) /* Don't do anything in release builds */
#endif
#define SAFE_FREE(PTR) if(NULL != (PTR))\
free(PTR);\
PTR = NULL
typedef struct ServiceInfo
{
char *pServiceType;
char *pServiceName;
char *pDomainName;
unsigned int serviceInterface;
char *pProtocolNumber;
char *pAddr;
unsigned short port;
char *pHostName;
char *pText;
DNSServiceRef sdRef;
struct ServiceInfo *pNext;
}ServiceInfo, ServiceInfoList;
typedef void(__stdcall *mDNSBrowingCallbackFunPtr)
(
char *pServiceType,
char *pServiceName,
char *pDomainName,
char serviceInterface,
char *pProtocolNumber,
char *pAddr,
unsigned short port,
char *pHostName,
char *pText
);
HANDLE m_browingThreadHandle = NULL;
DWORD m_idBrowsingThread = 0;
UINT_PTR m_browsingTimerId;
ServiceInfoList *m_pServiceInfoList = NULL;
const unsigned char unUsedServiceInfoMemHead[] =
{
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
}; //the impossible memory location value
ServiceInfoList *CreateServiceInfoList(void)
{
ServiceInfo *pServiceInfo;
pServiceInfo = (ServiceInfo*)malloc(sizeof(ServiceInfo));
memset(pServiceInfo, (unsigned char)0xff, sizeof(ServiceInfo));
return pServiceInfo;
}/*CreateServiceInfoList*/
ServiceInfo *CreateServiceInfoNode(ServiceInfoList *pServiceInfoList, DNSServiceRef sdRef)
{
ServiceInfo *pCurrent;
if (NULL == pServiceInfoList)
return NULL;
pCurrent = pServiceInfoList;
if (0 == memcmp(pServiceInfoList, &unUsedServiceInfoMemHead[0],
sizeof(unUsedServiceInfoMemHead)) )
{
memset(m_pServiceInfoList, 0, sizeof(ServiceInfo));
pCurrent->sdRef = sdRef;
return pCurrent;
}/*if */
while (NULL != pCurrent->pNext)
pCurrent = pCurrent->pNext;
pCurrent->pNext = (ServiceInfo*)malloc(sizeof(ServiceInfo));
pCurrent = pCurrent->pNext;
memset(pCurrent, (unsigned char)0x00, sizeof(ServiceInfo));
pCurrent->sdRef = sdRef;
return pCurrent;
}/*CreateServiceInfoNode*/
void CleanServiceInfoData(ServiceInfo *pServiceInfo)
{
if (NULL == pServiceInfo)
return;
SAFE_FREE(pServiceInfo->pServiceType); SAFE_FREE(pServiceInfo->pServiceName);
SAFE_FREE(pServiceInfo->pDomainName);
//pServiceInfo->serviceInterface;
SAFE_FREE(pServiceInfo->pProtocolNumber);
SAFE_FREE(pServiceInfo->pAddr); SAFE_FREE(pServiceInfo->pHostName);
if (NULL != pServiceInfo->pText)
free(pServiceInfo->pText);
pServiceInfo->pText = NULL;
}/*CleanServiceInfoData*/
void DestroyServiceInfo(ServiceInfo **ppServiceInfo)
{
CleanServiceInfoData(*ppServiceInfo);
SAFE_FREE(*ppServiceInfo);
}/*DestoryServiceInfo*/
void DestroyServiceInfoList(ServiceInfoList **ppServiceInfoList)
{
ServiceInfo *pCurrent;
pCurrent = *ppServiceInfoList;
if (0 == memcmp(pCurrent, &unUsedServiceInfoMemHead[0],
sizeof(unUsedServiceInfoMemHead)))
{
SAFE_FREE(pCurrent);
return;
}/*if unused*/
while (NULL != pCurrent)
{
ServiceInfo *pNext;
pNext = pCurrent->pNext;
DestroyServiceInfo(&pCurrent);
pCurrent = pNext;
}/*while*/
*ppServiceInfoList = NULL;
}/*CleanServiceInfoList*/
int GetServiceInfoListLength(ServiceInfoList *pServiceInfoList)
{
int i;
ServiceInfo *pCurrent;
if (NULL == pServiceInfoList)
return -1;
if (0 == memcmp(pServiceInfoList, &unUsedServiceInfoMemHead[0],
sizeof(unUsedServiceInfoMemHead)))
{
return 0;
}/*if */
pCurrent = pServiceInfoList;
i = 0;
while (NULL != pCurrent)
{
pCurrent = pCurrent->pNext;
i++;
}/*while*/
return i;
}/*GetServiceInfoListLength*/
void RemoveSpecificServiceInfoNode(
ServiceInfoList *pServiceInfoList, DNSServiceRef sdRef)
{
ServiceInfo *pCurrent, *pPrevious;
int n;
n = GetServiceInfoListLength(pServiceInfoList);
pCurrent = pServiceInfoList;
if (0 == n)
return;
if (1 == n)
{
if (pCurrent->sdRef == sdRef)
{
memset(pCurrent, 0xff, sizeof(ServiceInfo));
return;
}/*if */
}/*if 1 == n*/
/*if 1 < n and i == 0*/
if (pCurrent->sdRef == sdRef)
{
ServiceInfo *p2ndNode;
p2ndNode = pCurrent->pNext;
memcpy(pCurrent, p2ndNode, sizeof(ServiceInfo));
DestroyServiceInfo(&p2ndNode); p2ndNode = NULL;
return ;
}/*if */
pPrevious = pCurrent;
pCurrent = pCurrent->pNext;
while (NULL != pCurrent)
{
if (pCurrent->sdRef == sdRef)
{
pPrevious->pNext = pCurrent->pNext;
DestroyServiceInfo(&pCurrent);
break;
}
pPrevious = pCurrent;
pCurrent = pCurrent->pNext;
}/*if */
}/*RemoveSpecificServiceInfoNode*/
ServiceInfo *FindSpecificServiceInfoNode(
ServiceInfoList *pServiceInfoList, DNSServiceRef sdRef)
{
ServiceInfo *pCurrent;
pCurrent = pServiceInfoList;
while (NULL != pCurrent)
{
if (pCurrent->sdRef == sdRef)
break;
pCurrent = pCurrent->pNext;
}/*if */
return pCurrent;
}/*FindSpecificServiceInfoNode*/
void DNSSD_API ServiceGetAddressInfoCallback(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *pHostname,
const struct sockaddr *pAddress,
uint32_t ttl,
void *pContext)
{
if (0 == errorCode)
{
const struct sockaddr_in *in = (const struct sockaddr_in *)pAddress;
ServiceInfo *pNode;
pNode = FindSpecificServiceInfoNode(m_pServiceInfoList, sdRef);
if (NULL != pNode)
{
char temp[MAX_STR_LEN];
memset(&temp[0], 0, MAX_STR_LEN);
inet_ntop(AF_INET, (PVOID)&in->sin_addr, &temp[0], MAX_STR_LEN);
pNode->pAddr = _strdup(&temp[0]);
pNode->pProtocolNumber = _strdup("IPv4");
pNode->pHostName = _strdup(pHostname);
}
if (NULL != pContext)
{
mDNSBrowingCallbackFunPtr callbackPtr;
callbackPtr = (mDNSBrowingCallbackFunPtr)pContext;
callbackPtr(pNode->pServiceType, pNode->pServiceName, pNode->pDomainName,
pNode->serviceInterface, pNode->pProtocolNumber, pNode->pAddr,
pNode->port, pNode->pHostName, pNode->pText);
}
}/*if 0 == errorCode*/
//if (0 == (flags & kDNSServiceFlagsMoreComing)) {}
}/*ServiceGetAddressInfoCallback*/
void DNSSD_API ResolveServiceCallback(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *pFullname,
const char *pHosttarget,
uint16_t port,
uint16_t txtLen,
const unsigned char *pTxtRecord,
void *pContext)
{
char organizedTxt[MAX_STR_LEN];
MIB_IFROW IfRow;
DNSServiceRef client;
DWORD result;
DNSServiceErrorType err;
int i;
LOG("%s\n", __FUNCTION__);
if (0 != errorCode)
{
LOG("errorCode = %d\n", errorCode);
return;
}/*if 0 != errorCode*/
memset(&organizedTxt[0], 0, MAX_STR_LEN);
for (i = 0; i < txtLen; i++){
char c;
c = pTxtRecord[i];
if (0x20 > c || '/' == c)
c = '\n';
organizedTxt[i] = c;
}/*for i*/
organizedTxt[strlen(&organizedTxt[0])] = '\n';
IfRow.dwIndex = interfaceIndex;
result = GetIfEntry(&IfRow);
if (0 != result)
{
LOG("Error in GetIfEntry , error code = %d\n", result);
return ;
}/*if 0 != result*/
client = NULL;
err = DNSServiceGetAddrInfo(&client,
kDNSServiceFlagsTimeout,
interfaceIndex,
kDNSServiceProtocol_IPv4,
pHosttarget,
ServiceGetAddressInfoCallback,
pContext);
if (0 == err)
{
ServiceInfo *pNode;
pNode = FindSpecificServiceInfoNode(m_pServiceInfoList, sdRef);
if (NULL != pNode)
{
pNode->serviceInterface = interfaceIndex;
pNode->port = ((0xff & port) << 8) + (port >> 8);
if (0 == pTxtRecord || 0 == pTxtRecord[0])
pNode->pText = NULL;
else
pNode->pText = _strdup(&organizedTxt[0]);
//for next callback, the sdRef has been change, it is nessary to
//update the sdRef value for specifying the parameters in correct groups.
pNode->sdRef = client;
}/*if NULL != pNode */
LOG("Looking up %s on %s\n", pHosttarget, IfRow.bDescr);
}
else
{
LOG("Error looking up address info for %s\n", pHosttarget);
}/*if 0 == err*/
}/*ResolveServiceCallback*/
void DNSSD_API IterateServiceNameCallback(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *pServiceName,
const char *pRegtype,
const char *pReplyDomain,
void *context)
{
LOG("%s\n", __FUNCTION__);
if (0 == errorCode && (flags & kDNSServiceFlagsAdd))
{
DNSServiceRef client = NULL;
DNSServiceErrorType err = DNSServiceResolve(&client,
0,
interfaceIndex,
pServiceName,
pRegtype,
pReplyDomain,
ResolveServiceCallback,
context);
if (0 == err)
{
ServiceInfo *pNode;
pNode = FindSpecificServiceInfoNode(m_pServiceInfoList, sdRef);
if (NULL != pNode)
{
pNode->pServiceName = _strdup(pServiceName);
pNode->pServiceType = _strdup(pRegtype);
pNode->pDomainName = _strdup(pReplyDomain);
//for next callback, the sdRef has been change, it is nessary to
//update the sdRef value
//for specifying the parameters in correct groups.
pNode->sdRef = client;
}/*if NULL != pNode*/
}
else
{
LOG("Error trying to browse service instance: %s", pServiceName);
}
}
//if (0 == (kDNSServiceFlagsMoreComing & flags)){}
}/*IterateServiceNameCallback*/
void DNSSD_API IterateServiceTypesCallback(DNSServiceRef sdRef,
DNSServiceFlags flags,
uint32_t interfaceIndex,
DNSServiceErrorType errorCode,
const char *pServiceName,
const char *pRegtype,
const char *pReplyDomain,
void *pContext)
{
LOG("%s\n", __FUNCTION__);
if (0 == errorCode && (kDNSServiceFlagsAdd & flags))
{
unsigned int len, copyLen;
char serviceType[MAX_STR_LEN];
memset(&serviceType[0], 0, MAX_STR_LEN);
{
unsigned int i;
len = strlen(pRegtype);
copyLen = len;
if ('.' == pRegtype[len - 1])
len -= 1;
for (i = len - 1; i >= 0; i--){
if ('.' == pRegtype[i]){
copyLen = i;
break;
}
}/*for i*/
strncpy_s(&serviceType[0], MAX_STR_LEN, pServiceName, strlen(pServiceName));
strncat_s(&serviceType[0], MAX_STR_LEN, ".", 1);
strncat_s(&serviceType[0], MAX_STR_LEN, pRegtype, copyLen);
strncat_s(&serviceType[0], MAX_STR_LEN, ".", 1);
}/*local variable*/
{
DNSServiceRef client;
DNSServiceErrorType err;
err = DNSServiceBrowse(&client, 0, 0,&serviceType[0], "",
IterateServiceNameCallback, pContext);
LOG("Browsing for instances of %s\n", &serviceType[0]);
if (0 == err)
CreateServiceInfoNode(m_pServiceInfoList, client);
else
LOG("Error trying to browse service type: %s\n", &serviceType[0]);
}/*local variable*/
}/*if 0 == errorCode && (kDNSServiceFlagsAdd & flags) */
if (0 == (kDNSServiceFlagsMoreComing & flags))
RemoveSpecificServiceInfoNode(m_pServiceInfoList, sdRef);
return;
}/*IterateServiceTypesCallback*/
#define BROWERING_INTERVAL (250)
void CALLBACK BrowingTimerCallback(HWND hwnd, UINT uMsg, UINT timerId, DWORD dwTime)
{
int count = 0;
while (1)
{
fd_set readfds;
ServiceInfo *pCurrent;
struct timeval tv = { 0, 1000 };
if (0 == GetServiceInfoListLength(m_pServiceInfoList))
{
LOG("Done browsing\n");
KillTimer(NULL, m_browsingTimerId);
break;
}
FD_ZERO(&readfds);
pCurrent = m_pServiceInfoList;
while (NULL != pCurrent)
{
FD_SET(DNSServiceRefSockFD(pCurrent->sdRef), &readfds);
pCurrent = pCurrent->pNext;
}
if (0 < select(0, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv))
{
//
// While iterating through the loop, the callback functions might delete
// the client pointed to by the current iterator, so I have to increment
// it BEFORE calling DNSServiceProcessResult
//
pCurrent = m_pServiceInfoList;
while (NULL != pCurrent)
{
if (FD_ISSET(DNSServiceRefSockFD(pCurrent->sdRef), &readfds))
{
DNSServiceErrorType err
= DNSServiceProcessResult(pCurrent->sdRef);
if (++count > 10)
break;
}
pCurrent = pCurrent->pNext;
}/*while */
}
else
break;
if (count > 10)
break;
}/*while 1*/
}/*BrowingTimerCallback*/
DWORD WINAPI BrowsingThread(LPVOID lpParam)
{
MSG msg;
DNSServiceRef client = NULL;
DNSServiceErrorType err = DNSServiceBrowse(&client, 0, 0,
"_services._dns-sd._udp", "", IterateServiceTypesCallback, (void*)lpParam);
if (0 == err)
{
LOG("Browsing for service types using _services._dns-sd._udp\n");
SetTimer(NULL, 0, BROWERING_INTERVAL,
(TIMERPROC)&BrowingTimerCallback);
CreateServiceInfoNode(m_pServiceInfoList, client);
}
else
{
LOG("Error starting discovery: %d\n", err);
}/*if */
while (FALSE != GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}/*while*/
return 0;
}/*BrowsingThread*/
void mDNSBrowsingStop(void)
{
KillTimer(NULL, m_browsingTimerId);
if (0 != m_idBrowsingThread)
PostThreadMessage(m_idBrowsingThread, WM_QUIT, 0, 0);
m_idBrowsingThread = 0;
if (NULL != m_browingThreadHandle)
WaitForSingleObject(m_browingThreadHandle, 5 * 1000);
m_browingThreadHandle = NULL;
m_pServiceInfoList = NULL;
}/*mDNSStopmiscover*/
int mDNSBrowsingStart(mDNSBrowingCallbackFunPtr browingResultFunPtr)
{
mDNSBrowsingStop();
m_pServiceInfoList = CreateServiceInfoList();
m_browingThreadHandle = CreateThread(NULL, 0,
BrowsingThread, browingResultFunPtr, 0, &m_idBrowsingThread);
if (NULL == m_browingThreadHandle)
return -1;
return 0;
}/*mDNSDiscoverStart*/
void __stdcall mDNSBrowingResult(
char *pServiceType,
char *pServiceName,
char *pDomainName,
char serviceInterface,
char *pProtocolNumber,
char *pAddr,
unsigned short port,
char *pHostName,
char *pText
)
{
printf("\n");
printf("\tServiceType : %s\n", pServiceType);
printf("\tServiceName : %s\n", pServiceName);
printf("\tDomainName : %s\n", pDomainName);
printf("\tInterface : %d\n", serviceInterface);
printf("\tProtocol Number : %s\n", pProtocolNumber);
printf("\tAddress : %s\n", pAddr);
printf("\tport : %d\n", port);
printf("\tHostName : %s\n", pHostName);
printf("\tText : ");
if (NULL == pText || '\n' == pText[0] && '\n' == pText[1])
{
HANDLE hConsole;
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN |
FOREGROUND_BLUE | FOREGROUND_INTENSITY);
printf("NULL\n");
SetConsoleTextAttribute(hConsole, FOREGROUND_RED
| FOREGROUND_GREEN | FOREGROUND_BLUE);
printf("\n");
}
else
{
printf("%s\n", pText);
}
printf("\n");
}/*mDNSBrowingResult*/
int main(int argc, char *argv[])
{
#if(0)
WSADATA wsaData;
if (0 != WSAStartup(MAKEWORD(2, 0), &wsaData))
return -1;
#endif
mDNSBrowsingStart(mDNSBrowingResult);
getchar();
mDNSBrowsingStop();
#if(0)
WSACleanup();
#endif
return 0;
}/*main*/
After press Play (build and run) button, the discovering result would be printed in the console.