#include "cppcodec/base32_rfc4648.hpp" #include "resolver.h" #include "funcs.h" #include "HTTPheaders.h" #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) { if (!m_tcpServer->listen(m_address, m_port)) { throw "Server not binded"; } qDebug().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 Resolver::getSocketViaDescriptor(int id) { if (!m_sockets.contains(id)) { qDebug() << "Resolver::getSocketViaDescriptor: Requested socket not found"; } return m_sockets.value(id); } void Resolver::slotNewUser() { if (!m_tcpServer->isListening()) return; QTcpSocket* clientSocket = m_tcpServer->nextPendingConnection(); int idUserSock = clientSocket->socketDescriptor(); m_sockets.insert(idUserSock, QSharedPointer(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(sender()); QString req(clientSocket->readAll()); qDebug().noquote() << "\n->" << clientSocket->peerAddress().toString() << "[" + QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz") + "]"; bool web = req.startsWith("GET /"); QSharedPointer ts(new QTextStream(clientSocket)); if (web) { qDebug().noquote() << " └ Via web browser"; if (req.contains("toConverting=")) { processPage(req, ts, web); } else { startPage(ts, web); } } else { req.remove('\r'); req.remove('\n'); qDebug().noquote() << " └ Without web browser"; if (req.contains(":") or req.contains(".")) { processPage(req, ts, web); } else { startPage(ts, web); } } } void Resolver::slotClientDisconnected() { QTcpSocket* clientSocket = static_cast(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 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 ts, bool web) { QTcpSocket* clientSocket = static_cast(sender()); if (web) { qDebug().noquote() << " └ Start page"; QString page = http::HTML_PAGE; QString css = http::CSS_DEFAULT; css.replace("{{COLOR}}", "gray"); page.replace("{{STYLES}}", css); page.replace("{{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."); *ts << page; } else { qDebug().noquote() << " └ Incorrect request"; *ts << "{\n" " \"status\": false,\n" " \"answer\": \"Push any domain to resolve or IPv6 to convert to meship\"\n" "}\n"; } clientSocket->close(); } void Resolver::startPageWithMessage(const QString & value, QSharedPointer ts, const QString & msg, bool web) { qDebug().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("placeholder=\"IPv6 address or 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"); } else { value = req; } value.remove('+'); value = QByteArray::fromPercentEncoding(value.toUtf8()); qDebug().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.contains(":")) { toMeship(value, ts, web); } else if (value.endsWith(".meship")) { toIp(value, ts, web); } else if (value.contains(".")) { toRealResolv(value, ts, web); } else { startPageWithMessage(value, ts, "Input value must contains domain or IPv6", 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("placeholder=\"IPv6 address or 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("placeholder=\"IPv6 address or 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("placeholder=\"IPv6 address or 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); } }