mirror of
https://github.com/Mahdi-zarei/nekoray.git
synced 2025-12-18 20:50:09 +08:00
fix: fix hysteria(2) url parser and generator for port hopping
This commit is contained in:
parent
e73fd0dc4a
commit
d655b14f69
96
3rdparty/URLParser/url_parser.h
vendored
Normal file
96
3rdparty/URLParser/url_parser.h
vendored
Normal file
@ -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 <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "url_parser_function.h"
|
||||||
|
|
||||||
|
class URLParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct HTTP_URL
|
||||||
|
{
|
||||||
|
std::string scheme;
|
||||||
|
std::string host;
|
||||||
|
std::string port;
|
||||||
|
std::vector<std::string> path;
|
||||||
|
std::string query_string;
|
||||||
|
std::unordered_map<std::string, std::string> 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<std::string, std::string>::value_type(key, value));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
URLParserFunction::SplitQueryString(query, "=", key, value);
|
||||||
|
http_url.query.insert(std::unordered_map<std::string, std::string>::value_type(key, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return http_url;
|
||||||
|
};
|
||||||
|
};
|
||||||
47
3rdparty/URLParser/url_parser_function.h
vendored
Normal file
47
3rdparty/URLParser/url_parser_function.h
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -220,10 +220,11 @@ namespace Configs {
|
|||||||
|
|
||||||
QString QUICBean::ToShareLink() {
|
QString QUICBean::ToShareLink() {
|
||||||
QUrl url;
|
QUrl url;
|
||||||
|
QString portRange;
|
||||||
if (proxy_type == proxy_Hysteria) {
|
if (proxy_type == proxy_Hysteria) {
|
||||||
url.setScheme("hysteria");
|
url.setScheme("hysteria");
|
||||||
url.setHost(serverAddress);
|
url.setHost(serverAddress);
|
||||||
url.setPort(serverPort);
|
url.setPort(0);
|
||||||
QUrlQuery q;
|
QUrlQuery q;
|
||||||
q.addQueryItem("upmbps", Int2String(uploadMbps));
|
q.addQueryItem("upmbps", Int2String(uploadMbps));
|
||||||
q.addQueryItem("downmbps", Int2String(downloadMbps));
|
q.addQueryItem("downmbps", Int2String(downloadMbps));
|
||||||
@ -239,15 +240,16 @@ namespace Configs {
|
|||||||
if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn);
|
if (!alpn.isEmpty()) q.addQueryItem("alpn", alpn);
|
||||||
if (connectionReceiveWindow > 0) q.addQueryItem("recv_window", Int2String(connectionReceiveWindow));
|
if (connectionReceiveWindow > 0) q.addQueryItem("recv_window", Int2String(connectionReceiveWindow));
|
||||||
if (streamReceiveWindow > 0) q.addQueryItem("recv_window_conn", Int2String(streamReceiveWindow));
|
if (streamReceiveWindow > 0) q.addQueryItem("recv_window_conn", Int2String(streamReceiveWindow));
|
||||||
if (!serverPorts.empty())
|
if (!serverPorts.empty()) {
|
||||||
{
|
|
||||||
QStringList portList;
|
QStringList portList;
|
||||||
for (const auto& range : serverPorts)
|
for (const auto& range : serverPorts) {
|
||||||
{
|
QString modifiedRange = range;
|
||||||
portList.append(range.split(":"));
|
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 (!hop_interval.isEmpty()) q.addQueryItem("hop_interval", hop_interval);
|
||||||
if (!q.isEmpty()) url.setQuery(q);
|
if (!q.isEmpty()) url.setQuery(q);
|
||||||
if (!name.isEmpty()) url.setFragment(name);
|
if (!name.isEmpty()) url.setFragment(name);
|
||||||
@ -270,7 +272,7 @@ namespace Configs {
|
|||||||
} else if (proxy_type == proxy_Hysteria2) {
|
} else if (proxy_type == proxy_Hysteria2) {
|
||||||
url.setScheme("hy2");
|
url.setScheme("hy2");
|
||||||
url.setHost(serverAddress);
|
url.setHost(serverAddress);
|
||||||
url.setPort(serverPort);
|
url.setPort(0);
|
||||||
if (password.contains(":")) {
|
if (password.contains(":")) {
|
||||||
url.setUserName(SubStrBefore(password, ":"));
|
url.setUserName(SubStrBefore(password, ":"));
|
||||||
url.setPassword(SubStrAfter(password, ":"));
|
url.setPassword(SubStrAfter(password, ":"));
|
||||||
@ -284,20 +286,24 @@ namespace Configs {
|
|||||||
}
|
}
|
||||||
if (allowInsecure) q.addQueryItem("insecure", "1");
|
if (allowInsecure) q.addQueryItem("insecure", "1");
|
||||||
if (!sni.isEmpty()) q.addQueryItem("sni", sni);
|
if (!sni.isEmpty()) q.addQueryItem("sni", sni);
|
||||||
if (!serverPorts.empty())
|
if (!serverPorts.empty()) {
|
||||||
{
|
|
||||||
QStringList portList;
|
QStringList portList;
|
||||||
for (const auto& range : serverPorts)
|
for (const auto& range : serverPorts) {
|
||||||
{
|
QString modifiedRange = range;
|
||||||
portList.append(range.split(":"));
|
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 (!hop_interval.isEmpty()) q.addQueryItem("hop_interval", hop_interval);
|
||||||
if (!q.isEmpty()) url.setQuery(q);
|
if (!q.isEmpty()) url.setQuery(q);
|
||||||
if (!name.isEmpty()) url.setFragment(name);
|
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() {
|
QString WireguardBean::ToShareLink() {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "include/dataStore/ProxyEntity.hpp"
|
#include "include/dataStore/ProxyEntity.hpp"
|
||||||
#include "include/configs/proxy/includes.h"
|
#include "include/configs/proxy/includes.h"
|
||||||
|
#include "3rdparty/URLParser/url_parser.h"
|
||||||
|
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
|
|
||||||
@ -302,8 +303,16 @@ namespace Configs {
|
|||||||
|
|
||||||
bool QUICBean::TryParseLink(const QString &link) {
|
bool QUICBean::TryParseLink(const QString &link) {
|
||||||
auto url = QUrl(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());
|
auto query = QUrlQuery(url.query());
|
||||||
if (url.host().isEmpty() || url.port() == -1) return false;
|
|
||||||
|
|
||||||
if (url.scheme() == "hysteria") {
|
if (url.scheme() == "hysteria") {
|
||||||
// https://hysteria.network/docs/uri-scheme/
|
// https://hysteria.network/docs/uri-scheme/
|
||||||
@ -311,7 +320,7 @@ namespace Configs {
|
|||||||
|
|
||||||
name = url.fragment(QUrl::FullyDecoded);
|
name = url.fragment(QUrl::FullyDecoded);
|
||||||
serverAddress = url.host();
|
serverAddress = url.host();
|
||||||
serverPort = url.port();
|
if (serverPort > 0) serverPort = url.port();
|
||||||
obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfsParam").toUtf8());
|
obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfsParam").toUtf8());
|
||||||
allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure"));
|
allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure"));
|
||||||
uploadMbps = query.queryItemValue("upmbps").toInt();
|
uploadMbps = query.queryItemValue("upmbps").toInt();
|
||||||
@ -335,13 +344,10 @@ namespace Configs {
|
|||||||
connectionReceiveWindow = query.queryItemValue("recv_window").toInt();
|
connectionReceiveWindow = query.queryItemValue("recv_window").toInt();
|
||||||
streamReceiveWindow = query.queryItemValue("recv_window_conn").toInt();
|
streamReceiveWindow = query.queryItemValue("recv_window_conn").toInt();
|
||||||
|
|
||||||
if (query.hasQueryItem("server_ports"))
|
if (query.hasQueryItem("mport")) {
|
||||||
{
|
serverPorts = query.queryItemValue("mport").split(",");
|
||||||
auto portList = query.queryItemValue("server_ports").split("-");
|
for (int i=0; i < serverPorts.size(); i++) {
|
||||||
for (int i=0;i<portList.size();i+=2)
|
serverPorts[i].replace("-", ":");
|
||||||
{
|
|
||||||
if (i+1 >= portList.size()) break;
|
|
||||||
serverPorts += portList[i]+":"+portList[i+1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hop_interval = query.queryItemValue("hop_interval");
|
hop_interval = query.queryItemValue("hop_interval");
|
||||||
@ -366,7 +372,7 @@ namespace Configs {
|
|||||||
} else if (QStringList{"hy2", "hysteria2"}.contains(url.scheme())) {
|
} else if (QStringList{"hy2", "hysteria2"}.contains(url.scheme())) {
|
||||||
name = url.fragment(QUrl::FullyDecoded);
|
name = url.fragment(QUrl::FullyDecoded);
|
||||||
serverAddress = url.host();
|
serverAddress = url.host();
|
||||||
serverPort = url.port();
|
if (serverPort > 0) serverPort = url.port();
|
||||||
obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfs-password").toUtf8());
|
obfsPassword = QUrl::fromPercentEncoding(query.queryItemValue("obfs-password").toUtf8());
|
||||||
allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure"));
|
allowInsecure = QStringList{"1", "true"}.contains(query.queryItemValue("insecure"));
|
||||||
|
|
||||||
@ -375,13 +381,10 @@ namespace Configs {
|
|||||||
} else {
|
} else {
|
||||||
password = url.userName() + ":" + url.password();
|
password = url.userName() + ":" + url.password();
|
||||||
}
|
}
|
||||||
if (query.hasQueryItem("server_ports"))
|
if (query.hasQueryItem("mport")) {
|
||||||
{
|
serverPorts = query.queryItemValue("mport").split(",");
|
||||||
auto portList = query.queryItemValue("server_ports").split("-");
|
for (int i=0; i < serverPorts.size(); i++) {
|
||||||
for (int i=0;i<portList.size();i+=2)
|
serverPorts[i].replace("-", ":");
|
||||||
{
|
|
||||||
if (i+1 >= portList.size()) break;
|
|
||||||
serverPorts += portList[i]+":"+portList[i+1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hop_interval = query.queryItemValue("hop_interval");
|
hop_interval = query.queryItemValue("hop_interval");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user