File indexing completed on 2024-04-28 03:54:04

0001 /*
0002     This file is part of the KDE project
0003 
0004     SPDX-FileCopyrightText: 2004, 2005 Jakub Stachowski <qbast@go2.pl>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "mdnsd-responder.h"
0010 #include "mdnsd-sdevent.h"
0011 #include "publicservice.h"
0012 #include "servicebase_p.h"
0013 #include <QCoreApplication>
0014 #include <QStringList>
0015 
0016 #ifdef _WIN32
0017 #include <winsock2.h>
0018 #else
0019 #include <netinet/in.h>
0020 #endif
0021 
0022 #define KDNSSD_D PublicServicePrivate *d = static_cast<PublicServicePrivate *>(this->d.operator->())
0023 
0024 namespace KDNSSD
0025 {
0026 void publish_callback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *, const char *, void *context);
0027 class PublicServicePrivate : public Responder, public ServiceBasePrivate
0028 {
0029 public:
0030     PublicServicePrivate(PublicService *parent, const QString &name, const QString &type, unsigned int port, const QString &domain)
0031         : Responder()
0032         , ServiceBasePrivate(name, type, domain, QString(), port)
0033         , m_published(false)
0034         , m_parent(parent)
0035     {
0036     }
0037     bool m_published;
0038     PublicService *m_parent;
0039     QStringList m_subtypes;
0040     virtual void customEvent(QEvent *event);
0041 };
0042 
0043 PublicService::PublicService(const QString &name, const QString &type, unsigned int port, const QString &domain, const QStringList &subtypes)
0044     : QObject()
0045     , ServiceBase(new PublicServicePrivate(this, name, type, port, domain))
0046 {
0047     KDNSSD_D;
0048     if (domain.isNull()) {
0049         d->m_domain = "local.";
0050     }
0051     d->m_subtypes = subtypes;
0052 }
0053 
0054 PublicService::~PublicService()
0055 {
0056     stop();
0057 }
0058 
0059 void PublicService::setServiceName(const QString &serviceName)
0060 {
0061     KDNSSD_D;
0062     d->m_serviceName = serviceName;
0063     if (d->isRunning()) {
0064         stop();
0065         publishAsync();
0066     }
0067 }
0068 
0069 void PublicService::setDomain(const QString &domain)
0070 {
0071     KDNSSD_D;
0072     d->m_domain = domain;
0073     if (d->isRunning()) {
0074         stop();
0075         publishAsync();
0076     }
0077 }
0078 
0079 QStringList PublicService::subtypes() const
0080 {
0081     KDNSSD_D;
0082     return d->m_subtypes;
0083 }
0084 
0085 void PublicService::setType(const QString &type)
0086 {
0087     KDNSSD_D;
0088     d->m_type = type;
0089     if (d->isRunning()) {
0090         stop();
0091         publishAsync();
0092     }
0093 }
0094 
0095 void PublicService::setSubTypes(const QStringList &subtypes)
0096 {
0097     KDNSSD_D;
0098     d->m_subtypes = subtypes;
0099     if (d->isRunning()) {
0100         stop();
0101         publishAsync();
0102     }
0103 }
0104 
0105 void PublicService::setPort(unsigned short port)
0106 {
0107     KDNSSD_D;
0108     d->m_port = port;
0109     if (d->isRunning()) {
0110         stop();
0111         publishAsync();
0112     }
0113 }
0114 
0115 bool PublicService::isPublished() const
0116 {
0117     KDNSSD_D;
0118     return d->m_published;
0119 }
0120 
0121 void PublicService::setTextData(const QMap<QString, QByteArray> &textData)
0122 {
0123     KDNSSD_D;
0124     d->m_textData = textData;
0125     if (d->isRunning()) {
0126         stop();
0127         publishAsync();
0128     }
0129 }
0130 
0131 bool PublicService::publish()
0132 {
0133     KDNSSD_D;
0134     publishAsync();
0135     while (d->isRunning() && !d->m_published) {
0136         d->process();
0137     }
0138     return d->m_published;
0139 }
0140 
0141 void PublicService::stop()
0142 {
0143     KDNSSD_D;
0144     d->stop();
0145     d->m_published = false;
0146 }
0147 
0148 void PublicService::publishAsync()
0149 {
0150     KDNSSD_D;
0151     if (d->isRunning()) {
0152         stop();
0153     }
0154     TXTRecordRef txt;
0155     TXTRecordCreate(&txt, 0, 0);
0156     QMap<QString, QByteArray>::ConstIterator itEnd = d->m_textData.cend();
0157     for (QMap<QString, QByteArray>::ConstIterator it = d->m_textData.cbegin(); it != itEnd; ++it) {
0158         if (TXTRecordSetValue(&txt, it.key().toUtf8().constData(), it.value().length(), it.value().constData()) != kDNSServiceErr_NoError) {
0159             TXTRecordDeallocate(&txt);
0160             Q_EMIT published(false);
0161             return;
0162         }
0163     }
0164     DNSServiceRef ref;
0165     QString fullType = d->m_type;
0166     for (const QString &subtype : std::as_const(d->m_subtypes)) {
0167         fullType += ',' + subtype;
0168     }
0169     if (DNSServiceRegister(&ref,
0170                            0,
0171                            0,
0172                            d->m_serviceName.toUtf8().constData(),
0173                            fullType.toLatin1().constData(),
0174                            domainToDNS(d->m_domain).constData(),
0175                            NULL,
0176                            htons(d->m_port),
0177                            TXTRecordGetLength(&txt),
0178                            TXTRecordGetBytesPtr(&txt),
0179                            publish_callback,
0180                            reinterpret_cast<void *>(d))
0181         == kDNSServiceErr_NoError) {
0182         d->setRef(ref);
0183     }
0184     TXTRecordDeallocate(&txt);
0185     if (!d->isRunning()) {
0186         Q_EMIT published(false);
0187     }
0188 }
0189 
0190 void publish_callback(DNSServiceRef, DNSServiceFlags, DNSServiceErrorType errorCode, const char *name, const char *, const char *, void *context)
0191 {
0192     QObject *obj = reinterpret_cast<QObject *>(context);
0193     if (errorCode != kDNSServiceErr_NoError) {
0194         ErrorEvent err;
0195         QCoreApplication::sendEvent(obj, &err);
0196     } else {
0197         PublishEvent pev(QString::fromUtf8(name));
0198         QCoreApplication::sendEvent(obj, &pev);
0199     }
0200 }
0201 
0202 void PublicServicePrivate::customEvent(QEvent *event)
0203 {
0204     if (event->type() == QEvent::User + SD_ERROR) {
0205         m_parent->stop();
0206         Q_EMIT m_parent->published(false);
0207     }
0208     if (event->type() == QEvent::User + SD_PUBLISH) {
0209         m_published = true;
0210         Q_EMIT m_parent->published(true);
0211         m_serviceName = static_cast<PublishEvent *>(event)->m_name;
0212     }
0213 }
0214 
0215 void PublicService::virtual_hook(int, void *)
0216 {
0217 }
0218 
0219 }
0220 
0221 #include "moc_publicservice.cpp"