linux upnp端口映射测试程序

编译环境QT4.8-ARM-V500

用华为荣耀路由器测试成功,代码源自github,有修改

/*"QUpnpPortMapper.h"*/
#ifndef QUPNPPORTMAPPER_H
#define QUPNPPORTMAPPER_H

#include <QObject>
#include <QHostAddress>
#include <QUdpSocket>
#include <QTimer>
#include <QNetworkAccessManager>
#include <QDomElement>
#include <QUrl>

class QUpnpPortMapper : public QObject
{
	Q_OBJECT

signals:
	void discoverFinished(bool ok);
	void queryExternalAddressFinished(QHostAddress address, bool ok, QString errorString);
	void addPortMappingFinished(bool ok, QString errorString);
	void deletePortMappingFinished(bool ok, QString errorString);

public:
	QUpnpPortMapper(QObject *parent = 0);
	~QUpnpPortMapper();

	QHostAddress localAddress();
	bool isDiscoverOk();

public slots:
	bool open(const QHostAddress & localAddress);
	bool open(const QNetworkInterface & networkInterface);
	void close();
	bool discover(int timeout = 5000, bool wait = false);
	QHostAddress queryExternalAddress(bool wait = false);
	bool addPortMapping(QAbstractSocket::SocketType type, QHostAddress internalAddress, quint16 internalPort, quint16 externalPort, QString description = QString(), bool wait = false);
	bool deletePortMapping(QAbstractSocket::SocketType type, quint16 externalPort, bool wait = false);

private slots:
	void onDiscoverTimeout();
	void onUdpSocketReadyRead();
        void my_onUdpSocketReadyRead();
	void onReplyFinished(QNetworkReply * reply);
	void onReplyDiscover(QNetworkReply * reply);
	void onReplyQueryExternalAddress(QNetworkReply * reply);
	void onReplyAddPortMapping(QNetworkReply * reply);
	void onReplyDeletePortMapping(QNetworkReply * reply);

private:
	QString recursiveFindControlUrl(const QDomElement & parent);
	QString getXmlTabContent(QString content, QString tab, Qt::CaseSensitivity cs);

private:
	QHostAddress m_localAddress;
	QUdpSocket * m_udpSocket;
	QTimer * m_timer;
	QNetworkAccessManager * m_networkAccessManager;
	QUrl m_controlUrl;
	bool m_discoverOk;
	QHostAddress m_lastExternalAddress;
	bool m_lastAddPortMappingOk;
	bool m_lastDeletePortMappingOk;
};

#endif // QUPNPPORTMAPPER_H

/*"QUpnpPortMapper.cpp"*/
#include "QUpnpPortMapper.h"
#include <QtDebug>
#include <QNetworkInterface>
#include <QDomDocument>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QRegExp>
#include <QStringList>
#include <stdio.h>
/*WANIPConnection*/
static const unsigned char raw_discover[] = {
        0x4d,0x2d,0x53,0x45,0x41,0x52,0x43,0x48,0x20,0x2a,0x20,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0xd,0xa/*M-search*/,
        0x48,0x4f,0x53,0x54,0x3a,0x20,0x32,0x33,0x39,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x30,0x3a,0x31,0x39,0x30,0x30,0xd,0xa/*HOST*/,
        0x53,0x54,0x3a,0x20,0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0xd,0xa/*ST*/,
        0x4d,0x41,0x4e,0x3a,0x20,0x22,0x73,0x73,0x64,0x70,0x3a,0x64,0x69,0x73,0x63,0x6f,0x76,0x65,0x72,0x22,0xd,0xa,/*Man*/
        0x4d,0x58,0x3a,0x20,0x33,0xd,0xa,/*MX*/
        0xd,0xa,/*end*/
};
/*WANPPPConnection*/
static const unsigned char raw_ppp_discover[] = {
        0x4d,0x2d,0x53,0x45,0x41,0x52,0x43,0x48,0x20,0x2a,0x20,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0xd,0xa/*M-search*/,
        0x48,0x4f,0x53,0x54,0x3a,0x20,0x32,0x33,0x39,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x30,0x3a,0x31,0x39,0x30,0x30,0xd,0xa/*HOST*/,
        0x53,0x54,0x3a,0x20,0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x50,0x50,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0xd,0xa,/*WAN PPPConnection*/
        0x4d,0x41,0x4e,0x3a,0x20,0x22,0x73,0x73,0x64,0x70,0x3a,0x64,0x69,0x73,0x63,0x6f,0x76,0x65,0x72,0x22,0xd,0xa,/*Man*/
        0x4d,0x58,0x3a,0x20,0x33,0xd,0xa,/*MX*/
        0xd,0xa,/*end*/
};
/*IGD*/
static const unsigned char raw_igd_discover[] = {
        0x4d,0x2d,0x53,0x45,0x41,0x52,0x43,0x48,0x20,0x2a,0x20,0x48,0x54,0x54,0x50,0x2f,0x31,0x2e,0x31,0xd,0xa/*M-search*/,
        0x48,0x4f,0x53,0x54,0x3a,0x20,0x32,0x33,0x39,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x30,0x3a,0x31,0x39,0x30,0x30,0xd,0xa/*HOST*/,
        0x53,0x54,0x3a,0x20,0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x64,0x65,0x76,0x69,0x63,0x65,0x3a,0x49,0x6e,0x74,0x65,0x72,0x6e,0x65,0x74,0x47,0x61,0x74,0x65,0x77,0x61,0x79,0x44,0x65,0x76,0x69,0x63,0x65,0x3a,0x31,0xd,0xa,/*InternetGatewayDevice*/
        0x4d,0x41,0x4e,0x3a,0x20,0x22,0x73,0x73,0x64,0x70,0x3a,0x64,0x69,0x73,0x63,0x6f,0x76,0x65,0x72,0x22,0xd,0xa,/*Man*/
        0x4d,0x58,0x3a,0x20,0x33,0xd,0xa,/*MX*/
        0xd,0xa,/*end*/
};
static const unsigned char raw_queryExternalAddress[] = {
	0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x20,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x53,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,
	0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x2f,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x53,0x4f,0x41,0x50,0x2d,
	0x45,0x4e,0x56,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x2f,0x22,0x3e,
	0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x6d,0x3a,0x47,0x65,0x74,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x49,0x50,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x6d,0x3d,0x22,
	0x75,0x72,0x6e,0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0x22,0x2f,
	0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,
	0x6c,0x6f,0x70,0x65,0x3e,
};

static const unsigned char raw_addPortMapping[] = {
	0x3c,
	0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x20,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x53,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,
	0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x2f,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x53,0x4f,0x41,0x50,0x2d,
	0x45,0x4e,0x56,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x2f,0x22,0x3e,
	0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x6d,0x3a,0x41,0x64,0x64,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x6d,0x3d,0x22,0x75,0x72,0x6e,0x3a,0x73,0x63,
	0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0x22,0x3e,0x3c,0x4e,0x65,0x77,0x45,0x78,
	0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x28,0x25,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x3c,0x4e,0x65,
	0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x28,0x25,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,
	0x3c,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x28,0x25,0x54,0x79,0x70,0x65,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x3c,0x4e,0x65,0x77,0x45,0x6e,0x61,0x62,0x6c,0x65,0x64,0x3e,0x31,
	0x3c,0x2f,0x4e,0x65,0x77,0x45,0x6e,0x61,0x62,0x6c,0x65,0x64,0x3e,0x3c,0x4e,0x65,0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x43,0x6c,0x69,0x65,0x6e,0x74,0x3e,0x28,0x25,0x4c,0x6f,0x63,0x61,0x6c,0x41,0x64,0x64,0x72,0x65,0x73,0x73,0x25,0x29,
	0x3c,0x2f,0x4e,0x65,0x77,0x49,0x6e,0x74,0x65,0x72,0x6e,0x61,0x6c,0x43,0x6c,0x69,0x65,0x6e,0x74,0x3e,0x3c,0x4e,0x65,0x77,0x4c,0x65,0x61,0x73,0x65,0x44,0x75,0x72,0x61,0x74,0x69,0x6f,0x6e,0x3e,0x30,0x3c,0x2f,0x4e,0x65,0x77,0x4c,0x65,0x61,0x73,
	0x65,0x44,0x75,0x72,0x61,0x74,0x69,0x6f,0x6e,0x3e,0x3c,0x4e,0x65,0x77,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x3e,0x28,0x25,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,
	0x6f,0x6e,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x44,0x65,0x73,0x63,0x72,0x69,0x70,0x74,0x69,0x6f,0x6e,0x3e,0x3c,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,0x74,0x3e,0x3c,
	0x2f,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,0x74,0x3e,0x3c,0x2f,0x6d,0x3a,0x41,0x64,0x64,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,
	0x79,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x3e,
};

static const unsigned char raw_deletePortMapping[] = {
	0x3c,
	0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x20,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x53,0x74,0x79,0x6c,0x65,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,
	0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x63,0x6f,0x64,0x69,0x6e,0x67,0x2f,0x22,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x53,0x4f,0x41,0x50,0x2d,
	0x45,0x4e,0x56,0x3d,0x22,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2e,0x78,0x6d,0x6c,0x73,0x6f,0x61,0x70,0x2e,0x6f,0x72,0x67,0x2f,0x73,0x6f,0x61,0x70,0x2f,0x65,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x2f,0x22,0x3e,
	0x3c,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x6d,0x3a,0x44,0x65,0x6c,0x65,0x74,0x65,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x20,0x78,0x6d,0x6c,0x6e,0x73,0x3a,0x6d,0x3d,0x22,0x75,0x72,0x6e,
	0x3a,0x73,0x63,0x68,0x65,0x6d,0x61,0x73,0x2d,0x75,0x70,0x6e,0x70,0x2d,0x6f,0x72,0x67,0x3a,0x73,0x65,0x72,0x76,0x69,0x63,0x65,0x3a,0x57,0x41,0x4e,0x49,0x50,0x43,0x6f,0x6e,0x6e,0x65,0x63,0x74,0x69,0x6f,0x6e,0x3a,0x31,0x22,0x3e,0x3c,0x4e,0x65,
	0x77,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,0x28,0x25,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x45,0x78,0x74,0x65,0x72,0x6e,0x61,0x6c,0x50,0x6f,0x72,0x74,0x3e,
	0x3c,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x28,0x25,0x54,0x79,0x70,0x65,0x25,0x29,0x3c,0x2f,0x4e,0x65,0x77,0x50,0x72,0x6f,0x74,0x6f,0x63,0x6f,0x6c,0x3e,0x3c,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,
	0x74,0x3e,0x3c,0x2f,0x4e,0x65,0x77,0x52,0x65,0x6d,0x6f,0x74,0x65,0x48,0x6f,0x73,0x74,0x3e,0x3c,0x2f,0x6d,0x3a,0x44,0x65,0x6c,0x65,0x74,0x65,0x50,0x6f,0x72,0x74,0x4d,0x61,0x70,0x70,0x69,0x6e,0x67,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,
	0x4e,0x56,0x3a,0x42,0x6f,0x64,0x79,0x3e,0x3c,0x2f,0x53,0x4f,0x41,0x50,0x2d,0x45,0x4e,0x56,0x3a,0x45,0x6e,0x76,0x65,0x6c,0x6f,0x70,0x65,0x3e,
};


#define DECLARE_DATA(name)\
	static const QByteArray data_##name = QByteArray::fromRawData((const char*)raw_##name, sizeof(raw_##name));

DECLARE_DATA(discover)
DECLARE_DATA(ppp_discover)
DECLARE_DATA(igd_discover)
DECLARE_DATA(queryExternalAddress)
DECLARE_DATA(addPortMapping)
DECLARE_DATA(deletePortMapping)

QUpnpPortMapper::QUpnpPortMapper(QObject *parent)
	: QObject(parent)
{
	m_udpSocket = NULL;
	m_timer = NULL;
	m_networkAccessManager = NULL;
	m_discoverOk = false;
	m_lastAddPortMappingOk = false;
	m_lastDeletePortMappingOk = false;
}

QUpnpPortMapper::~QUpnpPortMapper()
{
	close();
}

QHostAddress QUpnpPortMapper::localAddress()
{
	return m_localAddress;
}

bool QUpnpPortMapper::isDiscoverOk()
{
	return m_discoverOk;
}

bool QUpnpPortMapper::open(const QHostAddress & localAddress)
{
	if (!m_localAddress.isNull())
	{
		qWarning() << QString("QUpnpPortMapper has already open");
		return false;
	}
	if (localAddress.isNull())
		return false;
	if (localAddress.protocol() == QAbstractSocket::IPv6Protocol)
	{
		qWarning() << QString("QUpnpPortMapper does not support IPv6");
		return false;
	}

	m_localAddress = localAddress;
	m_controlUrl = QUrl();
	m_networkAccessManager = new QNetworkAccessManager(this);
	m_discoverOk = false;
	m_lastExternalAddress = QHostAddress();
	m_lastAddPortMappingOk = false;
	m_lastDeletePortMappingOk = false;

	connect(m_networkAccessManager, SIGNAL(finished(QNetworkReply*)),
		this, SLOT(onReplyFinished(QNetworkReply*)));
	return true;
}

bool QUpnpPortMapper::open(const QNetworkInterface & networkInterface)
{
	foreach(QNetworkAddressEntry entry, networkInterface.addressEntries())
		if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol)
			return open(entry.ip());
	return false;
}

void QUpnpPortMapper::close()
{
	if (m_localAddress.isNull())
		return;

	if (m_udpSocket)
	{
		delete m_udpSocket;
		m_udpSocket = NULL;
	}

	if (m_timer)
	{
		delete m_timer;
		m_timer = NULL;
	}

	if (m_networkAccessManager)
	{
		delete m_networkAccessManager;
		m_networkAccessManager = NULL;
	}
	
	m_localAddress = QHostAddress();
	m_controlUrl = QString();
	m_lastExternalAddress = QHostAddress();
	m_lastAddPortMappingOk = false;
	m_lastDeletePortMappingOk = false;
}

bool QUpnpPortMapper::discover(int timeout, bool wait)
{
	if (m_discoverOk)
		return false;
	if (!m_udpSocket)
	{
		m_udpSocket = new QUdpSocket(this);
		m_udpSocket->bind(m_localAddress, 0);
                connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(my_onUdpSocketReadyRead()));
	}
	if (!m_timer)
	{
		m_timer = new QTimer(this);
		connect(m_timer, SIGNAL(timeout()), this, SLOT(onDiscoverTimeout()));
	}
        m_udpSocket->writeDatagram(data_ppp_discover, QHostAddress("239.255.255.250"), 1900);
	m_timer->setSingleShot(true);
	m_timer->start(timeout);

	if (wait)
	{
		QEventLoop eventLoop;
		connect(this, SIGNAL(discoverFinished(bool)), &eventLoop, SLOT(quit()));
		eventLoop.exec();
		return m_discoverOk;
	}

	return true;
}

QHostAddress QUpnpPortMapper::queryExternalAddress(bool wait)
{
	if (!m_discoverOk)
		return QHostAddress();
	QNetworkRequest request(m_controlUrl);
	request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
        request.setRawHeader("SOAPAction", "urn:schemas-upnp-org:service:WANPPPConnection:1#GetExternalIPAddress");
	QNetworkReply * reply = m_networkAccessManager->post(request, data_queryExternalAddress);
	reply->setProperty("reply-type", "queryExternalAddress");

	if (wait)
	{
		QEventLoop eventLoop;
		connect(this, SIGNAL(queryExternalAddressFinished(QHostAddress,bool,QString)), &eventLoop, SLOT(quit()));
		eventLoop.exec();
		return m_lastExternalAddress;
	}
	return QHostAddress();
}

bool QUpnpPortMapper::addPortMapping(QAbstractSocket::SocketType type, QHostAddress internalAddress, quint16 internalPort, quint16 externalPort, QString description, bool wait)
{
	if (!m_discoverOk)
		return false;
	QByteArray typeText;
	if (type == QAbstractSocket::TcpSocket)
		typeText = "TCP";
	else if (type == QAbstractSocket::UdpSocket)
		typeText = "UDP";
	else
		return false;
	QByteArray content = data_addPortMapping;
	content = content.replace("(%ExternalPort%)", QByteArray::number(externalPort));
	content = content.replace("(%InternalPort%)", QByteArray::number(internalPort));
	content = content.replace("(%Type%)", typeText);
	content = content.replace("(%LocalAddress%)", internalAddress.toString().toUtf8());
	content = content.replace("(%Description%)", description.toUtf8());

	QNetworkRequest request(m_controlUrl);
	request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
        request.setRawHeader("SOAPAction", "urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping");
	QNetworkReply * reply = m_networkAccessManager->post(request, content);
	reply->setProperty("reply-type", "addPortMapping");

	if (wait)
	{
		QEventLoop eventLoop;
		connect(this, SIGNAL(addPortMappingFinished(bool, QString)), &eventLoop, SLOT(quit()));
		eventLoop.exec();
		return m_lastAddPortMappingOk;
	}
	return true;
}

bool QUpnpPortMapper::deletePortMapping(QAbstractSocket::SocketType type, quint16 externalPort, bool wait)
{
	if (!m_discoverOk)
		return false;
	QByteArray typeText;
	if (type == QAbstractSocket::TcpSocket)
		typeText = "TCP";
	else if (type == QAbstractSocket::UdpSocket)
		typeText = "UDP";
	else
		return false;
	QByteArray content = data_deletePortMapping;
	content = content.replace("(%ExternalPort%)", QByteArray::number(externalPort));
	content = content.replace("(%Type%)", typeText);

	QNetworkRequest request(m_controlUrl);
	request.setHeader(QNetworkRequest::ContentTypeHeader, "text/xml");
        request.setRawHeader("SOAPAction", "urn:schemas-upnp-org:service:WANPPPConnection:1#DeletePortMapping");
	QNetworkReply * reply = m_networkAccessManager->post(request, content);
	reply->setProperty("reply-type", "deletePortMapping");

	if (wait)
	{
		QEventLoop eventLoop;
		connect(this, SIGNAL(deletePortMappingFinished(bool, QString)), &eventLoop, SLOT(quit()));
		eventLoop.exec();
		return m_lastDeletePortMappingOk;
	}
	return true;
}

void QUpnpPortMapper::onDiscoverTimeout()
{
	if (m_timer)
	{
		m_timer->stop();
		m_timer->deleteLater();
		m_timer = NULL;
	}

	if (m_udpSocket)
	{
		delete m_udpSocket;
		m_udpSocket = NULL;
	}
	
	emit discoverFinished(false);
}

void QUpnpPortMapper::onUdpSocketReadyRead()
{
	if (!m_udpSocket)
		return;

	while (m_udpSocket->hasPendingDatagrams())
	{
		char buffer[3000];
		QHostAddress hostAddress;
		quint16 port = 0;
		const int bufferSize = m_udpSocket->readDatagram(buffer, sizeof(buffer), &hostAddress, &port);

		const QString package = QString::fromUtf8(QByteArray::fromRawData(buffer, bufferSize));
		const QString tab = "Location:";
                const int tabPos = package.indexOf(tab, 0, Qt::CaseInsensitive);//匹配
		if(tabPos < 0)
			continue;

                int lineEndPos = package.indexOf('\n', tabPos);//匹配
		if (lineEndPos < 0)
			lineEndPos = package.size();

                QString location = package.mid(tabPos + tab.length(), lineEndPos - tabPos - tab.length());//截取中间的字符
                location = location.trimmed();//过滤两端空白字符

		QNetworkReply * reply = m_networkAccessManager->get(QNetworkRequest(QUrl(location)));
		reply->setProperty("reply-type", "discover");

		m_udpSocket->deleteLater();
		m_udpSocket = NULL;

		break;
	}
}
void QUpnpPortMapper::my_onUdpSocketReadyRead()
{
        if (!m_udpSocket)
                return;

        while (m_udpSocket->hasPendingDatagrams())
        {
                char buffer[3000];
                QHostAddress hostAddress;
                quint16 port = 0;
                const int bufferSize = m_udpSocket->readDatagram(buffer, sizeof(buffer), &hostAddress, &port);

                const QString package = QString::fromUtf8(QByteArray::fromRawData(buffer, bufferSize));
                printf("recv buf:%s\n",buffer);
                const QString tab = "Location:";
                const int tabPos = package.indexOf(tab, 0, Qt::CaseInsensitive);//匹配url开头
                if(tabPos < 0)
                        continue;
                const QString end_tab="\r\n";
                int lineEndPos = package.indexOf(end_tab, tabPos,Qt::CaseInsensitive);//匹配url结尾
                if (lineEndPos < 0)
                        lineEndPos = package.size();

                QString location = package.mid(tabPos + tab.length(), lineEndPos - tabPos - tab.length());//截取中间的字符
                location = location.trimmed();//过滤两端空白字符
                printf("control_url:begin at %d end at %d!\n",tabPos+tab.length(),lineEndPos);
                printf("url:%s\n",location.toStdString().data());
                QNetworkReply * reply = m_networkAccessManager->get(QNetworkRequest(QUrl(location)));
                reply->setProperty("reply-type", "discover");

                m_udpSocket->deleteLater();
                m_udpSocket = NULL;

                break;
        }
}
void QUpnpPortMapper::onReplyFinished(QNetworkReply * reply)
{
	reply->deleteLater();
	const QByteArray type = reply->property("reply-type").toByteArray();
	
	if (type == "discover")
        {
            printf("recv a discover reply\n");
            onReplyDiscover(reply);
        }	
	else if (type == "queryExternalAddress")
        {
            printf("recv a queryExternalAddress reply\n");
            onReplyQueryExternalAddress(reply);
        }
	else if (type == "addPortMapping")
        {
            printf("recv a addPortMapping reply\n");
            onReplyAddPortMapping(reply);
        }
	else if (type == "deletePortMapping")
        {
            printf("recv a deletePortMapping reply\n");
            onReplyDeletePortMapping(reply);
        }
}

void QUpnpPortMapper::onReplyDiscover(QNetworkReply * reply)
{
	QDomDocument xml;
	QString errorMsg;
	int errorLine = 0;
	int errorColumn = 0;

	if (m_timer)
	{
		m_timer->stop();
		m_timer->deleteLater();
		m_timer = NULL;
	}

	if (xml.setContent(reply, &errorMsg, &errorLine, &errorColumn))
	{
            printf("xml:%s\n",xml.toString().toStdString().data());
		QString relativeControlUrl = recursiveFindControlUrl(xml.documentElement());
                printf("relativeControlUrl:%s\n",relativeControlUrl.toStdString().data());
		if (relativeControlUrl.length() > 0)
			m_controlUrl = reply->url().resolved(QUrl(relativeControlUrl));
	}
	if (m_controlUrl.isValid())
        {
            printf("set m_discoverOk to true!\n");
            m_discoverOk = true;
        }
	emit discoverFinished(m_controlUrl.isValid());
}

void QUpnpPortMapper::onReplyQueryExternalAddress(QNetworkReply * reply)
{
	QString content = QString::fromUtf8(reply->readAll());
	QString addressText = getXmlTabContent(content, "NewExternalIPAddress", Qt::CaseInsensitive);
	if (!addressText.isNull())
	{
		m_lastExternalAddress = QHostAddress(addressText);
		emit queryExternalAddressFinished(m_lastExternalAddress, true, QString());
	}
	else
	{
		QString errorString = getXmlTabContent(content, "errorDescription", Qt::CaseInsensitive);
		m_lastExternalAddress = QHostAddress();
		emit queryExternalAddressFinished(QHostAddress(), false, errorString);
	}
}

void QUpnpPortMapper::onReplyAddPortMapping(QNetworkReply * reply)
{
	const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
	QString content = QString::fromUtf8(reply->readAll());
        //printf("content:%s\n",content.toStdString().data());
	if (statusCode == 200)
	{
		m_lastAddPortMappingOk = true;
		emit addPortMappingFinished(true, QString());
	}
	else
	{
		m_lastAddPortMappingOk = false;
		emit addPortMappingFinished(false, getXmlTabContent(content, "errorDescription", Qt::CaseInsensitive));
	}
}

void QUpnpPortMapper::onReplyDeletePortMapping(QNetworkReply * reply)
{
	const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
	QString content = QString::fromUtf8(reply->readAll());
        printf("content:%s\n",content.toStdString().data());
	if (statusCode == 200)
	{
		m_lastDeletePortMappingOk = true;
		emit deletePortMappingFinished(true, QString());
	}
	else
	{
		m_lastDeletePortMappingOk = false;
		emit deletePortMappingFinished(false, getXmlTabContent(content, "errorDescription", Qt::CaseInsensitive));
	}
}

QString QUpnpPortMapper::recursiveFindControlUrl(const QDomElement & parent)
{
	for (QDomElement device = parent.firstChildElement("device");
		!device.isNull(); device = device.nextSiblingElement("device"))
	{
		QDomElement serviceList = device.firstChildElement("serviceList");
		for (QDomElement service = serviceList.firstChildElement("service");
			!service.isNull(); service = service.nextSiblingElement("service"))

		{
			const QString serviceType = service.firstChildElement("serviceType").text();
                        if (serviceType == "urn:schemas-upnp-org:service:WANPPPConnection:1")
				return service.firstChildElement("controlURL").text();
		}

		QString result = recursiveFindControlUrl(device.firstChildElement("deviceList"));
		if(result.length() > 0)
			return result;
	}
	return QString();
}

QString QUpnpPortMapper::getXmlTabContent(QString content, QString tab, Qt::CaseSensitivity cs)
{
	QRegExp rx(QString("<%1>([^\\r\\n]*)</%1>").arg(tab));
	rx.setCaseSensitivity(cs);
	if (content.indexOf(rx) >= 0)
		return rx.capturedTexts().at(1);
	else
		return QString();
}

/*main.cpp*/
#include <QCoreApplication>
#include <QNetworkInterface>
#include "QUpnpPortMapper.h"
#include <iostream>

int main(int argc, char *argv[])
{
	QCoreApplication app(argc, argv);

	// list all NetworkInterface and choose one, or you can get your local address by calling a connected QTcpSocket with localAddress()
	QUpnpPortMapper mapper;
	QList<QNetworkInterface> allCards = QNetworkInterface::allInterfaces();
	for (int i = 0; i < allCards.size(); ++i)
	{
		QString line = QString("[%1] %2").arg(i + 1).arg(allCards[i].humanReadableName());
		qDebug() << line;
	}

	std::cout << "Choose : ";
	int index = 0;
	std::cin >> index;
	if (index < 1 || index > allCards.size())
		return 0;

	// firstly, open
	mapper.open(allCards.at(index - 1));
	qDebug() << QString("Internal address : %1").arg(mapper.localAddress().toString());

	// then discover
	if (!mapper.discover(5000, true))
	{
		qDebug() << "No upnp support";
                return 0;
	}

	// however, queryExternalAddress is not necessary
	qDebug() << "External address " << mapper.queryExternalAddress(true).toString();

	qDebug() << "Input internal & external port:";
	int internalPort, externalPort;
        std::cin >> internalPort ;//>> externalPort;

	// you can use TCP or UDP, the description must be REGULAR in order to not corrupt the posted XML
        bool ok = mapper.addPortMapping(QAbstractSocket::TcpSocket, mapper.localAddress(), internalPort, externalPort, "test", true);
        if (ok)
                qDebug() << "Mapping ok";
        else
        {
                qDebug() << "Mapping failed";
                return 0;
        }
//        bool ok2=mapper.deletePortMapping(QAbstractSocket::TcpSocket, internalPort,true);
//        if (ok2)
//                qDebug() << "delete "<<internalPort<<"Mapping ok";
//        else
//        {
//                qDebug() << "delete "<<internalPort<<"Mapping failed";
//                return 0;
//        }
	return app.exec();
}

 

If you are experiencing problems with the Universal Plug and Play service, your computer might not be able to automatically detect the presence of other networked devices, such as PCs, printers, Internet access points and so on. That is where the UPnP Test application comes in. This simple program is designed to help you identify the issues that prevent the UPnP protocol from functioning correctly. Before you get your hopes up, you should know that this tool does not solve the detected problems, but only performs a series of tests to identify the possible causes. One advantage is that the application does not require installation, so your system registry is not affected in any way. The interface is compact and simple, comprising only two panels: one that displays the test type and its short description and the other for viewing which of the tests passed and which failed. The program can verify whether the operating system provides support for the UPnP service and allows you to check if the Simple Service Discovery Protocol (SSDP) and the UPnPHost services are running. It also verifies the connection between your network adapter and your router and the system's capacity to receive UPnP messages, as well as the router's capability to report an external IP address. One of the tests is designed to check if the Windows firewall service is blocking the traffic between your router and the system, thus preventing UPnP from working. The results can be copied to your clipboard by simply pressing a button and the tests can be redone easily. If you want to fix the detected issues, the link in the main interface can prove useful. In conclusion, UPnP Test is a simple tool for detecting problems related to device-to-device networking. However, it can only suggest possible reasons why the UPnP is not working, fixing the detected issues is totally up to you.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值