File indexing completed on 2024-05-19 05:40:45
0001 // #include <winsock2.h> 0002 #include <iostream> 0003 #include <string> 0004 0005 #include "network/upnp/upnpnat.h" 0006 // #include "network/upnp/xmlParser.h" 0007 0008 #include <QNetworkInterface> 0009 #include <QTcpSocket> 0010 #include <QUdpSocket> 0011 #include <QUrl> 0012 #include <QXmlStreamReader> 0013 0014 #define MAX_BUFF_SIZE 102400 0015 0016 std::tuple<QString, int, QString> parseUrl(const QString& urlData) 0017 { 0018 QUrl url(urlData); 0019 return {url.host(), url.port(), url.path()}; 0020 } 0021 0022 /****************************************************************** 0023 ** Discovery Defines * 0024 *******************************************************************/ 0025 // clang-format off 0026 #define HTTPMU_HOST_ADDRESS "239.255.255.250" 0027 #define HTTPMU_HOST_ADDRESS_V6 "FF02::1" 0028 #define HTTPMU_HOST_PORT 1900 0029 #define SEARCH_REQUEST_STRING "M-SEARCH * HTTP/1.1\r\n" \ 0030 "ST:UPnP:rootdevice\r\n" \ 0031 "MX: 3\r\n" \ 0032 "Man:\"ssdp:discover\"\r\n" \ 0033 "HOST: 239.255.255.250:1900\r\n" \ 0034 "\r\n" 0035 #define HTTP_OK "200 OK" 0036 #define DEFAULT_HTTP_PORT 80 0037 0038 0039 /****************************************************************** 0040 ** Device and Service Defines * 0041 *******************************************************************/ 0042 0043 #define DEVICE_TYPE_1 "urn:schemas-upnp-org:device:InternetGatewayDevice:1" 0044 #define DEVICE_TYPE_2 "urn:schemas-upnp-org:device:WANDevice:1" 0045 #define DEVICE_TYPE_3 "urn:schemas-upnp-org:device:WANConnectionDevice:1" 0046 0047 #define SERVICE_WANIP "urn:schemas-upnp-org:service:WANIPConnection:1" 0048 #define SERVICE_WANPPP "urn:schemas-upnp-org:service:WANPPPConnection:1" 0049 0050 0051 /****************************************************************** 0052 ** Action Defines * 0053 *******************************************************************/ 0054 #define HTTP_HEADER_ACTION "POST %1 HTTP/1.1\r\n" \ 0055 "HOST: %2:%3\r\n" \ 0056 "SOAPACTION:\"%4#%5\"\r\n" \ 0057 "CONTENT-TYPE: text/xml ; charset=\"utf-8\"\r\n"\ 0058 "Content-Length: %6 \r\n\r\n" 0059 0060 #define SOAP_ACTION "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \ 0061 "<s:Envelope xmlns:s=" \ 0062 "\"http://schemas.xmlsoap.org/soap/envelope/\" " \ 0063 "s:encodingStyle=" \ 0064 "\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \ 0065 "<s:Body>\r\n" \ 0066 "<u:%1 xmlns:u=\"%2\">\r\n%3" \ 0067 "</u:%4>\r\n" \ 0068 "</s:Body>\r\n" \ 0069 "</s:Envelope>\r\n" 0070 0071 #define PORT_MAPPING_LEASE_TIME "0" //two year 0072 0073 #define ADD_PORT_MAPPING_PARAMS "<NewRemoteHost></NewRemoteHost>\r\n" \ 0074 "<NewExternalPort>%1</NewExternalPort>\r\n"\ 0075 "<NewProtocol>%2</NewProtocol>\r\n" \ 0076 "<NewInternalPort>%3</NewInternalPort>\r\n"\ 0077 "<NewInternalClient>%4</NewInternalClient>\r\n" \ 0078 "<NewEnabled>1</NewEnabled>\r\n" \ 0079 "<NewPortMappingDescription>%5</NewPortMappingDescription>\r\n" \ 0080 "<NewLeaseDuration>" \ 0081 PORT_MAPPING_LEASE_TIME \ 0082 "</NewLeaseDuration>\r\n" 0083 0084 #define ACTION_ADD "AddPortMapping" 0085 // clang-format on 0086 //********************************************************************************* 0087 0088 UpnpNat::UpnpNat(QObject* parent) : QObject(parent) {} 0089 0090 UpnpNat::~UpnpNat()= default; 0091 0092 void UpnpNat::init(int time, int inter) 0093 { 0094 m_time_out= time; 0095 m_interval= inter; 0096 setStatus(NAT_STAT::NAT_INIT); 0097 0098 for(auto const& address : QNetworkInterface::allAddresses()) 0099 { 0100 if(address.protocol() == QAbstractSocket::IPv4Protocol && address != QHostAddress(QHostAddress::LocalHost)) 0101 { 0102 if(m_subnet.isNull()) 0103 setLocalIp(address.toString()); 0104 else if(address.isInSubnet(m_subnet, m_mask)) 0105 { 0106 setLocalIp(address.toString()); 0107 } 0108 } 0109 } 0110 } 0111 0112 void UpnpNat::tcpConnect(const QString& host, int port, std::function<void()> onConnected, 0113 std::function<void()> onReadReady) 0114 { 0115 if(m_tcpSocket) 0116 m_tcpSocket.release(); 0117 0118 m_tcpSocket.reset(new QTcpSocket(this)); 0119 0120 connect(m_tcpSocket.get(), &QTcpSocket::readyRead, this, [onReadReady]() { onReadReady(); }); 0121 0122 connect(m_tcpSocket.get(), &QTcpSocket::connected, this, 0123 [this, onConnected]() 0124 { 0125 setStatus(NAT_STAT::NAT_TCP_CONNECTED); 0126 onConnected(); 0127 }); 0128 0129 connect(m_tcpSocket.get(), &QTcpSocket::errorOccurred, this, 0130 [this, host, port](QAbstractSocket::SocketError) 0131 { 0132 static int i= 0; 0133 ++i; 0134 setLastError(m_tcpSocket->errorString()); 0135 (i < m_time_out) ? m_tcpSocket->connectToHost(QHostAddress(host), port) : 0136 setLastError(m_tcpSocket->errorString()); 0137 }); 0138 m_tcpSocket->connectToHost(QHostAddress(host), port); 0139 } 0140 0141 void UpnpNat::discovery() 0142 { 0143 m_udpSocketV4= new QUdpSocket(this); 0144 0145 QHostAddress broadcastIpV4(HTTPMU_HOST_ADDRESS); 0146 // QHostAddress broadcastIpV6(HTTPMU_HOST_ADDRESS_V6); 0147 0148 m_udpSocketV4->bind(QHostAddress(QHostAddress::AnyIPv4), 0); 0149 // m_udpSocketV6->bind(QHostAddress(QHostAddress::AnyIPv6), m_udpSocketV4->localPort()); 0150 QByteArray datagram(SEARCH_REQUEST_STRING); 0151 0152 connect(m_udpSocketV4, &QTcpSocket::readyRead, this, 0153 [this]() 0154 { 0155 QByteArray datagram; 0156 while(m_udpSocketV4->hasPendingDatagrams()) 0157 { 0158 datagram.resize(int(m_udpSocketV4->pendingDatagramSize())); 0159 m_udpSocketV4->readDatagram(datagram.data(), datagram.size()); 0160 } 0161 0162 QString result(datagram); 0163 auto start= result.indexOf("http://"); 0164 auto end= result.indexOf("\r", start); 0165 0166 if(start < 0 || end < 0) 0167 { 0168 setLastError(tr("Unable to read the URL in server answer")); 0169 return; 0170 } 0171 0172 m_describe_url= result.sliced(start, end - start); 0173 0174 setStatus(NAT_STAT::NAT_FOUND); 0175 m_udpSocketV4->close(); 0176 }); 0177 0178 connect(m_udpSocketV4, &QUdpSocket::errorOccurred, this, 0179 [this](QAbstractSocket::SocketError) { setLastError(m_tcpSocket->errorString()); }); 0180 0181 m_udpSocketV4->writeDatagram(datagram, broadcastIpV4, HTTPMU_HOST_PORT); 0182 } 0183 0184 void UpnpNat::readDescription() 0185 { 0186 auto [host, port, path]= parseUrl(m_describe_url); 0187 if(host.isEmpty() || port < 0 || path.isEmpty()) 0188 { 0189 setLastError("Failed to parseURl: " + m_describe_url + "\n"); 0190 return; 0191 } 0192 0193 // connect 0194 QString resquest("GET %1 HTTP/1.1\r\nHost: %2:%3\r\n\r\n"); 0195 QString http_request= resquest.arg(path, host).arg(port); 0196 0197 auto connected= [this, http_request]() { m_tcpSocket->write(http_request.toLocal8Bit()); }; 0198 auto readAll= [this]() 0199 { 0200 auto data= m_tcpSocket->readAll(); 0201 if(m_description_info.isEmpty()) 0202 { 0203 auto pos= data.indexOf("<?xml"); 0204 if(pos >= 0) 0205 m_description_info= data.sliced(pos); 0206 } 0207 else 0208 m_description_info+= QString(data); 0209 0210 if(m_description_info.contains("</root>")) 0211 { 0212 setStatus(NAT_STAT::NAT_DESCRIPTION_FOUND); 0213 emit discoveryEnd(parseDescription()); 0214 } 0215 }; 0216 tcpConnect(host, port, connected, readAll); 0217 } 0218 0219 bool UpnpNat::parseDescription() 0220 { 0221 // qDebug() << m_description_info; 0222 QXmlStreamReader xml(m_description_info); 0223 bool isType1= false; 0224 bool isType2= false; 0225 bool isType3= false; 0226 0227 auto goToNextCharacter= [&xml]() 0228 { 0229 while(!xml.isCharacters() || xml.isWhitespace()) 0230 xml.readNext(); 0231 }; 0232 0233 while(!xml.atEnd()) 0234 { 0235 xml.readNext(); 0236 // qDebug() << xml.name(); 0237 if(xml.name() == QLatin1String("URLBase")) 0238 { 0239 goToNextCharacter(); 0240 m_base_url= xml.text().toString(); 0241 } 0242 if(xml.name() == QLatin1String("deviceType")) 0243 { 0244 // QSet<QString> set{DEVICE_TYPE_1, DEVICE_TYPE_2, DEVICE_TYPE_3}; 0245 goToNextCharacter(); 0246 auto text= xml.text().toString(); 0247 if(text == DEVICE_TYPE_1) 0248 isType1= true; 0249 if(text == DEVICE_TYPE_2) 0250 isType2= true; 0251 if(text == DEVICE_TYPE_3) 0252 isType3= true; 0253 } 0254 if(xml.name() == QLatin1String("serviceType")) 0255 { 0256 goToNextCharacter(); 0257 auto serviceType= xml.text().toString(); 0258 if((serviceType == QLatin1String(SERVICE_WANIP) || serviceType == QLatin1String(SERVICE_WANPPP)) 0259 && m_service_type.isEmpty()) 0260 { 0261 m_service_type= serviceType; 0262 } 0263 } 0264 if(xml.name() == QLatin1String("controlURL")) 0265 { 0266 goToNextCharacter(); 0267 m_control_url= xml.text().toString(); 0268 } 0269 } 0270 0271 if(m_base_url.isEmpty()) 0272 { 0273 auto index= m_describe_url.indexOf("/", 7); 0274 if(index < 0) 0275 { 0276 setLastError(tr("Fail to get base_URL from XMLNode \"URLBase\" or describe_url.\n")); 0277 return false; 0278 } 0279 m_base_url= m_describe_url.sliced(0, index); 0280 } 0281 0282 if(!isType1 || !isType2 || !isType3) 0283 { 0284 setLastError(tr("Fail to find proper service type: %1 %2 %3").arg(isType1).arg(isType2).arg(isType3)); 0285 return false; 0286 } 0287 0288 // make the complete control_url; 0289 if(!m_control_url.startsWith("http://", Qt::CaseInsensitive)) 0290 m_control_url= m_base_url + m_control_url; 0291 if(!m_service_describe_url.startsWith("http://", Qt::CaseInsensitive)) 0292 m_service_describe_url= m_base_url + m_service_describe_url; 0293 0294 #ifndef UNIT_TEST 0295 qDebug() << "##############"; 0296 qDebug() << "Service: " << m_service_type; 0297 qDebug() << "describe:" << m_describe_url; 0298 qDebug() << "Control:" << m_control_url; 0299 qDebug() << "Base:" << m_base_url; 0300 qDebug() << "service url:" << m_service_describe_url; 0301 qDebug() << "Description:" << m_description_info; 0302 qDebug() << "##############" << isType1 << isType2 << isType3; 0303 #endif 0304 return true; 0305 } 0306 0307 void UpnpNat::addPortMapping(const QString& description, const QString& destination_ip, unsigned short int port_ex, 0308 unsigned short int port_in, const QString& protocol) 0309 { 0310 auto [host, port, path]= parseUrl(m_control_url); 0311 if(host.isEmpty() || port < 0 || path.isEmpty()) 0312 { 0313 setLastError("Fail to parseURl: " + m_describe_url + "\n"); 0314 return; 0315 } 0316 0317 QString action_params(ADD_PORT_MAPPING_PARAMS); 0318 0319 action_params= action_params.arg(port_ex).arg(protocol).arg(port_in).arg(destination_ip).arg(description); 0320 0321 QString soap_message(SOAP_ACTION); 0322 soap_message= soap_message.arg(ACTION_ADD).arg(m_service_type).arg(action_params).arg(ACTION_ADD); 0323 0324 QString action_message(HTTP_HEADER_ACTION); 0325 action_message 0326 = action_message.arg(path).arg(host).arg(port).arg(m_service_type).arg(ACTION_ADD).arg(soap_message.size()); 0327 0328 QString http_request= action_message + soap_message; 0329 0330 auto connected= [this, http_request]() { m_tcpSocket->write(http_request.toLocal8Bit()); }; 0331 auto readAll= [this, description, protocol]() 0332 { 0333 auto data= m_tcpSocket->readAll(); 0334 if(data.indexOf(HTTP_OK) < 0 && status() != NAT_STAT::NAT_ADD) 0335 { 0336 0337 setLastError(tr("Fail to add port mapping (%1/%2) : error :\n").arg(description, protocol).arg(data)); 0338 emit portMappingEnd(false); 0339 return; 0340 } 0341 emit portMappingEnd(true); 0342 setStatus(NAT_STAT::NAT_ADD); 0343 }; 0344 0345 tcpConnect(host, port, connected, readAll); 0346 } 0347 0348 void UpnpNat::setStatus(NAT_STAT status) 0349 { 0350 if(m_status == status) 0351 return; 0352 m_status= status; 0353 emit statusChanged(); 0354 0355 if(NAT_STAT::NAT_FOUND == m_status) 0356 readDescription(); 0357 } 0358 0359 void UpnpNat::setLocalIp(const QString& ip) 0360 { 0361 if(m_localIp == ip) 0362 return; 0363 0364 m_localIp= ip; 0365 emit localIpChanged(); 0366 } 0367 QString UpnpNat::localIp() const 0368 { 0369 return m_localIp; 0370 } 0371 0372 UpnpNat::NAT_STAT UpnpNat::status() const 0373 { 0374 return m_status; 0375 } 0376 0377 void UpnpNat::setLastError(const QString& error) 0378 { 0379 if(m_last_error == error) 0380 return; 0381 m_last_error= error; 0382 if(!m_last_error.isEmpty()) 0383 setStatus(NAT_STAT::NAT_ERROR); 0384 emit lastErrorChanged(); 0385 } 0386 0387 QHostAddress UpnpNat::subnet() const 0388 { 0389 return m_subnet; 0390 } 0391 0392 void UpnpNat::setSubnet(const QHostAddress& subnet) 0393 { 0394 if(m_subnet == subnet) 0395 return; 0396 m_subnet= subnet; 0397 emit subnetChanged(); 0398 } 0399 0400 int UpnpNat::mask() const 0401 { 0402 return m_mask; 0403 } 0404 void UpnpNat::setMask(int mask) 0405 { 0406 if(mask == m_mask) 0407 return; 0408 m_mask= mask; 0409 emit maskChanged(); 0410 } 0411 0412 /*bool UpnpNat::parser_description() 0413 { 0414 XMLNode node= XMLNode::parseString(m_description_info.toStdString().c_str(), "root"); 0415 if(node.isEmpty()) 0416 { 0417 setLastError(tr("The device descripe XML file is not a valid XML file. Cann't find root element.\n")); 0418 setStatus(NAT_STAT::NAT_ERROR); 0419 return false; 0420 } 0421 0422 XMLNode baseURL_node= node.getChildNode("URLBase", 0); 0423 if(!baseURL_node.getText()) 0424 { 0425 auto index= m_describe_url.indexOf("/", 7); 0426 if(index < 0) 0427 { 0428 setLastError(tr("Fail to get base_URL from XMLNode \"URLBase\" or describe_url.\n")); 0429 setStatus(NAT_STAT::NAT_ERROR); 0430 return false; 0431 } 0432 m_base_url= m_describe_url.sliced(0, index); 0433 } 0434 else 0435 m_base_url= baseURL_node.getText(); 0436 0437 int num, i; 0438 XMLNode device_node, deviceList_node, deviceType_node; 0439 num= node.nChildNode("device"); 0440 for(i= 0; i < num; i++) 0441 { 0442 device_node= node.getChildNode("device", i); 0443 if(device_node.isEmpty()) 0444 break; 0445 deviceType_node= device_node.getChildNode("deviceType", 0); 0446 if(strcmp(deviceType_node.getText(), DEVICE_TYPE_1) == 0) 0447 break; 0448 } 0449 0450 if(device_node.isEmpty()) 0451 { 0452 setLastError("Fail to find device \"urn:schemas-upnp-org:device:InternetGatewayDevice:1 \"\n"); 0453 setStatus(NAT_STAT::NAT_ERROR); 0454 return false; 0455 } 0456 0457 deviceList_node= device_node.getChildNode("deviceList", 0); 0458 if(deviceList_node.isEmpty()) 0459 { 0460 setLastError(" Fail to find deviceList of device \"urn:schemas-upnp-org:device:InternetGatewayDevice:1 \"\n"); 0461 setStatus(NAT_STAT::NAT_ERROR); 0462 return false; 0463 } 0464 0465 // get urn:schemas-upnp-org:device:WANDevice:1 and it's devicelist 0466 num= deviceList_node.nChildNode("device"); 0467 for(i= 0; i < num; i++) 0468 { 0469 device_node= deviceList_node.getChildNode("device", i); 0470 if(device_node.isEmpty()) 0471 break; 0472 deviceType_node= device_node.getChildNode("deviceType", 0); 0473 if(strcmp(deviceType_node.getText(), DEVICE_TYPE_2) == 0) 0474 break; 0475 } 0476 0477 if(device_node.isEmpty()) 0478 { 0479 setLastError(tr("Fail to find device \"urn:schemas-upnp-org:device:WANDevice:1 \"\n")); 0480 setStatus(NAT_STAT::NAT_ERROR); 0481 return false; 0482 } 0483 0484 deviceList_node= device_node.getChildNode("deviceList", 0); 0485 if(deviceList_node.isEmpty()) 0486 { 0487 setLastError(tr(" Fail to find deviceList of device \"urn:schemas-upnp-org:device:WANDevice:1 \"\n")); 0488 setStatus(NAT_STAT::NAT_ERROR); 0489 return false; 0490 } 0491 0492 // get urn:schemas-upnp-org:device:WANConnectionDevice:1 and it's servicelist 0493 num= deviceList_node.nChildNode("device"); 0494 for(i= 0; i < num; i++) 0495 { 0496 device_node= deviceList_node.getChildNode("device", i); 0497 if(device_node.isEmpty()) 0498 break; 0499 deviceType_node= device_node.getChildNode("deviceType", 0); 0500 if(strcmp(deviceType_node.getText(), DEVICE_TYPE_3) == 0) 0501 break; 0502 } 0503 if(device_node.isEmpty()) 0504 { 0505 setLastError("Fail to find device \"urn:schemas-upnp-org:device:WANConnectionDevice:1\"\n"); 0506 setStatus(NAT_STAT::NAT_ERROR); 0507 return false; 0508 } 0509 0510 XMLNode serviceList_node, service_node, serviceType_node; 0511 serviceList_node= device_node.getChildNode("serviceList", 0); 0512 if(serviceList_node.isEmpty()) 0513 { 0514 setLastError(" Fail to find serviceList of device \"urn:schemas-upnp-org:device:WANDevice:1 \"\n"); 0515 setStatus(NAT_STAT::NAT_ERROR); 0516 return false; 0517 } 0518 0519 num= serviceList_node.nChildNode("service"); 0520 const char* serviceType; 0521 bool is_found= false; 0522 for(i= 0; i < num; i++) 0523 { 0524 service_node= serviceList_node.getChildNode("service", i); 0525 if(service_node.isEmpty()) 0526 break; 0527 serviceType_node= service_node.getChildNode("serviceType"); 0528 if(serviceType_node.isEmpty()) 0529 continue; 0530 serviceType= serviceType_node.getText(); 0531 if(strcmp(serviceType, SERVICE_WANIP) == 0 || strcmp(serviceType, SERVICE_WANPPP) == 0) 0532 { 0533 is_found= true; 0534 break; 0535 } 0536 } 0537 0538 if(!is_found) 0539 { 0540 setLastError("can't find \" SERVICE_WANIP \" or \" SERVICE_WANPPP \" service.\n"); 0541 setStatus(NAT_STAT::NAT_ERROR); 0542 return false; 0543 } 0544 0545 m_service_type= serviceType; 0546 0547 XMLNode controlURL_node= service_node.getChildNode("controlURL"); 0548 m_control_url= controlURL_node.getText(); 0549 0550 // make the complete control_url; 0551 if(m_control_url.indexOf("http://") < 0 && m_control_url.indexOf("HTTP://") < 0) 0552 m_control_url= m_base_url + m_control_url; 0553 if(m_service_describe_url.indexOf("http://") < 0 && m_service_describe_url.indexOf("HTTP://") < 0) 0554 m_service_describe_url= m_base_url + m_service_describe_url; 0555 0556 qDebug() << "-------------"; 0557 qDebug() << "Service: " << m_service_type; 0558 qDebug() << "describe:" << m_describe_url; 0559 qDebug() << "Control:" << m_control_url; 0560 qDebug() << "Base:" << m_base_url; 0561 qDebug() << "service url:" << m_service_describe_url; 0562 qDebug() << "Description:" << m_description_info; 0563 qDebug() << "-------------"; 0564 m_tcpSocket->close(); 0565 setStatus(NAT_STAT::NAT_GETCONTROL); 0566 return true; 0567 }*/