dnsSdLib.cpp 16 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (info@qt.nokia.com)
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
** If you have questions regarding the use of this file, please contact
** Nokia at info@qt.nokia.com.
**
**************************************************************************/

Fawzi Mohamed's avatar
Fawzi Mohamed committed
33 34
#include "syssocket.h" // this should be the first header included

35 36 37
#include "servicebrowser.h"
#include "servicebrowser_p.h"

38
#include <QtCore/QCoreApplication>
39 40 41 42 43
#include <QtCore/QDebug>
#include <QtCore/QLibrary>
#include <QtCore/QString>
#include <QtCore/QStringList>

44 45
#include <errno.h>

46
#ifndef NO_DNS_SD_LIB
47 48

#ifdef Q_OS_MACX
49
#define ZCONF_MDNS_STATIC_LINKING
50 51
#endif

52
#ifdef ZCONF_MDNS_STATIC_LINKING
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
extern "C" {
#include "dns_sd_funct.h"
}
#endif

#ifdef Q_OS_UNIX
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif

namespace ZeroConf {
namespace Internal {

extern "C" {
typedef void (DNSSD_API *RefDeallocatePtr)(DNSServiceRef sdRef);
typedef DNSServiceErrorType (DNSSD_API *ResolvePtr)(DNSServiceRef *sdRef, DNSServiceFlags flags,
                                                    uint32_t interfaceIndex, const char *name,
                                                    const char *regtype, const char *domain,
                                                    DNSServiceResolveReply callBack, void *context);
typedef DNSServiceErrorType (DNSSD_API *QueryRecordPtr)(DNSServiceRef *sdRef, DNSServiceFlags flags,
                                                        uint32_t interfaceIndex, const char *fullname,
                                                        uint16_t rrtype, uint16_t rrclass,
                                                        DNSServiceQueryRecordReply callBack, void *context);
typedef DNSServiceErrorType (DNSSD_API *GetAddrInfoPtr)(DNSServiceRef *sdRef, DNSServiceFlags flags,
                                                        uint32_t interfaceIndex, DNSServiceProtocol protocol,
                                                        const char *hostname, DNSServiceGetAddrInfoReply callBack,
                                                        void *context);
typedef DNSServiceErrorType (DNSSD_API *ReconfirmRecordPtr)(DNSServiceFlags flags, uint32_t interfaceIndex,
                                                            const char *fullname, uint16_t rrtype,
                                                            uint16_t rrclass, uint16_t rdlen, const void *rdata);
typedef DNSServiceErrorType (DNSSD_API *BrowsePtr)(DNSServiceRef *sdRef, DNSServiceFlags flags,
                                                   uint32_t interfaceIndex, const char *regtype, const char *domain,
                                                   DNSServiceBrowseReply callBack, void *context);
typedef DNSServiceErrorType (DNSSD_API *GetPropertyPtr)(const char *property, void *result, uint32_t *size);
typedef DNSServiceErrorType (DNSSD_API *ProcessResultPtr)(DNSServiceRef sdRef);
typedef DNSServiceErrorType (DNSSD_API *CreateConnectionPtr)(DNSServiceRef *sdRef);
typedef int (DNSSD_API *RefSockFDPtr)(DNSServiceRef sdRef);
}

// represents a zero conf library exposing the dns-sd interface
94
class DnsSdZConfLib : public ZConfLib{
95
    Q_DECLARE_TR_FUNCTIONS(ZeroConf)
96 97 98 99 100 101 102 103 104 105 106
private:
    RefDeallocatePtr m_refDeallocate;
    ResolvePtr m_resolve;
    QueryRecordPtr m_queryRecord;
    GetAddrInfoPtr m_getAddrInfo;
    ReconfirmRecordPtr m_reconfirmRecord;
    BrowsePtr m_browse;
    GetPropertyPtr m_getProperty;
    ProcessResultPtr m_processResult;
    CreateConnectionPtr m_createConnection;
    RefSockFDPtr m_refSockFD;
107
    QLibrary dnsSdLib;
108
public:
109 110 111 112
    enum {
        // Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this
        LONG_TIME = 100000000
    };
113

114
    DnsSdZConfLib(QString libName = QLatin1String("dns_sd"), ZConfLib::Ptr fallBack = ZConfLib::Ptr(0)) : ZConfLib(fallBack), dnsSdLib(libName)
115
    {
116
#ifndef ZCONF_MDNS_STATIC_LINKING
117
        // dynamic linking
118
        if (!dnsSdLib.load()) {
119
            m_isOk = false;
120
            m_errorMsg=tr("DnsSdZConfLib could not load native library");
121
        }
122 123 124 125 126 127 128 129 130 131
        m_refDeallocate = reinterpret_cast<RefDeallocatePtr>(dnsSdLib.resolve("DNSServiceRefDeallocate"));
        m_resolve = reinterpret_cast<ResolvePtr>(dnsSdLib.resolve("DNSServiceResolve"));
        m_queryRecord = reinterpret_cast<QueryRecordPtr>(dnsSdLib.resolve("DNSServiceQueryRecord"));
        m_getAddrInfo = reinterpret_cast<GetAddrInfoPtr>(dnsSdLib.resolve("DNSServiceGetAddrInfo"));
        m_reconfirmRecord = reinterpret_cast<ReconfirmRecordPtr>(dnsSdLib.resolve("DNSServiceReconfirmRecord"));
        m_browse = reinterpret_cast<BrowsePtr>(dnsSdLib.resolve("DNSServiceBrowse"));
        m_getProperty = reinterpret_cast<GetPropertyPtr>(dnsSdLib.resolve("DNSServiceGetProperty"));
        m_processResult = reinterpret_cast<ProcessResultPtr>(dnsSdLib.resolve("DNSServiceProcessResult")) ;
        m_createConnection = reinterpret_cast<CreateConnectionPtr>(dnsSdLib.resolve("DNSServiceCreateConnection"));
        m_refSockFD = reinterpret_cast<RefSockFDPtr>(dnsSdLib.resolve("DNSServiceRefSockFD"));
132 133 134 135 136 137 138 139 140 141 142 143 144
#else
        // static linking
        m_refDeallocate = reinterpret_cast<RefDeallocatePtr>(&DNSServiceRefDeallocate);
        m_resolve = reinterpret_cast<ResolvePtr>(&DNSServiceResolve);
        m_queryRecord = reinterpret_cast<QueryRecordPtr>(DNSServiceQueryRecord);
        m_getAddrInfo = reinterpret_cast<GetAddrInfoPtr>(&DNSServiceGetAddrInfo);
        m_reconfirmRecord = reinterpret_cast<ReconfirmRecordPtr>(&DNSServiceReconfirmRecord);
        m_browse = reinterpret_cast<BrowsePtr>(&DNSServiceBrowse);
        m_getProperty = reinterpret_cast<GetPropertyPtr>(&DNSServiceGetProperty);
        m_processResult = reinterpret_cast<ProcessResultPtr>(&DNSServiceProcessResult) ;
        m_createConnection = reinterpret_cast<CreateConnectionPtr>(&DNSServiceCreateConnection);
        m_refSockFD = reinterpret_cast<RefSockFDPtr>(&DNSServiceRefSockFD);
#endif
145
        if (DEBUG_ZEROCONF){
146 147 148 149 150 151 152 153 154 155
            if (m_refDeallocate == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_refDeallocate == 0");
            if (m_resolve == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_resolve == 0");
            if (m_queryRecord == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_queryRecord == 0");
            if (m_getAddrInfo == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_getAddrInfo == 0");
            if (m_reconfirmRecord == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_reconfirmRecord == 0");
            if (m_browse == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_browse == 0");
            if (m_getProperty == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_getProperty == 0");
            if (m_processResult == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_processResult == 0");
            if (m_createConnection == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_createConnection == 0");
            if (m_refSockFD == 0) qDebug() << QLatin1String("DnsSdZConfLib.m_refSockFD == 0");
156
        }
157 158
    }

159
    ~DnsSdZConfLib() {
160 161
    }

162
    QString name(){
163
        return QString::fromUtf8("DnsSdZeroConfLib@%1").arg(size_t(this),0,16);
164 165
    }

166
    // bool tryStartDaemon();
167
    void refDeallocate(DNSServiceRef sdRef) {
168 169 170 171
        if (m_refDeallocate == 0) return;
        m_refDeallocate(sdRef);
    }

172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
    void browserDeallocate(BrowserRef *bRef) {
        if (m_refDeallocate == 0) return;
        if (bRef) {
            m_refDeallocate(*reinterpret_cast<DNSServiceRef *>(bRef));
            *bRef=0;
        }
    }

    void stopConnection(ConnectionRef cRef)
    {
        int sock = refSockFD(cRef);
        if (sock>0)
            shutdown(sock,SHUT_RDWR);
    }

    void destroyConnection(ConnectionRef *sdRef) {
        if (m_refDeallocate == 0) return;
        if (sdRef) {
            m_refDeallocate(*reinterpret_cast<DNSServiceRef *>(sdRef));
            *sdRef=0;
        }
    }

    DNSServiceErrorType resolve(ConnectionRef cRef, DNSServiceRef *sdRef,
196 197
                                uint32_t interfaceIndex, const char *name,
                                const char *regtype, const char *domain,
198
                                ServiceGatherer *gatherer)
199 200
    {
        if (m_resolve == 0) return kDNSServiceErr_Unsupported;
201 202 203 204
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return m_resolve(sdRef, kDNSServiceFlagsShareConnection | kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout,
                         interfaceIndex, name, regtype, domain, &cServiceResolveReply, gatherer
                         );
205 206
    }

207
    DNSServiceErrorType queryRecord(ConnectionRef cRef, DNSServiceRef *sdRef,
208
                                    uint32_t interfaceIndex, const char *fullname,
209
                                    ServiceGatherer *gatherer)
210 211
    {
        if (m_queryRecord == 0) return kDNSServiceErr_Unsupported;
212 213 214 215
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return m_queryRecord(sdRef, kDNSServiceFlagsShareConnection | kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout,
                             interfaceIndex, fullname,
                             kDNSServiceType_TXT, kDNSServiceClass_IN, &cTxtRecordReply, gatherer);
216 217
    }

218
    DNSServiceErrorType getAddrInfo(ConnectionRef cRef, DNSServiceRef *sdRef,
219
                                    uint32_t interfaceIndex, DNSServiceProtocol protocol,
220
                                    const char *hostname, ServiceGatherer *gatherer)
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
    {
        enum { longTTL = 100 };
        if (m_getAddrInfo == 0) {
#ifdef Q_OS_UNIX
            // try to use getaddrinfo (for example on linux with avahi)
            struct addrinfo req, *ans; int err;
            memset(&req,0,sizeof(req));
            req.ai_flags = 0;
            req.ai_family = AF_UNSPEC;
            req.ai_socktype = SOCK_STREAM;
            req.ai_protocol = 0;
            if ((err = getaddrinfo(hostname, 0, &req, &ans)) != 0) {
                qDebug() << "getaddrinfo for " << hostname << " failed with " << gai_strerror(err);
                return kDNSServiceErr_Unsupported; // use another error here???
            }
            for (struct addrinfo *ansAtt = ans; ansAtt != 0; ansAtt = ansAtt->ai_next){
237 238
                gatherer->addrReply(kDNSServiceFlagsAdd, kDNSServiceErr_NoError,
                                    hostname, ansAtt->ai_addr, longTTL);
239 240 241 242 243 244 245
            }
            freeaddrinfo(ans);
            return kDNSServiceErr_NoError;
#else
            return kDNSServiceErr_Unsupported;
#endif
        }
246 247 248
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return m_getAddrInfo(sdRef, kDNSServiceFlagsShareConnection | kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout,
                             interfaceIndex, protocol, hostname, &cAddrReply, gatherer);
249 250
    }

251 252 253
    DNSServiceErrorType reconfirmRecord(ConnectionRef /*cRef*/, uint32_t /*interfaceIndex*/,
                                                const char * /*name*/, const char * /*type*/, const char * /*domain*/,
                                                const char * /*fullname*/)
254 255
    {
        if (m_reconfirmRecord == 0) return kDNSServiceErr_Unsupported;
256 257 258 259
        // reload and force update with in the callback with
        // m_reconfirmRecord(flags, interfaceIndex, fullname, rrtype,
        //                         rrclass, rdlen, rdata);
        return kDNSServiceErr_Unsupported;
260 261
    }

262
    DNSServiceErrorType browse(ConnectionRef cRef, BrowserRef *bRef,
263
                               uint32_t interfaceIndex, const char *regtype,
264
                               const char *domain, ServiceBrowserPrivate *browser)
265 266
    {
        if (m_browse == 0) return kDNSServiceErr_Unsupported;
267 268 269 270
        DNSServiceRef *sdRef = reinterpret_cast<DNSServiceRef *>(bRef);
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return m_browse(sdRef, kDNSServiceFlagsShareConnection | kDNSServiceFlagsSuppressUnusable,
                        interfaceIndex, regtype, domain, &cBrowseReply, browser);
271 272
    }

273 274 275 276
    DNSServiceErrorType getProperty(const char *property,  // Requested property (i.e. kDNSServiceProperty_DaemonVersion)
                                    void       *result,    // Pointer to place to store result
                                    uint32_t   *size       // size of result location
                                    )
277 278 279 280 281 282
    {
        if (m_getProperty == 0)
            return kDNSServiceErr_Unsupported;
        return m_getProperty(property, result, size);
    }

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
    ProcessStatus processResult(ConnectionRef sdRef) {
        if (m_processResult == 0)
            return ProcessedFailure;
        if (m_processResult(reinterpret_cast<DNSServiceRef>(sdRef)) != kDNSServiceErr_NoError)
            return ProcessedError;
        return ProcessedOk;
    }

    // alternative processResult implementation using select
    DNSServiceErrorType  processResultSelect(ConnectionRef cRef)
    {
#if _DNS_SD_LIBDISPATCH
        {
            main_queue = dispatch_get_main_queue();
            if (mainRef)  DNSServiceSetDispatchQueue(mainRef, main_queue);
            dispatch_main();
        }
#else
        {
            if (m_processResult == 0) return kDNSServiceErr_Unsupported;
            int dns_sd_fd  = (cRef?refSockFD(cRef):-1);
            int nfds = dns_sd_fd + 1;
            fd_set readfds;
            struct timeval tv;
            int result;
            while (true) {
                dns_sd_fd  = (cRef?refSockFD(cRef):-1);
                if (dns_sd_fd < 0)
                    return false;
                nfds = dns_sd_fd + 1;
                FD_ZERO(&readfds);
                FD_SET(dns_sd_fd , &readfds);

                tv.tv_sec  = LONG_TIME;
                tv.tv_usec = 0;

                result = select(nfds, &readfds, (fd_set *)NULL, (fd_set *)NULL, &tv);
                if (result > 0) {
                    if (FD_ISSET(dns_sd_fd , &readfds)) {
                        m_processResult(reinterpret_cast<DNSServiceRef>(cRef));
                        return true;
                    }
                } else if (result == 0) {
                    // we are idle... could do something productive... :)
                } else if (errno != EINTR) {
                    qDebug() << "select() returned " << result << " errno " << errno << strerror(errno);
                    return false;
                }
            }
        }
#endif
334 335
    }

336
    DNSServiceErrorType createConnection(ConnectionRef *sdRef) {
337
        if (m_createConnection == 0) return kDNSServiceErr_Unsupported;
338
        return m_createConnection(reinterpret_cast<DNSServiceRef *>(sdRef));
339 340
    }

341
    int refSockFD(ConnectionRef sdRef) {
342
        if (m_refSockFD == 0) return kDNSServiceErr_Unsupported;
343
        return m_refSockFD(reinterpret_cast<DNSServiceRef>(sdRef));
344 345 346
    }
};

347 348
ZConfLib::Ptr ZConfLib::createDnsSdLib(const QString &libName, ZConfLib::Ptr fallback) {
    return ZConfLib::Ptr(new DnsSdZConfLib(libName, fallback));
349 350 351 352 353
    return fallback;
}
} // namespace Internal
} // namespace ZeroConf

354
#else // NO_DNS_SD_LIB
355 356 357 358

namespace ZeroConf {
namespace Internal {

359
ZConfLib::Ptr ZConfLib::createDnsSdLib(const QString &/*extraPaths*/, ZConfLib::Ptr fallback) {
360 361 362 363 364 365 366
    return fallback;
}

} // namespace Internal
} // namespace ZeroConf
#endif