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