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"