2011-04-15 17:02:39 +04:00
/*
2017-09-07 03:00:04 +03:00
* Bittorrent Client using Qt and libtorrent .
* Copyright ( C ) 2011 Christophe Dumez < chris @ qbittorrent . org >
2011-04-15 17:02:39 +04:00
*
* 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 . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*
* In addition , as a special exception , the copyright holders give permission to
* link this program with the OpenSSL project ' s " OpenSSL " library ( or with
* modified versions of it that use the same license as the " OpenSSL " library ) ,
* and distribute the linked executables . You must obey the GNU General Public
* License in all respects for all of the code used other than " OpenSSL " . If you
* modify file ( s ) , you may extend this exception to your version of the file ( s ) ,
* but you are not obligated to do so . If you do not wish to do so , delete this
* exception statement from your version .
*/
2018-05-24 18:39:02 +03:00
# include "dnsupdater.h"
2011-04-15 17:02:39 +04:00
# include <QDebug>
2018-05-24 18:41:03 +03:00
# include <QRegularExpression>
2013-09-21 11:59:58 +04:00
# include <QUrlQuery>
2015-05-13 18:39:48 +03:00
2022-03-04 09:24:14 +03:00
# include "base/global.h"
2015-09-25 11:10:05 +03:00
# include "base/logger.h"
2017-09-07 03:00:04 +03:00
# include "base/net/downloadmanager.h"
2021-01-04 10:02:13 +03:00
# include "base/version.h"
2011-04-15 17:02:39 +04:00
2015-04-13 19:02:48 +03:00
using namespace Net ;
2015-05-13 18:39:48 +03:00
DNSUpdater : : DNSUpdater ( QObject * parent )
: QObject ( parent )
, m_state ( OK )
2021-12-06 08:53:52 +03:00
, m_service ( DNS : : Service : : None )
2011-04-15 17:02:39 +04:00
{
2015-05-13 18:39:48 +03:00
updateCredentials ( ) ;
2011-04-15 17:02:39 +04:00
2015-05-13 18:39:48 +03:00
// Load saved settings from previous session
const Preferences * const pref = Preferences : : instance ( ) ;
m_lastIPCheckTime = pref - > getDNSLastUpd ( ) ;
m_lastIP = QHostAddress ( pref - > getDNSLastIP ( ) ) ;
2011-04-15 17:02:39 +04:00
2015-05-13 18:39:48 +03:00
// Start IP checking timer
m_ipCheckTimer . setInterval ( IP_CHECK_INTERVAL_MS ) ;
2018-04-18 16:59:41 +03:00
connect ( & m_ipCheckTimer , & QTimer : : timeout , this , & DNSUpdater : : checkPublicIP ) ;
2015-05-13 18:39:48 +03:00
m_ipCheckTimer . start ( ) ;
2011-04-15 17:02:39 +04:00
2015-05-13 18:39:48 +03:00
// Check lastUpdate to avoid flooding
if ( ! m_lastIPCheckTime . isValid ( )
2020-11-16 10:02:11 +03:00
| | ( m_lastIPCheckTime . secsTo ( QDateTime : : currentDateTime ( ) ) * 1000 > IP_CHECK_INTERVAL_MS ) )
{
2015-05-13 18:39:48 +03:00
checkPublicIP ( ) ;
}
2011-04-15 17:02:39 +04:00
}
2015-05-13 18:39:48 +03:00
DNSUpdater : : ~ DNSUpdater ( )
{
// Save lastupdate time and last ip
Preferences * const pref = Preferences : : instance ( ) ;
pref - > setDNSLastUpd ( m_lastIPCheckTime ) ;
pref - > setDNSLastIP ( m_lastIP . toString ( ) ) ;
2011-04-15 17:02:39 +04:00
}
void DNSUpdater : : checkPublicIP ( )
{
2015-05-13 18:39:48 +03:00
Q_ASSERT ( m_state = = OK ) ;
2015-12-19 15:35:45 +03:00
2019-03-03 12:43:42 +03:00
DownloadManager : : instance ( ) - > download (
2022-03-04 09:24:14 +03:00
DownloadRequest ( u " http://checkip.dyndns.org " _qs ) . userAgent ( QStringLiteral ( " qBittorrent/ " QBT_VERSION_2 ) )
2019-03-03 12:43:42 +03:00
, this , & DNSUpdater : : ipRequestFinished ) ;
2015-12-19 15:35:45 +03:00
2015-05-13 18:39:48 +03:00
m_lastIPCheckTime = QDateTime : : currentDateTime ( ) ;
2011-04-15 17:02:39 +04:00
}
2019-03-01 10:38:16 +03:00
void DNSUpdater : : ipRequestFinished ( const DownloadResult & result )
2011-04-15 17:02:39 +04:00
{
2020-11-16 10:02:11 +03:00
if ( result . status ! = DownloadStatus : : Success )
{
2019-03-01 10:38:16 +03:00
qWarning ( ) < < " IP request failed: " < < result . errorString ;
return ;
}
2015-12-19 15:35:45 +03:00
// Parse response
2022-03-04 09:24:14 +03:00
const QRegularExpressionMatch ipRegexMatch = QRegularExpression ( u " Current IP Address: \\ s+([^<]+)</body> " _qs ) . match ( QString : : fromUtf8 ( result . data ) ) ;
2020-11-16 10:02:11 +03:00
if ( ipRegexMatch . hasMatch ( ) )
{
2018-05-24 18:41:03 +03:00
QString ipStr = ipRegexMatch . captured ( 1 ) ;
2015-12-19 15:35:45 +03:00
qDebug ( ) < < Q_FUNC_INFO < < " Regular expression captured the following IP: " < < ipStr ;
QHostAddress newIp ( ipStr ) ;
2020-11-16 10:02:11 +03:00
if ( ! newIp . isNull ( ) )
{
if ( m_lastIP ! = newIp )
{
2015-12-19 15:35:45 +03:00
qDebug ( ) < < Q_FUNC_INFO < < " The IP address changed, report the change to DynDNS... " ;
qDebug ( ) < < m_lastIP . toString ( ) < < " -> " < < newIp . toString ( ) ;
m_lastIP = newIp ;
updateDNSService ( ) ;
2015-05-13 18:39:48 +03:00
}
}
2020-11-16 10:02:11 +03:00
else
{
2015-12-19 15:35:45 +03:00
qWarning ( ) < < Q_FUNC_INFO < < " Failed to construct a QHostAddress from the IP string " ;
2011-04-15 17:02:39 +04:00
}
}
2020-11-16 10:02:11 +03:00
else
{
2015-12-19 15:35:45 +03:00
qWarning ( ) < < Q_FUNC_INFO < < " Regular expression failed to capture the IP address " ;
}
}
2011-04-15 17:02:39 +04:00
void DNSUpdater : : updateDNSService ( )
{
2015-05-13 18:39:48 +03:00
qDebug ( ) < < Q_FUNC_INFO ;
2015-12-19 15:35:45 +03:00
2015-05-13 18:39:48 +03:00
m_lastIPCheckTime = QDateTime : : currentDateTime ( ) ;
2019-03-03 12:43:42 +03:00
DownloadManager : : instance ( ) - > download (
2022-03-04 09:24:14 +03:00
DownloadRequest ( getUpdateUrl ( ) ) . userAgent ( QStringLiteral ( " qBittorrent/ " QBT_VERSION_2 ) )
2019-03-03 12:43:42 +03:00
, this , & DNSUpdater : : ipUpdateFinished ) ;
2011-04-15 17:02:39 +04:00
}
2015-12-19 15:35:45 +03:00
QString DNSUpdater : : getUpdateUrl ( ) const
2011-04-15 17:02:39 +04:00
{
2015-05-13 18:39:48 +03:00
QUrl url ;
2011-04-15 17:02:39 +04:00
# ifdef QT_NO_OPENSSL
2022-03-04 09:24:14 +03:00
url . setScheme ( u " http " _qs ) ;
2011-04-15 17:02:39 +04:00
# else
2022-03-04 09:24:14 +03:00
url . setScheme ( u " https " _qs ) ;
2011-04-15 17:02:39 +04:00
# endif
2015-05-13 18:39:48 +03:00
url . setUserName ( m_username ) ;
url . setPassword ( m_password ) ;
2011-04-15 17:02:39 +04:00
2015-05-13 18:39:48 +03:00
Q_ASSERT ( ! m_lastIP . isNull ( ) ) ;
// Service specific
2020-11-16 10:02:11 +03:00
switch ( m_service )
{
2021-12-06 08:53:52 +03:00
case DNS : : Service : : DynDNS :
2022-03-04 09:24:14 +03:00
url . setHost ( u " members.dyndns.org " _qs ) ;
2015-05-13 18:39:48 +03:00
break ;
2021-12-06 08:53:52 +03:00
case DNS : : Service : : NoIP :
2022-03-04 09:24:14 +03:00
url . setHost ( u " dynupdate.no-ip.com " _qs ) ;
2015-05-13 18:39:48 +03:00
break ;
default :
qWarning ( ) < < " Unrecognized Dynamic DNS service! " ;
2021-12-06 08:53:52 +03:00
Q_ASSERT ( false ) ;
break ;
2015-05-13 18:39:48 +03:00
}
2022-03-04 09:24:14 +03:00
url . setPath ( u " /nic/update " _qs ) ;
2013-09-21 11:59:58 +04:00
2015-05-13 18:39:48 +03:00
QUrlQuery urlQuery ( url ) ;
2022-03-04 09:24:14 +03:00
urlQuery . addQueryItem ( u " hostname " _qs , m_domain ) ;
urlQuery . addQueryItem ( u " myip " _qs , m_lastIP . toString ( ) ) ;
2015-05-13 18:39:48 +03:00
url . setQuery ( urlQuery ) ;
Q_ASSERT ( url . isValid ( ) ) ;
2013-09-21 11:59:58 +04:00
2015-05-13 18:39:48 +03:00
qDebug ( ) < < Q_FUNC_INFO < < url . toString ( ) ;
2015-12-19 15:35:45 +03:00
return url . toString ( ) ;
2011-04-15 17:02:39 +04:00
}
2019-03-01 10:38:16 +03:00
void DNSUpdater : : ipUpdateFinished ( const DownloadResult & result )
2015-12-19 15:35:45 +03:00
{
2019-03-01 10:38:16 +03:00
if ( result . status = = DownloadStatus : : Success )
2022-03-04 09:24:14 +03:00
processIPUpdateReply ( QString : : fromUtf8 ( result . data ) ) ;
2019-03-01 10:38:16 +03:00
else
qWarning ( ) < < " IP update failed: " < < result . errorString ;
2011-04-15 17:02:39 +04:00
}
void DNSUpdater : : processIPUpdateReply ( const QString & reply )
{
2015-05-13 18:39:48 +03:00
Logger * const logger = Logger : : instance ( ) ;
qDebug ( ) < < Q_FUNC_INFO < < reply ;
2022-03-04 09:24:14 +03:00
const QString code = reply . split ( u ' ' ) . first ( ) ;
2015-05-13 18:39:48 +03:00
qDebug ( ) < < Q_FUNC_INFO < < " Code: " < < code ;
2015-12-19 15:35:45 +03:00
2022-03-04 09:24:14 +03:00
if ( ( code = = u " good " ) | | ( code = = u " nochg " ) )
2020-11-16 10:02:11 +03:00
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Your dynamic DNS was successfully updated. " ) , Log : : INFO ) ;
return ;
}
2015-12-19 15:35:45 +03:00
2022-03-04 09:24:14 +03:00
if ( ( code = = u " 911 " ) | | ( code = = u " dnserr " ) )
2020-11-16 10:02:11 +03:00
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: The service is temporarily unavailable, it will be retried in 30 minutes. " ) , Log : : CRITICAL ) ;
m_lastIP . clear ( ) ;
// It will retry in 30 minutes because the timer was not stopped
return ;
}
2015-12-19 15:35:45 +03:00
2018-03-14 18:15:51 +03:00
// Everything below is an error, stop updating until the user updates something
2015-05-13 18:39:48 +03:00
m_ipCheckTimer . stop ( ) ;
2011-04-15 17:02:39 +04:00
m_lastIP . clear ( ) ;
2022-03-04 09:24:14 +03:00
if ( code = = u " nohost " )
2020-11-16 10:02:11 +03:00
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: hostname supplied does not exist under specified account. " ) , Log : : CRITICAL ) ;
m_state = INVALID_CREDS ;
return ;
}
2015-12-19 15:35:45 +03:00
2022-03-04 09:24:14 +03:00
if ( code = = u " badauth " )
2020-11-16 10:02:11 +03:00
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: Invalid username/password. " ) , Log : : CRITICAL ) ;
m_state = INVALID_CREDS ;
return ;
}
2015-12-19 15:35:45 +03:00
2022-03-04 09:24:14 +03:00
if ( code = = u " badagent " )
2020-11-16 10:02:11 +03:00
{
2021-09-12 11:45:00 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: qBittorrent was blacklisted by the service, please submit a bug report at http://bugs.qbittorrent.org. " ) ,
2015-05-13 18:39:48 +03:00
Log : : CRITICAL ) ;
m_state = FATAL ;
return ;
}
2015-12-19 15:35:45 +03:00
2022-03-04 09:24:14 +03:00
if ( code = = u " !donator " )
2020-11-16 10:02:11 +03:00
{
2022-03-04 09:24:14 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: %1 was returned by the service, please submit a bug report at http://bugs.qbittorrent.org. " ) . arg ( u " !donator " _qs ) ,
2015-05-13 18:39:48 +03:00
Log : : CRITICAL ) ;
m_state = FATAL ;
return ;
}
2015-12-19 15:35:45 +03:00
2022-03-04 09:24:14 +03:00
if ( code = = u " abuse " )
2020-11-16 10:02:11 +03:00
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: Your username was blocked due to abuse. " ) , Log : : CRITICAL ) ;
m_state = FATAL ;
}
2011-04-15 17:02:39 +04:00
}
void DNSUpdater : : updateCredentials ( )
{
2015-05-13 18:39:48 +03:00
if ( m_state = = FATAL ) return ;
Preferences * const pref = Preferences : : instance ( ) ;
Logger * const logger = Logger : : instance ( ) ;
bool change = false ;
// Get DNS service information
2020-11-16 10:02:11 +03:00
if ( m_service ! = pref - > getDynDNSService ( ) )
{
2015-05-13 18:39:48 +03:00
m_service = pref - > getDynDNSService ( ) ;
change = true ;
}
2020-11-16 10:02:11 +03:00
if ( m_domain ! = pref - > getDynDomainName ( ) )
{
2015-05-13 18:39:48 +03:00
m_domain = pref - > getDynDomainName ( ) ;
2022-03-04 09:24:14 +03:00
const QRegularExpressionMatch domainRegexMatch = QRegularExpression ( u " ^(?:(?! \\ d|-)[a-zA-Z0-9 \\ -]{1,63} \\ .)+[a-zA-Z]{2,}$ " _qs ) . match ( m_domain ) ;
2020-11-16 10:02:11 +03:00
if ( ! domainRegexMatch . hasMatch ( ) )
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: supplied domain name is invalid. " ) , Log : : CRITICAL ) ;
m_lastIP . clear ( ) ;
m_ipCheckTimer . stop ( ) ;
m_state = INVALID_CREDS ;
return ;
}
change = true ;
2011-04-15 17:02:39 +04:00
}
2020-11-16 10:02:11 +03:00
if ( m_username ! = pref - > getDynDNSUsername ( ) )
{
2015-05-13 18:39:48 +03:00
m_username = pref - > getDynDNSUsername ( ) ;
2020-11-16 10:02:11 +03:00
if ( m_username . length ( ) < 4 )
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: supplied username is too short. " ) , Log : : CRITICAL ) ;
m_lastIP . clear ( ) ;
m_ipCheckTimer . stop ( ) ;
m_state = INVALID_CREDS ;
return ;
}
change = true ;
2011-04-15 17:02:39 +04:00
}
2020-11-16 10:02:11 +03:00
if ( m_password ! = pref - > getDynDNSPassword ( ) )
{
2015-05-13 18:39:48 +03:00
m_password = pref - > getDynDNSPassword ( ) ;
2020-11-16 10:02:11 +03:00
if ( m_password . length ( ) < 4 )
{
2015-05-13 18:39:48 +03:00
logger - > addMessage ( tr ( " Dynamic DNS error: supplied password is too short. " ) , Log : : CRITICAL ) ;
m_lastIP . clear ( ) ;
m_ipCheckTimer . stop ( ) ;
m_state = INVALID_CREDS ;
return ;
}
change = true ;
2011-04-15 17:02:39 +04:00
}
2020-11-16 10:02:11 +03:00
if ( ( m_state = = INVALID_CREDS ) & & change )
{
2015-05-13 18:39:48 +03:00
m_state = OK ; // Try again
m_ipCheckTimer . start ( ) ;
checkPublicIP ( ) ;
}
2011-04-15 17:02:39 +04:00
}
2021-12-06 08:53:52 +03:00
QUrl DNSUpdater : : getRegistrationUrl ( const DNS : : Service service )
2011-04-15 17:02:39 +04:00
{
2020-11-16 10:02:11 +03:00
switch ( service )
{
2021-12-06 08:53:52 +03:00
case DNS : : Service : : DynDNS :
2022-03-04 09:24:14 +03:00
return { u " https://account.dyn.com/entrance/ " _qs } ;
2021-12-06 08:53:52 +03:00
case DNS : : Service : : NoIP :
2022-03-04 09:24:14 +03:00
return { u " https://www.noip.com/remote-access " _qs } ;
2015-05-13 18:39:48 +03:00
default :
2021-12-06 08:53:52 +03:00
Q_ASSERT ( false ) ;
break ;
2015-05-13 18:39:48 +03:00
}
2019-02-14 20:16:42 +03:00
return { } ;
2011-04-15 17:02:39 +04:00
}