mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-23 22:16:16 +02:00
merged Outurnate's bonjour service
This commit is contained in:
parent
010e797e1d
commit
68d73321e0
8 changed files with 331 additions and 26 deletions
|
@ -36,9 +36,9 @@ else ifeq ($(TARGET), MAC)
|
|||
|
||||
CXX = /usr/bin/g++
|
||||
STRIP = strip
|
||||
CXXFLAGS += -DHAS_TREMOR -DHAS_COREAUDIO -DFREEBSD -I/usr/local/include -Wno-unused-local-typedef -Wno-deprecated
|
||||
CXXFLAGS += -DHAS_TREMOR -DHAS_COREAUDIO -DFREEBSD -DHAS_BONJOUR -I/usr/local/include -Wno-unused-local-typedef -Wno-deprecated
|
||||
LDFLAGS = -logg -lvorbisidec -lFLAC -L/usr/local/lib -framework AudioToolbox -framework CoreFoundation
|
||||
OBJ += player/coreAudioPlayer.o
|
||||
OBJ += player/coreAudioPlayer.o browseBonjour.o
|
||||
|
||||
else
|
||||
|
||||
|
|
4
client/browseAvahi.cpp
Normal file → Executable file
4
client/browseAvahi.cpp
Normal file → Executable file
|
@ -180,7 +180,7 @@ void BrowseAvahi::client_callback(AvahiClient *c, AvahiClientState state, AVAHI_
|
|||
}
|
||||
|
||||
|
||||
bool BrowseAvahi::browse(const std::string& serviceName, int proto, AvahiResult& result, int timeout)
|
||||
bool BrowseAvahi::browse(const std::string& serviceName, mDNSResult& result, int timeout)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -194,7 +194,7 @@ bool BrowseAvahi::browse(const std::string& serviceName, int proto, AvahiResult&
|
|||
throw SnapException("BrowseAvahi - Failed to create client: " + std::string(avahi_strerror(error)));
|
||||
|
||||
/* Create the service browser */
|
||||
if (!(sb_ = avahi_service_browser_new(client_, AVAHI_IF_UNSPEC, proto, serviceName.c_str(), NULL, (AvahiLookupFlags)0, browse_callback, this)))
|
||||
if (!(sb_ = avahi_service_browser_new(client_, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, serviceName.c_str(), NULL, (AvahiLookupFlags)0, browse_callback, this)))
|
||||
throw SnapException("BrowseAvahi - Failed to create service browser: " + std::string(avahi_strerror(avahi_client_errno(client_))));
|
||||
|
||||
result_.valid_ = false;
|
||||
|
|
23
client/browseAvahi.h
Normal file → Executable file
23
client/browseAvahi.h
Normal file → Executable file
|
@ -15,6 +15,8 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
#ifndef BROWSEAVAHI_H
|
||||
#define BROWSEAVAHI_H
|
||||
|
||||
#include <avahi-client/client.h>
|
||||
#include <avahi-client/lookup.h>
|
||||
|
@ -23,25 +25,16 @@
|
|||
#include <avahi-common/malloc.h>
|
||||
#include <avahi-common/error.h>
|
||||
|
||||
#include <string>
|
||||
class BrowseAvahi;
|
||||
|
||||
#include "browsemDNS.h"
|
||||
|
||||
struct AvahiResult
|
||||
{
|
||||
int proto_;
|
||||
std::string ip_;
|
||||
std::string host_;
|
||||
size_t port_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
|
||||
class BrowseAvahi
|
||||
class BrowseAvahi : public BrowsemDNS
|
||||
{
|
||||
public:
|
||||
BrowseAvahi();
|
||||
~BrowseAvahi();
|
||||
bool browse(const std::string& serviceName, int proto, AvahiResult& result, int timeout);
|
||||
bool browse(const std::string& serviceName, mDNSResult& result, int timeout) override;
|
||||
|
||||
private:
|
||||
void cleanUp();
|
||||
|
@ -49,8 +42,8 @@ private:
|
|||
static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, AvahiProtocol protocol, AvahiBrowserEvent event, const char *name, const char *type, const char *domain, AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, void* userdata);
|
||||
static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata);
|
||||
AvahiClient* client_;
|
||||
AvahiResult result_;
|
||||
mDNSResult result_;
|
||||
AvahiServiceBrowser* sb_;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
|
268
client/browseBonjour.cpp
Executable file
268
client/browseBonjour.cpp
Executable file
|
@ -0,0 +1,268 @@
|
|||
#include "browseBonjour.h"
|
||||
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <deque>
|
||||
#ifdef WINDOWS
|
||||
#include <WinSock2.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include "common/log.h"
|
||||
#include "common/snapException.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct DNSServiceRefDeleter
|
||||
{
|
||||
void operator () (DNSServiceRef* ref)
|
||||
{
|
||||
DNSServiceRefDeallocate(*ref);
|
||||
delete ref;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unique_ptr<DNSServiceRef, DNSServiceRefDeleter> DNSServiceHandle;
|
||||
|
||||
string BonjourGetError(DNSServiceErrorType error)
|
||||
{
|
||||
switch (error)
|
||||
{
|
||||
case kDNSServiceErr_NoError:
|
||||
return "NoError";
|
||||
|
||||
default:
|
||||
case kDNSServiceErr_Unknown:
|
||||
return "Unknown";
|
||||
|
||||
case kDNSServiceErr_NoSuchName:
|
||||
return "NoSuchName";
|
||||
|
||||
case kDNSServiceErr_NoMemory:
|
||||
return "NoMemory";
|
||||
|
||||
case kDNSServiceErr_BadParam:
|
||||
return "BadParam";
|
||||
|
||||
case kDNSServiceErr_BadReference:
|
||||
return "BadReference";
|
||||
|
||||
case kDNSServiceErr_BadState:
|
||||
return "BadState";
|
||||
|
||||
case kDNSServiceErr_BadFlags:
|
||||
return "BadFlags";
|
||||
|
||||
case kDNSServiceErr_Unsupported:
|
||||
return "Unsupported";
|
||||
|
||||
case kDNSServiceErr_NotInitialized:
|
||||
return "NotInitialized";
|
||||
|
||||
case kDNSServiceErr_AlreadyRegistered:
|
||||
return "AlreadyRegistered";
|
||||
|
||||
case kDNSServiceErr_NameConflict:
|
||||
return "NameConflict";
|
||||
|
||||
case kDNSServiceErr_Invalid:
|
||||
return "Invalid";
|
||||
|
||||
case kDNSServiceErr_Firewall:
|
||||
return "Firewall";
|
||||
|
||||
case kDNSServiceErr_Incompatible:
|
||||
return "Incompatible";
|
||||
|
||||
case kDNSServiceErr_BadInterfaceIndex:
|
||||
return "BadInterfaceIndex";
|
||||
|
||||
case kDNSServiceErr_Refused:
|
||||
return "Refused";
|
||||
|
||||
case kDNSServiceErr_NoSuchRecord:
|
||||
return "NoSuchRecord";
|
||||
|
||||
case kDNSServiceErr_NoAuth:
|
||||
return "NoAuth";
|
||||
|
||||
case kDNSServiceErr_NoSuchKey:
|
||||
return "NoSuchKey";
|
||||
|
||||
case kDNSServiceErr_NATTraversal:
|
||||
return "NATTraversal";
|
||||
|
||||
case kDNSServiceErr_DoubleNAT:
|
||||
return "DoubleNAT";
|
||||
|
||||
case kDNSServiceErr_BadTime:
|
||||
return "BadTime";
|
||||
|
||||
case kDNSServiceErr_BadSig:
|
||||
return "BadSig";
|
||||
|
||||
case kDNSServiceErr_BadKey:
|
||||
return "BadKey";
|
||||
|
||||
case kDNSServiceErr_Transient:
|
||||
return "Transient";
|
||||
|
||||
case kDNSServiceErr_ServiceNotRunning:
|
||||
return "ServiceNotRunning";
|
||||
|
||||
case kDNSServiceErr_NATPortMappingUnsupported:
|
||||
return "NATPortMappingUnsupported";
|
||||
|
||||
case kDNSServiceErr_NATPortMappingDisabled:
|
||||
return "NATPortMappingDisabled";
|
||||
|
||||
case kDNSServiceErr_NoRouter:
|
||||
return "NoRouter";
|
||||
|
||||
case kDNSServiceErr_PollingMode:
|
||||
return "PollingMode";
|
||||
|
||||
case kDNSServiceErr_Timeout:
|
||||
return "Timeout";
|
||||
}
|
||||
}
|
||||
|
||||
struct mDNSReply
|
||||
{
|
||||
string name, regtype, domain;
|
||||
};
|
||||
|
||||
struct mDNSResolve
|
||||
{
|
||||
string fullName;
|
||||
uint16_t port;
|
||||
};
|
||||
|
||||
#define CHECKED(err) if((err)!=kDNSServiceErr_NoError)throw SnapException(BonjourGetError(err) + ":" + to_string(__LINE__));
|
||||
|
||||
void runService(const DNSServiceHandle& service)
|
||||
{
|
||||
auto socket = DNSServiceRefSockFD(*service);
|
||||
fd_set set;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(socket, &set);
|
||||
|
||||
timeval timeout;
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 500000;
|
||||
|
||||
while (select(FD_SETSIZE, &set, NULL, NULL, &timeout))
|
||||
{
|
||||
CHECKED(DNSServiceProcessResult(*service));
|
||||
timeout.tv_sec = 0;
|
||||
timeout.tv_usec = 500000;
|
||||
}
|
||||
}
|
||||
|
||||
bool BrowseBonjour::browse(const string& serviceName, mDNSResult& result, int timeout)
|
||||
{
|
||||
result.valid_ = false;
|
||||
|
||||
// Discover
|
||||
deque<mDNSReply> replyCollection;
|
||||
{
|
||||
DNSServiceHandle service(new DNSServiceRef(NULL));
|
||||
CHECKED(DNSServiceBrowse(service.get(), 0, 0, serviceName.c_str(), "local.",
|
||||
[](DNSServiceRef service,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char* serviceName,
|
||||
const char* regtype,
|
||||
const char* replyDomain,
|
||||
void* context)
|
||||
{
|
||||
auto replyCollection = static_cast<deque<mDNSReply>*>(context);
|
||||
|
||||
CHECKED(errorCode);
|
||||
replyCollection->push_back(mDNSReply{ string(serviceName), string(regtype), string(replyDomain) });
|
||||
}, &replyCollection));
|
||||
|
||||
runService(service);
|
||||
}
|
||||
|
||||
// Resolve
|
||||
deque<mDNSResolve> resolveCollection;
|
||||
{
|
||||
DNSServiceHandle service(new DNSServiceRef(NULL));
|
||||
for (auto& reply : replyCollection)
|
||||
CHECKED(DNSServiceResolve(service.get(), 0, 0, reply.name.c_str(), reply.regtype.c_str(), reply.domain.c_str(),
|
||||
[](DNSServiceRef service,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char* fullName,
|
||||
const char* hosttarget,
|
||||
uint16_t port,
|
||||
uint16_t txtLen,
|
||||
const unsigned char* txtRecord,
|
||||
void* context)
|
||||
{
|
||||
auto resultCollection = static_cast<deque<mDNSResolve>*>(context);
|
||||
|
||||
CHECKED(errorCode);
|
||||
resultCollection->push_back(mDNSResolve { string(hosttarget), ntohs(port) });
|
||||
}, &resolveCollection));
|
||||
|
||||
runService(service);
|
||||
}
|
||||
|
||||
// DNS/mDNS Resolve
|
||||
deque<mDNSResult> resultCollection(resolveCollection.size(), mDNSResult { 0, "", "", 0, false });
|
||||
{
|
||||
DNSServiceHandle service(new DNSServiceRef(NULL));
|
||||
unsigned i = 0;
|
||||
for (auto& resolve : resolveCollection)
|
||||
{
|
||||
resultCollection[i].port_ = resolve.port;
|
||||
CHECKED(DNSServiceGetAddrInfo(service.get(), kDNSServiceFlagsLongLivedQuery, 0, kDNSServiceProtocol_IPv4, resolve.fullName.c_str(),
|
||||
[](DNSServiceRef service,
|
||||
DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char* hostname,
|
||||
const sockaddr* address,
|
||||
uint32_t ttl,
|
||||
void* context)
|
||||
{
|
||||
auto result = static_cast<mDNSResult*>(context);
|
||||
|
||||
result->host_ = string(hostname);
|
||||
result->proto_ = address->sa_family;
|
||||
|
||||
char hostIP[NI_MAXHOST];
|
||||
char hostService[NI_MAXSERV];
|
||||
if (getnameinfo(address, sizeof(*address),
|
||||
hostIP, sizeof(hostIP),
|
||||
hostService, sizeof(hostService),
|
||||
NI_NUMERICHOST|NI_NUMERICSERV) == 0)
|
||||
result->ip_ = string(hostIP);
|
||||
else
|
||||
return;
|
||||
result->valid_ = true;
|
||||
}, &resultCollection[i++]));
|
||||
}
|
||||
|
||||
runService(service);
|
||||
}
|
||||
|
||||
if (resultCollection.size() == 0)
|
||||
return false;
|
||||
|
||||
if (resultCollection.size() != 1)
|
||||
logO << "Multiple servers found. Using first" << endl;
|
||||
|
||||
result = resultCollection[0];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef CHECKED
|
15
client/browseBonjour.h
Executable file
15
client/browseBonjour.h
Executable file
|
@ -0,0 +1,15 @@
|
|||
#ifndef BROWSEBONJOUR_H
|
||||
#define BROWSEBONJOUR_H
|
||||
|
||||
#include <dns_sd.h>
|
||||
|
||||
class BrowseBonjour;
|
||||
|
||||
#include "browsemDNS.h"
|
||||
|
||||
class BrowseBonjour : public BrowsemDNS
|
||||
{
|
||||
public:
|
||||
bool browse(const std::string& serviceName, mDNSResult& result, int timeout) override;
|
||||
};
|
||||
#endif
|
29
client/browsemDNS.h
Executable file
29
client/browsemDNS.h
Executable file
|
@ -0,0 +1,29 @@
|
|||
#ifndef BROWSEMDNS_H
|
||||
#define BROWSEMDNS_H
|
||||
|
||||
#include <string>
|
||||
|
||||
struct mDNSResult
|
||||
{
|
||||
int proto_;
|
||||
std::string ip_;
|
||||
std::string host_;
|
||||
uint16_t port_;
|
||||
bool valid_;
|
||||
};
|
||||
|
||||
class BrowsemDNS
|
||||
{
|
||||
public:
|
||||
virtual bool browse(const std::string& serviceName, mDNSResult& result, int timeout) = 0;
|
||||
};
|
||||
|
||||
#if defined(HAS_AVAHI)
|
||||
#include "browseAvahi.h"
|
||||
typedef BrowseAvahi BrowseZeroConf;
|
||||
#elif defined(HAS_BONJOUR)
|
||||
#include "browseBonjour.h"
|
||||
typedef BrowseBonjour BrowseZeroConf;
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -21,12 +21,11 @@
|
|||
|
||||
#include "popl.hpp"
|
||||
#include "controller.h"
|
||||
#include "browsemDNS.h"
|
||||
|
||||
#ifdef HAS_ALSA
|
||||
#include "player/alsaPlayer.h"
|
||||
#endif
|
||||
#ifdef HAS_AVAHI
|
||||
#include "browseAvahi.h"
|
||||
#endif
|
||||
#ifdef HAS_DAEMON
|
||||
#include "common/daemon.h"
|
||||
#endif
|
||||
|
@ -166,14 +165,14 @@ int main (int argc, char **argv)
|
|||
|
||||
if (host.empty())
|
||||
{
|
||||
#ifdef HAS_AVAHI
|
||||
BrowseAvahi browseAvahi;
|
||||
AvahiResult avahiResult;
|
||||
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
|
||||
BrowseZeroConf browser;
|
||||
mDNSResult avahiResult;
|
||||
while (!g_terminated)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (browseAvahi.browse("_snapcast._tcp", AVAHI_PROTO_INET, avahiResult, 5000))
|
||||
if (browser.browse("_snapcast._tcp", avahiResult, 5000))
|
||||
{
|
||||
host = avahiResult.ip_;
|
||||
port = avahiResult.port_;
|
||||
|
|
|
@ -297,6 +297,7 @@ static std::string getMacAddress(int sock)
|
|||
if (!(ifr.ifr_flags & IFF_LOOPBACK)) // don't count loopback
|
||||
{
|
||||
#ifdef __MACH__
|
||||
/// Dirty Mac version
|
||||
struct ifaddrs *ifap, *ifaptr;
|
||||
unsigned char *ptr;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue