diff --git a/3rdparty/URLParser/url_parser.h b/3rdparty/URLParser/url_parser.h new file mode 100644 index 0000000..d3625cd --- /dev/null +++ b/3rdparty/URLParser/url_parser.h @@ -0,0 +1,96 @@ +// +// URL Parser for C++ +// Created Oct 22, 2017. +// Website : https://github.com/dongbum/URLParser +// Usage : Just include this header file. +// + +#pragma once + +#include +#include +#include + +#include "url_parser_function.h" + +class URLParser +{ +public: + struct HTTP_URL + { + std::string scheme; + std::string host; + std::string port; + std::vector path; + std::string query_string; + std::unordered_map query; + }; + +public: + static HTTP_URL Parse(const std::string& input_url) + { + HTTP_URL http_url; + + size_t st = 0; + size_t before = 0; + + URLParserFunction::FindKeyword(input_url, st, before, "://", http_url.scheme); + URLParserFunction::FindKeyword(input_url, st, before, "/", http_url.host); + + size_t temp_st = 0; + size_t temp_before = 0; + std::string temp_ip; + std::string temp_port; + + if (true == URLParserFunction::FindKeyword(http_url.host, temp_st, temp_before, ":", temp_ip)) + { + http_url.port = std::string(&http_url.host[temp_before]); + http_url.host = temp_ip; + } + + while (true) + { + std::string path; + if (false == URLParserFunction::FindKeyword(input_url, st, before, "/", path)) + break; + + http_url.path.push_back(path); + } + + std::string path; + if (false == URLParserFunction::FindKeyword(input_url, st, before, "?", path)) + { + path = std::string(&input_url[st + 1]); + http_url.path.push_back(path); + return http_url; + } + + if (st < input_url.length()) + { + http_url.query_string = std::string(&input_url[st + 1]); + if (false == http_url.query_string.empty()) + { + std::string query; + st = 0; + before = 0; + + while (true) + { + std::string key, value; + + if (false == URLParserFunction::FindKeyword(http_url.query_string, st, before, "&", query)) + { + URLParserFunction::SplitQueryString(std::string(&http_url.query_string[before]), "=", key, value); + http_url.query.insert(std::unordered_map::value_type(key, value)); + break; + } + + URLParserFunction::SplitQueryString(query, "=", key, value); + http_url.query.insert(std::unordered_map::value_type(key, value)); + } + } + } + + return http_url; + }; +}; diff --git a/3rdparty/URLParser/url_parser_function.h b/3rdparty/URLParser/url_parser_function.h new file mode 100644 index 0000000..a8a6984 --- /dev/null +++ b/3rdparty/URLParser/url_parser_function.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include + +class URLParserFunction +{ +public: + static bool FindKeyword(const std::string& input_url, size_t& st, size_t& before, const std::string& delim, std::string& result) + { + char temp[1024] = { 0, }; + size_t temp_st = st; + memcpy(&temp_st, &st, sizeof(temp_st)); + + st = input_url.find(delim, before); + if (st == std::string::npos) + { + st = temp_st; + return false; + } + + memcpy(&temp[0], &input_url[before], st - before); + before = st + delim.length(); + + result = std::string(temp); + if (result.empty()) + return false; + + return true; + }; + + static bool SplitQueryString(const std::string& str, const std::string& delim, std::string& key, std::string& value) + { + char first[1024] = { 0, }; + char second[1024] = { 0, }; + + size_t st = str.find(delim, 0); + + memcpy(first, &str[0], st); + memcpy(second, &str[st + 1], str.length() - st); + + key = std::string(first); + value = std::string(second); + + return true; + }; +}; diff --git a/src/configs/proxy/Bean2Link.cpp b/src/configs/proxy/Bean2Link.cpp index 638deb3..362c1ca 100644 --- a/src/configs/proxy/Bean2Link.cpp +++ b/src/configs/proxy/Bean2Link.cpp @@ -220,10 +220,11 @@ namespace Configs { QString QUICBean::ToShareLink() { QUrl url; + QString portRange; if (proxy_type == proxy_Hysteria) { url.setScheme("hysteria"); url.setHost(serverAddress); - url.setPort(serverPort); + url.setPort(0); QUrlQuery q; q.addQueryItem("upmbps", Int2String(uploadMbps)); q.addQueryItem("downmbps", Int2String(downloadMbps)); @@ -239,15 +240,16 @@ namespace Configs { if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn); if (connectionReceiveWindow > 0) q.addQueryItem("recv_window", Int2String(connectionReceiveWindow)); if (streamReceiveWindow > 0) q.addQueryItem("recv_window_conn", Int2String(streamReceiveWindow)); - if (!serverPorts.empty()) - { + if (!serverPorts.empty()) { QStringList portList; - for (const auto& range : serverPorts) - { - portList.append(range.split(":")); + for (const auto& range : serverPorts) { + QString modifiedRange = range; + modifiedRange.replace(":", "-"); + portList.append(modifiedRange); } - q.addQueryItem("server_ports", portList.join("-")); - } + portRange = portList.join(","); + } else + url.setPort(serverPort); if (!hop_interval.isEmpty()) q.addQueryItem("hop_interval", hop_interval); if (!q.isEmpty()) url.setQuery(q); if (!name.isEmpty()) url.setFragment(name); @@ -270,7 +272,7 @@ namespace Configs { } else if (proxy_type == proxy_Hysteria2) { url.setScheme("hy2"); url.setHost(serverAddress); - url.setPort(serverPort); + url.setPort(0); if (password.contains(":")) { url.setUserName(SubStrBefore(password, ":")); url.setPassword(SubStrAfter(password, ":")); @@ -284,20 +286,24 @@ namespace Configs { } if (allowInsecure) q.addQueryItem("insecure", "1"); if (!sni.isEmpty()) q.addQueryItem("sni", sni); - if (!serverPorts.empty()) - { + if (!serverPorts.empty()) { QStringList portList; - for (const auto& range : serverPorts) - { - portList.append(range.split(":")); + for (const auto& range : serverPorts) { + QString modifiedRange = range; + modifiedRange.replace(":", "-"); + portList.append(modifiedRange); } - q.addQueryItem("server_ports", portList.join("-")); - } + portRange = portList.join(","); + } else + url.setPort(serverPort); if (!hop_interval.isEmpty()) q.addQueryItem("hop_interval", hop_interval); if (!q.isEmpty()) url.setQuery(q); if (!name.isEmpty()) url.setFragment(name); } - return url.toString(QUrl::FullyEncoded); + if (portRange.isEmpty()) + return url.toString(QUrl::FullyEncoded); + else + return url.toString(QUrl::FullyEncoded).replace(":0?", ":" + portRange + "?"); } QString WireguardBean::ToShareLink() { diff --git a/src/configs/proxy/Link2Bean.cpp b/src/configs/proxy/Link2Bean.cpp index 41bf45e..c9962c1 100644 --- a/src/configs/proxy/Link2Bean.cpp +++ b/src/configs/proxy/Link2Bean.cpp @@ -1,5 +1,6 @@ #include "include/dataStore/ProxyEntity.hpp" #include "include/configs/proxy/includes.h" +#include "3rdparty/URLParser/url_parser.h" #include @@ -302,8 +303,16 @@ namespace Configs { bool QUICBean::TryParseLink(const QString &link) { auto url = QUrl(link); + if (!url.isValid()) { + if(!url.errorString().startsWith("Invalid port")) + return false; + serverPort = 0; + serverPorts = QString::fromStdString(URLParser::Parse((link.split("?")[0] + "/").toStdString()).port).split(","); + for (int i=0; i < serverPorts.size(); i++) { + serverPorts[i].replace("-", ":"); + } + } auto query = QUrlQuery(url.query()); - if (url.host().isEmpty() || url.port() == -1) return false; if (url.scheme() == "hysteria") { // https://hysteria.network/docs/uri-scheme/ @@ -311,7 +320,7 @@ namespace Configs { name = url.fragment(QUrl::FullyDecoded); serverAddress = url.host(); - serverPort = url.port(); + if (serverPort > 0) serverPort = url.port(); obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfsParam").toUtf8()); allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure")); uploadMbps = query.queryItemValue("upmbps").toInt(); @@ -335,13 +344,10 @@ namespace Configs { connectionReceiveWindow = query.queryItemValue("recv_window").toInt(); streamReceiveWindow = query.queryItemValue("recv_window_conn").toInt(); - if (query.hasQueryItem("server_ports")) - { - auto portList = query.queryItemValue("server_ports").split("-"); - for (int i=0;i= portList.size()) break; - serverPorts += portList[i]+":"+portList[i+1]; + if (query.hasQueryItem("mport")) { + serverPorts = query.queryItemValue("mport").split(","); + for (int i=0; i < serverPorts.size(); i++) { + serverPorts[i].replace("-", ":"); } } hop_interval = query.queryItemValue("hop_interval"); @@ -366,7 +372,7 @@ namespace Configs { } else if (QStringList{"hy2", "hysteria2"}.contains(url.scheme())) { name = url.fragment(QUrl::FullyDecoded); serverAddress = url.host(); - serverPort = url.port(); + if (serverPort > 0) serverPort = url.port(); obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfs-password").toUtf8()); allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure")); @@ -375,13 +381,10 @@ namespace Configs { } else { password = url.userName() + ":" + url.password(); } - if (query.hasQueryItem("server_ports")) - { - auto portList = query.queryItemValue("server_ports").split("-"); - for (int i=0;i= portList.size()) break; - serverPorts += portList[i]+":"+portList[i+1]; + if (query.hasQueryItem("mport")) { + serverPorts = query.queryItemValue("mport").split(","); + for (int i=0; i < serverPorts.size(); i++) { + serverPorts[i].replace("-", ":"); } } hop_interval = query.queryItemValue("hop_interval");