📄 btjl.txt
字号:
比特精靈upnp.h
#ifndef MYUPNP_H_
#include "bt.h"
typedef bool (*PUPNPCALLBACK)(void* lp);
const int UPNPREPEAT = 5;
const int UPNPDELAY = 6;
class QMyUPNP
{
public:
QMyUPNP();
QMyUPNP(const QString& device);
~QMyUPNP();
void Clear();
bool Comfirmed()const{return (Valid()&&!m_controlurl.IsEmpty());}
bool Search(const QString& name, const char* type="service", int repeat=UPNPREPEAT, int delay=UPNPDELAY, int version=1, PUPNPCALLBACK lpCall=NULL);
bool GetDescription(int delay=UPNPDELAY);
QString GetProperty(const QString& name, const QString& rsp, int delay=UPNPDELAY);
bool InvokeCommand(const QString& name, QStringList& args,int delay=UPNPDELAY);
bool SetDescriptionUrl(const QString& name, const QString& descri);
void SetDeviceName(const QString& p){m_devicename = p;}
QString GetDeviceName()const{return m_devicename;}
QString GetDescriptionUrl()const{return m_description;}
QString GetControlUrl()const{return m_controlurl;}
QString GetBaseUrl()const{return m_baseurl;}
QString GetDescriptionName()const{return m_name;}
QString GetFriendlyName()const{return m_friendlyname;}
QString GetModelName()const{return m_modelname;}
QMyUPNP& operator=(const QMyUPNP& up);
protected:
bool Valid()const{return (!m_name.IsEmpty()&&!m_description.IsEmpty());}
void InternalClear();
bool InternalSearch(const QString& name, const char* type, int repeat, int delay, int version, ulong ip, PUPNPCALLBACK lpCall);
bool InternalSearch2(const QString& name, const char* type, int repeat, int delay, int version, ulong ip, PUPNPCALLBACK lpCall);
QString m_devicename;
QString m_name;
QString m_description;
QString m_baseurl;
QString m_controlurl;
QString m_friendlyname;
QString m_modelname;
int m_version;
};
inline QMyUPNP& QMyUPNP::operator=(const QMyUPNP& upnp)
{
if(this==&upnp)return *this;
m_devicename = upnp.m_devicename;
m_name = upnp.m_name;
m_description = upnp.m_description;
m_baseurl = upnp.m_baseurl;
m_controlurl = upnp.m_controlurl;
m_friendlyname = upnp.m_friendlyname;
m_modelname = upnp.m_modelname;
m_version = upnp.m_version;
return *this;
}
#define UPNPPORTMAP0 "WANIPConnection"
#define UPNPPORTMAP1 "WANPPPConnection"
#define UPNPGETEXTERNALIP "GetExternalIPAddress","NewExternalIPAddress"
#define UPNPADDPORTMAP "AddPortMapping"
#define UPNPDELPORTMAP "DeletePortMapping"
void GetAddPortmapArgs(QStringList& args, int eport, int iport, const char* iclient,
const char* descri, bool enabled = true, int dur = 0,
const char* type = "TCP", const char* remote = NULL);
void GetDeletePortmapArgs(QStringList& args, int eport, const char* type = "TCP", const char* remote=NULL);
#endif
比特精靈upnp.cpp
#include "upnp.h"
#include "qsocket.h"
#include "qudp.h"
/*ReloadIp: 獲取本機上最類似於192.168.0.x的內網IP
如果本機沒有內網IP,則返回INADDR_NONE*/
static ulong ReloadIp()//get 192.168.0.x
{
int len = 256;
char name[256+1];
hostent *host;
ulong tmpip = INADDR_NONE,
retip = INADDR_NONE;
memset(name, 0, len+1);
if(gethostname(name,len)==0)
{
host = gethostbyname(name);
if(host&&host->h_length>=sizeof(ulong))
{
len = 0;
while(host->h_addr_list[len])
{
tmpip = *((ulong*)(host->h_addr_list[len++]));
if(((tmpip&0xFF)==192&&((tmpip>>8)&0xFF)==168&&((tmpip>>16)&0xFF)==0)||IsLAN(tmpip)||!is_validip(retip))
{
retip = tmpip;
}
}
}
}
return retip;
}
static const ulong UPNPADDR = 0xFAFFFFEF;
static const int UPNPPORT = 1900;
static const char* URNPREFIX = "urn:schemas-upnp-org:";
static const char* LCA = "Location:";
static const char* LCB = "LOCATION:";
static const char* LCC = "location:";
/*LCA,LCB,LCC: 有些UPnP設備以LCA作為字段名稱,有些則用LCB,有些則用LCC,對於其他那些喜歡亂命名的廠商只好說抱歉了.*/
QMyUPNP::QMyUPNP()
: m_version(1)
{
}
QMyUPNP::QMyUPNP(const QString& device)
: m_devicename(device), m_version(1)
{
}
QMyUPNP::~QMyUPNP()
{
Clear();
}
/*InternalClear: 清空對像,但保留對像*/
void QMyUPNP::InternalClear()
{
m_baseurl = "";
m_controlurl = "";
m_friendlyname = "";
m_modelname = "";
}
/*Clear: 清空對像,同時對像失效.*/
void QMyUPNP::Clear()
{
m_name = "";
m_description = "";
m_version = 1;
InternalClear();
}
/*InternalSearch: 查找UPnP設備
實現過程:首先調用InternalSearch2進行查找的操作,如果沒有找到,則設置對象的設備名為IGD,再次調用InternalSearch2查找.
之所以分兩次查找是因為有些設備支持以服務名查找,而有些設備則只支持以設備名查找.
參數含義:
name: 要查找的服務/設備的名稱
type: name表示的是服務或設備(service/device)
repeat: 嘗試查找的次數
delay: 查找結果的最長等待時間
version: UPnP版本,默認1
ip: 查詢的目標IP,默認為UPnP的廣播IP0xFAFFFFEF,也可以指定IP以便當程序運行在某些主機上時也能查找.
lpCall: 一個回調函數,在查找過程中隨機調用.
返回值: 成功返回true,否則返回false*/
bool QMyUPNP::InternalSearch(const QString& name, const char* type, int repeat,
int delay, int version, ulong ip, PUPNPCALLBACK lpCall)
{
if(InternalSearch2(name,type,repeat,delay,version,ip,lpCall))return true;
QString back = m_devicename;
m_devicename = QString("urn:schemas-upnp-org:device:InternetGatewayDevice:") + m_version;
bool ok = InternalSearch2(name,type,repeat,delay,version,ip,lpCall);
m_devicename = back;
return ok;
}
/*InternalSearch2: 執行查找操作.
實現過程:
創建UDP套接字,如果IP為0,則先對255.255.255.255的1900端口發送廣播消息,再對UPnP的廣播IP發送消息.
參數函數同InternalSearch.*/
bool QMyUPNP::InternalSearch2(const QString& name, const char* type, int repeat,
int delay, int version, ulong ip, PUPNPCALLBACK lpCall)
{
static const char* USRS = "M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\n"
"MAN: \"ssdp:discover\"\r\nMX: ";
const int RCVLEN = 4096;
Clear();
if(name.IsEmpty())return false;
if(type==NULL)type = "service";
if(repeat<=0)repeat = UPNPREPEAT;
if(delay<=0)delay = UPNPDELAY;
if(version<=0)version = 1;
m_version = version;
m_name = QString(URNPREFIX) + type + ":" + name + ":" + version;
QString finding = m_devicename.IsEmpty() ? m_name : m_devicename;
QString shrs = QString(USRS) + delay + "\r\nST: " + finding + "\r\n\r\n";
QUdpSocket udp(true);
char a[50], b[50], c[50];
char rcv[RCVLEN + 1];
int rlen = 0;
int retry = 0;
const char* lc = "";
const char* le = "";
bool broad = (!is_validip(ip)); //send to 255.255.255.255
do
{
if(retry)sleep(1);
if(!is_validip(ip)) ip = UPNPADDR;
if(broad&&!udp.SendPacket(shrs.c_str(),shrs.Length(),INADDR_BROADCAST,UPNPPORT,delay/2))continue;
if(!udp.SendPacket(shrs.c_str(),shrs.Length(),ip,UPNPPORT,delay/2)
||(rlen=udp.RecvPacket(rcv,RCVLEN,delay))<=0)continue;
if(lpCall&&!lpCall(NULL))break;
if(rlen<=0||rlen>RCVLEN)continue;
rcv[rlen] = 0;
if(SplitterString(rcv,a,b,c,40,40,40)!=3||b[0]!='2')continue;
if(strstr(rcv,finding.c_str())==NULL)continue;
lc = strstr(rcv,LCA);
if(lc==NULL) lc = strstr(rcv,LCB);
if(lc==NULL) lc = strstr(rcv,LCC);
if(lc==NULL)continue;
lc += strlen(LCA);
while(lc[0]>0&&lc[0]<=0x20) lc++;
le = strchr(lc,'\n');
if(le==NULL)continue;
m_description = QString(lc,le - lc).Trim();
}while(++retry<repeat&&!Valid());
return GetDescription(delay);
}
/*Search: QMyUPnP的公開接口,由外部對像調用進行設備或服務的查找.
執行過程: 先對默認IP調用InternalSearch進行查找,如果沒找到,再判斷本機是否有局域網IP,如果有,則指定最類似於192.168.0.x的IP進行查詢.參數函數同InternalSearch.*/
bool QMyUPNP::Search(const QString& name,const char* type,int repeat,int delay,
int version, PUPNPCALLBACK lpCall)
{
ulong rip = 0;
if(InternalSearch(name,type,repeat,delay,version,0,lpCall))return true;
rip = ReloadIp();
if(is_validip(rip))return InternalSearch(name,type,repeat,delay,version,rip,lpCall);
return false;
}
/*SetDescriptionUrl: 由外部對像調用不通過查詢過程來創建一個有效的UPnP對像.
實現過程:對指定的URL執行GetDescription操作來判斷是否有效的UPnP對像.
參數含義:
name: 服務/設備名稱(全稱:如:urn:schemas-upnp-org:service:WANPPPConnection:1)
descri: 用於獲取設備/服務描述的目標URL.*/
bool QMyUPNP::SetDescriptionUrl(const QString& name, const QString& descri)
{
Clear();
m_name = name;
m_description = descri;
return GetDescription();
}
inline bool PPIsDigitChar(const char p)
{
return (p>='0'&&p<='9');
}
static int ParseInteger(const char* cstr)
{
const unsigned char* str = (const unsigned char*) cstr;
int value = 0;
assert(str);
if(str)
{
int retry = 20;
bool neg = (str[0]=='-');
if(neg)str++;
while(PPIsDigitChar(str[0])&&(--retry)>0)
{
value*=10;
value+=(str[0]-'0');
str++;
}
if(neg)value = -value;
}
return value;
}
static QString NGetAddressFromUrl(const QString& str,QString& post,QString& host,int& port)
{
port = 0;
post = "";
if(str.IsEmpty())return QString();
const char* src = str.c_str();
const char* start,
* pstart,
* end;
start = strstr(src,"://");
start = (start==NULL)?src:(start+3);
end = strchr(start,'/');
if(end==NULL)end = src + strlen(src);
host = QString(start,end-start);
post = (end[0]=='/')?QString(end):QString("/");
pstart = strchr(start,':');
port = (pstart==NULL||pstart>=end)?80:ParseInteger(pstart+1);
if(port==0)port = 80;
if(pstart&&pstart<end)end = pstart;
return QString(start,end-start);
}
/*GetDescription: 在目標對象的描述URL已經獲取的情況下獲取該對象的描述.
實現過程: 對指定的URL實行一個UPnP規範指定的GET操作,解析返回的模板.只有成功解析了,對像才有效.
參數:
delay: 最大等待時間.*/
bool QMyUPNP::GetDescription(int delay)
{
if(!Valid())return false;
InternalClear();
QString post, host, addr;
int port = 0;
addr = NGetAddressFromUrl(m_description, post, host, port);
if(addr.IsEmpty()||!is_validport(port))return false;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -