diff options
Diffstat (limited to 'krfb/kinetd/kinetd.cpp')
-rw-r--r-- | krfb/kinetd/kinetd.cpp | 658 |
1 files changed, 658 insertions, 0 deletions
diff --git a/krfb/kinetd/kinetd.cpp b/krfb/kinetd/kinetd.cpp new file mode 100644 index 00000000..5094712e --- /dev/null +++ b/krfb/kinetd/kinetd.cpp @@ -0,0 +1,658 @@ + +/*************************************************************************** + kinetd.cpp + -------------- + begin : Mon Feb 11 2002 + copyright : (C) 2002 by Tim Jansen + email : [email protected] + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "kinetd.h" +#include "kinetd.moc" +#include "kinetinterface.h" +#include "kuser.h" +#include "uuid.h" +#include <qregexp.h> +#include <kservicetype.h> +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <knotifyclient.h> +#include <ksockaddr.h> +#include <kextsock.h> +#include <klocale.h> +#include <kglobal.h> + +#include <unistd.h> +#include <fcntl.h> + +PortListener::PortListener(KService::Ptr s, + KConfig *config, + KServiceRegistry *srvreg) : + m_port(-1), + m_serviceRegistered(false), + m_socket(0), + m_config(config), + m_srvreg(srvreg), + m_dnssdreg(0) +{ + m_dnssdRegistered = false; + + m_uuid = createUUID(); + loadConfig(s); + + if (m_valid && m_enabled) + acquirePort(); +} + +bool PortListener::acquirePort() { + if (m_socket) { + if ((m_port >= m_portBase) && + (m_port < (m_portBase + m_autoPortRange))) + return true; + else + delete m_socket; + } + m_port = m_portBase; + m_socket = new KServerSocket(m_port, false); + while (!m_socket->bindAndListen()) { + m_port++; + if (m_port >= (m_portBase+m_autoPortRange)) { + kdDebug() << "Kinetd cannot load service "<<m_serviceName + <<": unable to get port" << endl; + m_port = -1; + delete m_socket; + m_socket = 0; + return false; + } + delete m_socket; + m_socket = new KServerSocket(m_port, false); + } + connect(m_socket, SIGNAL(accepted(KSocket*)), + SLOT(accepted(KSocket*))); + + bool s = m_registerService; + bool sd =m_dnssdRegister; + setServiceRegistrationEnabledInternal(false); + dnssdRegister(false); + setServiceRegistrationEnabledInternal(s); + dnssdRegister(sd); + return true; +} + +void PortListener::freePort() { + m_port = -1; + if (m_socket) + delete m_socket; + m_socket = 0; + setServiceRegistrationEnabledInternal(m_registerService); + dnssdRegister(false); +} + +void PortListener::loadConfig(KService::Ptr s) { + m_valid = true; + m_autoPortRange = 0; + m_enabled = true; + m_argument = QString::null; + m_multiInstance = false; + + QVariant vid, vport, vautoport, venabled, vargument, vmultiInstance, vurl, + vsattributes, vslifetime, vdname, vdtype, vddata; + + m_execPath = s->exec().utf8(); + vid = s->property("X-KDE-KINETD-id"); + vport = s->property("X-KDE-KINETD-port"); + vautoport = s->property("X-KDE-KINETD-autoPortRange"); + venabled = s->property("X-KDE-KINETD-enabled"); + vargument = s->property("X-KDE-KINETD-argument"); + vmultiInstance = s->property("X-KDE-KINETD-multiInstance"); + vurl = s->property("X-KDE-KINETD-serviceURL"); + vsattributes = s->property("X-KDE-KINETD-serviceAttributes"); + vslifetime = s->property("X-KDE-KINETD-serviceLifetime"); + vdname = s->property("X-KDE-KINETD-DNSSD-Name"); + vdtype = s->property("X-KDE-KINETD-DNSSD-Type"); + vddata = s->property("X-KDE-KINETD-DNSSD-Properties"); + + if (!vid.isValid()) { + kdDebug() << "Kinetd cannot load service "<<m_serviceName + <<": no id set" << endl; + m_valid = false; + return; + } + + if (!vport.isValid()) { + kdDebug() << "Kinetd cannot load service "<<m_serviceName + <<": invalid port" << endl; + m_valid = false; + return; + } + + m_serviceName = vid.toString(); + m_serviceLifetime = vslifetime.toInt(); + if (m_serviceLifetime < 120) // never less than 120 s + m_serviceLifetime = 120; + m_portBase = vport.toInt(); + if (vautoport.isValid()) + m_autoPortRange = vautoport.toInt(); + if (venabled.isValid()) + m_enabled = venabled.toBool(); + if (vargument.isValid()) + m_argument = vargument.toString(); + if (vmultiInstance.isValid()) + m_multiInstance = vmultiInstance.toBool(); + if (vurl.isValid()) { + m_serviceURL = vurl.toString(); + m_registerService = true; + } + else { + m_serviceURL = QString::null; + m_registerService = false; + } + if (vsattributes.isValid()) { + m_serviceAttributes = vsattributes.toString(); + } + else + m_serviceAttributes = ""; + if (vddata.isValid()) { + QStringList attrs = vddata.toStringList(); + for (QStringList::iterator it=attrs.begin(); + it!=attrs.end();it++) { + QString key = (*it).section('=',0,0); + QString value = processServiceTemplate((*it).section('=',1))[0]; + if (!key.isEmpty()) m_dnssdData[key]=value; + } + } + if (vdname.isValid() && vdtype.isValid()) { + m_dnssdName = processServiceTemplate(vdname.toString())[0]; + m_dnssdType = vdtype.toString(); + m_dnssdRegister = true; + kdDebug() << "DNS-SD register is enabled\n"; + } + else + m_dnssdRegister = false; + + + m_slpLifetimeEnd = QDateTime::currentDateTime().addSecs(m_serviceLifetime); + m_defaultPortBase = m_portBase; + m_defaultAutoPortRange = m_autoPortRange; + + m_config->setGroup("ListenerConfig"); + m_enabled = m_config->readBoolEntry("enabled_" + m_serviceName, + m_enabled); + m_portBase = m_config->readNumEntry("port_base_" + m_serviceName, + m_portBase); + m_autoPortRange = m_config->readNumEntry("auto_port_range_" + m_serviceName, + m_autoPortRange); + QDateTime nullTime; + m_expirationTime = m_config->readDateTimeEntry("enabled_expiration_"+m_serviceName, + &nullTime); + if ((!m_expirationTime.isNull()) && (m_expirationTime < QDateTime::currentDateTime())) + m_enabled = false; + m_registerService = m_config->readBoolEntry("enabled_srvreg_"+m_serviceName, + m_registerService); +} + +void PortListener::accepted(KSocket *sock) { + QString host, port; + KSocketAddress *ksa = KExtendedSocket::peerAddress(sock->socket()); + if ((!ksa) || !ksa->address()) { + delete sock; + return; + } + KExtendedSocket::resolve(ksa, host, port); + KNotifyClient::event("IncomingConnection", + i18n("Connection from %1").arg(host)); + delete ksa; + + if ((!m_enabled) || + ((!m_multiInstance) && m_process.isRunning())) { + delete sock; + return; + } + + // disable CLOEXEC flag, fixes #77412 + fcntl(sock->socket(), F_SETFD, fcntl(sock->socket(), F_GETFD) & ~FD_CLOEXEC); + + m_process.clearArguments(); + m_process << m_execPath << m_argument << QString::number(sock->socket()); + if (!m_process.start(KProcess::DontCare)) { + KNotifyClient::event("ProcessFailed", + i18n("Call \"%1 %2 %3\" failed").arg(m_execPath) + .arg(m_argument) + .arg(sock->socket())); + } + + delete sock; +} + +bool PortListener::isValid() { + return m_valid; +} + +bool PortListener::isEnabled() { + return m_enabled && m_valid; +} + +int PortListener::port() { + return m_port; +} + +QStringList PortListener::processServiceTemplate(const QString &a) { + QStringList l; + QValueVector<KInetInterface> v = KInetInterface::getAllInterfaces(false); + QValueVector<KInetInterface>::Iterator it = v.begin(); + while (it != v.end()) { + KInetSocketAddress *address = (*(it++)).address(); + if (!address) + continue; + QString hostName = address->nodeName(); + KUser u; + QString x = a; // replace does not work in const QString. Why?? + l.append(x.replace(QRegExp("%h"), KServiceRegistry::encodeAttributeValue(hostName)) + .replace(QRegExp("%p"), QString::number(m_port)) + .replace(QRegExp("%u"), KServiceRegistry::encodeAttributeValue(u.loginName())) + .replace(QRegExp("%i"), KServiceRegistry::encodeAttributeValue(m_uuid)) + .replace(QRegExp("%f"), KServiceRegistry::encodeAttributeValue(u.fullName()))); + } + return l; +} + +bool PortListener::setPort(int port, int autoPortRange) { + if ((port == m_portBase) && (autoPortRange == m_autoPortRange)) + return (m_port != -1); + + m_config->setGroup("ListenerConfig"); + if (port > 0) { + m_portBase = port; + m_autoPortRange = autoPortRange; + + m_config->writeEntry("port_base_" + m_serviceName, m_portBase); + m_config->writeEntry("auto_port_range_"+m_serviceName, m_autoPortRange); + } + else { + m_portBase = m_defaultPortBase; + m_autoPortRange = m_defaultAutoPortRange; + + m_config->deleteEntry("port_base_" + m_serviceName); + m_config->deleteEntry("auto_port_range_"+m_serviceName); + } + + m_config->sync(); + if (m_enabled) + return acquirePort(); + else + return false; +} + +void PortListener::setEnabled(bool e) { + setEnabledInternal(e, QDateTime()); +} + +void PortListener::setEnabledInternal(bool e, const QDateTime &ex) { + m_config->setGroup("ListenerConfig"); + m_config->writeEntry("enabled_" + m_serviceName, e); + m_config->writeEntry("enabled_expiration_"+m_serviceName, ex); + m_config->sync(); + + m_expirationTime = ex; + + if (e) { + if (m_port < 0) + acquirePort(); + m_enabled = m_port >= 0; + + } + else { + freePort(); + m_enabled = false; + } +} + +void PortListener::setEnabled(const QDateTime &ex) { + setEnabledInternal(true, ex); +} + +bool PortListener::isServiceRegistrationEnabled() { + return m_registerService; +} + +void PortListener::setServiceRegistrationEnabled(bool e) { + setServiceRegistrationEnabledInternal(e); + dnssdRegister(e && m_enabled); + m_config->setGroup("ListenerConfig"); + m_config->writeEntry("enable_srvreg_" + m_serviceName, e); + m_config->sync(); +} + +void PortListener::setServiceRegistrationEnabledInternal(bool e) { + m_registerService = e; + + if ((!m_srvreg) || m_serviceURL.isNull()) + return; + if (m_serviceRegistered == (m_enabled && e)) + return; + + if (m_enabled && e) { + m_registeredServiceURLs = processServiceTemplate(m_serviceURL); + QStringList attributes = processServiceTemplate(m_serviceAttributes); + QStringList::Iterator it = m_registeredServiceURLs.begin(); + QStringList::Iterator it2 = attributes.begin(); + while ((it != m_registeredServiceURLs.end()) && + (it2 != attributes.end())) { + if (!m_srvreg->registerService( + *(it++), + *(it2++), + m_serviceLifetime)) + kdDebug(7021) << "Failure registering SLP service (no slpd running?)"<< endl; + } + m_serviceRegistered = true; + // make lifetime 30s shorter, because the timeout is not precise + m_slpLifetimeEnd = QDateTime::currentDateTime().addSecs(m_serviceLifetime-30); + } else { + QStringList::Iterator it = m_registeredServiceURLs.begin(); + while (it != m_registeredServiceURLs.end()) + m_srvreg->unregisterService(*(it++)); + m_serviceRegistered = false; + } +} + +void PortListener::dnssdRegister(bool e) { + if (m_dnssdName.isNull() || m_dnssdType.isNull()) + return; + if (m_dnssdRegistered == e) + return; + if (e) { + m_dnssdRegistered=true; + m_dnssdreg = new DNSSD::PublicService(m_dnssdName,m_dnssdType,m_port); + m_dnssdreg->setTextData(m_dnssdData); + m_dnssdreg->publishAsync(); + } else { + m_dnssdRegistered=false; + delete m_dnssdreg; + m_dnssdreg=0; + } +} + +void PortListener::refreshRegistration() { + if (m_serviceRegistered && (m_slpLifetimeEnd.addSecs(-90) < QDateTime::currentDateTime())) { + setServiceRegistrationEnabledInternal(false); + setServiceRegistrationEnabledInternal(true); + } +} + +QDateTime PortListener::expiration() { + return m_expirationTime; +} + +QDateTime PortListener::serviceLifetimeEnd() { + if (m_serviceRegistered) + return m_slpLifetimeEnd; + else + return QDateTime(); +} + +QString PortListener::name() { + return m_serviceName; +} + +PortListener::~PortListener() { + setServiceRegistrationEnabledInternal(false); + delete m_socket; +} + + +KInetD::KInetD(QCString &n) : + KDEDModule(n) +{ + m_config = new KConfig("kinetdrc"); + m_srvreg = new KServiceRegistry(); + if (!m_srvreg->available()) { + kdDebug(7021) << "SLP not available"<< endl; + delete m_srvreg; + m_srvreg = 0; + } + m_portListeners.setAutoDelete(true); + connect(&m_expirationTimer, SIGNAL(timeout()), SLOT(setExpirationTimer())); + connect(&m_portRetryTimer, SIGNAL(timeout()), SLOT(portRetryTimer())); + connect(&m_reregistrationTimer, SIGNAL(timeout()), SLOT(reregistrationTimer())); + loadServiceList(); +} + +void KInetD::loadServiceList() +{ + m_portListeners.clear(); + + + KService::List kinetdModules = + KServiceType::offers("KInetDModule"); + for(KService::List::ConstIterator it = kinetdModules.begin(); + it != kinetdModules.end(); + it++) { + KService::Ptr s = *it; + PortListener *pl = new PortListener(s, m_config, m_srvreg); + if (pl->isValid()) + m_portListeners.append(pl); + else + delete pl; + } + + setExpirationTimer(); + setPortRetryTimer(true); + setReregistrationTimer(); +} + +void KInetD::expirationTimer() { + setExpirationTimer(); + setReregistrationTimer(); +} + +void KInetD::setExpirationTimer() { + QDateTime nextEx = getNextExpirationTime(); // disables expired portlistener! + if (!nextEx.isNull()) + m_expirationTimer.start(QDateTime::currentDateTime().secsTo(nextEx)*1000 + 30000, + false); + else + m_expirationTimer.stop(); +} + +void KInetD::portRetryTimer() { + setPortRetryTimer(true); + setReregistrationTimer(); +} + +void KInetD::setReregistrationTimer() { + QDateTime d; + PortListener *pl = m_portListeners.first(); + while (pl) { + QDateTime d2 = pl->serviceLifetimeEnd(); + if (!d2.isNull()) { + if (d2 < QDateTime::currentDateTime()) { + m_reregistrationTimer.start(0, true); + return; + } + else if (d.isNull() || (d2 < d)) + d = d2; + } + pl = m_portListeners.next(); + } + + if (!d.isNull()) { + int s = QDateTime::currentDateTime().secsTo(d); + if (s < 30) + s = 30; // max frequency 30s + m_reregistrationTimer.start(s*1000, true); + } + else + m_reregistrationTimer.stop(); +} + +void KInetD::reregistrationTimer() { + PortListener *pl = m_portListeners.first(); + while (pl) { + pl->refreshRegistration(); + pl = m_portListeners.next(); + } + setReregistrationTimer(); +} + +void KInetD::setPortRetryTimer(bool retry) { + int unmappedPorts = 0; + + PortListener *pl = m_portListeners.first(); + while (pl) { + if (pl->isEnabled() && (pl->port() < 0)) + if (retry) { + if (!pl->acquirePort()) + unmappedPorts++; + } + else if (pl->port() < 0) + unmappedPorts++; + pl = m_portListeners.next(); + } + + if (unmappedPorts > 0) + m_portRetryTimer.start(30000, false); + else + m_portRetryTimer.stop(); +} + +PortListener *KInetD::getListenerByName(QString name) +{ + PortListener *pl = m_portListeners.first(); + while (pl) { + if (pl->name() == name) + return pl; + pl = m_portListeners.next(); + } + return pl; +} + +// gets next expiration timer, SIDEEFFECT: disables expired portlisteners while doing this +QDateTime KInetD::getNextExpirationTime() +{ + PortListener *pl = m_portListeners.first(); + QDateTime d; + while (pl) { + QDateTime d2 = pl->expiration(); + if (!d2.isNull()) { + if (d2 < QDateTime::currentDateTime()) + pl->setEnabled(false); + else if (d.isNull() || (d2 < d)) + d = d2; + } + pl = m_portListeners.next(); + } + return d; +} + +QStringList KInetD::services() +{ + QStringList list; + PortListener *pl = m_portListeners.first(); + while (pl) { + list.append(pl->name()); + pl = m_portListeners.next(); + } + return list; +} + +bool KInetD::isEnabled(QString service) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return false; + + return pl->isEnabled(); +} + +int KInetD::port(QString service) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return -1; + + return pl->port(); +} + +bool KInetD::setPort(QString service, int port, int autoPortRange) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return false; + + bool s = pl->setPort(port, autoPortRange); + setPortRetryTimer(false); + setReregistrationTimer(); + return s; +} + +bool KInetD::isInstalled(QString service) +{ + PortListener *pl = getListenerByName(service); + return (pl != 0); +} + +void KInetD::setEnabled(QString service, bool enable) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return; + + pl->setEnabled(enable); + setExpirationTimer(); + setReregistrationTimer(); +} + +void KInetD::setEnabled(QString service, QDateTime expiration) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return; + + pl->setEnabled(expiration); + setExpirationTimer(); + setReregistrationTimer(); +} + +void KInetD::setServiceRegistrationEnabled(QString service, bool enable) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return; + + pl->setServiceRegistrationEnabled(enable); + setReregistrationTimer(); +} + +bool KInetD::isServiceRegistrationEnabled(QString service) +{ + PortListener *pl = getListenerByName(service); + if (!pl) + return false; + + return pl->isServiceRegistrationEnabled(); +} + +KInetD::~KInetD() { + m_portListeners.clear(); + delete m_config; + if (m_srvreg) + delete m_srvreg; +} + +extern "C" { + KDE_EXPORT KDEDModule *create_kinetd(QCString &name) + { + KGlobal::locale()->insertCatalogue("kinetd"); + return new KInetD(name); + } +} |