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();
}

猜你喜欢

转载自blog.csdn.net/xuyuanwang19931014/article/details/82498662