diff --git a/qtox.pro b/qtox.pro index dfe8f8440..3047e6d14 100644 --- a/qtox.pro +++ b/qtox.pro @@ -63,43 +63,32 @@ contains(JENKINS,YES) { # Rules for Windows, Mac OSX, and Linux win32 { RC_FILE = windows/qtox.rc - LIBS += -liphlpapi -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -lvpx -lpthread + LIBS += -liphlpapi -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lvpx -lpthread LIBS += -L$$PWD/libs/lib -lopencv_core248 -lopencv_highgui248 -lopencv_imgproc248 -lOpenAL32 -lopus LIBS += -lz -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -ljpeg -ltiff -lpng -ljasper -lIlmImf -lHalf -lws2_32 } else { macx { ICON = img/icons/qtox.icns - LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui + LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lvpx -framework OpenAL -lopencv_core -lopencv_highgui } else { # If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package contains(STATICPKG, YES) { target.path = /usr/bin INSTALLS += target - LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic + LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0 } else { - LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -lvpx -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc + LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc } contains(JENKINS, YES) { - LIBS = ./libs/lib/libsodium.a ./libs/lib/libtoxcore.a ./libs/lib/libtoxav.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libvpx.a ./libs/lib/libopus.a -lopencv_core -lopencv_highgui -lopenal + LIBS = ./libs/lib/libsodium.a ./libs/lib/libtoxcore.a ./libs/lib/libtoxav.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxdns.a ./libs/lib/libvpx.a ./libs/lib/libopus.a -lopencv_core -lopencv_highgui -lopenal } } } - -#### Static linux build -#LIBS += -Wl,-Bstatic -ltoxcore -ltoxav -lsodium -lvpx -lopus \ -# -lgstbase-0.10 -lgstreamer-0.10 -lgmodule-2.0 -lgstaudio-0.10 -lxml2 \ -# -lX11-xcb -lXi -lxcb-render-util -lxcb-glx -lxcb-render -ldbus-1 \ -# -lxcb -lXau -lXdmcp -lxcb-image -lxcb-icccm -lxcb-sync -lxcb-xfixes -lxcb-shm -lxcb-randr -lxcb-shape \ -# -lxcb-keysyms -lxcb-xkb -lfontconfig -lfreetype -lXrender -lXext -lX11 \ -# -lpng -lz -licui18n -licuuc -licudata -lm -lgthread-2.0 \ -# -pthread -lrt -lGL -lpthread -Wl,-Bdynamic -ldl -lc -#QMAKE_CXXFLAGS += -Os -flto -static-libstdc++ -static-libgcc - HEADERS += src/widget/form/addfriendform.h \ src/widget/form/chatform.h \ src/widget/form/groupchatform.h \ diff --git a/src/widget/form/addfriendform.cpp b/src/widget/form/addfriendform.cpp index b80e3000d..a6d244820 100644 --- a/src/widget/form/addfriendform.cpp +++ b/src/widget/form/addfriendform.cpp @@ -18,12 +18,25 @@ #include #include +#include #include #include "ui_mainwindow.h" #include "src/core.h" +#include "src/misc/cdata.h" +#include "tox/toxdns.h" + +#include #define TOX_ID_LENGTH 2*TOX_FRIEND_ADDRESS_SIZE +const AddFriendForm::tox3_server AddFriendForm::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}} +}; + AddFriendForm::AddFriendForm() : dns(this) { dns.setType(QDnsLookup::TXT); @@ -50,7 +63,6 @@ AddFriendForm::AddFriendForm() : dns(this) headLayout.addWidget(&headLabel); connect(&sendButton, SIGNAL(clicked()), this, SLOT(onSendTriggered())); - connect(&dns, SIGNAL(finished()), this, SLOT(handleDnsLookup())); } AddFriendForm::~AddFriendForm() @@ -99,50 +111,102 @@ void AddFriendForm::onSendTriggered() showWarning(tr("You can't add yourself as a friend!","When trying to add your own Tox ID as friend")); else emit friendRequested(id, getMessage()); - this->toxId.setText(""); - this->message.setText(""); + this->toxId.clear(); + this->message.clear(); } else { - id = id.replace("@", "._tox."); - dns.setName(id); - dns.lookup(); + // If we're querying one of our pinned server, do a tox3 request directly + QString servname = id.mid(id.indexOf('@')+1); + for (const AddFriendForm::tox3_server& pin : pinnedServers) + { + if (servname == pin.name) + { + queryTox3(pin, id); + return; + } + } + + // Otherwise try tox3 if we can get a pubkey or fallback to tox1 + QByteArray pubkey = fetchLastTextRecord("_tox."+servname); + if (!pubkey.isEmpty()) + { + QByteArray servnameData = servname.toUtf8(); + AddFriendForm::tox3_server server; + server.name = servnameData.data(); + server.pubkey = (uint8_t*)pubkey.data(); + queryTox3(server, id); + } + else + { + queryTox1(id); + } } } -void AddFriendForm::handleDnsLookup() +QByteArray AddFriendForm::fetchLastTextRecord(const QString& record, bool silent) { - const QString idKeyWord("id="), verKeyWord("v="); + QByteArray result; + + 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) { - showWarning(tr("This address does not exist","The DNS gives the Tox ID associated to toxme.se addresses")); - return; + 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) { - showWarning(tr("Error while looking up DNS","The DNS gives the Tox ID associated to toxme.se addresses")); - return; + if (!silent) + showWarning(tr("Error while looking up DNS","The DNS gives the Tox ID associated to toxme.se addresses")); + return result; } const QList textRecords = dns.textRecords(); - if (textRecords.length() != 1) { - showWarning(tr("Unexpected number of text records", "Error with the DNS")); - return; + if (textRecords.isEmpty()) { + if (!silent) + showWarning(tr("No text record found", "Error with the DNS")); + return result; } - const QList textRecordValues = textRecords.first().values(); + const QList textRecordValues = textRecords.last().values(); if (textRecordValues.length() != 1) { - showWarning(tr("Unexpected number of values in text record", "Error with the DNS")); - return; + if (!silent) + showWarning(tr("Unexpected number of values in text record", "Error with the DNS")); + return result; } - const QString entry(textRecordValues.first()); + result = textRecordValues.first(); + return result; +} - // Check toxdns protocol version, we only support tox1 for now - int verx = entry.indexOf(verKeyWord); +void AddFriendForm::queryTox1(const QString& record) +{ + QString realRecord = record; + realRecord.replace("@", "._tox."); + const QString entry = fetchLastTextRecord(realRecord, false); + if (entry.isEmpty()) + return; + + // Check toxdns protocol version + int verx = entry.indexOf("v="); if (verx) { - verx += verKeyWord.size(); + verx += 2; int verend = entry.indexOf(';', verx); if (verend) { - QString ver = entry.mid(verx, verend); + QString ver = entry.mid(verx, verend-verx); if (ver != "tox1") { showWarning(tr("The version of Tox DNS used by this server is not supported", "Error with the DNS")); @@ -151,13 +215,14 @@ void AddFriendForm::handleDnsLookup() } } - int idx = entry.indexOf(idKeyWord); + // Get the tox id + int idx = entry.indexOf("id="); if (idx < 0) { showWarning(tr("The DNS lookup does not contain any Tox ID", "Error with the DNS")); return; } - idx += idKeyWord.length(); + idx += 3; if (entry.length() < idx + static_cast(TOX_ID_LENGTH)) { showWarning(tr("The DNS lookup does not contain a valid Tox ID", "Error with the DNS")); return; @@ -171,6 +236,86 @@ void AddFriendForm::handleDnsLookup() // finally we got it emit friendRequested(friendAdress, getMessage()); - this->toxId.setText(""); - this->message.setText(""); + this->toxId.clear(); + this->message.clear(); +} + +void AddFriendForm::queryTox3(const tox3_server& server, QString &record) +{ + 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 "<toxId.clear(); + this->message.clear(); + return; + + // Centralized error handling, fallback on tox1 queries +fallbackOnTox1: + if (tox_dns3) + tox_dns3_kill(tox_dns3); + queryTox1(record); + return; } diff --git a/src/widget/form/addfriendform.h b/src/widget/form/addfriendform.h index 6fd9d736c..896407c73 100644 --- a/src/widget/form/addfriendform.h +++ b/src/widget/form/addfriendform.h @@ -43,7 +43,24 @@ signals: private slots: void onSendTriggered(); - void handleDnsLookup(); + +private: + struct tox3_server + { + tox3_server()=default; + tox3_server(const char* _name, uint8_t _pk[32]):name{_name},pubkey{_pk}{} + + const char* name; ///< Hostname of the server, e.g. toxme.se + uint8_t* pubkey; ///< Public key of the tox3 server, usually 256bit long + }; + +private: + void queryTox1(const QString& record); ///< Record should look like user@domain.tld + void queryTox3(const tox3_server& server, QString& record); ///< Record should look like user@domain.tld, may fallback on queryTox1 + /// Try to fetch the first entry of the given TXT record + /// Returns an empty object on failure. May block for up to ~3s + /// May display message boxes on error if silent if false + QByteArray fetchLastTextRecord(const QString& record, bool silent=true); private: QLabel headLabel, toxIdLabel, messageLabel; @@ -55,6 +72,7 @@ private: /** will be used for dns discovery if necessary */ QDnsLookup dns; + static const tox3_server pinnedServers[]; }; #endif // ADDFRIENDFORM_H