#define UPNPPORTMAP0 "WANIPConnection"
#define UPNPPORTMAP1 "WANPPPConnection"
#define UPNPGETEXTERNALIP "GetExternalIPAddress"/*"NewExternalIPAddress"*/
#define UPNPADDPORTMAP "AddPortMapping"
#define UPNPDELPORTMAP "DeletePortMapping"
#define UPNPGETGENERICPORTMAPPINGENTRY "GetGenericPortMappingEntry"
#define GETSPECIFICPORTMAPPINGENTRY "GetSpecificPortMappingEntry"
#define NUMBEROFDEVICES 3
#ifdef WIN32
#else
#define S_OK 0
#endif
#define E_UNAT_NOT_IN_LAN -1 // 已是公网IP
#define E_UNAT_CANNOT_FIND_ROUTER -2 // 找不到路由器
#define E_UNAT_ACTION_HTTP_ERRORCODE -3 // Action返回Http失败码
#define E_UNAT_ENTRY_MAYBE_FULL -4 // 端口映射的表项可能已满
#define E_UNAT_UNKNOWN_ERROR -5 // 未知错误
#define E_UNAT_CREATE_SOCKET_FAILED -6 // 创建Socket失败
#define SERCHSTATUS_SERCH 0 // 步进状态
#define SERCHSTATUS_GETDESCRI 1
#define SOAPSTATUS_SEND 0
#define SOAPSTATUS_RECV 1
const char *devices[][2] = {
{UPNPPORTMAP1, "service"},
{UPNPPORTMAP0, "service"},
{"InternetGatewayDevice", "device"},
};
const unsigned long UPNPADDR = 0xFAFFFFEF;//250_255_255_239
const unsigned short UPNPPORT = 1900;
static const char *URNPREFIX = "urn:schemas-upnp-org:";
static const char xmlpro[1024] = "<?xml version=\"1.0\"?><s:Envelope\r\n xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n <s:Body>\r\n </s:Body>\r\n</s:Envelope>\r\n\r\n";
static char s_szctlurl[1024] =""; //存放控制URL
static int s_iversion = 1;
static char s_cname[1024]="";
static XOS_BOOL s_bSearched = XOS_FALSE; //标记搜索状态
static XOS_BOOL s_bInAddSOAP = XOS_FALSE;
static unsigned int s_uiLastErrorCode = 0;
static TUPnPNATPortMapping UPnPMapInfo; //存放端口映射信息
static int s_SerchStatus; //搜索网关设备获取URL状态
static int s_SOAPStatus; //soap_action状态
static char s_szdescri[1024]; //location的具体值
static char s_szxml[10240]; //SOAP TCP接受的数据
static char *s_pCurWritePos = NULL;
static int s_iWritedCount = 0;
static XOS_U16 s_u16ExternalAdd = 0;
static XOS_U16 s_u16ExternalPort = 0; //外网映射端口
static XOS_U32 s_u32ExternalIp = 0; //外网IP
static XOS_SOCKET s_udpsock = XOS_INVALID_SOCKET;
static XOS_SOCKET s_tcpsock = XOS_INVALID_SOCKET;
/* 功能描述: 判断内网ip
* 参数说明:
* unsigned long nIP [IN]:
* 返回值: XOS_BOOL
* 备注:
*/
XOS_BOOL ISLANIP(unsigned long nIP)
{
// filter LAN IP's
// -------------------------------------------
// 0.*
// 10.0.0.0 - 10.255.255.255 class A
// 172.16.0.0 - 172.31.255.255 class B
// 192.168.0.0 - 192.168.255.255 class C
unsigned char nFirst = (unsigned char)nIP;
unsigned char nSecond = (unsigned char)(nIP >> 8);
if (nFirst==192 && nSecond==168) // check this 1st, because those LANs IPs are mostly spreaded
{
return XOS_TRUE;
}
if (nFirst==172 && nSecond>=16 && nSecond<=31)
{
return XOS_TRUE;
}
if (nFirst==0 || nFirst==10)
{
return XOS_TRUE;
}
return XOS_FALSE;
}
/* 功能描述: 返回字符串起始处首个非指定子字符离起始处的距离
* 参数说明:
* 备注:
*/
int StringSpanIncluding(const char *pszBlock, const char *pszSet)
{
return strspn(pszBlock,pszSet);
}
/* 功能描述: 返回首次出现子字符时其数组下标
* 备注:
*/
int StringSpanExcluding(const char *pszBlock,const char *pszSet)
{
return strcspn(pszBlock, pszSet);
}
/* 功能描述: 从字符串中取出第一句语句 (\r\n分割的第一个语句)
* 参数说明:
* const char *strSrc [IN]:要处理的源信息
* const char *pszTokens [IN]:标识字符串
* int* piStart [IN]:定位信息
* char *strDest [OUT]:输出语句
*/
void Tokenize(const char *strSrc, const char *pszTokens, int* piStart,char *strDest)
{
int nExcluding;
int nIncluding;
int iFrom;
int nUntil;
if(*piStart < 0)
{
strDest[0] = '\0';
return ;
}
if(NULL == pszTokens || *pszTokens == 0)
{
if(*piStart < (int)strlen(strSrc))
{
strcpy(strDest,strSrc + *piStart);
return ;
}
}
else
{
const char *pszPlace = strSrc + *piStart;
const char *pszEnd = strSrc + strlen(strSrc);
if(pszPlace < pszEnd)
{
nIncluding = StringSpanIncluding(pszPlace,pszTokens);
if((pszPlace+nIncluding) < pszEnd)
{
pszPlace += nIncluding;
nExcluding = StringSpanExcluding( pszPlace, pszTokens);
iFrom = *piStart+nIncluding;
nUntil = nExcluding;
*piStart = iFrom+nUntil+1;
memcpy(strDest,strSrc + iFrom,nUntil);
strDest[nUntil] = '\0';
return ;
}
}
}
*piStart = -1;
strDest[0] = 0;
}
/* 功能描述: 附加字符串
*/
void Append(char *src,const char *newstr)
{
strcat(src,newstr);
}
/* 功能描述: 去掉字符串左边的space
* 返回值: void
* 备注:isspace(int c) 检查参数c是否为空格字符,也就是判断是否为空格('')、定位字符('\t')
* CR('\r')、换行('\n')、垂直定位字符('\v')或翻页(' \f')的情况若参数c为空格字符,
* 则返回TRUE,否则返回NULL(0)。
*/
void TrimLeft(char *str)
{
int pos = 0;
int nLen = strlen(str);
if(0 == nLen)
{
return ;
}
while(pos < nLen && isspace(str[pos]))
{
pos ++;
}
if(pos > 0)
{
memcpy(str,str+pos,nLen - pos + 1);
}
}
/*
* 功能描述: 去掉字符串右边的space
*/
void TrimRight(char *str)
{
int pos;
if(0 == strlen(str))
{
return ;
}
pos = strlen(str) - 1;
while(pos > 0 && isspace(str[pos]))
{
str[pos] = '\0';
pos --;
}
}
/* 功能描述: 解析设备HTTP回应消息,解析办法为取第一句信息,确定"HTTP/1.1 200 OK"中的'2'
* 参数说明:
* const char *response [IN]:要判断的字符串
* char *result [OUT]:解析后的字符串
* 返回值: XOS_BOOL
* 备注:
*/
XOS_BOOL parseHTTPResponse(const char *response, char *result)
{
int pos = 0;
char status[1024];
char status1[1024];
Tokenize(response,"\r\n", &pos,status);
strcpy(result,response);
memcpy(result,result + pos,strlen(result) - pos + 1);
pos = 0;
Tokenize(status," ", &pos,status1);
Tokenize(status," ", &pos,status1);
if (0 == strlen(status1) || status1[0]!='2')
{
return XOS_FALSE;
}
return XOS_TRUE;
}
/* 功能描述: 返回查找到该字符串离起始位置的距离
* 返回值: int
*/
int FindStrPos(const char *str,const char *lpszSub,int nStart)
{
char *lpsz;
//nStart = 0;
lpsz = strstr(str + nStart,lpszSub);
return (lpsz == NULL) ? -1 : (int)(lpsz - str);
}
/* 功能描述:
* 参数说明:
* const char *str [IN]: 类似http://192.168.14.1:1900/igd.xml
* char *post [OUT]: 类似igd.xml
* char *host [OUT]: 类似192.168.14.1:1900
* int* pport [OUT]: 类似1900
* char *strAddress [OUT]: 类似192.168.14.1
* 返回值: void
* 备注:
*/
void NGetAddressFromUrl(const char *str, char *post, char *host, int* pport,char *strAddress)
{
char *s;
int pos;
char strPort[128];
s = (char *)str;//描述
assert(s != NULL);
post[0] = '\0';
host[0] = '\0';
*pport = 0;
pos= FindStrPos(s,"://",0); //离起始点的距离
//if (!pos) return CString();
if (-1 == pos)
{
return;
}
s = s+pos+3;
pos = FindStrPos(s,"/",0);
//if (!pos) {
if (-1 == pos)
{
strcpy(host,s);
s = (char *)str + strlen(str); //让s指向字符串尾
}
else
{
memcpy(host,s,pos);
host[pos] = '\0';
s = s+pos;
}
if (0 == strlen(s))
{
post[0] = '\0';
}
else
{
strcpy(post,s); //post类似igd.xml
}
pos = 0;
strAddress[0] = '\0';
Tokenize(host,":", &pos,strAddress);
//如果用pos==-1,调用Tokenize会抛出异常。
//s = host.Tokenize(_T(":"), pos);
// ZeroMemory(strPort,128);
memset(strPort, 0, 128);
if(strlen(strAddress) > 0 && pos != -1)
{
Tokenize(host,":", &pos,strPort); //获取端口号
}
//如果用pos==-1,调用Tokenize会抛出异常。
if (0 == strlen(strPort))
{
*pport = 80;
}
else
{
*pport = atoi(strPort); //字符串转化为整形数
}
}
/* 功能描述: 获取属性值
* 参数说明:
* const char *all [IN ]:输入的XML
* const char *name [IN ]:属性名
* char *strValue [OUT]:属性值
* 返回值: void
* 备注:
*/
void getProperty(const char *all, const char *name,char *strValue)
{
char *startTag;
char *endTag;
int posStart;
int posEnd;
startTag = (char *)malloc(strlen(name)+3);
assert(startTag != NULL);
endTag = (char *)malloc(strlen(name)+4);
assert(endTag != NULL);
strValue[0] = '\0';
sprintf(startTag,"<%s>",name);
sprintf(endTag,"</%s>",name);
posStart = FindStrPos(all,startTag,0);
if (posStart<0)
{
free(startTag);
free(endTag);
return ;
}
posEnd = FindStrPos(all,endTag, posStart);//标记下。。。。。。。。。。
if (posStart>=posEnd)
{
free(startTag);
free(endTag);
return ;
}
memcpy(strValue,all+posStart + strlen(startTag),posEnd - posStart - strlen(startTag));
strValue[posEnd - posStart - strlen(startTag)] = '\0';
free(startTag);
free(endTag);
}
// /* 功能描述:
// * 参数说明:
// * 返回值: unsigned int
// * 备注:
// */
// unsigned int GetLastActionErrorCode()
// {
// return s_uiLastErrorCode;
// }
/* 功能描述:
* 参数说明:
* unsigned long dwLastErrorCode []:
* 返回值: void
* 备注:
*/
void SetLastActionErrorCode(unsigned int uiLastErrorCode)
{
uiLastErrorCode = uiLastErrorCode;
}
/* 功能描述: 寻在sub字符串(不区分大小写),返回指针。
* 参数说明:
* const char *str [IN ]:
* const char *lpszSub [IN ]:要寻找的sub字符串。
* 返回值: 字符串所在位置指针。
* 备注:
*/
char *FindStrI(const char *str,const char *lpszSub)
{
int nLen = strlen(str);
int nSubLen = strlen(lpszSub);
char *strpos = NULL;
char *temp =(char *)malloc(nLen + 1);
int pos = 0;
strcpy(temp,str);
while(pos < nLen - nSubLen)
{
char Tmpchar = str[pos + nSubLen];
temp[pos + nSubLen] = '\0';
#ifdef WIN32
if(0 == stricmp(temp+pos,lpszSub)) //比较字符串,不区分大小写
#else
if(0 == strcasecmp(temp+pos, lpszSub))
#endif
{
strpos = (char *)str + pos;
break;
}
temp[pos + nSubLen] = Tmpchar;
pos ++;
}
free(temp);
return strpos;
}
/* 功能描述: 判下载HTTP包是否完成.
* HTTP包的格式为HTTP头加上XML信息体,判断方法为确定HTTP头的长度和信息体
* 的长度小于等于接受到的实际长度
* 参数说明:
* 返回值: 成功返回XOS_TRUE
* 备注:
*/
XOS_BOOL IsLengthedHttpPacketComplete(const char *packet, int len)
{
const char STR_CONTENT_LENGTH[] = "Content-Length:";
const int STRLEN_CONTENT_LENGTH = sizeof(STR_CONTENT_LENGTH) / sizeof(char) - 1;
const char STR_DOUBLE_NEWLINE[] = "\r\n\r\n";
const int STRLEN_DOUBLE_NEWLINE = sizeof(STR_DOUBLE_NEWLINE) / sizeof(char) - 1;
char *pContLenPos = NULL;
char *pNewLinePos = NULL;
char *pLenStartPos = NULL;
char *pLenEndPos = NULL;
char *szLength = NULL;
char *pDoubleNewLinePos = NULL;
int iContentLen = 0;
unsigned int nBufSize;
int iHeadLen = 0;
pContLenPos = FindStrI(packet, STR_CONTENT_LENGTH);
if (NULL == pContLenPos)
{
return XOS_FALSE;
}
// \r\n所在位置
pNewLinePos = strstr(pContLenPos, "\r\n");
if (NULL == pNewLinePos)
{
return XOS_FALSE;
}
获取长度
pLenStartPos = pContLenPos + STRLEN_CONTENT_LENGTH;
pLenEndPos = pNewLinePos;
nBufSize = pLenEndPos - pLenStartPos + 1;
szLength = (char *)malloc(nBufSize);
memcpy(szLength, pLenStartPos, nBufSize - 1);
szLength[nBufSize - 1] = 0;
iContentLen = atoi(szLength);
//printf("===========XML信息体长度为:%d!\r\n",iContentLen);
free(szLength);
szLength = NULL;
pDoubleNewLinePos = (char *)strstr(packet, STR_DOUBLE_NEWLINE);
if (NULL == pDoubleNewLinePos)
{
return XOS_FALSE;
}
iHeadLen = pDoubleNewLinePos - packet + STRLEN_DOUBLE_NEWLINE;
//printf("===========HTTP头长度为:%d!\r\n",iHeadLen);
//printf("响应数据的总长度应该为:%d!,实际接收:%d!\r\n",iHeadLen+iContentLen,len);
if (len >= iHeadLen + iContentLen)
{
return XOS_TRUE;
}
else
{
return XOS_FALSE;
}
}
/* 功能描述: 发送SOAP request请求包,并接受response返回信息
* 参数说明:
* const char *addr [IN]: IP地址
* unsigned short port [IN]:端口
* const char *request [IN]:
* char *response [OUT]:
* 返回值: long
* 备注:
*/
int SOAP_action(const char *addr, unsigned short port, const char *request, char *response)
{
char buffer[10240];
int length = strlen(request);
u_long lv = 1;
int iSendLen = 0;
int rv,iRet;
int rlen = 0;
char result[1024];
int iResponseCode = -1;
char strErrorCode[100];
//unsigned long ip = inet_addr(addr);
XOS_U32 uiIp = XOS_AddrToU32(addr);
if (s_tcpsock == XOS_INVALID_SOCKET)
{
s_tcpsock = XOS_TCPConnectNB(uiIp,port);
if (XOS_INVALID_SOCKET == s_tcpsock)
{
return E_UNAT_CREATE_SOCKET_FAILED;
}
// ioctlsocket(s_tcpsock, FIONBIO, &lv); //允许非阻塞
xos_setsocknonblock(s_tcpsock);
}
switch(s_SOAPStatus)
{
case SOAPSTATUS_SEND:
strcpy(buffer,request);
rv = XOS_TCPSendDataNB(s_tcpsock, buffer, &iSendLen, strlen(buffer));
if (rv == 0)
{
xlprintf("[%s] SOAP_action:发送SOAP请求包成功!!\r\n","upnpnat");
memset(s_szxml,0,sizeof(s_szxml));
s_pCurWritePos = s_szxml;
s_SOAPStatus = SOAPSTATUS_RECV;
}
else if (rv == 1)
{
iRet = -5;
break;
}
else if (rv == 2)
{
iRet = -5;
break;
}
else
{
iRet = -5;
xlprintf("[%s] SOAPACTION XOS_TCPSendDataNB failed.\r\n","upnpnat");
//XOS_CloseSocket(s_tcpsock);
//s_tcpsock = XOS_INVALID_SOCKET;
//return -1;
break;
}
//接收
case SOAPSTATUS_RECV:
//一次不一定能接受完
rlen = XOS_TCPRecvNB(s_tcpsock,s_pCurWritePos,sizeof(s_szxml)-s_iWritedCount);
if (0 == rlen)
{
iRet = -5;
//XOS_CloseSocket(s_tcpsock);
break;
}
else if (rlen > 0)
{
s_pCurWritePos += rlen;
//printf("==================%d.\r\n",rlen);
s_iWritedCount += rlen;
}
else
{
iRet = -5;
xlprintf("[%s] SOAPACTION XOS_TCPRecvNB failed.\r\n","upnpnat");
XOS_CloseSocket(s_tcpsock);
s_tcpsock = XOS_INVALID_SOCKET;
memset(s_szxml, 0, 10240);
// ZeroMemory(s_szxml,10240);
s_iWritedCount = 0;
s_pCurWritePos = NULL;
s_SOAPStatus = SOAPSTATUS_SEND;
break;
}
if (!IsLengthedHttpPacketComplete(s_szxml, s_iWritedCount))
{
xlprintf("[%s] IsLengthedHttpPacketComplete:not yet!\r\n","upnpnat");
break;
}
xlprintf("[%s] SOAPACTION XOS_TCPRecvNB success.\r\n","upnpnat");
rlen = s_iWritedCount;
memcpy(response,s_szxml,rlen);
response[rlen] = '\0';
// printf("\r\n%s\r\n",response);
//清零,下次使用
XOS_CloseSocket(s_tcpsock);
s_tcpsock = XOS_INVALID_SOCKET;
memset(s_szxml, 0, 10240);
// ZeroMemory(s_szxml,10240);
s_iWritedCount = 0;
s_pCurWritePos = NULL;
if (!parseHTTPResponse(response, result))
{
getProperty(response, "errorCode",strErrorCode);
if (strlen(strErrorCode) > 0)
{
iResponseCode = atoi(strErrorCode);
}
SetLastActionErrorCode(iResponseCode);
iRet = E_UNAT_ACTION_HTTP_ERRORCODE;
}
else
{
iRet = 0;
}
s_SOAPStatus = SOAPSTATUS_SEND;//重置状态
break;
default:
assert(0);
break;
}
return iRet;
}
/* 功能描述: 获取描述
* 参数说明:
* const char *description [IN]: 描述地址
* 返回值: XOS_BOOL
* 备注:
*/
XOS_BOOL GetDescription(const char *description)
{
char m_friendlyname[1024];
char m_modelname[1024];
char m_baseurl[1024];
char post[1024];
char host[1024];
char addr[16];
char request[1024];
char result[10240];
char response[10240];
char serviceType[1024];
char temp[1024];
int port = 0;
int d ;
int pos;
if(0 == strlen(description))
{
return XOS_FALSE;
}
//description http://192.168.14.1:1900/igd.xml
//post igd.xml
//host 192.168.14.1:1900
//port 1900
//addr 192.168.14.1
NGetAddressFromUrl(description, post, host, &port,addr); //解析描述文件的URL,获得主机(host)、端口(port)、路径(path)
if(0 == strlen(addr))
{
return XOS_FALSE;
}
//printf("post:%s, host:%s, addr:%s, port:%d. \r\n",post,host,addr,port);
sprintf(request,"GET %s HTTP/1.1\r\nHOST: %s\r\nACCEPT-LANGUAGE: en\r\n\r\n",post, host);
//printf("\r\n%s\r\n",request);
if (SOAP_action(addr, (unsigned short)port, request, response) < 0)
{
//printf("GetDescription SOAP_action获取描述失败!\r\n");
return XOS_FALSE;
}
if (!parseHTTPResponse(response, result))
{
xlprintf("[%s] GetDescription SOAP_action获取描述解析失败!\r\n","upnpnat");
return XOS_FALSE;
}
getProperty(result, "friendlyName",m_friendlyname);
getProperty(result, "modelName",m_modelname);
getProperty(result, "URLBase",m_baseurl);
if(0 == strlen(m_baseurl))
{
sprintf(m_baseurl,"http://%s/",host);
}
if(m_baseurl[strlen(m_baseurl) - 1]!='/')
{
strcat(m_baseurl ,"/");
}
for (d=0; d<NUMBEROFDEVICES; d++)
{
sprintf(s_cname,"%s%s:%s:%d", URNPREFIX, devices[d][1], devices[d][0], s_iversion);
sprintf(serviceType,"<serviceType>%s</serviceType>",s_cname);
pos = FindStrPos(result,serviceType,0);
if (pos >= 0)
{
strcpy(result,result + pos + strlen(serviceType));
pos = FindStrPos(result,"</service>",0);
if (pos >= 0)
{
//strcpy(result,result + pos);
result[pos] = '\0';
getProperty(result, "controlURL",s_szctlurl);
if (strlen(s_szctlurl) > 0 && s_szctlurl[0] == '/')
{
strcpy(temp,s_szctlurl + 1);
strcpy(s_szctlurl,m_baseurl);
strcat(s_szctlurl,temp); //获得control URL的地址
}
break;
}
}
}
if(strlen(s_szctlurl) > 0)
{
return XOS_TRUE;
}
return XOS_FALSE;
}
/* 功能描述:搜索网关设备
* 参数说明:
* int version [IN]:
* XOS_BOOL bUseDefaultGateway [IN]:
* 返回值: XOS_BOOL
* 备注: 控制点多播寻找感兴趣的设备和服务,
*/
XOS_BOOL InternalSearch(int version)
{
unsigned long uReqIp = 0;
unsigned long uDefGW = 0;
// int iSleepTime = 1000;// 1s
// XOS_SOCKET s;
u_long lv = 1;
XOS_BOOL bBroadcast = XOS_TRUE;
int rlen = 0;
char buffer[10240];
char result[10240];
char line[10240];
char name[1024];
char m_description[10240];
char request[10240];
int i,j;
int k =0;
if(version<=0)
{
version = 1;
}
s_iversion = version;
uReqIp = UPNPADDR;//多播地址址239.255.255.250
if (s_udpsock == XOS_INVALID_SOCKET)
{
s_udpsock = XOS_UDPBind(0, 0);
if (XOS_INVALID_SOCKET == s_udpsock)
{
printf("UPnP InternalSearch XOS_UDPBind failed!\r\n");
return XOS_FALSE;
}
// ioctlsocket(s_udpsock, FIONBIO, &lv);//FIONBIO:允许非阻塞
xos_setsocknonblock(s_udpsock);
// setsockopt(s_udpsock,SOL_SOCKET,SO_BROADCAST,(CHAR *)&bBroadcast,sizeof(BOOL));//设置允许发送广播包
xos_setsockbroadcast(s_udpsock);
}
switch(s_SerchStatus)
{
case SERCHSTATUS_SERCH:
s_bSearched = XOS_FALSE;
for (j=0; j<NUMBEROFDEVICES; j++)
{
sprintf(s_cname,"%s%s:%s:%d", URNPREFIX, devices[j][1], devices[j][0], s_iversion);
sprintf(request,"M-SEARCH * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nMAN: \"ssdp:discover\"\r\nMX: %d\r\nST: %s\r\n\r\n",
6, s_cname);
XOS_UDPSendToNB(s_udpsock, request, strlen(request), uReqIp, UPNPPORT);
if (rlen != 0)
{
printf("XOS_UDPSendToNB Fail:%d.\r\n",uReqIp);
break;
}
}
rlen = XOS_UDPRecvFromNB(s_udpsock,buffer,sizeof(buffer),NULL,NULL);
if (rlen <= 0)
{
//bSerched = XOS_FALSE;
//XOS_CloseSocket(s);
break;
}
XOS_CloseSocket(s_udpsock);
s_udpsock = XOS_INVALID_SOCKET;
buffer[rlen] = '\0';
if (!parseHTTPResponse(buffer,result))
{
xlprintf("[%s] 寻找UPNP设备:设备发送回应消息parseHTTPResponse失败!\r\n","upnpnat");
break;
}
//printf("设备发送发现请求响应消息:\r\n%s\r\n",buffer);
for (i=0; i<NUMBEROFDEVICES; i++)
{
sprintf(s_cname,"%s%s:%s:%d",URNPREFIX,devices[i][1],devices[i][0],version);
if (strstr(result,s_cname) != NULL)//strstr:在串中查找指定字符串的第一次出现
{
for (j = 0;;)
{
Tokenize(result,"\r\n",&j,line);
if (0 == strlen(line))
{
return XOS_FALSE;
}
memcpy(name,line,9);
name[9] = '\0';
#ifdef WIN32
_strupr(name); //不区分大小写,转化为大写
#else
while (name[k])
{
name[k] = toupper(name[k]); //linux下转化成大写
k++;
}
#endif
if (0 == strcmp(name,"LOCATION:"))
{
memcpy(line,line+9,strlen(line)-9 + 1);//line存放location的具体值http://192.168.14.1:1900/igd.xml
strcpy(m_description,line);
TrimLeft(m_description);
TrimRight(m_description);
strcpy(s_szdescri,m_description);
xlprintf("[%s] 找到location的具体值:%s\r\n","upnpnat",m_description);
s_SerchStatus = SERCHSTATUS_GETDESCRI;
}
}
}
}
if (s_SerchStatus != SERCHSTATUS_GETDESCRI)
{
break;
}
case SERCHSTATUS_GETDESCRI:
if (GetDescription(s_szdescri))
{
s_SerchStatus = SERCHSTATUS_SERCH;
s_bSearched = XOS_TRUE;
}
else
{
s_bSearched = XOS_FALSE;
}
break;
default:
assert(0);
break;
}
return s_bSearched;
}
/* 功能描述: 搜索设备(路由器)
* 参数说明:
* XOS_BOOL bUseDefaultGateway = XOS_TRUE [IN]:
* 返回值: long
* 备注:
*/
int SearchDevice()
{
//路由器的外网IP可能会改变,URL内容会不会变,????????搜索过获取URL就不用重复获取了
if (s_bSearched)
{
if (strlen(s_szctlurl) > 0)
{
return 0;
}
}
// s_bSearched = XOS_TRUE;
//InternalSearch(1);
if (InternalSearch(1))
{
return 0;
}
else
{
return E_UNAT_UNKNOWN_ERROR;
}
}
int InvokeCommand(const char *pcnt,const char *pcommand,char *psr,char *pstrResponse)
{
char strValue[1024];
char post[1024], host[1024], addr[1024];
char result[1024];
int port = 0;
long hr;
NGetAddressFromUrl(s_szctlurl,post,host,&port,addr);
if(0 == strlen(addr))
{
return E_UNAT_UNKNOWN_ERROR;//
}
Append(psr,"POST ");
Append(psr,post);
Append(psr," HTTP/1.1\r\nHOST: ");
Append(psr,host);
Append(psr,"\r\nContent-Length: ");
sprintf(strValue,"%d",strlen(pcnt));
Append(psr,strValue);
Append(psr,"\r\nContent-Type: text/xml; charset=\"utf-8\"\r\nSOAPAction: \"");
Append(psr,s_cname);
Append(psr,"#");
Append(psr,pcommand);
Append(psr,"\"\r\n\r\n");
Append(psr,pcnt);
//printf("..................................\r\n%s\r\n",psr);
pstrResponse[0] = '\0';
hr = SOAP_action(addr,(unsigned short)port,psr,pstrResponse); //发送控制信息
if (hr < 0)
{
return hr;
}
if (!parseHTTPResponse(pstrResponse, result)) //result存放剩余语句
{
return E_UNAT_ACTION_HTTP_ERRORCODE;
}
return S_OK;
}
/* 功能描述: 获得外网端口
* 返回值:
*/
int GetExternalIPAddress()
{
char strResponse[1024];
char cnt[10240];
char psr[10240];
char szIP[16];
char *s = NULL;
mymxml_t *p_mymxml;
int pri_len,iRet;
if(0 == strlen(s_szctlurl))
{
return E_UNAT_UNKNOWN_ERROR;
}
memset(cnt, 0, 10240);
memset(psr, 0, 10240);
memset(szIP, 0, 16);
// ZeroMemory(cnt,10240);
// ZeroMemory(psr,10240);
// ZeroMemory(szIP,16);
s = (char*)xmlpro;
p_mymxml = mymxmlLoadString(s);
pri_len = mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetExternalIPAddress");
mymxmlSetAttributeValue(p_mymxml, "", "xmlns:u", s_cname);
mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlDelete(p_mymxml);
iRet = InvokeCommand(cnt,UPNPGETEXTERNALIP,psr,strResponse);
if (iRet == S_OK)
{
s_bInAddSOAP = XOS_TRUE;
getProperty(strResponse, "NewExternalIPAddress",szIP);
if (strlen(szIP) > 0)
{
s_u32ExternalIp = XOS_AddrToU32(szIP);
xlprintf("[%s] UPnPNATMapping GetExternalIPAddress is:%s.\r\n","upnpnat",szIP);
}
}
return iRet;
}
//该函数根据指定设备和外网端口获得内网信息
int GetSpecificPortMappingEntry(const char *lpszRemoteHost, //[in ]""
unsigned short usExternalPort, //[in ]
const char *lpszPortMappingProtocol, //[in ]
unsigned short *pusInternalPort, //[out]NULL
char *pstrInternalClient, //[out]
XOS_BOOL *pbEnable, //[out]NULL
char *pstrDescription, //[out]NULL
unsigned long *pulPortMappingLeaseDuration) //[out]NULL
{
char strResponse[1024];
char string[25];
char cnt[10240];
char psr[10240];
char strValue[1024];
char*s = (char*)xmlpro;
mymxml_t *p_mymxml;
int pri_len,iRet;
// mxml_node_t *p_mxml_node_t;
if(0 == strlen(s_szctlurl))
{
return XOS_FALSE;
}
memset(cnt, 0, 10240);
memset(psr, 0, 10240);
// ZeroMemory(cnt,10240);
// ZeroMemory(psr,10240);
p_mymxml = mymxmlLoadString(s);
pri_len = mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetSpecificPortMappingEntry");
mymxmlSetAttributeValue(p_mymxml,"","xmlns:u",s_cname);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetSpecificPortMappingEntry/NewRemoteHost");
mymxmlSetValue(p_mymxml, "", lpszRemoteHost);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetSpecificPortMappingEntry/NewExternalPort");
sprintf(string,"%d",usExternalPort);
mymxmlSetValue(p_mymxml, "", string/*itoa(usExternalPort,string, 10)(const char *)&usExternalPort*/);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetSpecificPortMappingEntry/NewProtocol");
mymxmlSetValue(p_mymxml, "", lpszPortMappingProtocol);
mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlDelete(p_mymxml);
iRet = InvokeCommand(cnt,GETSPECIFICPORTMAPPINGENTRY,psr,strResponse);
if (NULL != pusInternalPort)
{
getProperty(strResponse, "NewInternalPort",strValue);
*pusInternalPort = (unsigned short)atoi(strValue);
}
if (NULL != pstrInternalClient)
{
getProperty(strResponse, "NewInternalClient",pstrInternalClient);
}
if (NULL != pbEnable)
{
getProperty(strResponse, "NewEnabled",strValue);
*pbEnable = atoi(strValue) == 0 ? XOS_FALSE : XOS_TRUE;
}
if (NULL != pstrDescription)
{
getProperty(strResponse, "NewPortMappingDescription",pstrDescription);
}
if (NULL != pulPortMappingLeaseDuration)
{
getProperty(strResponse, "NewLeaseDuration",strValue);
*pulPortMappingLeaseDuration = (unsigned long)atoi(strValue);
}
return 0;
}
/* 功能描述: 获得端口映射信息
* 返回值:
*/
int GetGenericPortMappingEntry(unsigned int uiPortMappingIndex)
{
char strResponse[1024];
char string[25];
char cnt[10240];
char psr[10240];
int pri_len,iRet;
char*s = (char*)xmlpro;
mymxml_t *p_mymxml;
if(0 == strlen(s_szctlurl))
{
return XOS_FALSE;
}
memset(cnt, 0, 10240);
memset(psr, 0, 10240);
// ZeroMemory(cnt,10240);
// ZeroMemory(psr,10240);
p_mymxml = mymxmlLoadString(s);
pri_len = mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetGenericPortMappingEntry");
mymxmlSetAttributeValue(p_mymxml, "", "xmlns:u", s_cname);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:GetGenericPortMappingEntry/NewPortMappingIndex");
sprintf(string, "%d", uiPortMappingIndex);
mymxmlSetValue(p_mymxml, "", string/*itoa(uiPortMappingIndex,string, 10)*/);
mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlDelete(p_mymxml);
iRet = InvokeCommand(cnt,UPNPGETGENERICPORTMAPPINGENTRY,psr,strResponse);
return iRet;
}
int AddPortMapping(const char *lpszRemoteHost,/*""*/
unsigned short usExternalPort,
const char *lpszPortMappingProtocol,
unsigned short usInternalPort,
const char *lpszInternalClient,
const char *lpszPortMappingDescription /*= NULL*/,
XOS_BOOL bPortMappingEnabled/* = XOS_TRUE*/,
unsigned long ulPortMappingLeaseDuration/* = 0*/)
{
char strResponse[1024];
char string[25];
char cnt[10240];
char psr[10240];
int pri_len,iRet;
//
char*s = (char*)xmlpro;
mymxml_t *p_mymxml;
// mxml_node_t *p_mxml_node_t;
if(0 == strlen(s_szctlurl))
{
return XOS_FALSE;
}
memset(cnt, 0, 10240);
memset(psr, 0, 10240);
// ZeroMemory(cnt,10240);
// ZeroMemory(psr,10240);
p_mymxml = mymxmlLoadString(s);
pri_len = mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping");
mymxmlSetAttributeValue(p_mymxml, "", "xmlns:u", s_cname);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewRemoteHost");
mymxmlSetValue(p_mymxml, "", lpszRemoteHost);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewExternalPort");
sprintf(string, "%d", usExternalPort);
mymxmlSetValue(p_mymxml, "", string/*itoa(usExternalPort,string, 10)(const char *)&usExternalPort*/);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewProtocol");
mymxmlSetValue(p_mymxml, "", lpszPortMappingProtocol);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewInternalPort");
sprintf(string, "%d", usInternalPort);
mymxmlSetValue(p_mymxml, "", string/*itoa(usInternalPort,string, 10)(const char *)&usInternalPort*/);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewInternalClient");
mymxmlSetValue(p_mymxml, "", lpszInternalClient);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewPortMappingDescription");
mymxmlSetValue(p_mymxml, "", lpszPortMappingDescription);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewEnabled");
sprintf(string, "%d", bPortMappingEnabled);
mymxmlSetValue(p_mymxml, "", string/*itoa(bPortMappingEnabled,string, 10)(const char *)&bPortMappingEnabled*/);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:AddPortMapping/NewLeaseDuration");
sprintf(string, "%d", ulPortMappingLeaseDuration);
mymxmlSetValue(p_mymxml, "", string/*itoa(ulPortMappingLeaseDuration,string, 10)(const char *)&ulPortMappingLeaseDuration*/);
mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlDelete(p_mymxml);
iRet = InvokeCommand(cnt,UPNPADDPORTMAP,psr,strResponse);
if (iRet == S_OK)
{
s_bInAddSOAP = XOS_FALSE;
}
return iRet;
}
/* 功能描述: 删除指定端口映射
* 参数说明:
* const char *lpszRemoteHost [IN ]: UPNP设备主机(路由器)
* unsigned short usExternalPort [IN ]: 映射端口
* const char *lpszPortMappingProtocol [IN ]: 协议TCP/UDP
* 返回值:
*/
int DeletePortMapping(const char *lpszRemoteHost,
unsigned short usExternalPort,
const char *lpszPortMappingProtocol)
{
char strResponse[1024];
int pri_len,iRet;
char string[25];
char cnt[10240];
char psr[10240];
char*s = (char*)xmlpro;
mymxml_t *p_mymxml;
// mxml_node_t *p_mxml_node_t;
if(0 == strlen(s_szctlurl))
{
return XOS_FALSE;
}
memset(cnt, 0, 10240);
memset(psr, 0, 10240);
// ZeroMemory(cnt,10240);
// ZeroMemory(psr,10240);
p_mymxml = mymxmlLoadString(s);
pri_len = mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:DeletePortMapping");
mymxmlSetAttributeValue(p_mymxml, "", "xmlns:u", s_cname);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:DeletePortMapping/NewRemoteHost");
mymxmlSetValue(p_mymxml, "", lpszRemoteHost);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:DeletePortMapping/NewExternalPort");
sprintf(string, "%d", usExternalPort);
mymxmlSetValue(p_mymxml, "", string/*itoa(usExternalPort,string,10)*/);
mymxmlSetCurPath(p_mymxml, "/s:Envelope/s:Body/u:DeletePortMapping/NewProtocol");
mymxmlSetValue(p_mymxml, "", lpszPortMappingProtocol);
mymxmlSaveString(p_mymxml, cnt, sizeof(cnt));
mymxmlDelete(p_mymxml);
iRet = InvokeCommand(cnt,UPNPDELPORTMAP,psr,strResponse);
return iRet;
}
XOS_U16 UPnPNAT_GetExternalPort()
{
return s_u16ExternalPort;
}
XOS_U32 UPnPNAT_GetExternalIP()
{
return s_u32ExternalIp;
}
XOS_BOOL UPnPNAT_DelPort()
{
long hr;
if (!UPnPMapInfo.bAddMapping)
{
xlprintf("[%s] UPnPNAT_DelPort:UPnPPort isn't add!!!\r\n","upnpnat");
return XOS_TRUE;
}
hr = DeletePortMapping("", s_u16ExternalPort, UPnPMapInfo.szProtocol);
if (hr != 0)
{
return XOS_FALSE;
}
else
{
UPnPMapInfo.bAddMapping = XOS_FALSE;
s_u16ExternalAdd = 0;
s_bInAddSOAP = XOS_TRUE;
s_u16ExternalPort = 0;
}
return XOS_TRUE;
}
XOS_BOOL UPnPNAT_Close()
{
long hr;
if (!UPnPMapInfo.bAddMapping)
{
return XOS_TRUE;
}
hr = DeletePortMapping("", s_u16ExternalPort, UPnPMapInfo.szProtocol);
if (hr != 0)
{
xlprintf("[%s] DeletePortMapping Fail:%d\r\n", "upnpnat", hr);
return XOS_FALSE;
}
else
{
xlprintf("[%s] UPnPPort Close OK!!\r\n","upnpnat");
s_bSearched = XOS_FALSE;
UPnPMapInfo.bAddMapping = XOS_FALSE;
s_u16ExternalAdd = 0;
s_bInAddSOAP = XOS_FALSE;
s_u16ExternalPort = 0;
}
return XOS_TRUE;
}
void UPnPNAT_AddMapping(TUPnPNATPortMapping *pMap)
{
strcpy(UPnPMapInfo.szDescription,pMap->szDescription);
strcpy(UPnPMapInfo.szInternalIP,pMap->szInternalIP);
strcpy(UPnPMapInfo.szProtocol,pMap->szProtocol);
UPnPMapInfo.u16beginExternalPort = pMap->u16beginExternalPort;
UPnPMapInfo.u16InternalPort = pMap->u16InternalPort;
}
XOS_BOOL UPnPNAT_Init()
{
memset(&UPnPMapInfo,0,sizeof(UPnPMapInfo));
UPnPMapInfo.bAddMapping = XOS_FALSE;
return XOS_TRUE;
}
void UPnPNAT_Timer()
{
long hr;
//查询是否添加过端口映射
if (UPnPMapInfo.bAddMapping)
{
return;
}
if (UPnPMapInfo.u16beginExternalPort == 0)
{
xlprintf("[%s] UPnPMapInfo.u16beginExternalPort Have Not Add\r\n","upnpnat");
return;
}
if(!ISLANIP(XOS_AddrToU32(UPnPMapInfo.szInternalIP)))
{
xlprintf("[%s] UPnPNATMaping input InternalIP is not a LAN IP!\r\n", "upnpnat");
return;
}
//搜索网关设备
if (SearchDevice(1) < 0)
{
xlprintf("[%s] UPnPNATMaping SearchDevice cannot find router!\r\n", "upnpnat");
return ;
}
if (!s_bInAddSOAP)
{
//获取外网IP
hr = GetExternalIPAddress();
}
else
{
//添加映射端口
hr = AddPortMapping("", (XOS_U16)(UPnPMapInfo.u16beginExternalPort + s_u16ExternalAdd), UPnPMapInfo.szProtocol,
UPnPMapInfo.u16InternalPort, UPnPMapInfo.szInternalIP, UPnPMapInfo.szDescription, XOS_TRUE, 0);
switch(hr)
{
case 0:
xlprintf("[%s] UPnPNAT Add Mapping successfully!\r\n","upnpnat");
s_bSearched = XOS_FALSE;
UPnPMapInfo.bAddMapping = XOS_TRUE;
s_u16ExternalPort = UPnPMapInfo.u16beginExternalPort + s_u16ExternalAdd;
// printf("=================s_iExternalPort :%d================\r\n",s_iExternalPort);
break;
case E_UNAT_ACTION_HTTP_ERRORCODE:
if (718 == s_uiLastErrorCode)
{
s_bInAddSOAP = XOS_TRUE;
UPnPMapInfo.bAddMapping = XOS_FALSE;
s_u16ExternalAdd += 1;
xlprintf("[%s] UPnPNAT Add Mapping Have Been Used,Try Next:%d\r\n", "upnpnat", UPnPMapInfo.u16beginExternalPort);
}
else
{
xlprintf("[%s] UPnPMapping error :ACTION HTTP ERROR!\r\n", "upnpnat");
//return E_UNAT_ACTION_HTTP_ERRORCODE;
}
break;
default:
break ;
}
}
return ;
}