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

#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 ;
}

你可能感兴趣的:(String,socket,action,SOAP,newline,tokenize)