2014-11-08 05:45:21 +08:00
/*
Copyright ( C ) 2014 by Project Tox < https : //tox.im>
This file is part of qTox , a Qt - based graphical interface for Tox .
This program is libre 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 3 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 COPYING file for more details .
*/
2014-11-06 22:12:10 +08:00
# include "src/toxdns.h"
# include "src/misc/cdata.h"
# include <QMessageBox>
# include <QCoreApplication>
# include <QThread>
# include <QDebug>
# include <tox/tox.h>
# include <tox/toxdns.h>
# define TOX_ID_LENGTH 2*TOX_FRIEND_ADDRESS_SIZE
const ToxDNS : : tox3_server ToxDNS : : pinnedServers [ ]
{
{ " toxme.se " , ( uint8_t [ 32 ] ) { 0x5D , 0x72 , 0xC5 , 0x17 , 0xDF , 0x6A , 0xEC , 0x54 , 0xF1 , 0xE9 , 0x77 , 0xA6 , 0xB6 , 0xF2 , 0x59 , 0x14 ,
0xEA , 0x4C , 0xF7 , 0x27 , 0x7A , 0x85 , 0x02 , 0x7C , 0xD9 , 0xF5 , 0x19 , 0x6D , 0xF1 , 0x7E , 0x0B , 0x13 } } ,
{ " utox.org " , ( uint8_t [ 32 ] ) { 0xD3 , 0x15 , 0x4F , 0x65 , 0xD2 , 0x8A , 0x5B , 0x41 , 0xA0 , 0x5D , 0x4A , 0xC7 , 0xE4 , 0xB3 , 0x9C , 0x6B ,
0x1C , 0x23 , 0x3C , 0xC8 , 0x57 , 0xFB , 0x36 , 0x5C , 0x56 , 0xE8 , 0x39 , 0x27 , 0x37 , 0x46 , 0x2A , 0x12 } }
} ;
void ToxDNS : : showWarning ( const QString & message )
{
QMessageBox warning ;
warning . setWindowTitle ( " Tox " ) ;
warning . setText ( message ) ;
warning . setIcon ( QMessageBox : : Warning ) ;
warning . exec ( ) ;
}
QByteArray ToxDNS : : fetchLastTextRecord ( const QString & record , bool silent )
{
QByteArray result ;
QDnsLookup dns ;
dns . setType ( QDnsLookup : : TXT ) ;
dns . setName ( record ) ;
dns . lookup ( ) ;
int timeout ;
for ( timeout = 0 ; timeout < 30 & & ! dns . isFinished ( ) ; + + timeout )
{
qApp - > processEvents ( ) ;
QThread : : msleep ( 100 ) ;
}
if ( timeout > = 30 ) {
dns . abort ( ) ;
if ( ! silent )
showWarning ( tr ( " The connection timed out " , " The DNS gives the Tox ID associated to toxme.se addresses " ) ) ;
return result ;
}
if ( dns . error ( ) = = QDnsLookup : : NotFoundError ) {
if ( ! silent )
showWarning ( tr ( " This address does not exist " , " The DNS gives the Tox ID associated to toxme.se addresses " ) ) ;
return result ;
}
else if ( dns . error ( ) ! = QDnsLookup : : NoError ) {
if ( ! silent )
showWarning ( tr ( " Error while looking up DNS " , " The DNS gives the Tox ID associated to toxme.se addresses " ) ) ;
return result ;
}
const QList < QDnsTextRecord > textRecords = dns . textRecords ( ) ;
if ( textRecords . isEmpty ( ) ) {
if ( ! silent )
showWarning ( tr ( " No text record found " , " Error with the DNS " ) ) ;
return result ;
}
const QList < QByteArray > textRecordValues = textRecords . last ( ) . values ( ) ;
if ( textRecordValues . length ( ) ! = 1 ) {
if ( ! silent )
showWarning ( tr ( " Unexpected number of values in text record " , " Error with the DNS " ) ) ;
return result ;
}
result = textRecordValues . first ( ) ;
return result ;
}
QString ToxDNS : : queryTox1 ( const QString & record , bool silent )
{
QString realRecord = record , toxId ;
realRecord . replace ( " @ " , " ._tox. " ) ;
const QString entry = fetchLastTextRecord ( realRecord , silent ) ;
if ( entry . isEmpty ( ) )
return toxId ;
// Check toxdns protocol version
int verx = entry . indexOf ( " v= " ) ;
if ( verx ) {
verx + = 2 ;
int verend = entry . indexOf ( ' ; ' , verx ) ;
if ( verend )
{
QString ver = entry . mid ( verx , verend - verx ) ;
if ( ver ! = " tox1 " )
{
if ( ! silent )
showWarning ( tr ( " The version of Tox DNS used by this server is not supported " , " Error with the DNS " ) ) ;
return toxId ;
}
}
}
// Get the tox id
int idx = entry . indexOf ( " id= " ) ;
if ( idx < 0 ) {
if ( ! silent )
showWarning ( tr ( " The DNS lookup does not contain any Tox ID " , " Error with the DNS " ) ) ;
return toxId ;
}
idx + = 3 ;
if ( entry . length ( ) < idx + static_cast < int > ( TOX_ID_LENGTH ) ) {
if ( ! silent )
showWarning ( tr ( " The DNS lookup does not contain a valid Tox ID " , " Error with the DNS " ) ) ;
return toxId ;
}
toxId = entry . mid ( idx , TOX_ID_LENGTH ) ;
if ( ! ToxID : : isToxId ( toxId ) ) {
if ( ! silent )
showWarning ( tr ( " The DNS lookup does not contain a valid Tox ID " , " Error with the DNS " ) ) ;
return toxId ;
}
return toxId ;
}
QString ToxDNS : : queryTox3 ( const tox3_server & server , const QString & record , bool silent )
{
QByteArray nameData = record . left ( record . indexOf ( ' @ ' ) ) . toUtf8 ( ) , id , realRecord ;
QString entry , toxIdStr ;
int toxIdSize , idx , verx , dns_string_len ;
const int dns_string_maxlen = 128 ;
void * tox_dns3 = tox_dns3_new ( server . pubkey ) ;
if ( ! tox_dns3 )
{
qWarning ( ) < < " queryTox3: failed to create a tox_dns3 object for " < < server . name < < " , using tox1 as a fallback " ;
goto fallbackOnTox1 ;
}
uint32_t request_id ;
uint8_t dns_string [ dns_string_maxlen ] ;
dns_string_len = tox_generate_dns3_string ( tox_dns3 , dns_string , dns_string_maxlen , & request_id ,
( uint8_t * ) nameData . data ( ) , nameData . size ( ) ) ;
if ( dns_string_len < 0 ) // We can always fallback on tox1 if toxdns3 fails
{
qWarning ( ) < < " queryTox3: failed to generate dns3 string for " < < server . name < < " , using tox1 as a fallback " ;
goto fallbackOnTox1 ;
}
realRecord = ' _ ' + QByteArray ( ( char * ) dns_string , dns_string_len ) + " ._tox. " + server . name ;
entry = fetchLastTextRecord ( realRecord , silent ) ;
if ( entry . isEmpty ( ) )
{
qWarning ( ) < < " queryTox3: Server " < < server . name < < " returned no record, using tox1 as a fallback " ;
goto fallbackOnTox1 ;
}
// Check toxdns protocol version
verx = entry . indexOf ( " v= " ) ;
if ( verx ! = - 1 ) {
verx + = 2 ;
int verend = entry . indexOf ( ' ; ' , verx ) ;
if ( verend ! = - 1 )
{
QString ver = entry . mid ( verx , verend - verx ) ;
if ( ver ! = " tox3 " )
{
qWarning ( ) < < " queryTox3: Server " < < server . name < < " returned a bad version ( " < < ver < < " ), using tox1 as a fallback " ;
goto fallbackOnTox1 ;
}
}
}
// Get and decrypt the tox id
idx = entry . indexOf ( " id= " ) ;
if ( idx < 0 ) {
qWarning ( ) < < " queryTox3: Server " < < server . name < < " returned an empty id, using tox1 as a fallback " ;
goto fallbackOnTox1 ;
}
idx + = 3 ;
id = entry . mid ( idx ) . toUtf8 ( ) ;
uint8_t toxId [ TOX_FRIEND_ADDRESS_SIZE ] ;
toxIdSize = tox_decrypt_dns3_TXT ( tox_dns3 , toxId , ( uint8_t * ) id . data ( ) , id . size ( ) , request_id ) ;
if ( toxIdSize < 0 ) // We can always fallback on tox1 if toxdns3 fails
{
qWarning ( ) < < " queryTox3: failed to decrypt dns3 reply for " < < server . name < < " , using tox1 as a fallback " ;
goto fallbackOnTox1 ;
}
tox_dns3_kill ( tox_dns3 ) ;
toxIdStr = CFriendAddress : : toString ( toxId ) ;
return toxIdStr ;
// Centralized error handling, fallback on tox1 queries
fallbackOnTox1 :
if ( tox_dns3 )
tox_dns3_kill ( tox_dns3 ) ;
2014-11-11 06:55:13 +08:00
# if TOX1_SILENT_FALLBACK
toxIdStr = queryTox1 ( record , silent ) ;
2015-01-05 04:02:24 +08:00
# elif TOX1_ASK_FALLBACK
2015-02-07 02:07:25 +08:00
QMessageBox : : StandardButton btn = QMessageBox : : warning ( nullptr , " qTox " , tr ( " It appears that qTox has to use the old tox1 protocol to access DNS record of your friend's Tox ID. \n \
Unfortunately tox1 is not secure , and you are at risk of someone hijacking what is sent between you and ToxDNS service . \ n \
Should tox1 be used anyway ? \ n \
If unsure , press “ No ” , so that request to ToxDNS service will not be made using unsecure protocol . " ), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
2015-01-23 03:21:07 +08:00
if ( btn = = QMessageBox : : Yes )
2015-01-05 04:02:24 +08:00
queryTox1 ( record , silent ) ;
2014-11-11 06:55:13 +08:00
# endif
2014-11-06 22:12:10 +08:00
return toxIdStr ;
}
ToxID ToxDNS : : resolveToxAddress ( const QString & address , bool silent )
{
ToxID toxId ;
if ( address . isEmpty ( ) ) {
return toxId ;
} else if ( ToxID : : isToxId ( address ) ) {
toxId = ToxID : : fromString ( address ) ;
return toxId ;
} else {
// If we're querying one of our pinned server, do a tox3 request directly
QString servname = address . mid ( address . indexOf ( ' @ ' ) + 1 ) ;
for ( const ToxDNS : : tox3_server & pin : ToxDNS : : pinnedServers )
{
if ( servname = = pin . name )
{
toxId = ToxID : : fromString ( queryTox3 ( pin , address , silent ) ) ;
return toxId ;
}
}
// Otherwise try tox3 if we can get a pubkey or fallback to tox1
QByteArray pubkey = fetchLastTextRecord ( " _tox. " + servname , true ) ;
if ( ! pubkey . isEmpty ( ) )
{
QByteArray servnameData = servname . toUtf8 ( ) ;
ToxDNS : : tox3_server server ;
server . name = servnameData . data ( ) ;
server . pubkey = ( uint8_t * ) pubkey . data ( ) ;
toxId = ToxID : : fromString ( queryTox3 ( server , address , silent ) ) ;
}
else
{
2014-11-11 06:55:13 +08:00
# if TOX1_SILENT_FALLBACK
2014-11-06 22:12:10 +08:00
toxId = ToxID : : fromString ( queryTox1 ( address , silent ) ) ;
2015-01-05 04:02:24 +08:00
# elif TOX1_ASK_FALLBACK
2015-02-07 02:07:25 +08:00
QMessageBox : : StandardButton btn = QMessageBox : : warning ( nullptr , " qTox " , tr ( " It appears that qTox has to use the old tox1 protocol to access DNS record of your friend's Tox ID. \n \
Unfortunately tox1 is not secure , and you are at risk of someone hijacking what is sent between you and ToxDNS service . \ n \
Should tox1 be used anyway ? \ n \
If unsure , press “ No ” , so that request to ToxDNS service will not be made using unsecure protocol . " ), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
2015-01-05 04:02:24 +08:00
if ( btn = = QMessageBox : : Ok )
toxId = ToxID : : fromString ( queryTox1 ( address , silent ) ) ;
2014-11-11 06:55:13 +08:00
# else
return toxId ;
# endif
2014-11-06 22:12:10 +08:00
}
return toxId ;
}
}