/*
CVSNT mdns helpers - apple
Copyright (C) 2004 Tony Hoyle and March-Hare Software Ltd
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <delayimp.h>
#else
#define SOCKET int
#endif
#if (__APPLE__) && (!defined(__DARWIN_OS__) || __DARWIN_OS__ > 7)
#include <sys/errno.h>
#endif
#include <config.h>
#include "lib/api_system.h"
#include "cvs_string.h"
#include "ServerIO.h"
#include "mdns_apple.h"
#if (__APPLE__) && (__GNUC__ == 0x03) && (!defined(__DARWIN_OS__) || __DARWIN_OS__ < 8)
// Bizarrely, OSX doesn't define these...
enum
{
kDNSServiceClass_IN = 1, /* Internet */
kDNSServiceType_A = 1, /* Host address. */
kDNSServiceType_AAAA = 28, /* Ip6 Address. */
kDNSServiceType_SRV = 33, /* Server Selection. */
};
#endif
#ifndef max
#define max(a,b) (((a)>(b))?(a):(b))
#endif
void CMdnsHelperApple::_reg_reply(DNSServiceRef m_client, DNSServiceFlags flags, DNSServiceErrorType errorCode,
const char *name, const char *regtype, const char *domain, void *context)
{
((CMdnsHelperApple*)context)->reg_reply(m_client,flags,errorCode,name,regtype,domain);
}
void CMdnsHelperApple::reg_reply(DNSServiceRef m_client, DNSServiceFlags flags, DNSServiceErrorType errorCode,
const char *name, const char *regtype, const char *domain)
{
printf("Got a reply for %s.%s%s: ", name, regtype, domain);
switch (errorCode)
{
case kDNSServiceErr_NoError: printf("Name now registered and active\n"); break;
case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); break;
default: printf("Error %d\n", errorCode); return;
}
}
void CMdnsHelperApple::_browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
const char *replyName, const char *replyType, const char *replyDomain, void *context)
{
((CMdnsHelperApple*)context)->browse_reply(client,flags,ifIndex,errorCode,replyName,replyType,replyDomain);
}
struct browse_host_t
{
unsigned short port;
cvs::string target;
cvs::string txt;
unsigned char ipv4[4];
unsigned char ipv6[16];
};
void CMdnsHelperApple::browse_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
const char *replyName, const char *replyType, const char *replyDomain)
{
if(flags & kDNSServiceFlagsAdd)
{
DNSServiceRef ref,ref2;
browse_host_t host;
timeval timeout;
fd_set set;
SOCKET socket,socket2;
host.port=0;
DNSServiceResolve(&ref, 0, ifIndex, replyName, replyType, replyDomain, _resolve_reply, &host);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
socket = DNSServiceRefSockFD(ref);
FD_ZERO(&set);
FD_SET(socket, &set);
select((int)socket + 1, &set, NULL, NULL, &timeout);
if(FD_ISSET(socket, &set))
DNSServiceProcessResult(ref);
DNSServiceRefDeallocate(ref);
if(host.port)
{
cvs::string fullname;
cvs::sprintf(fullname,80,"%s.%s%s",replyName,replyType,replyDomain);
if(fullname.length()>0 && fullname[fullname.length()-1]=='.')
fullname.resize(fullname.size()-1);
if(m_callbacks->srv_fn)
{
m_callbacks->srv_fn(fullname.c_str(),host.port,host.target.c_str(),m_userdata);
}
if(m_callbacks->txt_fn && host.txt.size())
m_callbacks->txt_fn(fullname.c_str(),host.txt.c_str(),m_userdata);
if(m_callbacks->ipv4_fn || m_callbacks->ipv6_fn)
{
if(m_callbacks->ipv4_fn)
DNSServiceQueryRecord(&ref, 0, ifIndex, host.target.c_str(), kDNSServiceType_A, kDNSServiceClass_IN, _query_reply, &host);
if(m_callbacks->ipv6_fn)
DNSServiceQueryRecord(&ref2, 0, ifIndex, host.target.c_str(), kDNSServiceType_AAAA, kDNSServiceClass_IN, _query_reply, &host);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
if(m_callbacks->ipv4_fn)
socket = DNSServiceRefSockFD(ref);
else
socket = NULL;
if(m_callbacks->ipv6_fn)
socket2 = DNSServiceRefSockFD(ref2);
else
socket = NULL;
while(socket || socket2)
{
FD_ZERO(&set);
if(socket)
FD_SET(socket, &set);
if(socket2)
FD_SET(socket2, &set);
if(select(max((int)socket + 1,(int)socket2+1), &set, NULL, NULL, &timeout)<=0)
break;
if(socket && FD_ISSET(socket, &set))
{
DNSServiceProcessResult(ref);
socket = NULL;
m_callbacks->ipv4_fn(host.target.c_str(), host.ipv4, m_userdata);
}
if(socket2 && FD_ISSET(socket2, &set))
{
DNSServiceProcessResult(ref2);
socket2 = NULL;
m_callbacks->ipv6_fn(host.target.c_str(), host.ipv6, m_userdata);
}
}
if(m_callbacks->ipv4_fn)
DNSServiceRefDeallocate(ref);
if(m_callbacks->ipv4_fn)
DNSServiceRefDeallocate(ref2);
}
}
}
time(&m_lastreply);
}
void CMdnsHelperApple::_resolve_reply(DNSServiceRef client, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const char *txtRecord, void *context)
{
browse_host_t *host = (browse_host_t*)context;
host->port = htons(opaqueport);
host->target = hosttarget;
host->txt = txtRecord;
if(host->target.length()>0 && host->target[host->target.length()-1]=='.')
host->target.resize(host->target.size()-1);
}
void CMdnsHelperApple::_query_reply(DNSServiceRef client, DNSServiceFlags flags,
uint32_t ifIndex, DNSServiceErrorType errorCode, const char *fullname,
uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata,
uint32_t ttl, void *context)
{
browse_host_t *host = (browse_host_t*)context;
switch(rrtype)
{
case kDNSServiceType_A:
memcpy(host->ipv4,rdata,4);
break;
case kDNSServiceType_AAAA:
memcpy(host->ipv6,rdata,16);
break;
}
}
CMdnsHelperApple::CMdnsHelperApple()
{
m_client = NULL;
}
CMdnsHelperApple::~CMdnsHelperApple()
{
close();
}
int CMdnsHelperApple::open()
{
#ifdef _WIN32
#if (DELAYLOAD_VERSION >= 0x0200) || (_DELAY_IMP_VER > 1)
__try
{
// __HrLoadAllImportsForDll() is defined in DelayImp.h, but not the
// one that is included in the PlatformSDK
if (FAILED(__HrLoadAllImportsForDll("dnssd.dll")))
{
CServerIo::error("Couldn't find dnssd.dll - failing mdns initialisation\n");
return -1;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
CServerIo::error("Couldn't find dnssd.dll - failing mdns initialisation\n");
return -1;
}
#else
#error Visual Studio 2003 required, and the Platform SDK include must be higher in the search order than the VC++ Include Directory $(VCInstallDir).
#endif
#endif
DNSServiceRef ref = NULL;
DNSServiceCreateConnection(&ref);
if(!ref)
{
CServerIo::error("Couldn't connect to apple mdns service - is responder running?\n");
return -1;
}
DNSServiceRefDeallocate(ref);
return 0;
}
int CMdnsHelperApple::publish(const char *instance, const char *service, const char *location, int port, const char *text)
{
if(text && !*text)
text = NULL;
// Sort this out later
int txtLen = 0;
void *txtRecord = NULL;
char tmp[256];
strncpy(tmp,service,sizeof(tmp));
char *p=tmp+strlen(tmp)-1;
if(strlen(tmp)>0 && *p=='.') *(p--)='\0';
if(strlen(tmp)>6 && !strcmp(p-5,".local")) *(p-5)='\0';
service = tmp;
DNSServiceErrorType err = DNSServiceRegister(&m_client, 0, 0, instance, service, NULL, location, htons(port), txtLen, txtRecord, _reg_reply, this);
if (!m_client || err != kDNSServiceErr_NoError)
{
printf("Unable to register with mDNS responder (%d)\n",err);
return (-1);
}
return 0;
}
int CMdnsHelperApple::step()
{
if(!m_client)
return 0;
int dns_sd_fd = m_client ? DNSServiceRefSockFD(m_client) : -1;
int nfds = dns_sd_fd + 1;
fd_set readfds;
struct timeval tv;
int result;
// 1. Set up the fd_set as usual here.
// This example m_client has no file descriptors of its own,
// but a real application would call FD_SET to add them to the set here
FD_ZERO(&readfds);
// 2. Add the fd for our m_client(s) to the fd_set
if (m_client) FD_SET(dns_sd_fd , &readfds);
// 3. Set up the timeout.
tv.tv_sec = 1;
tv.tv_usec = 0;
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (result > 0)
{
DNSServiceErrorType err = kDNSServiceErr_NoError;
if (m_client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(m_client);
if (err) { CServerIo::trace(3, "DNSServiceProcessResult returned %d", err); }
}
else if (result == 0)
{
}
else
{
CServerIo::trace(3,"select() returned %d errno %d %s\n", result, errno, strerror(errno));
}
return 0;
}
int CMdnsHelperApple::browse(const char *service, MdnsBrowseCallback *callbacks, void *userdata)
{
m_userdata = userdata;
m_callbacks = callbacks;
DNSServiceBrowse(&m_client, 0, 0, service, 0, _browse_reply, this);
time_t t;
if(!m_client)
{
CServerIo::trace(3,"DNSServiceBrowse() failed.\n");
return -1;
}
time(&m_lastreply);
do
{
step();
time(&t);
} while(t<m_lastreply+2);
DNSServiceRefDeallocate(m_client);
m_client = NULL;
return 0;
}
int CMdnsHelperApple::close()
{
if(m_client)
DNSServiceRefDeallocate(m_client);
m_client = NULL;
return 0;
}
extern "C" CMdnsHelperBase *MdnsHelperApple_Alloc()
{
return new CMdnsHelperApple();
}
syntax highlighted by Code2HTML, v. 0.9.1