Files
OpenSceneGraph/src/osgPlugins/ZeroConfDevice/AutoDiscoveryWinImpl.cpp
2013-06-19 17:26:59 +00:00

306 lines
11 KiB
C++

#include "AutoDiscoveryWinImpl.h"
#include "Autodiscovery.h"
#include "dns_sd.h"
#include <sstream>
typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
#ifndef HeapEnableTerminationOnCorruption
# define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
#endif
static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
const char *name, const char *regtype, const char *domain, void *context)
{
if (context) {
AutoDiscoveryServerImpl* impl = static_cast<AutoDiscoveryServerImpl*>(context);
if (errorCode == kDNSServiceErr_NoError)
{
if (flags & kDNSServiceFlagsAdd) impl->serviceAdded();
else impl->serviceRemoved();
}
else if (errorCode == kDNSServiceErr_NameConflict)
{
OSG_WARN << "AutoDiscoveryServerImpl :: Name conflict" << std::endl;
impl->errorOccured();
}
else {
OSG_WARN << "AutoDiscoveryServerImpl :: error " << errorCode << std::endl;
impl->errorOccured();
}
}
}
static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
const char *nam, const char *typ, const char *dom, const char *host, const char *port, AutoDiscoveryServerImpl* context)
{
DNSServiceFlags flags = 0;
uint16_t PortAsNumber = atoi(port);
Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
unsigned char txt[2048] = "";
unsigned char *ptr = txt;
if (nam[0] == '.' && nam[1] == 0) nam = ""; // We allow '.' on the command line as a synonym for empty string
if (dom[0] == '.' && dom[1] == 0) dom = ""; // We allow '.' on the command line as a synonym for empty string
OSG_INFO << "AutoDiscoveryImpl :: Registering Service " << (nam[0] ? nam : "<<Default>>") << " " << typ << " " << (dom[0] ? "." : "", dom) << std::endl;
if (host && *host) {
OSG_INFO << "AutoDiscoveryImpl :: host " << host << " port " << port << std::endl;
}
// printf("\n");
//flags |= kDNSServiceFlagsAllowRemoteQuery;
//flags |= kDNSServiceFlagsNoAutoRename;
return(DNSServiceRegister(sdref, flags, kDNSServiceInterfaceIndexAny, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, context));
}
AutoDiscoveryServerImpl::AutoDiscoveryServerImpl(const std::string& type, unsigned int port)
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
std::ostringstream ss;
ss << port;
DNSServiceErrorType err = RegisterService(&client, "", type.c_str(), "", NULL, ss.str().c_str(), this);
if (!client || err != kDNSServiceErr_NoError) {
OSG_WARN << "AutoDiscoveryImpl :: DNSService call failed " << (long int)err << std::endl;
}
}
void AutoDiscoveryServerImpl::update()
{
int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1;
int nfds = dns_sd_fd + 1;
fd_set readfds;
struct timeval tv;
int result;
// (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1;
// 1. Set up the fd_set as usual here.
// This example 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 client(s) to the fd_set
if (client ) FD_SET(dns_sd_fd , &readfds);
//if (client_pa) FD_SET(dns_sd_fd2, &readfds);
// 3. Set up the timeout.
tv.tv_sec = 0;
tv.tv_usec = 0;
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (result > 0)
{
DNSServiceErrorType err = kDNSServiceErr_NoError;
if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client );
// else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa);
// if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; }
}
}
void AutoDiscoveryServerImpl::serviceAdded()
{
OSG_INFO << "AutoDiscoveryImpl :: Service added" << std::endl;
}
void AutoDiscoveryServerImpl::serviceRemoved()
{
OSG_INFO << "AutoDiscoveryImpl :: Service removed" << std::endl;
}
struct ContextDNSServiceRefPair {
DNSServiceRef ref;
AutoDiscoveryClientImpl* context;
};
static void DNSSD_API zonedata_resolve(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txt, void *context)
{
union { uint16_t s; u_char b[2]; } port = { opaqueport };
uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
ContextDNSServiceRefPair* ref_pair = static_cast<ContextDNSServiceRefPair*>(context);
if (!ref_pair) return;
if (errorCode) {
OSG_WARN << "AutoDiscoveryImpl :: Error code " << errorCode << std::endl;
return;
}
ref_pair->context->serviceAdded(fullname, hosttarget, PortAsNumber);
/*
const char *p = fullname;
char n[kDNSServiceMaxDomainName];
char t[kDNSServiceMaxDomainName];
const unsigned char *max = txt + txtLen;
(void)sdref; // Unused
(void)ifIndex; // Unused
(void)context; // Unused
//if (!(flags & kDNSServiceFlagsAdd)) return;
if (errorCode) { printf("Error code %d\n", errorCode); return; }
if (CopyLabels(n, n + kDNSServiceMaxDomainName, &p, 3)) return; // Fetch name+type
p = fullname;
if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 1)) return; // Skip first label
if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 2)) return; // Fetch next two labels (service type)
if (num_printed++ == 0)
{
printf("\n");
printf("; To direct clients to browse a different domain, substitute that domain in place of '@'\n");
printf("%-47s PTR %s\n", "lb._dns-sd._udp", "@");
printf("\n");
printf("; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.\n");
printf("; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local\n");
printf("; names with the correct fully-qualified (unicast) domain name of the target host offering the service.\n");
}
printf("\n");
printf("%-47s PTR %s\n", t, n);
printf("%-47s SRV 0 0 %d %s ; Replace with unicast FQDN of target host\n", n, PortAsNumber, hosttarget);
printf("%-47s TXT ", n);
while (txt < max)
{
const unsigned char *const end = txt + 1 + txt[0];
txt++; // Skip over length byte
printf(" \"");
while (txt<end)
{
if (*txt == '\\' || *txt == '\"') printf("\\");
printf("%c", *txt++);
}
printf("\"");
}
printf("\n");
*/
ref_pair->context->removeRef(ref_pair->ref);
DNSServiceRefDeallocate(ref_pair->ref);
free(ref_pair);
}
static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
const char *replyName, const char *replyType, const char *replyDomain, void *context)
{
if (errorCode) {
OSG_WARN << "AutoDiscoveryImpl :: Error code " << errorCode << std::endl;
return;
}
AutoDiscoveryClientImpl* impl = static_cast<AutoDiscoveryClientImpl*>(context);
if(!impl) return;
if (!(flags & kDNSServiceFlagsAdd)) {
impl->serviceRemoved(replyName, replyType, replyDomain);
return;
}
ContextDNSServiceRefPair* ref_pair = new ContextDNSServiceRefPair();
ref_pair->context = static_cast<AutoDiscoveryClientImpl*>(context);
DNSServiceErrorType err = DNSServiceResolve(&ref_pair->ref, 0, ifIndex, replyName, replyType, replyDomain, zonedata_resolve, ref_pair);
if (!ref_pair->ref || err != kDNSServiceErr_NoError) {
OSG_WARN << "AutoDiscoveryImpl :: DNSServiceResolve call failed " << (long int)err << std::endl;
} else
impl->addRef(ref_pair->ref);
}
AutoDiscoveryServerImpl::~AutoDiscoveryServerImpl()
{
if (client) DNSServiceRefDeallocate(client);
}
AutoDiscoveryClientImpl::AutoDiscoveryClientImpl(const std::string& type, DiscoveredServicesCallback* cb)
{
_cb = cb;
DNSServiceErrorType err = DNSServiceBrowse(&client, 0, kDNSServiceInterfaceIndexAny, type.c_str(), "", browse_reply, this);
if (!client || err != kDNSServiceErr_NoError) {
OSG_WARN << "AutoDiscoveryImpl :: DNSServiceBrowse call failed " << (long int)err << std::endl;
}
}
void AutoDiscoveryClientImpl::update()
{
updateRef(client);
for(std::list<DNSServiceRef>::iterator i = resolveRefs.begin(); i != resolveRefs.end(); ++i) {
updateRef(*i);
}
for(std::list<DNSServiceRef>::iterator i = resolveRefsToDelete.begin(); i != resolveRefsToDelete.end(); ++i) {
resolveRefs.remove(*i);
}
resolveRefsToDelete.clear();
}
void AutoDiscoveryClientImpl::updateRef(DNSServiceRef& ref)
{
int dns_sd_fd = ref ? DNSServiceRefSockFD(ref ) : -1;
int nfds = dns_sd_fd + 1;
fd_set readfds;
struct timeval tv;
int result;
// (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1;
// 1. Set up the fd_set as usual here.
// This example 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 client(s) to the fd_set
if (ref ) FD_SET(dns_sd_fd , &readfds);
//if (client_pa) FD_SET(dns_sd_fd2, &readfds);
// 3. Set up the timeout.
tv.tv_sec = 0;
tv.tv_usec = 0;
result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
if (result > 0)
{
DNSServiceErrorType err = kDNSServiceErr_NoError;
if (client && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(ref );
// else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa);
// if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; }
}
}
AutoDiscoveryClientImpl::~AutoDiscoveryClientImpl()
{
if (client) DNSServiceRefDeallocate(client);
}
void AutoDiscoveryClientImpl::serviceRemoved(const std::string& replyName, const std::string& replyType, const std::string& replyDomain)
{
// TODO
std::string full_name = replyName+"."+replyType+"."+replyDomain;
// TODO full_name = cefix::strReplaceAll<std::string>(full_name, "..", ".");
AddressMap::iterator itr = _addresses.find(full_name);
if (itr == _addresses.end()) {
OSG_INFO << "AutoDiscoveryImpl :: no services found to remove? " << full_name << std::endl;
return;
}
if (!_cb.valid())
return;
for(AddressVector::iterator i = itr->second.begin(); i != itr->second.end(); ++i) {
_cb->serviceRemoved(i->first, i->second);
}
_addresses.erase(itr);
}
void AutoDiscoveryClientImpl::serviceAdded(const std::string& fullname, const std::string& host, unsigned int port)
{
if (_cb.valid()) {
_addresses[fullname].push_back(std::make_pair(host, port));
_cb->serviceAdded(host, port);
}
}