static CString NGetAddressFromUrl(const CString& str, CString& post, CString& host, int& port)
{
CString s = str;
post = _T("");
host = post;
port = 0;
int pos = s.Find(_T("://"));
if (!pos) return CString();
s.Delete(0, pos + 3);
pos = s.Find('/');
if (!pos) {
host = s;
s = _T("");
} else {
host = s.Mid(0, pos);
s.Delete(0, pos);
}
if (s.IsEmpty()) {
post = _T("");
} else {
post = s;
}
pos = 0;
CString addr = host.Tokenize(_T(":"), pos);
s = host.Tokenize(_T(":"), pos);
if (s.IsEmpty()) {
port = 80;
} else {
port = _tstoi(s);
}
return addr;
}
bool MyUPnP::GetDescription()
{
if(!Valid())return false;
CString post, host, addr;
int port = 0;
addr = NGetAddressFromUrl(m_description, post, host, port);
if(addr.IsEmpty())return false;
CString request = CString(_T("GET ")) + post + _T(" HTTP/1.1\r\nHOST: ") + host + _T("\r\nACCEPT-LANGUAGE: en\r\n\r\n");
CString response;
if (!SOAP_action(addr, port, request, response)) return false;
CString result;
if (!parseHTTPResponse(response, result)) return false;
m_friendlyname = getProperty(result, _T("friendlyName"));
m_modelname = getProperty(result, _T("modelName"));
m_baseurl = getProperty(result, _T("URLBase"));
if(m_baseurl.IsEmpty())m_baseurl = CString(_T("http://")) + host + _T("/");
if(m_baseurl[m_baseurl.GetLength() - 1]!='/')m_baseurl += _T("/");
CString serviceType = _T("<serviceType>") + m_name + _T("</serviceType>");
int pos = result.Find(serviceType);
if (pos >= 0) {
result.Delete(0, pos + serviceType.GetLength());
pos = result.Find(_T("</service>"));
if (pos >= 0) {
result = result.Mid(0, pos);
m_controlurl = getProperty(result, _T("controlURL"));
if (!m_controlurl.IsEmpty() && m_controlurl[0] == '/') {
m_controlurl = m_baseurl + m_controlurl.Mid(1);
}
}
}
return isComplete();
}
CString MyUPnP::GetProperty(const CString& name, CString& response)
{
if (!isComplete())return CString();
CString post, host, addr;
int port = 0;
addr = NGetAddressFromUrl(m_controlurl, post, host, port);
if(addr.IsEmpty())return CString();
CString cnt;
CString psr;
cnt.Append(_T("<s:Envelope\r\n xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n "));
cnt.Append(_T("s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n <s:Body>\r\n <u:"));
cnt.Append(name);
cnt.Append(_T(" xmlns:u=\""));
cnt.Append(m_name);
cnt.Append(_T("\">\r\n </u:"));
cnt.Append(name);
cnt.Append(_T(">\r\n </s:Body>\r\n</s:Envelope>\r\n\r\n"));
psr.Append(_T("POST "));
psr.Append(post);
psr.Append(_T(" HTTP/1.1\r\nHOST: "));
psr.Append(host);
psr.Append(_T("\r\nContent-Length: "));
psr.Append(getString(CStringA(cnt).GetLength()));
psr.Append(_T("\r\nContent-Type: text/xml; charset=\"utf-8\"\r\nSOAPAction: \""));
psr.Append(m_name);
psr.Append(_T("#"));
psr.Append(name);
psr.Append(_T("\"\r\n\r\n"));
psr.Append(cnt);
CString request = psr;
if (!SOAP_action(addr, port, request, response)) return CString();
CString result;
if (!parseHTTPResponse(response, result)) return CString();
return getProperty(result, response);
}
bool MyUPnP::InvokeCommand(const CString& name, const CString& args)
{
if(!isComplete())return false;
CString post, host, addr;
int port = 0;
addr = NGetAddressFromUrl(m_controlurl, post, host, port);
if(addr.IsEmpty())return false;
CString cnt;
CString psr;
cnt.Append(_T("<?xml version=\"1.0\"?><s:Envelope\r\n xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"\r\n "));
cnt.Append(_T("s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n <s:Body>\r\n <u:"));
cnt.Append(name);
cnt.Append(_T(" xmlns:u=\""));
cnt.Append(m_name);
cnt.Append(_T("\">\r\n"));
cnt.Append(args);
cnt.Append(_T(" </u:"));
cnt.Append(name);
cnt.Append(_T(">\r\n </s:Body>\r\n</s:Envelope>\r\n\r\n"));
psr.Append(_T("POST "));
psr.Append(post);
psr.Append(_T(" HTTP/1.1\r\nHOST: "));
psr.Append(host);
psr.Append(_T("\r\nContent-Length: "));
psr.Append(getString(CStringA(cnt).GetLength()));
psr.Append(_T("\r\nContent-Type: text/xml; charset=\"utf-8\"\r\nSOAPAction: \""));
psr.Append(m_name);
psr.Append(_T("#"));
psr.Append(name);
psr.Append(_T("\"\r\n\r\n"));
psr.Append(cnt);
CString response;
CString request = psr;
if (!SOAP_action(addr, port, request, response)) return false;
CString result;
if (!parseHTTPResponse(response, result)) return false;
return true;
}
bool MyUPnP::addPortmap(int eport, int iport, const CString& iclient, const CString& descri, const CString& type)
{
CString args;
args.Empty();
args.Append(GetArgString(_T("NewRemoteHost"), _T("")));
args.Append(GetArgString(_T("NewExternalPort"), eport));
args.Append(GetArgString(_T("NewProtocol"), type));
args.Append(GetArgString(_T("NewInternalPort"), iport));
args.Append(GetArgString(_T("NewInternalClient"), iclient));
args.Append(GetArgString(_T("NewEnabled"), _T("1")));
args.Append(GetArgString(_T("NewPortMappingDescription"), descri));
args.Append(GetArgString(_T("NewLeaseDuration"), 0));
return InvokeCommand(UPNPADDPORTMAP, args);
}
bool MyUPnP::deletePortmap(int eport, const CString& type)
{
CString args;
args.Empty();
args.Append(GetArgString(_T("NewRemoteHost"), _T("")));
args.Append(GetArgString(_T("NewExternalPort"), eport));
args.Append(GetArgString(_T("NewProtocol"), type));
return InvokeCommand(UPNPDELPORTMAP, args);
}
/////////////////////////////////////////////////////////////////////////////////
// Adds a NAT Port Mapping
// Params:
// UPNPNAT_MAPPING *mapping -> Port Mapping Data
// If mapping->externalPort is 0, then
// mapping->externalPort gets the value of mapping->internalPort
// bool tryRandom:
// If If mapping->externalPort is in use, tries to find a free
// random external port.
//
// Return:
// UNAT_OK:
// Successfull.
// UNAT_EXTERNAL_PORT_IN_USE:
// Error, you are trying to add a port mapping with an external port
// in use.
// UNAT_NOT_IN_LAN:
// Error, you aren't in a LAN -> no router or firewall
// UNAT_ERROR:
// Error, use GetLastError() to get an error description.
/////////////////////////////////////////////////////////////////////////////////
MyUPnP::UPNPNAT_RETURN MyUPnP::AddNATPortMapping(UPNPNAT_MAPPING *mapping, bool tryRandom)
{
CString ProtoStr, Proto;
if(!IsLANIP(GetLocalIP())){
SetLastError(_T("You aren't behind a Hardware Firewall or Router"));
return UNAT_NOT_IN_LAN;
}
if (!isComplete()) {
Search();
if (!isComplete()) {
SetLastError(_T("Can not found a UPnP Router"));
return UNAT_ERROR;
}
}
if (mapping->protocol == UNAT_TCP){
Proto = _T("TCP");
ProtoStr = _T("TCP");
}
else {
Proto = _T("UDP");
ProtoStr = _T("UDP");
}
if(mapping->externalPort == 0)
mapping->externalPort = mapping->internalPort;
int retries = 255;
WORD rndPort = mapping->externalPort;
for (int retries = 255; retries; retries--) {
CString Desc;
Desc.Format(_T("eMule (%s) [%s: %u]"), mapping->description, ProtoStr, mapping->externalPort);
if (addPortmap(mapping->externalPort, mapping->internalPort, GetLocalIPStr(), Desc, Proto)) {
m_Mappings.AddTail(*mapping);
return UNAT_OK;
}
if (!tryRandom) {
SetLastError(_T("External NAT port in use"));
return UNAT_EXTERNAL_PORT_IN_USE;
}
mapping->externalPort = 2049 + (65535 - 2049) * rand() / (RAND_MAX + 1);
}
SetLastError(_T("External NAT port in use: Too many retries"));
return UNAT_EXTERNAL_PORT_IN_USE;
}