123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421 |
- #include "cppcodec/base32_rfc4648.hpp"
- #include "resolver.h"
- #include "funcs.h"
- #include "HTTPheaders.h"
- #include <QTcpSocket>
- #include <QRegularExpression>
- #include <QDateTime>
- #include <QHostInfo>
- #include <QDebug>
- #include <QFile>
- #ifdef _WIN32
- #include <ws2tcpip.h>
- #else
- #include <arpa/inet.h>
- #endif
- Resolver::Resolver(const QString& addr, quint16 port, QObject* parent) :
- QObject(parent),
- m_tcpServer(new QTcpServer),
- m_address(addr),
- m_port(port),
- m_NoApi(false),
- m_NoHttp(false),
- m_NoResolve(false)
- {
- if (!m_tcpServer->listen(m_address, m_port)) {
- throw "Server not binded";
- }
- qInfo().noquote() << "<-" << m_tcpServer->serverAddress().toString() + " : " +
- QString::number(m_tcpServer->serverPort()) << "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") + "]";
- connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(slotNewUser()));
- }
- void Resolver::closeSocketViaDescriptor(int id)
- {
- if(m_sockets.contains(id)) {
- m_sockets.value(id)->close();
- }
- }
- QSharedPointer<QTcpSocket> Resolver::getSocketViaDescriptor(int id)
- {
- if (!m_sockets.contains(id)) {
- qInfo() << "Resolver::getSocketViaDescriptor: Requested socket not found";
- }
- return m_sockets.value(id);
- }
- void Resolver::disableAPI()
- {
- m_NoApi = true;
- }
- void Resolver::disableWebPage()
- {
- m_NoHttp = true;
- }
- void Resolver::disableResolv()
- {
- m_NoResolve = true;
- http::HTML_PAGE.replace("IPv6 address or domain", "IPv6 address or meship domain");
- }
- void Resolver::slotNewUser()
- {
- if (!m_tcpServer->isListening()) return;
- QTcpSocket* clientSocket = m_tcpServer->nextPendingConnection();
- int idUserSock = clientSocket->socketDescriptor();
- m_sockets.insert(idUserSock, QSharedPointer<QTcpSocket>(clientSocket));
- connect (m_sockets.value(idUserSock).data(), SIGNAL(readyRead()), this, SLOT(slotReadyClient()));
- connect (m_sockets.value(idUserSock).data(), SIGNAL(disconnected()), this, SLOT(slotClientDisconnected()));
- }
- void Resolver::slotReadyClient()
- {
- QTcpSocket* clientSocket = static_cast<QTcpSocket*>(sender());
- QString req(clientSocket->readAll());
- qInfo().noquote() << "\n->" << clientSocket->peerAddress().toString()
- << "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") + "]";
- QSharedPointer<QTextStream> ts(new QTextStream(clientSocket));
- if (m_NoHttp) {
- if (req.startsWith("POST /") or req.startsWith("HEAD /") or req.startsWith("GET /")) {
- qInfo().noquote() << " └ HTTP (dropped)";
- *ts << http::HEADER_REJECT;
- clientSocket->close();
- return;
- }
- }
- if (req.startsWith("POST /")) {
- qInfo().noquote() << " └ POST request (dropped)";
- clientSocket->close();
- return;
- }
- if (req.startsWith("HEAD /")) {
- qInfo().noquote() << " └ HEAD request";
- *ts << http::HEADER_OK;
- clientSocket->close();
- return;
- }
- bool web = req.startsWith("GET /");
- if (web) {
- qInfo().noquote() << " └ HTTP";
- if (req.contains("toConverting=")) {
- processPage(req, ts, web);
- } else {
- startPage(ts, web);
- }
- } else if (not m_NoApi) {
- req.remove('\r');
- req.remove('\n');
- qInfo().noquote() << " └ API";
- if (req.contains(":") or req.contains(".") or req.contains("version")) {
- processPage(req, ts, web);
- } else {
- startPage(ts, web);
- }
- }
- else {
- qInfo().noquote() << " └ API (dropped)";
- clientSocket->close();
- }
- }
- void Resolver::slotClientDisconnected()
- {
- QTcpSocket* clientSocket = static_cast<QTcpSocket*>(sender());
- auto idUserSock = clientSocket->socketDescriptor();
- m_sockets.remove(idUserSock);
- }
- void Resolver::convertStrToRaw(const QString &str, Address &array)
- {
- inet_pton(AF_INET6, str.toUtf8(), (void*)array.data());
- }
- QString Resolver::getBase32(const Address &rawAddr)
- {
- return cppcodec::base32_rfc4648::encode(rawAddr.data(), ADDRIPV6_SIZE).c_str();
- }
- QString Resolver::decodeMeshToIP(const QString &meshname)
- {
- std::string mesh = pickupStringForMeshname(meshname.toStdString()) + "======"; // 6 паддингов - норма для IPv6 адреса
- std::vector<uint8_t> raw;
- try {
- raw = cppcodec::base32_rfc4648::decode(mesh);
- } catch (cppcodec::padding_error) {
- return QString();
- } catch (cppcodec::symbol_error) {
- return QString();
- }
- Address rawAddr;
- for(int i = 0; i < 16; ++i)
- rawAddr[i] = raw[i];
- return getAddress(rawAddr);
- }
- std::string Resolver::pickupStringForMeshname(std::string str)
- {
- bool dot = false;
- std::string::iterator delend;
- for (auto it = str.begin(); it != str.end(); it++)
- {
- *it = toupper(*it); // делаем все буквы заглавными для обработки
- if(*it == '.') {
- delend = it;
- dot = true;
- }
- }
- if (dot)
- for (auto it = str.end(); it != delend; it--)
- str.pop_back(); // удаляем доменную зону
- return str;
- }
- QString Resolver::getAddress(const Address& rawAddr)
- {
- char ipStrBuf[46];
- inet_ntop(AF_INET6, rawAddr.data(), ipStrBuf, 46);
- return QString(ipStrBuf);
- }
- void Resolver::startPage(QSharedPointer<QTextStream> ts, bool web)
- {
- QTcpSocket* clientSocket = static_cast<QTcpSocket*>(sender());
- if (web) {
- qInfo().noquote() << " └ Start page";
- QString page = http::HTML_PAGE;
- QString css = http::CSS_DEFAULT;
- QString text;
- if (m_NoApi and not m_NoResolve) {
- text = "<p>Input any domain to resolve or IPv6 to convert to <a href=\"https://github.com/zhoreeq/meshname\" style=\"text-decoration: none\">meship</a>.</p>";
- } else if (m_NoApi and m_NoResolve) {
- text = "<p>Input meship domain to resolve or IPv6 to convert to <a href=\"https://github.com/zhoreeq/meshname\" style=\"text-decoration: none\">meship</a>.</p>";
- } else if (not m_NoResolve) {
- text = "<p>Input any domain to resolve or IPv6 to convert to <a href=\"https://github.com/zhoreeq/meshname\" style=\"text-decoration: none\">meship</a>.</p>"
- "<p>Also you can use Mario DNS tool via command line (telnet or netcat for example) to get result in JSON. Just pass the value for processing.";
- } else { // no resolve
- text = "<p>Input meship domain to resolve or IPv6 to convert to <a href=\"https://github.com/zhoreeq/meshname\" style=\"text-decoration: none\">meship</a>.</p>"
- "<p>Also you can use Mario DNS tool via command line (telnet or netcat for example) to get result in JSON. Just pass the value for processing.";
- }
- css.replace("{{COLOR}}", "gray");
- page.replace("{{STYLES}}", css);
- page.replace("{{TEXT}}", text);
- *ts << page;
- }
- else {
- QString msg;
- if (m_NoResolve) {
- msg = "Push meship domain to resolve or IPv6 to convert to meship";
- } else {
- msg = "Push any domain to resolve or IPv6 to convert to meship";
- }
- qInfo().noquote() << " └ Incorrect request";
- *ts << "{\n"
- " \"status\": false,\n"
- " \"answer\": \"" + msg + "\"\n"
- "}\n";
- }
- clientSocket->close();
- }
- void Resolver::startPageWithMessage(const QString & value, QSharedPointer<QTextStream> ts, const QString & msg, bool web)
- {
- qInfo().noquote() << " └ " + msg;
- if (web) {
- QString page = http::HTML_PAGE;
- QString css = http::CSS_DEFAULT;
- css.replace("{{COLOR}}", "red");
- page.replace("{{STYLES}}", css);
- page.replace("{{TEXT}}", msg);
- if (!value.isEmpty()) {
- page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
- }
- *ts << page;
- }
- else {
- *ts << "{\n"
- " \"status\": false,\n"
- " \"answer\": \"" + msg + "\"\n"
- "}\n";
- }
- }
- void Resolver::processPage(const QString& req, QSharedPointer<QTextStream> ts, bool web)
- {
- QTcpSocket* clientSocket = static_cast<QTcpSocket*>(sender());
- QString value;
- if (web) {
- value = funcs::getValue(req, "toConverting");
- value.remove('+');
- value = QByteArray::fromPercentEncoding(value.toUtf8());
- } else {
- value = req;
- }
- qInfo().noquote() << " └ " + value;
- const uint8_t MAX_LEN = 60;
- if (value.size() > MAX_LEN) {
- startPageWithMessage("", ts, "Maximum input value length = " + QString::number(MAX_LEN), web);
- }
- else if (value == "version") {
- toVersion(value, ts, web);
- }
- else if (value.contains(":")) {
- toMeship(value, ts, web);
- }
- else if (value.endsWith(".meship")) {
- toIp(value, ts, web);
- }
- else if (value.contains(".") and not m_NoResolve) {
- toRealResolv(value, ts, web);
- }
- else {
- QString text;
- m_NoResolve
- ? text ="Input value must contains meship domain or IPv6"
- : text = "Input value must contains domain or IPv6";
- startPageWithMessage(value, ts, text, web);
- }
- clientSocket->close();
- }
- void Resolver::toMeship(const QString & value, QSharedPointer<QTextStream> ts, bool web)
- {
- Address rawAddr;
- convertStrToRaw(value, rawAddr);
- QString base32 = getBase32(rawAddr);
- base32 = base32.toLower();
- base32.remove('=');
- base32 += ".meship";
- if (web) {
- QString page = http::HTML_PAGE;
- QString css = http::CSS_DEFAULT;
- css.replace("{{COLOR}}", "green");
- page.replace("{{STYLES}}", css);
- page.replace("{{TEXT}}", base32);
- page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
- *ts << page;
- }
- else {
- *ts << "{\n"
- " \"status\": true,\n"
- " \"answer\": \"" + base32 + "\"\n"
- "}\n";
- }
- }
- void Resolver::toIp(const QString & value, QSharedPointer<QTextStream> ts, bool web)
- {
- QString result = decodeMeshToIP(value);
- if (result.isEmpty()) {
- startPageWithMessage(value, ts, "Failed: meship domain must have 26 base32 (RFC4648) characters only", web);
- return;
- }
- if (web) {
- QString page = http::HTML_PAGE;
- QString css = http::CSS_DEFAULT;
- css.replace("{{COLOR}}", "green");
- page.replace("{{STYLES}}", css);
- page.replace("{{TEXT}}", result);
- page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
- *ts << page;
- }
- else {
- *ts << "{\n"
- " \"status\": true,\n"
- " \"answer\": \"" + result + "\"\n"
- "}\n";
- }
- }
- void Resolver::toRealResolv(const QString & value, QSharedPointer<QTextStream> ts, bool web)
- {
- QHostInfo host = QHostInfo::fromName(value);
- if (host.error() == QHostInfo::NoError) {
- auto addrs = host.addresses();
- if (web) {
- QString addresses;
- for (auto a: addrs) {
- addresses += a.toString() + "<br>";
- }
- QString page = http::HTML_PAGE;
- QString css = http::CSS_DEFAULT;
- css.replace("{{COLOR}}", "green");
- page.replace("{{STYLES}}", css);
- page.replace("{{TEXT}}", addresses);
- page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
- *ts << page;
- }
- else {
- *ts << "{\n"
- " \"status\": true,\n"
- " \"answer\": [\n";
- for (auto it = addrs.begin(); it != addrs.end(); it++) {
- *ts << " \"" + it->toString() + "\"";
- if (it+1 != addrs.end()) {
- *ts << ",";
- }
- *ts << "\n";
- }
- *ts << " ]\n"
- "}\n";
- }
- } else {
- QString msg;
- if (value.endsWith(".meshname")) {
- msg = "Domain not resolved. Try \".meship\" instead \".meshname\" for direct translation to address";
- } else {
- msg = "Failed: " + host.errorString();
- }
- startPageWithMessage(value, ts, msg, web);
- }
- }
- void Resolver::toVersion(const QString &value , QSharedPointer<QTextStream> ts, bool web)
- {
- if (web) {
- QString page = http::HTML_PAGE;
- QString css = http::CSS_DEFAULT;
- css.replace("{{COLOR}}", "gray");
- page.replace("{{STYLES}}", css);
- page.replace("{{TEXT}}", VERSION);
- page.replace(QRegularExpression("placeholder=\".*domain\""), "value=\"" + value + "\"");
- *ts << page;
- }
- else {
- *ts << "{\n"
- " \"status\": true,\n"
- " \"answer\": \"" + VERSION + "\"\n"
- "}\n";
- }
- }
|