zeroconf: correct txt record handling, small avahi fixes

Change-Id: I660c12773a39655eec45d5ab3e00f70f5590dda7
Reviewed-by: Christian Kandeler <christian.kandeler@nokia.com>
This commit is contained in:
Fawzi Mohamed
2011-10-28 13:56:15 +02:00
parent 4245774cf2
commit 3c4f6cd41b
3 changed files with 65 additions and 28 deletions

View File

@@ -204,7 +204,7 @@ public:
return kDNSServiceErr_Unknown; return kDNSServiceErr_Unknown;
AvahiServiceResolver *resolver = m_serviceResolverNew(connection->client, interfaceIndex, AVAHI_PROTO_INET, name, regtype, domain, AVAHI_PROTO_INET, AvahiServiceResolver *resolver = m_serviceResolverNew(connection->client, interfaceIndex, AVAHI_PROTO_INET, name, regtype, domain, AVAHI_PROTO_INET,
AvahiLookupFlags(0), &cAvahiResolveReply, gatherer); AvahiLookupFlags(0), &cAvahiResolveReply, gatherer);
// *sdRef = reinterpret_cast<DNSServiceRef>(resolver); // as we delete in the callback we don't need it... //*sdRef = reinterpret_cast<DNSServiceRef>(resolver); // add for restart?
if (!resolver) if (!resolver)
return kDNSServiceErr_Unknown; // avahi_strerror(avahi_client_errno(connection->client)); return kDNSServiceErr_Unknown; // avahi_strerror(avahi_client_errno(connection->client));
return kDNSServiceErr_NoError; return kDNSServiceErr_NoError;
@@ -325,9 +325,11 @@ ZConfLib::Ptr ZConfLib::createAvahiLib(const QString &libName, ZConfLib::Ptr fal
return ZConfLib::Ptr(new AvahiZConfLib(libName, fallback)); return ZConfLib::Ptr(new AvahiZConfLib(libName, fallback));
} }
extern "C" void cAvahiResolveReply( AvahiServiceResolver * r, AvahiIfIndex interface, AvahiProtocol /*protocol*/, extern "C" void cAvahiResolveReply(
AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *hostName, AvahiServiceResolver * r, AvahiIfIndex interface, AvahiProtocol /*protocol*/,
const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags /*flags*/, void* context) AvahiResolverEvent event, const char * /*name*/, const char * /*type*/,
const char * /*domain*/, const char *hostName, const AvahiAddress *address, uint16_t port,
AvahiStringList *txt, AvahiLookupResultFlags /*flags*/, void* context)
{ {
enum { defaultTtl = 0 }; enum { defaultTtl = 0 };
ServiceGatherer *sg = reinterpret_cast<ServiceGatherer *>(context); ServiceGatherer *sg = reinterpret_cast<ServiceGatherer *>(context);
@@ -335,24 +337,18 @@ extern "C" void cAvahiResolveReply( AvahiServiceResolver * r, AvahiIfIndex inter
qDebug() << "context was null in cAvahiResolveReply"; qDebug() << "context was null in cAvahiResolveReply";
return; return;
} }
const unsigned char * txtPtr = 0;
unsigned short txtLen = 0;
AvahiStringList *txtAtt=0; AvahiStringList *txtAtt=0;
const unsigned char emptyTxt[1]="";
switch (event) { switch (event) {
case AVAHI_RESOLVER_FAILURE: case AVAHI_RESOLVER_FAILURE:
sg->serviceResolveReply(0, kDNSServiceErr_Timeout, interface, 0, QString(), 0, 0); sg->serviceResolveReply(0, kDNSServiceErr_Timeout, interface, 0, QString(), 0, 0);
break; break;
case AVAHI_RESOLVER_FOUND: case AVAHI_RESOLVER_FOUND:
if (txt) {
txtPtr = txt->text;
txtLen = static_cast<unsigned short>(txt->size);
qDebug() << "Error: txt was null in cAvahiResolveReply for service:" << name << " type:" << type << " domain:" << domain;
}
txtAtt=(txt?txt->next:0);
sg->serviceResolveReply(kDNSServiceFlagsAdd|((txtAtt || address)?kDNSServiceFlagsMoreComing:0), interface, kDNSServiceErr_NoError, sg->serviceResolveReply(kDNSServiceFlagsAdd|((txtAtt || address)?kDNSServiceFlagsMoreComing:0), interface, kDNSServiceErr_NoError,
hostName, QString::number(port), txtLen, txtPtr); hostName, QString::number(port), 0, emptyTxt);
txtAtt=txt;
while (txtAtt) { while (txtAtt) {
sg->txtRecordReply(kDNSServiceFlagsAdd|((txtAtt->next || address)?kDNSServiceFlagsMoreComing:0),kDNSServiceErr_NoError, sg->txtFieldReply(kDNSServiceFlagsAdd|((txtAtt->next || address)?kDNSServiceFlagsMoreComing:0),kDNSServiceErr_NoError,
static_cast<unsigned short>(txtAtt->size),txtAtt->text,-1); static_cast<unsigned short>(txtAtt->size),txtAtt->text,-1);
txtAtt = txtAtt->next; txtAtt = txtAtt->next;
} }
@@ -366,11 +362,13 @@ extern "C" void cAvahiResolveReply( AvahiServiceResolver * r, AvahiIfIndex inter
ipv4.sin_family = AF_INET; ipv4.sin_family = AF_INET;
memcpy(&(ipv4.sin_addr),&(address->data.ipv4.address),sizeof(ipv4.sin_addr)); memcpy(&(ipv4.sin_addr),&(address->data.ipv4.address),sizeof(ipv4.sin_addr));
sg->addrReply(kDNSServiceFlagsAdd, kDNSServiceErr_NoError, hostName, reinterpret_cast<sockaddr*>(&ipv4), defaultTtl); sg->addrReply(kDNSServiceFlagsAdd, kDNSServiceErr_NoError, hostName, reinterpret_cast<sockaddr*>(&ipv4), defaultTtl);
break;
case (AVAHI_PROTO_INET6): case (AVAHI_PROTO_INET6):
memset(&ipv6,0,sizeof(ipv6)); memset(&ipv6,0,sizeof(ipv6));
ipv6.sin6_family = AF_INET6; ipv6.sin6_family = AF_INET6;
memcpy(&(ipv6.sin6_addr), &(address->data.ipv6.address), sizeof(ipv6.sin6_addr)); memcpy(&(ipv6.sin6_addr), &(address->data.ipv6.address), sizeof(ipv6.sin6_addr));
sg->addrReply(kDNSServiceFlagsAdd, kDNSServiceErr_NoError, hostName, reinterpret_cast<sockaddr*>(&ipv6), defaultTtl); sg->addrReply(kDNSServiceFlagsAdd, kDNSServiceErr_NoError, hostName, reinterpret_cast<sockaddr*>(&ipv6), defaultTtl);
break;
default: default:
if (DEBUG_ZEROCONF) if (DEBUG_ZEROCONF)
qDebug() << "Warning: ignoring address with protocol " << address->proto << " for service " << sg->fullName(); qDebug() << "Warning: ignoring address with protocol " << address->proto << " for service " << sg->fullName();

View File

@@ -204,13 +204,14 @@ QDebug operator<<(QDebug dbg, const Service &service)
<< " fullName:" << service.fullName() << ", port:" << service.port() << " fullName:" << service.fullName() << ", port:" << service.port()
<< ", txtRecord:{"; << ", txtRecord:{";
bool first=true; bool first=true;
const ServiceTxtRecord &txtRecord = service.txtRecord(); QHashIterator<QString, QString> i(service.txtRecord());
foreach (const QString &k, txtRecord){ while (i.hasNext()){
i.next();
if (first) if (first)
first = false; first = false;
else else
dbg << ", "; dbg << ", ";
dbg << k << ":" << txtRecord.value(k); dbg << i.key() << ":" << i.value();
} }
dbg << "}, "; dbg << "}, ";
if (const QHostInfo *host = service.host()){ if (const QHostInfo *host = service.host()){
@@ -734,7 +735,6 @@ void ServiceGatherer::serviceResolveReply(DNSServiceFlags fl
} }
if (publishedService) publishedService->invalidate(); // delay this to enactServiceChange? if (publishedService) publishedService->invalidate(); // delay this to enactServiceChange?
serviceBrowser->updateFlowStatusForFlags(flags); serviceBrowser->updateFlowStatusForFlags(flags);
uint16_t nKeys = txtRecordGetCount(txtLen, rawTxtRecord); uint16_t nKeys = txtRecordGetCount(txtLen, rawTxtRecord);
for (uint16_t i = 0; i < nKeys; ++i){ for (uint16_t i = 0; i < nKeys; ++i){
enum { maxTxtLen= 256 }; enum { maxTxtLen= 256 };
@@ -749,11 +749,7 @@ void ServiceGatherer::serviceResolveReply(DNSServiceFlags fl
break; break;
} }
keyBuf[maxTxtLen-1] = 0; // just to be sure keyBuf[maxTxtLen-1] = 0; // just to be sure
if (flags & kDNSServiceFlagsAdd) { currentService->m_txtRecord.insert(QString::fromUtf8(keyBuf),QString::fromUtf8(valueCStr, valLen));
txtRecord[QString::fromUtf8(keyBuf)] = QString::fromUtf8(valueCStr, valLen);
} else {
txtRecord.remove(QString::fromUtf8(keyBuf)); // check value???
}
} }
currentService->m_interfaceNr = interfaceIndex; currentService->m_interfaceNr = interfaceIndex;
currentService->m_port = port; currentService->m_port = port;
@@ -809,23 +805,65 @@ void ServiceGatherer::txtRecordReply(DNSServiceFlags flags,
qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " error " << txtErr qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " error " << txtErr
<< " decoding txt record of service " << currentService->fullName(); << " decoding txt record of service " << currentService->fullName();
if ((flags & kDNSServiceFlagsAdd) == 0) if ((flags & kDNSServiceFlagsAdd) == 0)
txtRecord.clear(); currentService->m_txtRecord.clear();
break; break;
} }
keyBuf[255] = 0; // just to be sure keyBuf[255] = 0; // just to be sure
if (flags & kDNSServiceFlagsAdd) { if (flags & kDNSServiceFlagsAdd) {
txtRecord[QString::fromUtf8(keyBuf)] = QString::fromUtf8(valueCStr, valLen); currentService->m_txtRecord.insert(QString::fromUtf8(keyBuf), QString::fromUtf8(valueCStr, valLen));
} else { } else {
txtRecord.remove(QString::fromUtf8(keyBuf)); // check value??? currentService->m_txtRecord.remove(QString::fromUtf8(keyBuf)); // check value???
} }
} }
if ((flags & kDNSServiceFlagsAdd) != 0) { if ((flags & kDNSServiceFlagsAdd) != 0) {
status |= TxtConnectionSuccess; status |= TxtConnectionSuccess;
} }
if (txtRecord.count() != 0 && currentServiceCanBePublished()) if (currentService->m_txtRecord.count() != 0 && currentServiceCanBePublished())
serviceBrowser->pendingGathererAdd(gatherer()); serviceBrowser->pendingGathererAdd(gatherer());
} }
void ServiceGatherer::txtFieldReply(DNSServiceFlags flags,
DNSServiceErrorType errorCode,
uint16_t txtLen,
const void *rawTxtRecord,
uint32_t /*ttl*/){
if (errorCode != kDNSServiceErr_NoError){
if (errorCode == kDNSServiceErr_Timeout){
if ((status & TxtConnectionSuccess) == 0){
qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " failed txt gathering for service "
<< currentService->fullName() << " as it did timeout";
status |= TxtConnectionFailed;
}
} else {
qDebug() << "ServiceBrowser " << serviceBrowser->serviceType << " failed txt gathering for service "
<< currentService->fullName() << " with error " << errorCode;
status |= TxtConnectionFailed;
}
if (status & TxtConnectionActive) {
status &= ~TxtConnectionActive;
lib()->refDeallocate(txtConnection);
serviceBrowser->updateFlowStatusForCancel();
}
return;
}
serviceBrowser->updateFlowStatusForFlags(flags);
uint16_t keyLen=0;
const char *txt=reinterpret_cast<const char *>(rawTxtRecord);
while (keyLen < txtLen) {
if (txt[keyLen]=='=')
break;
++keyLen;
}
if (flags & kDNSServiceFlagsAdd) {
currentService->m_txtRecord.insert(QString::fromUtf8(txt, keyLen),
QString::fromUtf8(txt + keyLen + 1,
((txtLen>keyLen)?txtLen - keyLen - 1:0)));
} else {
currentService->m_txtRecord.remove(QString::fromUtf8(txt, keyLen)); // check value???
}
}
void ServiceGatherer::addrReply(DNSServiceFlags flags, void ServiceGatherer::addrReply(DNSServiceFlags flags,
DNSServiceErrorType errorCode, DNSServiceErrorType errorCode,
const char *hostname, const char *hostname,

View File

@@ -112,7 +112,6 @@ protected:
/// class that gathers all needed info on a service, all its methods (creation included) are supposed to be called by the listener/reaction thread /// class that gathers all needed info on a service, all its methods (creation included) are supposed to be called by the listener/reaction thread
class ServiceGatherer { class ServiceGatherer {
public: public:
QHash<QString, QString> txtRecord;
QString hostName; QString hostName;
ServiceBrowserPrivate *serviceBrowser; ServiceBrowserPrivate *serviceBrowser;
QHostInfo *host; QHostInfo *host;
@@ -158,6 +157,8 @@ public:
void txtRecordReply(DNSServiceFlags flags, DNSServiceErrorType errorCode, void txtRecordReply(DNSServiceFlags flags, DNSServiceErrorType errorCode,
uint16_t txtLen, const void *rawTxtRecord, uint32_t ttl); uint16_t txtLen, const void *rawTxtRecord, uint32_t ttl);
void txtFieldReply(DNSServiceFlags flags, DNSServiceErrorType errorCode,
uint16_t txtLen, const void *rawTxtRecord, uint32_t ttl);
void addrReply(DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, void addrReply(DNSServiceFlags flags, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address,
uint32_t ttl); uint32_t ttl);