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 }*/