📄 upnpnat.cpp
字号:
/*************************************************************************** * Copyright (C) 2005-2006 Gao Xianchao * * 2007 Gao Xianchao gnap_an linux_lyb ahlongxp * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/#include <unistd.h>#include <errno.h>#include <fcntl.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <glib.h>#include "UPnpNat.h"#include "log.h"#include "utils.h"/***************************************************************** General Defines *****************************************************************/#define HTTP_OK "200 OK"#define DEFAULT_HTTP_PORT 80#define MAX_PORT_SIZE 6#define SIZEOF_HTTP 7 /* size of "http://" string */#define RECEIVE_TIMEOUT 10000#define CONSECUTIVE_RECEIVE_TIMEOUT 500#define DISCOVERY_TIMEOUT 1000/***************************************************************** Discovery/Description Defines *****************************************************************/#define NUM_UDP_ATTEMPTS 2/* Address and port of an SSDP request used for discovery */#define HTTPMU_HOST_ADDRESS "239.255.255.250"#define HTTPMU_HOST_PORT 1900#define SEARCH_REQUEST_DEVICE "urn:schemas-upnp-org:service:%s"#define SEARCH_REQUEST_STRING "M-SEARCH * HTTP/1.1\r\n" \ "HOST: 239.255.255.250:1900\r\n" \ "ST:upnp:rootdevice\r\n" \ "Man:\"ssdp:discover\"\r\n" \ "MX: 3\r\n" \ "\r\n"#define MAX_DISCOVERY_RECEIVE_SIZE 400#define MAX_DESCRIPTION_RECEIVE_SIZE 7000#define MAX_DESCRIPTION_HTTP_HEADER_SIZE 100/******************************************************************** Action Defines ********************************************************************/#define HTTP_HEADER_ACTION "POST %s HTTP/1.1\r\n" \ "HOST: %s\r\n" \ "SOAPACTION: " \ "\"urn:schemas-upnp-org:" \ "service:%s#%s\"\r\n" \ "CONTENT-TYPE: text/xml ; charset=\"utf-8\"\r\n"\ "Content-Length: %" G_GSIZE_FORMAT "\r\n\r\n"#define SOAP_ACTION "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \ "<s:Envelope xmlns:s=" \ "\"http://schemas.xmlsoap.org/soap/envelope/\" " \ "s:encodingStyle=" \ "\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \ "<s:Body>\r\n" \ "<u:%s xmlns:u=" \ "\"urn:schemas-upnp-org:service:%s\">\r\n%s" \ "</u:%s>\r\n" \ "</s:Body>\r\n" \ "</s:Envelope>\r\n"#define PORT_MAPPING_LEASE_TIME "0"#define PORT_MAPPING_DESCRIPTION "BitStorm"#define ADD_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \ "<NewExternalPort>%i</NewExternalPort>\r\n"\ "<NewProtocol>%s</NewProtocol>\r\n" \ "<NewInternalPort>%i</NewInternalPort>\r\n"\ "<NewInternalClient>%s" \ "</NewInternalClient>\r\n" \ "<NewEnabled>1</NewEnabled>\r\n" \ "<NewPortMappingDescription>" \ PORT_MAPPING_DESCRIPTION \ "</NewPortMappingDescription>\r\n" \ "<NewLeaseDuration>" \ PORT_MAPPING_LEASE_TIME \ "</NewLeaseDuration>\r\n"#define DELETE_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \ "<NewExternalPort>%i" \ "</NewExternalPort>\r\n" \ "<NewProtocol>%s</NewProtocol>\r\n" #define wanIP "WANIPConnection:1"#define wanPPP "WANPPPConnection:1" // class CUPnpNatExplorerCUPnpNatExplorer::CUPnpNatExplorer(): _reactor(NULL), _eventHandler(NULL), _discoverTimerID(0), _found(false){}CUPnpNatExplorer::~CUPnpNatExplorer(){}void CUPnpNatExplorer::setSocketReactor(ISocketReactor* reactor){ _reactor = reactor;}void CUPnpNatExplorer::setEventHandler(IUPnpNatExplorerEventHandler* handler){ _eventHandler = handler;}void CUPnpNatExplorer::start(){ LOG_DEBUG("CUPnpNatExplorer::start"); _found = false; CSocket::setReactor(NULL); CSocket::close(); CSocket::createUDPSocket(); CSocket::setReactor(_reactor); discover();}void CUPnpNatExplorer::stop(){ CSocket::setReactor(NULL); CSocket::close(); }void CUPnpNatExplorer::discover(){ struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = inet_addr(HTTPMU_HOST_ADDRESS); server.sin_port = htons(HTTPMU_HOST_PORT); std::string sendBuffer = SEARCH_REQUEST_STRING; CSocket::maskRead(true); sendto(getHandle(), sendBuffer.data(), sendBuffer.size(), 0, (const struct sockaddr*)&server, sizeof(server)); if(_discoverTimerID != 0) { _reactor->removeTimer(_discoverTimerID); } _discoverTimerID = _reactor->addTimer(this, 5000, true); }void CUPnpNatExplorer::parseDiscoverReponse(char* buffer, int len){ LOG_DEBUG("CUPnpNatExplorer::parseDiscoverReponse"); LOG_DEBUG("response = "<<buffer); gchar* buf = buffer; gchar* startDescURL; gchar* endDescURL; gchar* descURL; if(g_strstr_len(buf, strlen(buf), HTTP_OK) == NULL) { return; } if((startDescURL = g_strstr_len(buf, strlen(buf), "http://")) == NULL) { return; } endDescURL = g_strstr_len(startDescURL, strlen(startDescURL), "\r"); if(endDescURL == NULL) { endDescURL = g_strstr_len(startDescURL, strlen(startDescURL), "\n"); if(endDescURL == NULL) { return; } else if(endDescURL == startDescURL) { return; } } else if(endDescURL == startDescURL) { return; } descURL = g_strndup(startDescURL, endDescURL-startDescURL); LOG_DEBUG("descURL="<<descURL); _found = true; if(_eventHandler != NULL) { _eventHandler->OnGetDescriptionUrl(descURL); } g_free(descURL); }int CUPnpNatExplorer::handleRead(){ struct sockaddr_in from; socklen_t length = sizeof(struct sockaddr_in); char* buffer = new char[MAX_DISCOVERY_RECEIVE_SIZE]; int ret = 0; for(;;) { ret = recvfrom(getHandle(), buffer, MAX_DISCOVERY_RECEIVE_SIZE, 0, (struct sockaddr*)&from, &length); if(ret == -1 && errno == EINTR) { continue; } break; } if(ret > 0) { buffer[ret] = 0; parseDiscoverReponse(buffer, ret); } delete[] buffer; return 0;}int CUPnpNatExplorer::handleWrite(){ return 0;}void CUPnpNatExplorer::handleClose(){ CSocket::setReactor(NULL); CSocket::close();}void CUPnpNatExplorer::onTimer(unsigned int id){ if(id == _discoverTimerID) { if(!_found) { discover(); } }}CUPnpNatDescriptionParser::CUPnpNatDescriptionParser(): _reactor(NULL), _eventHandler(NULL), _timeoutTimerID(0), _state(DPS_INIT){}CUPnpNatDescriptionParser::~CUPnpNatDescriptionParser(){}void CUPnpNatDescriptionParser::setSocketReactor(ISocketReactor* reactor){ _reactor = reactor;}void CUPnpNatDescriptionParser::setDescriptionUrl(const char* url){ _url = url;}void CUPnpNatDescriptionParser::setEventHandler(IUPnpNatDescriptionParserEventHandler* handler){ _eventHandler = handler;}void CUPnpNatDescriptionParser::start(){ _timeoutTimerID = _reactor->addTimer(this, 5000, true); connect(); _state = DPS_WORKING;}void CUPnpNatDescriptionParser::stop(){ if(_timeoutTimerID != 0) { _reactor->removeTimer(_timeoutTimerID); _timeoutTimerID = 0; } CSocket::setReactor(NULL); CSocket::close();}int CUPnpNatDescriptionParser::handleRead(){ char buffer[1024]; for(;;) { int ret = recv(getHandle(), buffer, sizeof(buffer), 0); LOG_DEBUG("recv return "<<ret); if(ret == 0) { maskRead(false); return -1; } if(ret == -1) { if(errno == EINTR) { continue; } if(errno != EAGAIN) { maskRead(false); return -1; } break; } if(ret > 0) { _recvBuffer.append(buffer, ret); } } return 0;}int CUPnpNatDescriptionParser::handleWrite(){ LOG_DEBUG("CUPnpNatDescriptionParser::handleWrite"); sendRequest(); maskWrite(false); return 0;}void CUPnpNatDescriptionParser::handleClose(){ LOG_DEBUG("CUPnpNatDescriptionParser::handleClose"); bool parseResult = false; if(_recvBuffer.size() > 0) { char str[1024]; std::string ip; std::string path; unsigned short port; parseUrl(_url.c_str(), ip, &port, path); sprintf(str, "http://%s:%d", ip.c_str(), port); parseResult= parseResponse(_recvBuffer.c_str(), str, wanIP); if(!parseResult) { parseResult = parseResponse(_recvBuffer.c_str(), str, wanPPP); } } if(!parseResult) { _state = DPS_ERROR; } else { _state = DPS_OK; } CSocket::setReactor(NULL); CSocket::close();}void CUPnpNatDescriptionParser::onTimer(unsigned int id){ if( id == _timeoutTimerID) { handleClose(); _timeoutTimerID = 0; }}void CUPnpNatDescriptionParser::connect(){ std::string ip; std::string path; unsigned short port; parseUrl(_url.c_str(), ip, &port, path); LOG_DEBUG("ip="<<ip<<" port="<<port<<" path="<<path); CSocket::createTCPSocket(); CSocket::setReactor(_reactor); CSocket::connect(ip.c_str(), port); CSocket::maskWrite(true); }void CUPnpNatDescriptionParser::sendRequest(){ std::string ip; std::string path; unsigned short port; parseUrl(_url.c_str(), ip, &port, path); gchar* httpRequest = g_strdup_printf("GET %s HTTP/1.1\r\nHost: %s:%d\r\n\r\n", path.c_str(), ip.c_str(), port); LOG_DEBUG("httpRequest="<<httpRequest); send(getHandle(), httpRequest, strlen(httpRequest), 0); g_free(httpRequest); maskRead(true); }bool CUPnpNatDescriptionParser::parseResponse(const char* response, const char* httpUrl, const char* serviceType){ gchar* httpResponse = (gchar*)response; gchar* httpURL = (gchar*)httpUrl; gchar* xmlRoot; gchar* baseURL; gchar* controlURL; gchar* service; xmlnode* xmlRootNode; xmlnode* serviceTypeNode; xmlnode* controlURLNode; xmlnode* baseURLNode; // LOG_DEBUG("response = \n"<<response); /* make sure we have a valid http response */ if(g_strstr_len(httpResponse, strlen(httpResponse), HTTP_OK) == NULL) { return false; } /* find the root of the xml document */ if((xmlRoot = g_strstr_len(httpResponse, strlen(httpResponse), "<root")) == NULL) { return false; } /* create the xml root node */ if((xmlRootNode = xmlnode_from_str(xmlRoot, size_t(-1))) == NULL) { return false; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -