mirror of
https://github.com/RetroShare/RetroShare.git
synced 2024-12-26 16:09:35 -05:00
Added basic ZeroConf interface.
- registers oneself, browses and resolves services. - Lots still TODO: - parse TxtRecords. - Track peers. - feedback to libretroshare - etc, etc. Enabled ZeroConf in libretroshare.pro & rsinit.cc. Compiles and runs on OSX. Added RelayHandler to Dht, to enable external control of Relays. Marked pqiAssist Interface for changes... will be revamped with ZeroConf. git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-dhtmods@4727 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
2048bb5e47
commit
4d2175636e
@ -91,6 +91,8 @@ p3BitDht::p3BitDht(std::string id, pqiConnectCb *cb, p3NetMgr *nm,
|
||||
|
||||
mPeerSharer = NULL;
|
||||
|
||||
mRelayHandler = NULL;
|
||||
|
||||
std::string dhtVersion = "RS51"; // should come from elsewhere!
|
||||
mOwnRsId = id;
|
||||
|
||||
@ -162,6 +164,23 @@ void p3BitDht::setupPeerSharer(pqiNetAssistPeerShare *sharer)
|
||||
mPeerSharer = sharer;
|
||||
}
|
||||
|
||||
|
||||
/* Support for Outsourced Relay Handling */
|
||||
|
||||
void p3BitDht::installRelayHandler(p3BitDhtRelayHandler *handler)
|
||||
{
|
||||
/* The Handler is mutex protected, as its installation can occur when the dht is already running */
|
||||
RsStackMutex stack(dhtMtx); /********* LOCKED *********/
|
||||
|
||||
mRelayHandler = handler;
|
||||
}
|
||||
|
||||
UdpRelayReceiver *p3BitDht::getRelayReceiver()
|
||||
{
|
||||
return mRelay;
|
||||
}
|
||||
|
||||
|
||||
void p3BitDht::start()
|
||||
{
|
||||
#ifdef DEBUG_BITDHT
|
||||
|
@ -118,6 +118,21 @@ class PeerAction
|
||||
|
||||
|
||||
|
||||
/******
|
||||
* Adding the ability to install alternative Handler
|
||||
* for monitoring/controlling Relay Connections outside of p3bitdht.
|
||||
***/
|
||||
|
||||
class p3BitDhtRelayHandler
|
||||
{
|
||||
public:
|
||||
|
||||
int (*mInstallRelay)(const bdId *srcId, const bdId *destId, uint32_t mode, uint32_t &bandwidth);
|
||||
int (*mLogFailedConnection)(const bdId *srcId, const bdId *destId, uint32_t mode, uint32_t errcode);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class UdpRelayReceiver;
|
||||
class UdpStunner;
|
||||
@ -251,6 +266,14 @@ void UdpConnectionFailed_locked(DhtPeerDetails *dpd);
|
||||
void ReleaseProxyExclusiveMode_locked(DhtPeerDetails *dpd, bool addrChgLikely);
|
||||
|
||||
|
||||
/*** RELAY HANDLER CODE ***/
|
||||
void installRelayHandler(p3BitDhtRelayHandler *);
|
||||
UdpRelayReceiver *getRelayReceiver();
|
||||
|
||||
int RelayHandler_InstallRelayConnection(const bdId *srcId, const bdId *destId, uint32_t mode, uint32_t &bandwidth);
|
||||
int RelayHandler_LogFailedProxyAttempt(const bdId *srcId, const bdId *destId, uint32_t mode, uint32_t errcode);
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
************************** Internal Accounting (p3bitdht_peers.cc) ****************************
|
||||
************************************************************************************************/
|
||||
@ -264,6 +287,9 @@ void ReleaseProxyExclusiveMode_locked(DhtPeerDetails *dpd, bool addrChgLikely);
|
||||
//int addOther(const std::string pid);
|
||||
int removePeer(const std::string pid);
|
||||
|
||||
// Can be used externally too.
|
||||
int calculateNodeId(const std::string pid, bdNodeId *id);
|
||||
|
||||
private:
|
||||
|
||||
DhtPeerDetails *addInternalPeer_locked(const std::string pid, int type);
|
||||
@ -276,7 +302,6 @@ int lookupNodeId_locked(const std::string pid, bdNodeId *id);
|
||||
int lookupRsId_locked(const bdNodeId *id, std::string &pid);
|
||||
int storeTranslation_locked(const std::string pid);
|
||||
int removeTranslation_locked(const std::string pid);
|
||||
int calculateNodeId(const std::string pid, bdNodeId *id);
|
||||
|
||||
UdpBitDht *mUdpBitDht; /* has own mutex, is static except for creation/destruction */
|
||||
UdpStunner *mDhtStunner;
|
||||
@ -289,6 +314,9 @@ int calculateNodeId(const std::string pid, bdNodeId *id);
|
||||
|
||||
RsMutex dhtMtx;
|
||||
|
||||
|
||||
p3BitDhtRelayHandler *mRelayHandler;
|
||||
|
||||
std::string mOwnRsId;
|
||||
bdNodeId mOwnDhtId;
|
||||
|
||||
|
@ -516,6 +516,13 @@ int p3BitDht::ConnectCallback(const bdId *srcId, const bdId *proxyId, const bdId
|
||||
bdStdPrintId(std::cerr, destId);
|
||||
std::cerr << std::endl;
|
||||
#endif
|
||||
/* if there is an error code - then it is just to inform us of a failed attempt */
|
||||
if (errcode)
|
||||
{
|
||||
RelayHandler_LogFailedProxyAttempt(srcId, destId, mode, errcode);
|
||||
/* END MID FAILED ATTEMPT */
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t bandwidth = 0;
|
||||
|
||||
@ -1622,7 +1629,7 @@ int p3BitDht::checkProxyAllowed(const bdId *srcId, const bdId *destId, int mode,
|
||||
/* will install the Relay Here... so that we reserve the Relay Space for later.
|
||||
* decide on relay bandwidth limitation as well
|
||||
*/
|
||||
if (installRelayConnection(srcId, destId, bandwidth))
|
||||
if (RelayHandler_InstallRelayConnection(srcId, destId, mode, bandwidth))
|
||||
{
|
||||
#ifdef DEBUG_PEERNET
|
||||
std::cerr << "p3BitDht::checkProxyAllowed() Successfully added Relay, Connection OKAY";
|
||||
@ -2385,4 +2392,43 @@ void p3BitDht::ConnectionFeedback(std::string pid, int mode)
|
||||
}
|
||||
|
||||
|
||||
/***** Check for a RelayHandler... and call its functions preferentially */
|
||||
|
||||
int p3BitDht::RelayHandler_LogFailedProxyAttempt(const bdId *srcId, const bdId *destId, uint32_t mode, uint32_t errcode)
|
||||
{
|
||||
|
||||
{
|
||||
RsStackMutex stack(dhtMtx); /********* LOCKED *********/
|
||||
|
||||
if ((mRelayHandler) && (mRelayHandler->mLogFailedConnection))
|
||||
{
|
||||
return mRelayHandler->mLogFailedConnection(srcId, destId, mode, errcode);
|
||||
}
|
||||
}
|
||||
|
||||
/* NO standard handler */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int p3BitDht::RelayHandler_InstallRelayConnection(const bdId *srcId, const bdId *destId,
|
||||
uint32_t mode, uint32_t &bandwidth)
|
||||
{
|
||||
|
||||
{
|
||||
RsStackMutex stack(dhtMtx); /********* LOCKED *********/
|
||||
|
||||
if ((mRelayHandler) && (mRelayHandler->mInstallRelay))
|
||||
{
|
||||
return mRelayHandler->mInstallRelay(srcId, destId, mode, bandwidth);
|
||||
}
|
||||
}
|
||||
|
||||
/* standard handler */
|
||||
return installRelayConnection(srcId, destId, bandwidth);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -108,6 +108,7 @@ SOURCES += tcponudp/udppeer.cc \
|
||||
}
|
||||
|
||||
|
||||
|
||||
test_bitdht {
|
||||
# DISABLE TCP CONNECTIONS...
|
||||
DEFINES *= P3CONNMGR_NO_TCP_CONNECTIONS
|
||||
@ -121,7 +122,6 @@ test_bitdht {
|
||||
|
||||
|
||||
|
||||
|
||||
use_blogs {
|
||||
|
||||
HEADERS += services/p3blogs.h
|
||||
@ -307,6 +307,8 @@ mac {
|
||||
HEADERS += upnp/upnputil.h
|
||||
SOURCES += upnp/upnputil.c
|
||||
|
||||
CONFIG += zeroconf
|
||||
|
||||
# Beautiful Hack to fix 64bit file access.
|
||||
QMAKE_CXXFLAGS *= -Dfseeko64=fseeko -Dftello64=ftello -Dfopen64=fopen -Dvstatfs64=vstatfs
|
||||
|
||||
@ -638,3 +640,14 @@ minimal {
|
||||
services/p3gamelauncher.cc \
|
||||
services/p3photoservice.cc
|
||||
}
|
||||
|
||||
zeroconf {
|
||||
|
||||
HEADERS += zeroconf/p3zeroconf.h \
|
||||
|
||||
SOURCES += zeroconf/p3zeroconf.cc \
|
||||
|
||||
DEFINES *= RS_ENABLE_ZEROCONF
|
||||
|
||||
}
|
||||
|
||||
|
@ -146,26 +146,17 @@ virtual bool dropPeer(std::string id) = 0;
|
||||
virtual int addBadPeer(const struct sockaddr_in &addr, uint32_t reason, uint32_t flags, uint32_t age) = 0;
|
||||
virtual int addKnownPeer(const std::string &pid, const struct sockaddr_in &addr, uint32_t flags) = 0;
|
||||
|
||||
//virtual int addFriend(const std::string pid) = 0;
|
||||
//virtual int addFriendOfFriend(const std::string pid) = 0;
|
||||
//virtual int addOther(const std::string pid) = 0;
|
||||
|
||||
|
||||
virtual void ConnectionFeedback(std::string pid, int mode) = 0;
|
||||
|
||||
/* extract current peer status */
|
||||
virtual bool getPeerStatus(std::string id,
|
||||
struct sockaddr_in &laddr, struct sockaddr_in &raddr,
|
||||
uint32_t &type, uint32_t &mode) = 0;
|
||||
uint32_t &type, uint32_t &mode) = 0; // DEPRECIATE.
|
||||
|
||||
virtual bool setAttachMode(bool on) = 0;
|
||||
|
||||
|
||||
//virtual bool getExternalInterface(struct sockaddr_in &raddr,
|
||||
// uint32_t &mode) = 0;
|
||||
virtual bool setAttachMode(bool on) = 0; // FIXUP.
|
||||
|
||||
/***** Stats for Network / DHT *****/
|
||||
virtual bool getNetworkStats(uint32_t &netsize, uint32_t &localnetsize) = 0;
|
||||
virtual bool getNetworkStats(uint32_t &netsize, uint32_t &localnetsize) = 0; // DEPRECIATE.
|
||||
|
||||
protected:
|
||||
std::string mPeerId;
|
||||
|
@ -1710,8 +1710,12 @@ RsTurtle *rsTurtle = NULL ;
|
||||
#include "util/rsdir.h"
|
||||
#include "util/rsrandom.h"
|
||||
|
||||
#include "upnp/upnphandler.h"
|
||||
//#include "dht/opendhtmgr.h"
|
||||
#ifdef RS_ENABLE_ZEROCONF
|
||||
#include "zeroconf/p3zeroconf.h"
|
||||
//#include "zeroconf/p3zcnatassist.h"
|
||||
#else
|
||||
#include "upnp/upnphandler.h"
|
||||
#endif
|
||||
|
||||
#include "services/p3disc.h"
|
||||
#include "services/p3msgservice.h"
|
||||
@ -1874,7 +1878,6 @@ int RsServer::StartupRetroShare()
|
||||
// for (std::list<std::string>::iterator sslIdsIt = sslIds.begin(); sslIdsIt != sslIds.end(); sslIdsIt++) {
|
||||
// mConnMgr->addFriend(*sslIdsIt);
|
||||
// }
|
||||
pqiNetAssistFirewall *mUpnpMgr = new upnphandler();
|
||||
//p3DhtMgr *mDhtMgr = new OpenDHTMgr(ownId, mConnMgr, RsInitConfig::configDir);
|
||||
/**************************** BITDHT ***********************************/
|
||||
|
||||
@ -2152,7 +2155,21 @@ int RsServer::StartupRetroShare()
|
||||
mNetMgr->addNetListener(mProxyStack);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef RS_ENABLE_ZEROCONF
|
||||
p3ZeroConf *mZeroConf = new p3ZeroConf(ownId, ownId, mLinkMgr, mNetMgr);
|
||||
mNetMgr->addNetAssistConnect(2, mZeroConf);
|
||||
mNetMgr->addNetListener(mZeroConf);
|
||||
|
||||
// Apple's UPnP & NAT-PMP assistance.
|
||||
//p3zcNatAssist *mZcNatAssist = new p3zcNatAssist();
|
||||
//mNetMgr->addNetAssistFirewall(2, mZcNatAssist);
|
||||
#else
|
||||
|
||||
// Original UPnP Interface.
|
||||
pqiNetAssistFirewall *mUpnpMgr = new upnphandler();
|
||||
mNetMgr->addNetAssistFirewall(1, mUpnpMgr);
|
||||
#endif
|
||||
|
||||
/**************************************************************************/
|
||||
/* need to Monitor too! */
|
||||
|
33
libretroshare/src/zeroconf/NOTES.txt
Normal file
33
libretroshare/src/zeroconf/NOTES.txt
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
The Code in this directory refers to APPLEs ZeroConf Library.
|
||||
|
||||
We have two classes: p3ZeroConf & p3ZeroConfNat
|
||||
The first provides ZeroConf(Bonjour) discovery services.
|
||||
The second provides UPnP & NAT-PMP Nat Port Forwarding.
|
||||
|
||||
OSX
|
||||
----------------
|
||||
Both should compile with no problems under OSX.
|
||||
Both will be compiled by default.
|
||||
|
||||
p3ZeroConf is enabled by default.
|
||||
p3ZeroConfNAT will become the default PortForwarding Service (once tested).
|
||||
|
||||
Windows
|
||||
----------------
|
||||
Under Windows, you require Apple's header files & library to compile.
|
||||
If you are missing the libraries, you can disable their compilation in libretroshare.
|
||||
|
||||
Furthermore - it'll only work if the Apple DNS Service is running on the Windows PC.
|
||||
p3ZeroConf will be enabled by default (if included in the compilation).
|
||||
p3ZeroConfNAT will not be enabled by default.
|
||||
|
||||
Linux
|
||||
----------------
|
||||
Neither of these classes will compile or be enabled under Linux.
|
||||
There is another library: Avahi - which provides ZeroConf services.
|
||||
It is likely to have a totally different interface -
|
||||
so it will have to be coded up separately.
|
||||
|
||||
|
||||
|
1091
libretroshare/src/zeroconf/p3zeroconf.cc
Normal file
1091
libretroshare/src/zeroconf/p3zeroconf.cc
Normal file
File diff suppressed because it is too large
Load Diff
231
libretroshare/src/zeroconf/p3zeroconf.h
Normal file
231
libretroshare/src/zeroconf/p3zeroconf.h
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* libretroshare/src/zeroconf: p3zeroconf.h
|
||||
*
|
||||
* ZeroConf interface for RetroShare.
|
||||
*
|
||||
* Copyright 2011-2011 by Robert Fernie.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License Version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
* USA.
|
||||
*
|
||||
* Please report all bugs and problems to "retroshare@lunamutt.com".
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MRK_P3_ZEROCONF_H
|
||||
#define MRK_P3_ZEROCONF_H
|
||||
|
||||
#include "pqi/pqiassist.h"
|
||||
#include "retroshare/rsdht.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "pqi/pqinetwork.h"
|
||||
#include "pqi/pqimonitor.h"
|
||||
#include "util/rsthreads.h"
|
||||
|
||||
#include <dns_sd.h>
|
||||
|
||||
|
||||
class zcBrowseResult
|
||||
{
|
||||
public:
|
||||
DNSServiceFlags flags;
|
||||
uint32_t interfaceIndex;
|
||||
std::string serviceName;
|
||||
std::string regtype;
|
||||
std::string replyDomain;
|
||||
};
|
||||
|
||||
|
||||
class zcResolveResult
|
||||
{
|
||||
public:
|
||||
zcResolveResult() { return; } // :txtRecord(NULL) { return; }
|
||||
~zcResolveResult() { return; } //{ if (txtRecord) { free(txtRecord); txtRecord = NULL; } }
|
||||
|
||||
DNSServiceFlags flags;
|
||||
uint32_t interfaceIndex;
|
||||
std::string fullname;
|
||||
std::string hosttarget;
|
||||
uint16_t port;
|
||||
uint16_t txtLen;
|
||||
//unsigned char *txtRecord;
|
||||
};
|
||||
|
||||
|
||||
class zcQueryResult
|
||||
{
|
||||
public:
|
||||
zcQueryResult() { return; } //:rdata(NULL) { return; }
|
||||
~zcQueryResult() {return; } //{ if (rdata) { free(rdata); rdata = NULL; } }
|
||||
|
||||
DNSServiceFlags flags;
|
||||
uint32_t interfaceIndex;
|
||||
std::string fullname;
|
||||
uint16_t rrtype;
|
||||
uint16_t rrclass;
|
||||
uint16_t rdlen;
|
||||
//void *rdata;
|
||||
uint32_t ttl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class zcPeerDetails
|
||||
{
|
||||
public:
|
||||
|
||||
};
|
||||
|
||||
class p3NetMgr;
|
||||
|
||||
class p3ZeroConf: public pqiNetAssistConnect, public pqiNetListener
|
||||
{
|
||||
public:
|
||||
p3ZeroConf(std::string gpgid, std::string sslid, pqiConnectCb *cb, p3NetMgr *nm);
|
||||
virtual ~p3ZeroConf();
|
||||
|
||||
/*** OVERLOADED from pqiNetListener ***/
|
||||
|
||||
virtual bool resetListener(struct sockaddr_in &local);
|
||||
|
||||
void start(); /* starts up the bitdht thread */
|
||||
|
||||
/* pqiNetAssist - external interface functions */
|
||||
virtual int tick();
|
||||
virtual void enable(bool on);
|
||||
virtual void shutdown(); /* blocking call */
|
||||
virtual void restart();
|
||||
|
||||
virtual bool getEnabled();
|
||||
virtual bool getActive();
|
||||
virtual bool getNetworkStats(uint32_t &netsize, uint32_t &localnetsize);
|
||||
|
||||
/* pqiNetAssistConnect - external interface functions */
|
||||
|
||||
/* add / remove peers */
|
||||
virtual bool findPeer(std::string id);
|
||||
virtual bool dropPeer(std::string id);
|
||||
|
||||
virtual int addBadPeer(const struct sockaddr_in &addr, uint32_t reason, uint32_t flags, uint32_t age);
|
||||
virtual int addKnownPeer(const std::string &pid, const struct sockaddr_in &addr, uint32_t flags);
|
||||
|
||||
/* feedback on success failure of Connections */
|
||||
virtual void ConnectionFeedback(std::string pid, int state);
|
||||
|
||||
/* extract current peer status */
|
||||
virtual bool getPeerStatus(std::string id,
|
||||
struct sockaddr_in &laddr, struct sockaddr_in &raddr,
|
||||
uint32_t &type, uint32_t &mode);
|
||||
|
||||
virtual bool setAttachMode(bool on);
|
||||
|
||||
/* pqiNetAssistConnect - external interface functions */
|
||||
|
||||
|
||||
public:
|
||||
|
||||
// Callbacks must be public -> so they can be accessed.
|
||||
void callbackRegister(DNSServiceRef sdRef, DNSServiceFlags flags,
|
||||
DNSServiceErrorType errorCode,
|
||||
const char *name, const char *regtype, const char *domain);
|
||||
|
||||
void callbackBrowse(DNSServiceRef sdRef, DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
|
||||
const char *serviceName, const char *regtype, const char *replyDomain);
|
||||
|
||||
void callbackResolve( DNSServiceRef sdRef, DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
|
||||
const char *fullname, const char *hosttarget, uint16_t port,
|
||||
uint16_t txtLen, const unsigned char *txtRecord);
|
||||
|
||||
void callbackQueryIp( DNSServiceRef sdRef, DNSServiceFlags flags,
|
||||
uint32_t interfaceIndex, DNSServiceErrorType errorCode,
|
||||
const char *fullname, uint16_t rrtype, uint16_t rrclass,
|
||||
uint16_t rdlen, const void *rdata, uint32_t ttl);
|
||||
|
||||
private:
|
||||
|
||||
void createTxtRecord();
|
||||
|
||||
/* monitoring fns */
|
||||
void checkServiceFDs();
|
||||
void locked_checkFD(DNSServiceRef ref);
|
||||
int checkResolveAction();
|
||||
int checkQueryAction();
|
||||
int checkQueryResults();
|
||||
|
||||
|
||||
/**** THESE ARE MAINLY SEMI LOCKED.... not quite sure when the callback will happen! ***/
|
||||
|
||||
int locked_startRegister();
|
||||
void locked_stopRegister();
|
||||
|
||||
int locked_startBrowse();
|
||||
int locked_stopBrowse();
|
||||
|
||||
|
||||
void locked_startResolve(uint32_t idx, std::string name,
|
||||
std::string regtype, std::string domain);
|
||||
int locked_stopResolve();
|
||||
|
||||
void locked_startQueryIp(uint32_t idx, std::string fullname);
|
||||
int locked_stopQueryIp();
|
||||
|
||||
std::string displayDNSServiceError(DNSServiceErrorType errcode);
|
||||
|
||||
|
||||
bool mRegistered;
|
||||
bool mTextOkay;
|
||||
bool mPortOkay;
|
||||
|
||||
uint16_t mLocalPort;
|
||||
std::string mTextRecord;
|
||||
|
||||
DNSServiceRef mRegisterRef;
|
||||
DNSServiceRef mBrowseRef;
|
||||
DNSServiceRef mResolveRef;
|
||||
DNSServiceRef mQueryRef;
|
||||
|
||||
uint32_t mRegisterStatus;
|
||||
uint32_t mBrowseStatus;
|
||||
uint32_t mResolveStatus;
|
||||
uint32_t mQueryStatus;
|
||||
|
||||
|
||||
std::list<zcBrowseResult> mBrowseResults;
|
||||
std::list<zcResolveResult> mResolveResults;
|
||||
std::list<zcQueryResult> mQueryResults;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
p3NetMgr *mNetMgr;
|
||||
|
||||
RsMutex mZcMtx;
|
||||
|
||||
std::string mOwnGpgId;
|
||||
std::string mOwnSslId;
|
||||
|
||||
time_t mMinuteTS;
|
||||
|
||||
/* translation maps */
|
||||
|
||||
};
|
||||
|
||||
#endif /* MRK_P3_ZEROCONF_H */
|
||||
|
Loading…
Reference in New Issue
Block a user