#include "cppcodec/base32_rfc4648.hpp" #include "resolver.h" #include "funcs.h" #include "HTTPheaders.h" #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #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::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(); connect (clientSocket, SIGNAL(readyRead()), this, SLOT(slotReadyClient())); connect (clientSocket, SIGNAL(disconnected()), clientSocket, SLOT(deleteLater())); } void Resolver::slotReadyClient() { QTcpSocket* clientSocket = static_cast(sender()); QString req(clientSocket->readAll()); qInfo().noquote() << "\n->" << clientSocket->peerAddress().toString() << "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") + "]"; QSharedPointer 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::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 raw; try { raw = cppcodec::base32_rfc4648::decode(mesh); } catch (cppcodec::padding_error const&) { return QString(); } catch (cppcodec::symbol_error const&) { 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 ts, bool web) { QTcpSocket* clientSocket = static_cast(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 = "

Input any domain to resolve or IPv6 to convert to meship.

"; } else if (m_NoApi and m_NoResolve) { text = "

Input meship domain to resolve or IPv6 to convert to meship.

"; } else if (not m_NoResolve) { text = "

Input any domain to resolve or IPv6 to convert to meship.

" "

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 = "

Input meship domain to resolve or IPv6 to convert to meship.

" "

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 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 ts, bool web) { QTcpSocket* clientSocket = static_cast(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 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 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 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() + "
"; } 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 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"; } }