前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >UPnP端口映射实现过程(二)

UPnP端口映射实现过程(二)

作者头像
TSINGEYE清眸物联
发布2023-01-04 14:29:18
6680
发布2023-01-04 14:29:18
举报
代码语言:javascript
复制
#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 ;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2012-06-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档