Update On Sun Jul 21 20:29:04 CEST 2024

This commit is contained in:
github-action[bot]
2024-07-21 20:29:05 +02:00
parent e06319d65a
commit 67b34eb55e
121 changed files with 1783 additions and 790 deletions
+1
View File
@@ -709,3 +709,4 @@ Update On Wed Jul 17 20:32:28 CEST 2024
Update On Thu Jul 18 20:31:52 CEST 2024
Update On Fri Jul 19 20:31:21 CEST 2024
Update On Sat Jul 20 20:32:08 CEST 2024
Update On Sun Jul 21 20:28:53 CEST 2024
+8 -8
View File
@@ -204,24 +204,24 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient(
method = http3.MethodGet0RTT
}
url := doh.url
url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf))
httpReq, err := http.NewRequestWithContext(ctx, method, url.String(), nil)
requestUrl := *doh.url // don't modify origin url
requestUrl.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf))
httpReq, err := http.NewRequestWithContext(ctx, method, requestUrl.String(), nil)
if err != nil {
return nil, fmt.Errorf("creating http request to %s: %w", url, err)
return nil, fmt.Errorf("creating http request to %s: %w", doh.url, err)
}
httpReq.Header.Set("Accept", "application/dns-message")
httpReq.Header.Set("User-Agent", "")
httpResp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("requesting %s: %w", url, err)
return nil, fmt.Errorf("requesting %s: %w", doh.url, err)
}
defer httpResp.Body.Close()
body, err := io.ReadAll(httpResp.Body)
if err != nil {
return nil, fmt.Errorf("reading %s: %w", url, err)
return nil, fmt.Errorf("reading %s: %w", doh.url, err)
}
if httpResp.StatusCode != http.StatusOK {
@@ -230,7 +230,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient(
"expected status %d, got %d from %s",
http.StatusOK,
httpResp.StatusCode,
url,
doh.url,
)
}
@@ -239,7 +239,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient(
if err != nil {
return nil, fmt.Errorf(
"unpacking response from %s: body is %s: %w",
url,
doh.url,
body,
err,
)
+4 -4
View File
@@ -936,7 +936,7 @@ dependencies = [
"windows-sys 0.52.0",
"winreg 0.52.0",
"wry",
"zip 2.1.4",
"zip 2.1.5",
"zip-extensions",
]
@@ -7921,9 +7921,9 @@ dependencies = [
[[package]]
name = "zip"
version = "2.1.4"
version = "2.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e29ab4097989787b2029a5981c41b7bfb427b5a601e23f455daacb4d0360a9e9"
checksum = "b895748a3ebcb69b9d38dcfdf21760859a4b0d0b0015277640c2ef4c69640e6f"
dependencies = [
"aes",
"arbitrary",
@@ -7954,7 +7954,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb0a99499b3497d765525c5d05e3ade9ca4a731c184365c19472c3fd6ba86341"
dependencies = [
"zip 2.1.4",
"zip 2.1.5",
]
[[package]]
@@ -6,7 +6,6 @@ import (
"go.uber.org/atomic"
)
// todo: move to internal/lb
type Node struct {
Address string
Label string
+1 -1
View File
@@ -6,8 +6,8 @@ import (
"net/url"
"github.com/Ehco1996/ehco/internal/constant"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/pkg/lb"
"go.uber.org/zap"
)
+1 -1
View File
@@ -8,13 +8,13 @@ import (
"github.com/Ehco1996/ehco/internal/cmgr"
"github.com/Ehco1996/ehco/internal/conn"
"github.com/Ehco1996/ehco/internal/constant"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/metrics"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/sagernet/sing-box/common/sniff"
"github.com/sagernet/sing/common/buf"
"github.com/sagernet/sing/common/bufio"
"github.com/Ehco1996/ehco/pkg/lb"
"go.uber.org/zap"
)
+1 -1
View File
@@ -7,8 +7,8 @@ import (
"github.com/Ehco1996/ehco/internal/cmgr"
"github.com/Ehco1996/ehco/internal/constant"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/Ehco1996/ehco/pkg/lb"
)
type TCPHandShakeF func(remote *lb.Node) (net.Conn, error)
+1 -1
View File
@@ -7,9 +7,9 @@ import (
"time"
"github.com/Ehco1996/ehco/internal/constant"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/metrics"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/Ehco1996/ehco/pkg/lb"
"go.uber.org/zap"
)
+1 -1
View File
@@ -8,9 +8,9 @@ import (
"github.com/xtaci/smux"
"go.uber.org/zap"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/metrics"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/Ehco1996/ehco/pkg/lb"
)
var (
+1 -1
View File
@@ -12,10 +12,10 @@ import (
"github.com/Ehco1996/ehco/internal/conn"
"github.com/Ehco1996/ehco/internal/constant"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/metrics"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/Ehco1996/ehco/internal/web"
"github.com/Ehco1996/ehco/pkg/lb"
)
var (
+1 -1
View File
@@ -13,9 +13,9 @@ import (
"github.com/labstack/echo/v4"
"github.com/xtaci/smux"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/metrics"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/Ehco1996/ehco/pkg/lb"
)
var (
+1 -1
View File
@@ -13,9 +13,9 @@ import (
"github.com/labstack/echo/v4"
"github.com/xtaci/smux"
"github.com/Ehco1996/ehco/internal/lb"
"github.com/Ehco1996/ehco/internal/metrics"
"github.com/Ehco1996/ehco/internal/relay/conf"
"github.com/Ehco1996/ehco/pkg/lb"
)
var (
+2 -1
View File
@@ -102,7 +102,8 @@ define KernelPackage/fb
CONFIG_VT_CONSOLE=y \
CONFIG_VT_HW_CONSOLE_BINDING=y
FILES:=$(LINUX_DIR)/drivers/video/fbdev/core/fb.ko \
$(LINUX_DIR)/lib/fonts/font.ko
$(LINUX_DIR)/lib/fonts/font.ko \
$(LINUX_DIR)/drivers/video/fbdev/core/fb_io_fops.ko@ge6.6
AUTOLOAD:=$(call AutoLoad,06,fb font)
endef
+1
View File
@@ -247,6 +247,7 @@ CONFIG_FB_CORE=y
CONFIG_FB_DEFERRED_IO=y
CONFIG_FB_DEVICE=y
CONFIG_FB_EFI=y
CONFIG_FB_IOMEM_FOPS=y
CONFIG_FB_IOMEM_HELPERS=y
CONFIG_FB_MODE_HELPERS=y
CONFIG_FB_SIMPLE=y
+8 -8
View File
@@ -204,24 +204,24 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient(
method = http3.MethodGet0RTT
}
url := doh.url
url.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf))
httpReq, err := http.NewRequestWithContext(ctx, method, url.String(), nil)
requestUrl := *doh.url // don't modify origin url
requestUrl.RawQuery = fmt.Sprintf("dns=%s", base64.RawURLEncoding.EncodeToString(buf))
httpReq, err := http.NewRequestWithContext(ctx, method, requestUrl.String(), nil)
if err != nil {
return nil, fmt.Errorf("creating http request to %s: %w", url, err)
return nil, fmt.Errorf("creating http request to %s: %w", doh.url, err)
}
httpReq.Header.Set("Accept", "application/dns-message")
httpReq.Header.Set("User-Agent", "")
httpResp, err := client.Do(httpReq)
if err != nil {
return nil, fmt.Errorf("requesting %s: %w", url, err)
return nil, fmt.Errorf("requesting %s: %w", doh.url, err)
}
defer httpResp.Body.Close()
body, err := io.ReadAll(httpResp.Body)
if err != nil {
return nil, fmt.Errorf("reading %s: %w", url, err)
return nil, fmt.Errorf("reading %s: %w", doh.url, err)
}
if httpResp.StatusCode != http.StatusOK {
@@ -230,7 +230,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient(
"expected status %d, got %d from %s",
http.StatusOK,
httpResp.StatusCode,
url,
doh.url,
)
}
@@ -239,7 +239,7 @@ func (doh *dnsOverHTTPS) exchangeHTTPSClient(
if err != nil {
return nil, fmt.Errorf(
"unpacking response from %s: body is %s: %w",
url,
doh.url,
body,
err,
)
+22 -13
View File
@@ -14,8 +14,8 @@ Description:
"proxy": "..."
}
`--listen` can be specified multiple times on the command line,
and can be either a string or an array of strings in the JSON file.
Specifying a flag multiple times on the command line is equivalent to
having an array of multiple strings in the JSON file.
Uses "config.json" by default if run without arguments.
@@ -29,18 +29,16 @@ Options:
Prints version.
--listen=<proto>://[addr][:port]
--listen=socks://[[user]:[pass]@][addr][:port]
--listen=LISTEN-URI
Listens at addr:port with protocol <proto>.
LISTEN-URI = <LISTEN-PROTO>"://"[<USER>":"<PASS>"@"][<ADDR>][":"<PORT>]
LISTEN-PROTO = "socks" | "http" | "redir"
Listens at addr:port with protocol <LISTEN-PROTO>.
Can be specified multiple times to listen on multiple ports.
Available proto: socks, http, redir.
Default proto, addr, port: socks, 0.0.0.0, 1080.
* http: Supports only proxying https:// URLs, no http://.
* redir: Works with certain iptables setup.
Note: redir requires specific iptables rules and uses no authentication.
(Redirecting locally originated traffic)
iptables -t nat -A OUTPUT -d $proxy_server_ip -j RETURN
@@ -57,10 +55,21 @@ Options:
The artificial results are not saved for privacy, so restarting the
resolver may cause downstream to cache stale results.
--proxy=<proto>://<user>:<pass>@<hostname>[:<port>]
--proxy=PROXY
Routes traffic via the proxy server. Connects directly by default.
Available proto: https, quic. Infers port by default.
PROXY = PROXY-CHAIN | SOCKS-PROXY
PROXY-CHAIN = <PROXY-URI>[","<PROXY-CHAIN>]
PROXY-URI = <PROXY-PROTO>"://"<USER>":"<PASS>"@"<HOSTNAME>[":"<PORT>]
PROXY-PROTO = "http" | "https" | "quic"
SOCKS-PROXY = "socks://"<HOSTNAME>[":"<PORT>]
Routes traffic via the proxy chain.
The default proxy is directly connection without proxying.
The last PROXY-URI is negotiated automatically for Naive padding.
Limitations:
* QUIC proxies cannot follow TCP-based proxies in a proxy chain.
* The user needs to ensure there is no loop in the proxy chain.
* SOCKS proxies do not support chaining, authentication, or Naive padding.
--insecure-concurrency=<N>
+1 -1
View File
@@ -137,7 +137,7 @@ bool ProxyChain::IsValidInternal() const {
return false;
}
seen_quic = true;
} else if (proxy_server.is_https()) {
} else if (proxy_server.is_https() || proxy_server.is_http()) {
seen_https = true;
} else {
return false;
@@ -487,9 +487,17 @@ int HttpProxyConnectJob::DoBeginConnect() {
int HttpProxyConnectJob::DoTransportConnect() {
ProxyServer::Scheme scheme = GetProxyServerScheme();
if (scheme == ProxyServer::SCHEME_HTTP) {
nested_connect_job_ = std::make_unique<TransportConnectJob>(
priority(), socket_tag(), common_connect_job_params(),
params_->transport_params(), this, &net_log());
if (params_->is_over_transport()) {
nested_connect_job_ = std::make_unique<TransportConnectJob>(
priority(), socket_tag(), common_connect_job_params(),
params_->transport_params(), this, &net_log());
} else if (params_->is_over_http()) {
nested_connect_job_ = std::make_unique<HttpProxyConnectJob>(
priority(), socket_tag(), common_connect_job_params(),
params_->http_params(), this, &net_log());
} else {
CHECK(false) << "Invalid nested connect job";
}
} else {
DCHECK_EQ(scheme, ProxyServer::SCHEME_HTTPS);
DCHECK(params_->is_over_ssl());
@@ -99,6 +99,13 @@ class NET_EXPORT_PRIVATE HttpProxySocketParams
return quic_ssl_config_;
}
bool is_over_http() const {
return nested_params_ && nested_params_->is_http_proxy();
}
const scoped_refptr<HttpProxySocketParams>& http_params() const {
return nested_params_->http_proxy();
}
const HostPortPair& endpoint() const { return endpoint_; }
const ProxyChain& proxy_chain() const { return proxy_chain_; }
const ProxyServer& proxy_server() const {
@@ -388,15 +388,15 @@ int TCPSocketPosix::GetPeerAddress(IPEndPoint* address) const {
int TCPSocketPosix::SetDefaultOptionsForServer() {
DCHECK(socket_);
#if BUILDFLAG(IS_LINUX)
#ifdef SO_REUSEPORT
int reuseport = 1;
int rv =
setsockopt(socket_->socket_fd(), SOL_SOCKET, SO_REUSEPORT,
reinterpret_cast<const char*>(&reuseport), sizeof(reuseport));
if (rv < 0) {
// Ignore errors that the option does not exist.
if (rv != 0 && errno != ENOPROTOOPT)
return MapSystemError(errno);
}
#endif
#endif // SO_REUSEPORT
return AllowAddressReuse();
}
@@ -10,6 +10,7 @@
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
@@ -40,6 +41,8 @@ constexpr int kMaxPaddingSize = kMinPaddingSize + 32;
HttpProxyServerSocket::HttpProxyServerSocket(
std::unique_ptr<StreamSocket> transport_socket,
const std::string& user,
const std::string& pass,
ClientPaddingDetectorDelegate* padding_detector_delegate,
const NetworkTrafficAnnotationTag& traffic_annotation,
const std::vector<PaddingType>& supported_padding_types)
@@ -53,7 +56,12 @@ HttpProxyServerSocket::HttpProxyServerSocket(
header_write_size_(-1),
net_log_(transport_->NetLog()),
traffic_annotation_(traffic_annotation),
supported_padding_types_(supported_padding_types) {}
supported_padding_types_(supported_padding_types) {
if (!user.empty() || !pass.empty()) {
basic_auth_ =
std::string("Basic ").append(base::Base64Encode(user + ":" + pass));
}
}
HttpProxyServerSocket::~HttpProxyServerSocket() {
Disconnect();
@@ -347,6 +355,15 @@ int HttpProxyServerSocket::DoHeaderReadComplete(int result) {
headers.AddHeadersFromString(headers_str);
}
if (!basic_auth_.empty()) {
std::string proxy_auth;
headers.GetHeader(HttpRequestHeaders::kProxyAuthorization, &proxy_auth);
if (proxy_auth != basic_auth_) {
LOG(WARNING) << "Invalid Proxy-Authorization: " << proxy_auth;
return ERR_INVALID_ARGUMENT;
}
}
if (is_http_1_0) {
GURL url(uri);
if (!url.is_valid()) {
@@ -35,6 +35,8 @@ class HttpProxyServerSocket : public StreamSocket {
public:
HttpProxyServerSocket(
std::unique_ptr<StreamSocket> transport_socket,
const std::string& user,
const std::string& pass,
ClientPaddingDetectorDelegate* padding_detector_delegate,
const NetworkTrafficAnnotationTag& traffic_annotation,
const std::vector<PaddingType>& supported_padding_types);
@@ -117,6 +119,8 @@ class HttpProxyServerSocket : public StreamSocket {
bool was_ever_used_;
int header_write_size_;
std::string basic_auth_;
HostPortPair request_endpoint_;
NetLogWithSource net_log_;
+82 -12
View File
@@ -3,14 +3,27 @@
// found in the LICENSE file.
#include "net/tools/naive/naive_config.h"
#include <algorithm>
#include <iostream>
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"
#include "net/base/url_util.h"
#include "url/gurl.h"
namespace net {
namespace {
ProxyServer MyProxyUriToProxyServer(std::string_view uri) {
if (uri.compare(0, 7, "quic://") == 0) {
return ProxySchemeHostAndPortToProxyServer(ProxyServer::SCHEME_QUIC,
uri.substr(7));
}
return ProxyUriToProxyServer(uri, ProxyServer::SCHEME_INVALID);
}
} // namespace
NaiveListenConfig::NaiveListenConfig() = default;
NaiveListenConfig::NaiveListenConfig(const NaiveListenConfig&) = default;
@@ -114,22 +127,79 @@ bool NaiveConfig::Parse(const base::Value::Dict& value) {
if (const base::Value* v = value.Find("proxy")) {
if (const std::string* str = v->GetIfString(); str && !str->empty()) {
GURL url(*str);
net::GetIdentityFromURL(url, &proxy_user, &proxy_pass);
base::StringTokenizer proxy_uri_list(*str, ",");
std::vector<ProxyServer> proxy_servers;
bool seen_tcp = false;
while (proxy_uri_list.GetNext()) {
std::string token(proxy_uri_list.token());
GURL url(token);
GURL::Replacements remove_auth;
remove_auth.ClearUsername();
remove_auth.ClearPassword();
GURL url_no_auth = url.ReplaceComponents(remove_auth);
proxy_url = url_no_auth.GetWithEmptyPath().spec();
if (proxy_url.empty()) {
std::cerr << "Invalid proxy" << std::endl;
std::u16string proxy_user;
std::u16string proxy_pass;
net::GetIdentityFromURL(url, &proxy_user, &proxy_pass);
GURL::Replacements remove_auth;
remove_auth.ClearUsername();
remove_auth.ClearPassword();
GURL url_no_auth = url.ReplaceComponents(remove_auth);
std::string proxy_uri = url_no_auth.GetWithEmptyPath().spec();
if (proxy_uri.back() == '/') {
proxy_uri.pop_back();
}
proxy_servers.emplace_back(MyProxyUriToProxyServer(proxy_uri));
const ProxyServer& last = proxy_servers.back();
if (last.is_quic()) {
if (seen_tcp) {
std::cerr << "QUIC proxy cannot follow TCP-based proxies"
<< std::endl;
return false;
}
origins_to_force_quic_on.insert(HostPortPair::FromURL(url));
} else if (last.is_https() || last.is_http() || last.is_socks()) {
seen_tcp = true;
} else {
std::cerr << "Invalid proxy scheme" << std::endl;
return false;
}
AuthCredentials auth(proxy_user, proxy_pass);
if (!auth.Empty()) {
if (last.is_socks()) {
std::cerr << "SOCKS proxy with auth is not supported" << std::endl;
} else {
std::string proxy_url(token);
if (proxy_url.compare(0, 7, "quic://") == 0) {
proxy_url.replace(0, 4, "https");
}
auth_store[url::SchemeHostPort{GURL{proxy_url}}] = auth;
}
}
}
if (proxy_servers.size() > 1 &&
std::any_of(proxy_servers.begin(), proxy_servers.end(),
[](const ProxyServer& s) { return s.is_socks(); })) {
// See net/socket/connect_job_params_factory.cc
// DCHECK(proxy_server.is_socks());
// DCHECK_EQ(1u, proxy_chain.length());
std::cerr
<< "Multi-proxy chain containing SOCKS proxies is not supported."
<< std::endl;
return false;
}
if (std::any_of(proxy_servers.begin(), proxy_servers.end(),
[](const ProxyServer& s) { return s.is_quic(); })) {
proxy_chain = ProxyChain::ForIpProtection(proxy_servers);
} else {
proxy_chain = ProxyChain(proxy_servers);
}
if (!proxy_chain.IsValid()) {
std::cerr << "Invalid proxy chain" << std::endl;
return false;
} else if (proxy_url.back() == '/') {
proxy_url.pop_back();
}
} else {
std::cerr << "Invalid proxy" << std::endl;
std::cerr << "Invalid proxy argument" << std::endl;
return false;
}
}
+10 -4
View File
@@ -4,16 +4,22 @@
#ifndef NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
#define NET_TOOLS_NAIVE_NAIVE_CONFIG_H_
#include <map>
#include <optional>
#include <set>
#include <string>
#include <vector>
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/values.h"
#include "net/base/auth.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_address.h"
#include "net/base/proxy_chain.h"
#include "net/http/http_request_headers.h"
#include "net/tools/naive/naive_protocol.h"
#include "url/scheme_host_port.h"
namespace net {
@@ -37,10 +43,10 @@ struct NaiveConfig {
HttpRequestHeaders extra_headers;
std::string proxy_url = "direct://";
std::u16string proxy_user;
std::u16string proxy_pass;
// The last server is assumed to be Naive.
ProxyChain proxy_chain = ProxyChain::Direct();
std::set<HostPortPair> origins_to_force_quic_on;
std::map<url::SchemeHostPort, AuthCredentials> auth_store;
std::string host_resolver_rules;
@@ -116,8 +116,9 @@ void NaiveProxy::DoConnect() {
traffic_annotation_);
} else if (protocol_ == ClientProtocol::kHttp) {
socket = std::make_unique<HttpProxyServerSocket>(
std::move(accepted_socket_), padding_detector_delegate.get(),
traffic_annotation_, supported_padding_types_);
std::move(accepted_socket_), listen_user_, listen_pass_,
padding_detector_delegate.get(), traffic_annotation_,
supported_padding_types_);
} else if (protocol_ == ClientProtocol::kRedir) {
socket = std::move(accepted_socket_);
} else {
@@ -175,22 +175,11 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
builder.DisableHttpCache();
builder.set_net_log(net_log);
std::string proxy_url = config.proxy_url;
bool force_quic = false;
if (proxy_url.compare(0, 7, "quic://") == 0) {
proxy_url.replace(0, 4, "https");
force_quic = true;
}
ProxyConfig proxy_config;
proxy_config.proxy_rules().ParseFromString(proxy_url);
if (force_quic) {
const ProxyServer& proxy_server =
proxy_config.proxy_rules().single_proxies.First().First();
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
ProxyChain::ForIpProtection({ProxyServer(
ProxyServer::Scheme::SCHEME_QUIC, proxy_server.host_port_pair())}));
}
proxy_config.proxy_rules().type =
net::ProxyConfig::ProxyRules::Type::PROXY_LIST;
proxy_config.proxy_rules().single_proxies.SetSingleProxyChain(
config.proxy_chain);
LOG(INFO) << "Proxying via "
<< proxy_config.proxy_rules().single_proxies.ToDebugString();
auto proxy_service =
@@ -209,7 +198,7 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
CertVerifier::CreateDefault(std::move(cert_net_fetcher)));
builder.set_proxy_delegate(std::make_unique<NaiveProxyDelegate>(
config.extra_headers,
config.extra_headers,
std::vector<PaddingType>{PaddingType::kVariant1, PaddingType::kNone}));
if (config.no_post_quantum == true) {
@@ -229,22 +218,20 @@ std::unique_ptr<URLRequestContext> BuildURLRequestContext(
auto context = builder.Build();
if (!config.proxy_url.empty() && !config.proxy_user.empty() &&
!config.proxy_pass.empty()) {
if (!config.origins_to_force_quic_on.empty()) {
auto* quic = context->quic_context()->params();
quic->supported_versions = {quic::ParsedQuicVersion::RFCv1()};
quic->origins_to_force_quic_on.insert(
config.origins_to_force_quic_on.begin(),
config.origins_to_force_quic_on.end());
}
for (const auto& [k, v] : config.auth_store) {
auto* session = context->http_transaction_factory()->GetSession();
auto* auth_cache = session->http_auth_cache();
GURL proxy_gurl(proxy_url);
if (force_quic) {
auto* quic = context->quic_context()->params();
quic->supported_versions = {quic::ParsedQuicVersion::RFCv1()};
quic->origins_to_force_quic_on.insert(
net::HostPortPair::FromURL(proxy_gurl));
}
url::SchemeHostPort auth_origin(proxy_gurl);
AuthCredentials credentials(config.proxy_user, config.proxy_pass);
auth_cache->Add(auth_origin, HttpAuth::AUTH_PROXY,
auth_cache->Add(k, HttpAuth::AUTH_PROXY,
/*realm=*/{}, HttpAuth::AUTH_SCHEME_BASIC, {},
/*challenge=*/"Basic", credentials, /*path=*/"/");
/*challenge=*/"Basic", v, /*path=*/"/");
}
return context;
@@ -72,8 +72,13 @@ Error NaiveProxyDelegate::OnBeforeTunnelRequest(
// protocols.
if (proxy_chain.is_direct())
return OK;
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
if (proxy_chain.GetProxyServer(chain_index).is_socks())
const ProxyServer& proxy_server = proxy_chain.GetProxyServer(chain_index);
if (proxy_server.is_socks())
return OK;
// Only the last server is attempted for padding
// because proxy chaining will corrupt the padding.
if (chain_index != proxy_chain.length() - 1)
return OK;
// Sends client-side padding header regardless of server support
@@ -83,7 +88,7 @@ Error NaiveProxyDelegate::OnBeforeTunnelRequest(
// Enables Fast Open in H2/H3 proxy client socket once the state of server
// padding support is known.
if (padding_type_by_server_[proxy_chain].has_value()) {
if (padding_type_by_server_[proxy_server].has_value()) {
extra_headers->SetHeader("fastopen", "1");
}
extra_headers->MergeFrom(extra_headers_);
@@ -123,8 +128,13 @@ Error NaiveProxyDelegate::OnTunnelHeadersReceived(
// protocols.
if (proxy_chain.is_direct())
return OK;
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
if (proxy_chain.GetProxyServer(chain_index).is_socks())
const ProxyServer& proxy_server = proxy_chain.GetProxyServer(chain_index);
if (proxy_server.is_socks())
return OK;
// Only the last server is attempted for padding
// because proxy chaining will corrupt the padding.
if (chain_index != proxy_chain.length() - 1)
return OK;
// Detects server padding support, even if it changes dynamically.
@@ -134,26 +144,23 @@ Error NaiveProxyDelegate::OnTunnelHeadersReceived(
return ERR_INVALID_RESPONSE;
}
std::optional<PaddingType>& padding_type =
padding_type_by_server_[proxy_chain];
padding_type_by_server_[proxy_server];
if (!padding_type.has_value() || padding_type != new_padding_type) {
LOG(INFO) << proxy_chain.ToDebugString() << " negotiated padding type: "
LOG(INFO) << ProxyServerToProxyUri(proxy_server)
<< " negotiated padding type: "
<< ToReadableString(*new_padding_type);
padding_type = new_padding_type;
}
return OK;
}
std::optional<PaddingType> NaiveProxyDelegate::GetProxyServerPaddingType(
std::optional<PaddingType> NaiveProxyDelegate::GetProxyChainPaddingType(
const ProxyChain& proxy_chain) {
// Not possible to negotiate padding capability given the underlying
// protocols.
if (proxy_chain.is_direct())
return PaddingType::kNone;
CHECK_EQ(proxy_chain.length(), 1u) << "Multi-hop proxy not supported";
if (proxy_chain.GetProxyServer(0).is_socks())
return PaddingType::kNone;
return padding_type_by_server_[proxy_chain];
return padding_type_by_server_[proxy_chain.Last()];
}
PaddingDetectorDelegate::PaddingDetectorDelegate(
@@ -186,7 +193,7 @@ std::optional<PaddingType> PaddingDetectorDelegate::GetServerPaddingType() {
if (cached_server_padding_type_.has_value())
return cached_server_padding_type_;
cached_server_padding_type_ =
naive_proxy_delegate_->GetProxyServerPaddingType(proxy_chain_);
naive_proxy_delegate_->GetProxyChainPaddingType(proxy_chain_);
return cached_server_padding_type_;
}
@@ -44,8 +44,8 @@ class NaiveProxyDelegate : public ProxyDelegate {
// This only affects h2 proxy client socket.
Error OnBeforeTunnelRequest(const ProxyChain& proxy_chain,
size_t chain_index,
HttpRequestHeaders* extra_headers) override;
size_t chain_index,
HttpRequestHeaders* extra_headers) override;
Error OnTunnelHeadersReceived(
const ProxyChain& proxy_chain,
@@ -56,7 +56,7 @@ class NaiveProxyDelegate : public ProxyDelegate {
ProxyResolutionService* proxy_resolution_service) override {}
// Returns empty if the padding type has not been negotiated.
std::optional<PaddingType> GetProxyServerPaddingType(
std::optional<PaddingType> GetProxyChainPaddingType(
const ProxyChain& proxy_chain);
private:
@@ -66,7 +66,7 @@ class NaiveProxyDelegate : public ProxyDelegate {
HttpRequestHeaders extra_headers_;
// Empty value means padding type has not been negotiated.
std::map<ProxyChain, std::optional<PaddingType>> padding_type_by_server_;
std::map<ProxyServer, std::optional<PaddingType>> padding_type_by_server_;
};
class ClientPaddingDetectorDelegate {
+13
View File
@@ -295,3 +295,16 @@ test_naive('HTTP-HTTP-HTTP', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=http://127.0.0.1:{PORT2}',
'--log --listen=http://:{PORT2} --proxy=http://127.0.0.1:{PORT3}',
'--log --listen=http://:{PORT3}')
test_naive('HTTP-HTTP (with auth)', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT1} --proxy=http://hello:world@127.0.0.1:{PORT2}',
'--log --listen=http://hello:world@127.0.0.1:{PORT2}')
test_naive('HTTPa-HTTPb,HTTPc (chaining with remote loop)', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://:{PORT2}',
'--log --listen=http://:{PORT1} --proxy=http://127.0.0.1:{PORT2},http://127.0.0.1:{PORT2}')
test_naive('HTTPa-HTTPb,HTTPc (chaining with multiple auth)', 'http://127.0.0.1:{PORT1}',
'--log --listen=http://hello:world2@127.0.0.1:{PORT2}',
'--log --listen=http://hello:world3@127.0.0.1:{PORT3}',
'--log --listen=http://127.0.0.1:{PORT1} --proxy=http://hello:world2@127.0.0.1:{PORT2},http://hello:world3@127.0.0.1:{PORT3}')
+5 -5
View File
@@ -7,13 +7,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=alist
PKG_VERSION:=3.35.0
PKG_WEB_VERSION:=3.35.0
PKG_RELEASE:=8
PKG_VERSION:=3.36.0
PKG_WEB_VERSION:=3.36.0
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/alist-org/alist/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=e349a178cd41fff9b668e9d8df9ff1b407b7f6d6fd3dbb2f8a7ca9d0d5ecad55
PKG_HASH:=809072becc16613755c33286c5b19539f81dcb2b40f0c4cd4838c4614946c874
PKG_LICENSE:=GPL-3.0
PKG_LICENSE_FILE:=LICENSE
@@ -23,7 +23,7 @@ define Download/$(PKG_NAME)-web
FILE:=$(PKG_NAME)-web-$(PKG_WEB_VERSION).tar.gz
URL_FILE:=dist.tar.gz
URL:=https://github.com/alist-org/alist-web/releases/download/$(PKG_WEB_VERSION)/
HASH:=940608c2b9f64cf585ad4d241545e5f1e59e5f6e54ef8ea2c9c3a29998313fc7
HASH:=b997d3ecf92f447cc1daca9b1ae9b9dc3f28445589db3dc054174ecc15a600a9
endef
PKG_BUILD_DEPENDS:=golang/host
@@ -616,6 +616,10 @@ run_chinadns_ng() {
([ -z "${_default_tag}" ] || [ "${_default_tag}" = "smart" ] || [ "${_default_tag}" = "none_noip" ]) && _default_tag="none"
echo "default-tag ${_default_tag}" >> ${_CONF_FILE}
[ "${_flag}" = "default" ] && [ "${_default_tag}" = "none" ] && {
echo "verdict-cache 4096" >> ${_CONF_FILE}
}
ln_run "$(first_type chinadns-ng)" chinadns-ng "${_LOG_FILE}" -C ${_CONF_FILE}
}
@@ -473,7 +473,7 @@ namespace Ryujinx.UI.Widgets
private void ManageDlc_Clicked(object sender, EventArgs args)
{
new DlcWindow(_virtualFileSystem, _applicationData.IdString, _applicationData).Show();
new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show();
}
private void ManageCheats_Clicked(object sender, EventArgs args)
@@ -24,7 +24,7 @@ namespace Ryujinx.UI.Windows
public class DlcWindow : Window
{
private readonly VirtualFileSystem _virtualFileSystem;
private readonly string _applicationId;
private readonly string _applicationIdBase;
private readonly string _dlcJsonPath;
private readonly List<DownloadableContentContainer> _dlcContainerList;
@@ -36,16 +36,16 @@ namespace Ryujinx.UI.Windows
[GUI] TreeSelection _dlcTreeSelection;
#pragma warning restore CS0649, IDE0044
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, titleId, applicationData) { }
public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationId, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
{
builder.Autoconnect(this);
_applicationId = applicationId;
_applicationIdBase = applicationIdBase;
_virtualFileSystem = virtualFileSystem;
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationId, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationId.ToUpper()}]";
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]";
try
{
@@ -163,7 +163,7 @@ namespace Ryujinx.UI.Windows
if (nca.Header.ContentType == NcaContentType.PublicData)
{
if (nca.GetProgramIdBase() != (ulong.Parse(_applicationId, NumberStyles.HexNumber) & ~0x1FFFUL))
if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber))
{
continue;
}
@@ -51,7 +51,7 @@ namespace Ryujinx.UI.Windows
_applicationData = applicationData;
_virtualFileSystem = virtualFileSystem;
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "updates.json");
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json");
_radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
try
@@ -67,7 +67,7 @@ namespace Ryujinx.UI.Windows
};
}
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdString}]";
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]";
// Try to get updates from PFS first
AddUpdate(_applicationData.Path, true);
@@ -15,6 +15,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
public ServiceAccessControl ServiceAccessControl { get; private set; }
public KernelAccessControl KernelAccessControl { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACI0 data.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
public Aci0(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);
@@ -19,6 +19,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
public ServiceAccessControl ServiceAccessControl { get; private set; }
public KernelAccessControl KernelAccessControl { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACID data.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public Acid(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);
@@ -11,6 +11,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int Unknown3 { get; private set; }
public int Unknown4 { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public FsAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);
@@ -9,6 +9,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int Version { get; private set; }
public ulong PermissionsBitmask { get; private set; }
/// <exception cref="InvalidNpdmException">The stream contains invalid data.</exception>
/// <exception cref="NotImplementedException">The ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public FsAccessHeader(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);
@@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
{
public int[] Capabilities { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public KernelAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);
@@ -24,6 +24,13 @@ namespace Ryujinx.HLE.Loaders.Npdm
public Aci0 Aci0 { get; private set; }
public Acid Acid { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid NPDM data.</exception>
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public Npdm(Stream stream)
{
BinaryReader reader = new(stream);
@@ -9,6 +9,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
{
public IReadOnlyDictionary<string, bool> Services { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public ServiceAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);
@@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
ulong titleIdBase = mainNca.GetProgramIdBase();
// Load update information if exists.
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json");
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
if (File.Exists(titleUpdateMetadataPath))
{
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;
@@ -118,7 +118,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
device.Configuration.ContentManager.ClearAocData();
// Load DownloadableContents.
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json");
if (File.Exists(addOnContentMetadataPath))
{
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer);
@@ -21,6 +21,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
public long CurrentTime { get; private set; }
public IEnumerable<MultiWaitHolderBase> MultiWaits => _multiWaits;
public MultiWaitImpl()
{
_multiWaits = new List<MultiWaitHolderBase>();
@@ -1,4 +1,5 @@
using Ryujinx.Horizon.Sdk.OsTypes.Impl;
using System.Collections.Generic;
namespace Ryujinx.Horizon.Sdk.OsTypes
{
@@ -6,6 +7,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
{
private readonly MultiWaitImpl _impl;
public IEnumerable<MultiWaitHolderBase> MultiWaits => _impl.MultiWaits;
public MultiWait()
{
_impl = new MultiWaitImpl();
@@ -3,6 +3,7 @@ using Ryujinx.Horizon.Sdk.OsTypes;
using Ryujinx.Horizon.Sdk.Sf.Cmif;
using Ryujinx.Horizon.Sdk.Sm;
using System;
using System.Linq;
namespace Ryujinx.Horizon.Sdk.Sf.Hipc
{
@@ -116,6 +117,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
while (WaitAndProcessRequestsImpl())
{
}
// Unlink pending sessions, dispose expects them to be already unlinked.
ServerSession[] serverSessions = Enumerable.OfType<ServerSession>(_multiWait.MultiWaits).ToArray();
foreach (ServerSession serverSession in serverSessions)
{
if (serverSession.IsLinked)
{
serverSession.UnlinkFromMultiWaitHolder();
}
}
}
public void WaitAndProcessRequests()
@@ -42,6 +42,8 @@ namespace Ryujinx.UI.App.Common
[JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL;
[JsonIgnore] public string IdBaseString => IdBase.ToString("x16");
public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath)
{
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
@@ -72,37 +72,43 @@ namespace Ryujinx.UI.App.Common
return resourceByteArray;
}
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath)
{
ApplicationData data = new()
{
Icon = _nspIcon,
Path = filePath,
};
using UniqueRef<IFile> npdmFile = new();
try
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
if (ResultFs.PathNotFound.Includes(result))
{
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
Npdm npdm = new(npdmFile.Get.AsStream());
if (ResultFs.PathNotFound.Includes(result))
{
Npdm npdm = new(npdmFile.Get.AsStream());
data.Name = npdm.TitleName;
data.Id = npdm.Aci0.TitleId;
}
return data;
data.Name = npdm.TitleName;
data.Id = npdm.Aci0.TitleId;
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception.Message}");
return null;
}
return data;
}
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath)
{
bool isExeFs = false;
@@ -170,99 +176,88 @@ namespace Ryujinx.UI.App.Common
return null;
}
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
private List<ApplicationData> GetApplicationsFromPfs(IFileSystem pfs, string filePath)
{
var applications = new List<ApplicationData>();
string extension = Path.GetExtension(filePath).ToLower();
try
foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel))
{
foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel))
ApplicationData applicationData = new()
{
ApplicationData applicationData = new()
Id = titleId,
Path = filePath,
};
Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
// Check if there is an update available.
if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
{
// Replace the original ControlFs by the updated one.
controlFs = updatedControlFs;
}
if (controlFs == null)
{
continue;
}
ReadControlData(controlFs, controlHolder.ByteSpan);
GetApplicationInformation(ref controlHolder.Value, ref applicationData);
// Read the icon from the ControlFS and store it as a byte array
try
{
using UniqueRef<IFile> icon = new();
controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
using MemoryStream stream = new();
icon.Get.AsStream().CopyTo(stream);
applicationData.Icon = stream.ToArray();
}
catch (HorizonResultException)
{
foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
{
Id = titleId,
Path = filePath,
};
if (entry.Name == "control.nacp")
{
continue;
}
Nca mainNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Program);
Nca controlNca = content.GetNcaByType(_virtualFileSystem.KeySet, ContentType.Control);
using var icon = new UniqueRef<IFile>();
BlitStruct<ApplicationControlProperty> controlHolder = new(1);
IFileSystem controlFs = controlNca?.OpenFileSystem(NcaSectionType.Data, _checkLevel);
// Check if there is an update available.
if (IsUpdateApplied(mainNca, out IFileSystem updatedControlFs))
{
// Replace the original ControlFs by the updated one.
controlFs = updatedControlFs;
}
if (controlFs == null)
{
continue;
}
ReadControlData(controlFs, controlHolder.ByteSpan);
GetApplicationInformation(ref controlHolder.Value, ref applicationData);
// Read the icon from the ControlFS and store it as a byte array
try
{
using UniqueRef<IFile> icon = new();
controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
using MemoryStream stream = new();
icon.Get.AsStream().CopyTo(stream);
applicationData.Icon = stream.ToArray();
}
catch (HorizonResultException)
{
foreach (DirectoryEntryEx entry in controlFs.EnumerateEntries("/", "*"))
if (applicationData.Icon != null)
{
if (entry.Name == "control.nacp")
{
continue;
}
using var icon = new UniqueRef<IFile>();
controlFs.OpenFile(ref icon.Ref, entry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
using MemoryStream stream = new();
icon.Get.AsStream().CopyTo(stream);
applicationData.Icon = stream.ToArray();
if (applicationData.Icon != null)
{
break;
}
break;
}
applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
}
applicationData.ControlHolder = controlHolder;
applications.Add(applicationData);
applicationData.Icon ??= extension == ".xci" ? _xciIcon : _nspIcon;
}
}
catch (MissingKeyException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
}
catch (InvalidDataException)
{
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {filePath}");
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}");
applicationData.ControlHolder = controlHolder;
applications.Add(applicationData);
}
return applications;
@@ -319,52 +314,43 @@ namespace Ryujinx.UI.App.Common
BinaryReader reader = new(file);
ApplicationData application = new();
try
file.Seek(24, SeekOrigin.Begin);
int assetOffset = reader.ReadInt32();
if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
{
file.Seek(24, SeekOrigin.Begin);
byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
int assetOffset = reader.ReadInt32();
long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
if (Encoding.ASCII.GetString(Read(assetOffset, 4)) == "ASET")
ulong nacpOffset = reader.ReadUInt64();
ulong nacpSize = reader.ReadUInt64();
// Reads and stores game icon as byte array
if (iconSize > 0)
{
byte[] iconSectionInfo = Read(assetOffset + 8, 0x10);
long iconOffset = BitConverter.ToInt64(iconSectionInfo, 0);
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
ulong nacpOffset = reader.ReadUInt64();
ulong nacpSize = reader.ReadUInt64();
// Reads and stores game icon as byte array
if (iconSize > 0)
{
application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
}
else
{
application.Icon = _nroIcon;
}
// Read the NACP data
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
GetApplicationInformation(ref controlHolder.Value, ref application);
application.Icon = Read(assetOffset + iconOffset, (int)iconSize);
}
else
{
application.Icon = _nroIcon;
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
}
application.ControlHolder = controlHolder;
applications.Add(application);
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
// Read the NACP data
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
return false;
GetApplicationInformation(ref controlHolder.Value, ref application);
}
else
{
application.Icon = _nroIcon;
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
}
application.ControlHolder = controlHolder;
applications.Add(application);
break;
@@ -377,34 +363,21 @@ namespace Ryujinx.UI.App.Common
}
case ".nca":
{
try
ApplicationData application = new();
Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
if (!nca.IsProgram() || nca.IsPatch())
{
ApplicationData application = new();
Nca nca = new(_virtualFileSystem.KeySet, new FileStream(applicationPath, FileMode.Open, FileAccess.Read).AsStorage());
if (!nca.IsProgram() || nca.IsPatch())
{
return false;
}
application.Icon = _ncaIcon;
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
application.ControlHolder = controlHolder;
applications.Add(application);
}
catch (InvalidDataException)
{
Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
return false;
}
application.Icon = _ncaIcon;
application.Name = Path.GetFileNameWithoutExtension(applicationPath);
application.ControlHolder = controlHolder;
applications.Add(application);
break;
}
// If its an NSO we just set defaults
@@ -417,16 +390,35 @@ namespace Ryujinx.UI.App.Common
};
applications.Add(application);
break;
}
}
}
catch (MissingKeyException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
return false;
}
catch (InvalidDataException)
{
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}");
return false;
}
catch (IOException exception)
{
Logger.Warning?.Print(LogClass.Application, exception.Message);
return false;
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
return false;
}
foreach (var data in applications)
{
+10 -18
View File
@@ -367,32 +367,24 @@ namespace Ryujinx.Ava
}
var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
using var bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
using SKBitmap bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length);
SKBitmap bitmapToSave = null;
using SKBitmap bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
using SKCanvas canvas = new SKCanvas(bitmapToSave);
if (e.FlipX || e.FlipY)
{
bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
canvas.Clear(SKColors.Black);
using var canvas = new SKCanvas(bitmapToSave);
float scaleX = e.FlipX ? -1 : 1;
float scaleY = e.FlipY ? -1 : 1;
canvas.Clear(SKColors.Transparent);
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
float scaleX = e.FlipX ? -1 : 1;
float scaleY = e.FlipY ? -1 : 1;
canvas.SetMatrix(matrix);
canvas.DrawBitmap(bitmap, SKPoint.Empty);
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
canvas.SetMatrix(matrix);
canvas.DrawBitmap(bitmap, new SKPoint(e.FlipX ? -bitmap.Width : 0, e.FlipY ? -bitmap.Height : 0));
}
SaveBitmapAsPng(bitmapToSave ?? bitmap, path);
bitmapToSave?.Dispose();
SaveBitmapAsPng(bitmapToSave, path);
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
}
@@ -103,7 +103,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_storageProvider = desktop.MainWindow.StorageProvider;
}
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "dlc.json");
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
if (!File.Exists(_downloadableContentJsonPath))
{
@@ -88,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StorageProvider = desktop.MainWindow.StorageProvider;
}
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdString, "updates.json");
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdBaseString, "updates.json");
try
{
@@ -96,7 +96,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdString} at {TitleUpdateJsonPath}");
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdBaseString} at {TitleUpdateJsonPath}");
TitleUpdateWindowData = new TitleUpdateMetadata
{
@@ -38,7 +38,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "",
CloseButtonText = "",
Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdString),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdBaseString),
};
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
@@ -40,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "",
CloseButtonText = "",
Content = new TitleUpdateWindow(virtualFileSystem, applicationData),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdString),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString),
};
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
+9 -9
View File
@@ -3,38 +3,38 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=chinadns-ng
PKG_VERSION:=2024.07.20
PKG_VERSION:=2024.07.21
PKG_RELEASE:=1
ifeq ($(ARCH),aarch64)
ifeq ($(BOARD),rockchip)
PKG_ARCH:=chinadns-ng+wolfssl@aarch64-linux-musl@generic+v8a@fast+lto
PKG_HASH:=605cdcf46b2b82a4c4989747e5e16b577a5b42574d0432d1ff0bc689daa62124
PKG_HASH:=1a65fadc48956231eba439efd9ae914f6e5eb0f5d257a34cd8c6b0d5734ade57
else
PKG_ARCH:=chinadns-ng+wolfssl_noasm@aarch64-linux-musl@generic+v8a@fast+lto
PKG_HASH:=dfa60cf7cdac7bd4ccfc9d3ed4bc99e8a8ed4e8542e42dacfb2daa2ca9e0fc85
PKG_HASH:=9414c8f237b8a919d411fb38a7d2aa83c7617ae441695af54ea84feb22b0639c
endif
else ifeq ($(ARCH),arm)
ARM_CPU_FEATURES:=$(word 2,$(subst +,$(space),$(call qstrip,$(CONFIG_CPU_TYPE))))
ifeq ($(ARM_CPU_FEATURES),)
PKG_ARCH:=chinadns-ng+wolfssl@arm-linux-musleabi@generic+v6+soft_float@fast+lto
PKG_HASH:=873bb30baf21372860652dbff40a08cfbf5b727e1150e63fd81fa0f262f6091a
PKG_HASH:=d9095d72dba4018c21ab794c81e467011eee8b3582822a3fa45b803bfdf0d4fa
else
PKG_ARCH:=chinadns-ng+wolfssl@arm-linux-musleabihf@generic+v7a@fast+lto
PKG_HASH:=228a4d46d5f16b6cf7d6bb3aa03838dfdee0ffd62ad6f23d22b2d0d0294f719c
PKG_HASH:=61fff30848f687d93e58bb91029252818d76e0980d4d32fd2bf6d67dfa51cd4f
endif
else ifeq ($(ARCH),mips)
PKG_ARCH:=chinadns-ng+wolfssl@mips-linux-musl@mips32+soft_float@fast+lto
PKG_HASH:=35c70189394e55c1f71b1b35ac8bece1fa82d45c28dbd31fcda7cf47c676df8a
PKG_HASH:=1b804d4c450d10cb6f91ac0656121bf9fd3e499f15ea569770b8d5523d5290c2
else ifeq ($(ARCH),mipsel)
PKG_ARCH:=chinadns-ng+wolfssl@mipsel-linux-musl@mips32+soft_float@fast+lto
PKG_HASH:=bf399fd12935b5982d054ce531d3a70f19f1f2fbf26c4c5b8a934ce3538699d0
PKG_HASH:=82d07d148e2c20d4247df7baa0421f1c365954c0953e0e0fbe76e1cd78d1f1b2
else ifeq ($(ARCH),i386)
PKG_ARCH:=chinadns-ng+wolfssl@i386-linux-musl@i686@fast+lto
PKG_HASH:=c43cce76dd63f4dd1a938718dc3a7360b72814ec12893aa4af1a63ca2a6d46aa
PKG_HASH:=8fd1f8cc1ee5f24a10a45367d3444a7af0618f01bf5ea1d14c8bfac856062a23
else ifeq ($(ARCH),x86_64)
PKG_ARCH:=chinadns-ng+wolfssl@x86_64-linux-musl@x86_64@fast+lto
PKG_HASH:=6c89e28872ddfa5fc134e6505795a3a84c7bc2efee078446159318832504402c
PKG_HASH:=8d600757acf4fcb6250aef6ba5bf19d9a2182a188e900d1201381ab0a52a5463
else
PKG_HASH:=dummy
endif
@@ -616,6 +616,10 @@ run_chinadns_ng() {
([ -z "${_default_tag}" ] || [ "${_default_tag}" = "smart" ] || [ "${_default_tag}" = "none_noip" ]) && _default_tag="none"
echo "default-tag ${_default_tag}" >> ${_CONF_FILE}
[ "${_flag}" = "default" ] && [ "${_default_tag}" = "none" ] && {
echo "verdict-cache 4096" >> ${_CONF_FILE}
}
ln_run "$(first_type chinadns-ng)" chinadns-ng "${_LOG_FILE}" -C ${_CONF_FILE}
}
+2 -2
View File
@@ -21,13 +21,13 @@ define Download/geoip
HASH:=8128db0c1431f4c6854dfb7740b497ee0ac73f0f3a52a1e0040c508f7d79c0a4
endef
GEOSITE_VER:=20240713050854
GEOSITE_VER:=20240720181558
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=dbaba085b1de1b8b875f3db78fb367aef080f24993af01abe21f4d4ba99048be
HASH:=873ad7f4ad185ba7a70c5addbfd064703f22a7a8e4e21e4114a8ea98da7dd5ad
endef
GEOSITE_IRAN_VER:=202407201213
+1 -1
View File
@@ -1,9 +1,9 @@
<Application
x:Class="v2rayN.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:conv="clr-namespace:v2rayN.Converters"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
ShutdownMode="OnExplicitShutdown"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
@@ -1066,11 +1066,11 @@ namespace v2rayN.Handler
return 0;
}
public static int AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, out string indexId)
public static int AddCustomServer4Multiple(Config config, List<ProfileItem> selecteds, ECoreType coreType, out string indexId)
{
indexId = Utils.GetMD5(Global.CoreMultipleLoadConfigFileName);
string configPath = Utils.GetConfigPath(Global.CoreMultipleLoadConfigFileName);
if (CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, out string msg) != 0)
if (CoreConfigHandler.GenerateClientMultipleLoadConfig(config, configPath, selecteds, coreType, out string msg) != 0)
{
return -1;
}
@@ -1083,10 +1083,10 @@ namespace v2rayN.Handler
var profileItem = LazyConfig.Instance.GetProfileItem(indexId) ?? new();
profileItem.indexId = indexId;
profileItem.remarks = "Multi-server Config";
profileItem.remarks = coreType == ECoreType.sing_box ? Resx.ResUI.menuSetDefaultMultipleServer : Resx.ResUI.menuSetDefaultLoadBalanceServer;
profileItem.address = Global.CoreMultipleLoadConfigFileName;
profileItem.configType = EConfigType.Custom;
profileItem.coreType = ECoreType.sing_box;
profileItem.coreType = coreType;
AddServerCommon(config, profileItem, true);
@@ -150,13 +150,25 @@ namespace v2rayN.Handler.CoreConfig
return 0;
}
public static int GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, out string msg)
public static int GenerateClientMultipleLoadConfig(Config config, string fileName, List<ProfileItem> selecteds, ECoreType coreType, out string msg)
{
if (new CoreConfigSingbox(config).GenerateClientMultipleLoadConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
msg = ResUI.CheckServerSettings;
if (coreType == ECoreType.sing_box)
{
return -1;
if (new CoreConfigSingbox(config).GenerateClientMultipleLoadConfig(selecteds, out SingboxConfig? singboxConfig, out msg) != 0)
{
return -1;
}
JsonUtils.ToFile(singboxConfig, fileName, false);
}
else if (coreType == ECoreType.Xray)
{
if (new CoreConfigV2ray(config).GenerateClientMultipleLoadConfig(selecteds, out V2rayConfig? v2rayConfig, out msg) != 0)
{
return -1;
}
JsonUtils.ToFile(v2rayConfig, fileName, false);
}
JsonUtils.ToFile(singboxConfig, fileName, false);
return 0;
}
@@ -71,6 +71,130 @@ namespace v2rayN.Handler.CoreConfig
return 0;
}
public int GenerateClientMultipleLoadConfig(List<ProfileItem> selecteds, out V2rayConfig? v2rayConfig, out string msg)
{
v2rayConfig = null;
try
{
if (_config == null)
{
msg = ResUI.CheckServerSettings;
return -1;
}
msg = ResUI.InitialConfiguration;
string result = Utils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = Utils.GetEmbedText(Global.V2raySampleOutbound);
if (Utils.IsNullOrEmpty(result) || txtOutbound.IsNullOrEmpty())
{
msg = ResUI.FailedGetDefaultConfiguration;
return -1;
}
v2rayConfig = JsonUtils.Deserialize<V2rayConfig>(result);
if (v2rayConfig == null)
{
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
GenLog(v2rayConfig);
GenInbounds(v2rayConfig);
GenRouting(v2rayConfig);
GenDns(null, v2rayConfig);
GenStatistic(v2rayConfig);
v2rayConfig.outbounds.RemoveAt(0);
var tagProxy = new List<string>();
foreach (var it in selecteds)
{
if (it.configType == EConfigType.Custom)
{
continue;
}
if (it.configType is EConfigType.Hysteria2 or EConfigType.Tuic or EConfigType.Wireguard)
{
continue;
}
if (it.port <= 0)
{
continue;
}
var item = LazyConfig.Instance.GetProfileItem(it.indexId);
if (item is null)
{
continue;
}
if (it.configType is EConfigType.VMess or EConfigType.VLESS)
{
if (Utils.IsNullOrEmpty(item.id) || !Utils.IsGuidByParse(item.id))
{
continue;
}
}
if (item.configType == EConfigType.Shadowsocks
&& !Global.SsSecuritiesInSingbox.Contains(item.security))
{
continue;
}
if (item.configType == EConfigType.VLESS && !Global.Flows.Contains(item.flow))
{
continue;
}
//outbound
var outbound = JsonUtils.Deserialize<Outbounds4Ray>(txtOutbound);
GenOutbound(item, outbound);
outbound.tag = $"{Global.ProxyTag}-{tagProxy.Count + 1}";
v2rayConfig.outbounds.Add(outbound);
tagProxy.Add(outbound.tag);
}
if (tagProxy.Count <= 0)
{
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
//add balancers
var balancer = new BalancersItem4Ray
{
selector = [Global.ProxyTag],
strategy = new() { type = "roundRobin" },
tag = $"{Global.ProxyTag}-round",
};
v2rayConfig.routing.balancers = [balancer];
//add rule
var rules = v2rayConfig.routing.rules.Where(t => t.outboundTag == Global.ProxyTag).ToList();
if (rules?.Count > 0)
{
foreach (var rule in rules)
{
rule.outboundTag = null;
rule.balancerTag = balancer.tag;
}
}
else
{
v2rayConfig.routing.rules.Add(new()
{
network = "tcp,udp",
balancerTag = balancer.tag,
type = "field"
});
}
return 0;
}
catch (Exception ex)
{
Logging.SaveLog(ex.Message, ex);
msg = ResUI.FailedGenDefaultConfiguration;
return -1;
}
}
public int GenerateClientSpeedtestConfig(List<ServerTestItem> selecteds, out V2rayConfig? v2rayConfig, out string msg)
{
v2rayConfig = null;
@@ -897,7 +1021,7 @@ namespace v2rayN.Handler.CoreConfig
return 0;
}
private int GenDns(ProfileItem node, V2rayConfig v2rayConfig)
private int GenDns(ProfileItem? node, V2rayConfig v2rayConfig)
{
try
{
@@ -960,8 +1084,10 @@ namespace v2rayN.Handler.CoreConfig
return 0;
}
private int GenDnsDomains(ProfileItem node, JsonNode dns)
private int GenDnsDomains(ProfileItem? node, JsonNode dns)
{
if (node == null)
{ return 0; }
var servers = dns["servers"];
if (servers != null)
{
+2 -2
View File
@@ -27,8 +27,8 @@ namespace v2rayN.Handler
Environment.SetEnvironmentVariable("xray.location.asset", Utils.GetBinPath(""), EnvironmentVariableTarget.Process);
}
public void LoadCore(ProfileItem? node) {
public void LoadCore(ProfileItem? node)
{
if (node == null)
{
ShowMsg(false, ResUI.CheckServerSettings);
+10 -6
View File
@@ -294,12 +294,16 @@ namespace v2rayN.Handler
UseProxy = webProxy != null
});
var timer = Stopwatch.StartNew();
await client.GetAsync(url, cts.Token);
timer.Stop();
responseTime = (int)timer.Elapsed.TotalMilliseconds;
List<int> oneTime = [];
for (int i = 0; i < 2; i++)
{
var timer = Stopwatch.StartNew();
await client.GetAsync(url, cts.Token);
timer.Stop();
oneTime.Add((int)timer.Elapsed.TotalMilliseconds);
await Task.Delay(100);
}
responseTime = oneTime.Where(x => x > 0).OrderBy(x => x).FirstOrDefault();
}
catch //(Exception ex)
{
@@ -240,7 +240,7 @@ namespace v2rayN.Handler
{
if (wParam == IntPtr.Zero && Marshal.PtrToStringUni(lParam) == "ImmersiveColorSet")
{
update(!Utils.IsLightTheme());
update(!Utils.IsLightTheme());
}
}
}
+1 -1
View File
@@ -49,7 +49,7 @@ namespace v2rayN.Models
switch (configType)
{
case EConfigType.Custom:
summary += string.Format("{0}", remarks);
summary += string.Format("[{1}]{0}", remarks, coreType.ToString());
break;
default:
@@ -392,6 +392,8 @@ namespace v2rayN.Models
///
/// </summary>
public List<RulesItem4Ray> rules { get; set; }
public List<BalancersItem4Ray>? balancers { get; set; }
}
[Serializable]
@@ -406,6 +408,8 @@ namespace v2rayN.Models
public string? outboundTag { get; set; }
public string? balancerTag { get; set; }
public List<string>? ip { get; set; }
public List<string>? domain { get; set; }
@@ -413,6 +417,18 @@ namespace v2rayN.Models
public List<string>? protocol { get; set; }
}
public class BalancersItem4Ray
{
public List<string>? selector { get; set; }
public BalancersStrategy4Ray? strategy { get; set; }
public string? tag { get; set; }
}
public class BalancersStrategy4Ray
{
public string? type { get; set; }
}
public class StreamSettings4Ray
{
/// <summary>
+10 -1
View File
@@ -1249,7 +1249,16 @@ namespace v2rayN.Resx {
}
/// <summary>
/// 查找类似 Multi-server set to active 的本地化字符串。
/// 查找类似 Multi-server load balancing 的本地化字符串。
/// </summary>
public static string menuSetDefaultLoadBalanceServer {
get {
return ResourceManager.GetString("menuSetDefaultLoadBalanceServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Multi-Server Preferred Latency 的本地化字符串。
/// </summary>
public static string menuSetDefaultMultipleServer {
get {
+13 -10
View File
@@ -241,10 +241,10 @@
<value>Is unpacking......</value>
</data>
<data name="MsgUpdateSubscriptionEnd" xml:space="preserve">
<value>Update subscription end</value>
<value>Update subscriptions end</value>
</data>
<data name="MsgUpdateSubscriptionStart" xml:space="preserve">
<value>Update subscription starts</value>
<value>Update subscriptions start</value>
</data>
<data name="MsgUpdateV2rayCoreSuccessfully" xml:space="preserve">
<value>Update Core successfully</value>
@@ -452,16 +452,16 @@
<value>Update current subscription with proxy</value>
</data>
<data name="menuSubscription" xml:space="preserve">
<value>Subscription group</value>
<value>Subscription Group</value>
</data>
<data name="menuSubSetting" xml:space="preserve">
<value>Subscription group settings</value>
</data>
<data name="menuSubUpdate" xml:space="preserve">
<value>Update subscription without proxy</value>
<value>Update subscriptions without proxy</value>
</data>
<data name="menuSubUpdateViaProxy" xml:space="preserve">
<value>Update subscription with proxy</value>
<value>Update subscriptions with proxy</value>
</data>
<data name="menuSystemproxy" xml:space="preserve">
<value>System proxy</value>
@@ -575,7 +575,7 @@
<value>Share</value>
</data>
<data name="LvEnabled" xml:space="preserve">
<value>Enabled Update</value>
<value>Enable update</value>
</data>
<data name="LvSort" xml:space="preserve">
<value>Sort</value>
@@ -917,7 +917,7 @@
<value>Support DnsObject, Click to view the document</value>
</data>
<data name="SubUrlTips" xml:space="preserve">
<value>Group please leave blank here</value>
<value>For group please leave blank here</value>
</data>
<data name="TipChangeRouting" xml:space="preserve">
<value>Routing setting is changed</value>
@@ -1037,13 +1037,13 @@
<value>Restart as Administrator</value>
</data>
<data name="LvMoreUrl" xml:space="preserve">
<value>More urls, separated by commas;Subscription conversion will be invalid</value>
<value>More URLs, separated by commas; Subscription conversion will be invalid</value>
</data>
<data name="SpeedDisplayText" xml:space="preserve">
<value>{0} : {1}/s↑ | {2}/s↓</value>
</data>
<data name="LvAutoUpdateInterval" xml:space="preserve">
<value>Automatic update interval(minutes)</value>
<value>Automatic update interval (minutes)</value>
</data>
<data name="TbSettingsLogEnabledToFile" xml:space="preserve">
<value>Enable logging to file</value>
@@ -1247,9 +1247,12 @@
<value>Default domain strategy for outbound</value>
</data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
<value>Multi-server set to active</value>
<value>Multi-Server Preferred Latency</value>
</data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>Main layout orientation(Require restart)</value>
</data>
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
<value>Multi-server load balancing</value>
</data>
</root>
+4 -1
View File
@@ -1244,9 +1244,12 @@
<value>Outbound默认解析策略</value>
</data>
<data name="menuSetDefaultMultipleServer" xml:space="preserve">
<value>多服务器设为活动(多选)</value>
<value>多服务器优选延迟 (多选)</value>
</data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>主界面布局方向(需重启)</value>
</data>
<data name="menuSetDefaultLoadBalanceServer" xml:space="preserve">
<value>多服务器负载均衡 (多选)</value>
</data>
</root>
@@ -187,7 +187,7 @@ namespace v2rayN.ViewModels
{
ClashApiHandler.Instance.GetClashProxies(_config, (it, it2) =>
{
UpdateHandler(false, "Refresh Clash Proxies");
//UpdateHandler(false, "Refresh Clash Proxies");
proxies = it?.proxies;
providers = it2?.providers;
@@ -413,7 +413,7 @@ namespace v2rayN.ViewModels
private void ProxiesDelayTest(bool blAll)
{
UpdateHandler(false, "Clash Proxies Latency Test");
//UpdateHandler(false, "Clash Proxies Latency Test");
ClashApiHandler.Instance.ClashProxiesDelayTest(blAll, _proxyDetails.ToList(), (item, result) =>
{
@@ -736,7 +736,7 @@ namespace v2rayN.ViewModels
public void TestServerAvailability()
{
var item = ConfigHandler.GetDefaultServer(_config);
if (item == null || item.configType == EConfigType.Custom)
if (item == null)
{
return;
}
@@ -1293,4 +1293,4 @@ namespace v2rayN.ViewModels
#endregion UI
}
}
}
@@ -74,6 +74,7 @@ namespace v2rayN.ViewModels
public ReactiveCommand<Unit, Unit> SetDefaultServerCmd { get; }
public ReactiveCommand<Unit, Unit> ShareServerCmd { get; }
public ReactiveCommand<Unit, Unit> SetDefaultMultipleServerCmd { get; }
public ReactiveCommand<Unit, Unit> SetDefaultLoadBalanceServerCmd { get; }
//servers move
public ReactiveCommand<Unit, Unit> MoveTopCmd { get; }
@@ -170,8 +171,13 @@ namespace v2rayN.ViewModels
}, canEditRemove);
SetDefaultMultipleServerCmd = ReactiveCommand.Create(() =>
{
SetDefaultMultipleServer();
SetDefaultMultipleServer(ECoreType.sing_box);
}, canEditRemove);
SetDefaultLoadBalanceServerCmd = ReactiveCommand.Create(() =>
{
SetDefaultMultipleServer(ECoreType.Xray);
}, canEditRemove);
//servers move
MoveTopCmd = ReactiveCommand.Create(() =>
{
@@ -612,14 +618,14 @@ namespace v2rayN.ViewModels
await DialogHost.Show(dialog, "RootDialog");
}
private void SetDefaultMultipleServer()
private void SetDefaultMultipleServer(ECoreType coreType)
{
if (GetProfileItems(out List<ProfileItem> lstSelecteds, true) < 0)
{
return;
}
if (ConfigHandler.AddCustomServer4Multiple(_config, lstSelecteds, out string indexId) != 0)
if (ConfigHandler.AddCustomServer4Multiple(_config, lstSelecteds, coreType, out string indexId) != 0)
{
_noticeHandler?.Enqueue(ResUI.OperationFailed);
return;
@@ -16,8 +16,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
@@ -1,12 +1,11 @@
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.ClashConnectionsView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:v2rayN.Views"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:resx="clr-namespace:v2rayN.Resx"
xmlns:vms="clr-namespace:v2rayN.ViewModels"
d:DesignHeight="450"
@@ -29,7 +28,7 @@
<ComboBox
x:Name="cmbSorting"
Width="100"
Margin="8"
Margin="8,0"
Style="{StaticResource DefComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingUpSpeed}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDownSpeed}" />
@@ -55,7 +54,7 @@
Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleButton
x:Name="togAutoRefresh"
Margin="8"
Margin="8,0"
HorizontalAlignment="Left" />
</WrapPanel>
@@ -1,12 +1,12 @@
<reactiveui:ReactiveUserControl
x:Class="v2rayN.Views.ClashProxiesView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converters="clr-namespace:v2rayN.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveui="http://reactiveui.net"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:converters="clr-namespace:v2rayN.Converters"
xmlns:resx="clr-namespace:v2rayN.Resx"
xmlns:vms="clr-namespace:v2rayN.ViewModels"
d:DesignHeight="450"
@@ -34,7 +34,7 @@
<ComboBox
x:Name="cmbRulemode"
Width="80"
Margin="8"
Margin="8,0"
Style="{StaticResource DefComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeRule}" />
<ComboBoxItem Content="{x:Static resx:ResUI.menuModeGlobal}" />
@@ -49,7 +49,7 @@
<ComboBox
x:Name="cmbSorting"
Width="60"
Margin="8"
Margin="8,0"
Style="{StaticResource DefComboBox}">
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingDelay}" />
<ComboBoxItem Content="{x:Static resx:ResUI.TbSortingName}" />
@@ -81,7 +81,7 @@
Text="{x:Static resx:ResUI.TbAutoRefresh}" />
<ToggleButton
x:Name="togAutoRefresh"
Margin="8"
Margin="8,0"
HorizontalAlignment="Left" />
</WrapPanel>
<DockPanel>
@@ -15,7 +15,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
_config = LazyConfig.Instance.GetConfig();
@@ -18,7 +18,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
_config = LazyConfig.Instance.GetConfig();
_config.globalHotkeys ??= new List<KeyEventItem>();
@@ -115,10 +115,16 @@
x:Name="menuShareServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuShareServer}" />
<Separator />
<MenuItem
x:Name="menuSetDefaultMultipleServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSetDefaultMultipleServer}" />
<MenuItem
x:Name="menuSetDefaultLoadBalanceServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSetDefaultLoadBalanceServer}" />
<Separator />
<MenuItem
x:Name="menuMixedTestServer"
@@ -63,6 +63,7 @@ namespace v2rayN.Views
this.BindCommand(ViewModel, vm => vm.SetDefaultServerCmd, v => v.menuSetDefaultServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.ShareServerCmd, v => v.menuShareServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultMultipleServerCmd, v => v.menuSetDefaultMultipleServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SetDefaultLoadBalanceServerCmd, v => v.menuSetDefaultLoadBalanceServer).DisposeWith(disposables);
//servers move
this.OneWayBind(ViewModel, vm => vm.SubItems, v => v.cmbMoveToGroup.ItemsSource).DisposeWith(disposables);
@@ -12,8 +12,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
@@ -14,8 +14,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
this.PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
@@ -13,8 +13,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Closing += RoutingSettingWindow_Closing;
this.PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
@@ -12,8 +12,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
@@ -14,8 +14,6 @@ namespace v2rayN.Views
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
ViewModel = new SubSettingViewModel(this);
+1 -1
View File
@@ -10,7 +10,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>v2rayN.ico</ApplicationIcon>
<Copyright>Copyright © 2017-2024 (GPLv3)</Copyright>
<FileVersion>6.51</FileVersion>
<FileVersion>6.52</FileVersion>
<SupportedOSPlatformVersion>7.0</SupportedOSPlatformVersion>
</PropertyGroup>
Binary file not shown.
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<dict/>
<key>development</key>
<true/>
</dict>
</plist>
Binary file not shown.
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
+5 -7
View File
@@ -142,7 +142,6 @@
669468492C076C2800146109 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
66973EB621797719001FEA1E /* ServiceManagement.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ServiceManagement.framework; path = System/Library/Frameworks/ServiceManagement.framework; sourceTree = SDKROOT; };
66A358672C39517F00914A25 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
66A358682C3959AE00914A25 /* build.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = build.plist; sourceTree = "<group>"; };
66A5CE4521706B5A009B08B2 /* Pods_V2rayU.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_V2rayU.framework; sourceTree = BUILT_PRODUCTS_DIR; };
66A77BE2268225790097A126 /* v2ray-core */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "v2ray-core"; path = "Build/v2ray-core"; sourceTree = "<group>"; };
66ACB19F21757D5B005B5881 /* MainMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
@@ -297,7 +296,6 @@
66FEAD44217D75D7009DECF9 /* Build */ = {
isa = PBXGroup;
children = (
66A358682C3959AE00914A25 /* build.plist */,
6625848E2AB746D500DFDC1E /* appdmg.sh */,
66FEAD45217D75FC009DECF9 /* release.sh */,
6D6DFD93CE866018306A090E /* pac */,
@@ -432,7 +430,7 @@
};
buildConfigurationList = 664EB36C216C9A5E00B6AE0D /* Build configuration list for PBXProject "V2rayU" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = "zh-Hans";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
@@ -836,7 +834,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.2.2;
CURRENT_PROJECT_VERSION = 4.2.3;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
@@ -848,7 +846,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 4.2.2;
MARKETING_VERSION = 4.2.3;
ONLY_ACTIVE_ARCH = YES;
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -874,7 +872,7 @@
CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 4.2.2;
CURRENT_PROJECT_VERSION = 4.2.3;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = "";
@@ -886,7 +884,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 11.0;
MARKETING_VERSION = 4.2.2;
MARKETING_VERSION = 4.2.3;
ONLY_ACTIVE_ARCH = NO;
PRODUCT_BUNDLE_IDENTIFIER = net.yanue.V2rayU;
PRODUCT_NAME = "$(TARGET_NAME)";
+39 -39
View File
@@ -300,45 +300,6 @@
</button>
</subviews>
</view>
<scrollView focusRingType="none" fixedFrame="YES" borderType="line" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="in5-Uo-f8c">
<rect key="frame" x="25" y="66" width="220" height="241"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" copiesOnScroll="NO" id="vCh-HN-u7f">
<rect key="frame" x="1" y="1" width="218" height="239"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="L49-ez-XJl">
<rect key="frame" x="0.0" y="0.0" width="218" height="239"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="remark" width="206" minWidth="40" maxWidth="1000" id="0WD-Qv-ia2">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Text Cell" usesSingleLineMode="YES" id="YQ1-7i-YOl">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
</clipView>
<edgeInsets key="contentInsets" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="dpI-Rq-Xuz">
<rect key="frame" x="1" y="224" width="218" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="ys3-G3-wz7">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
<segmentedControl verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="yay-tY-9RI">
<rect key="frame" x="25" y="55" width="220" height="23"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
@@ -374,6 +335,45 @@
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<scrollView focusRingType="none" fixedFrame="YES" borderType="line" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="in5-Uo-f8c">
<rect key="frame" x="25" y="56" width="220" height="251"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" copiesOnScroll="NO" id="vCh-HN-u7f">
<rect key="frame" x="1" y="1" width="218" height="249"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView focusRingType="none" verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" columnReordering="NO" columnResizing="NO" multipleSelection="NO" emptySelection="NO" autosaveColumns="NO" typeSelect="NO" id="L49-ez-XJl">
<rect key="frame" x="0.0" y="0.0" width="218" height="249"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="remark" width="206" minWidth="40" maxWidth="1000" id="0WD-Qv-ia2">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" title="Text Cell" usesSingleLineMode="YES" id="YQ1-7i-YOl">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</tableColumn>
</tableColumns>
</tableView>
</subviews>
</clipView>
<edgeInsets key="contentInsets" left="0.0" right="0.0" top="0.0" bottom="0.0"/>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="dpI-Rq-Xuz">
<rect key="frame" x="1" y="224" width="218" height="16"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="ys3-G3-wz7">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
</scrollView>
</subviews>
<point key="canvasLocation" x="-130" y="-187"/>
</view>
+1 -1
View File
@@ -528,7 +528,7 @@ class ConfigWindowController: NSWindowController, NSWindowDelegate, NSTabViewDel
// grpc
self.grpcServiceName.stringValue = v2rayConfig.streamGrpc.serviceName
self.grpcUseragent.stringValue = v2rayConfig.streamGrpc.user_agent
self.grpcUseragent.stringValue = v2rayConfig.streamGrpc.user_agent ?? ""
self.grpcMulti.intValue = v2rayConfig.streamGrpc.multiMode ? 1 : 0
// ========================== stream end =======================
+43 -17
View File
@@ -204,6 +204,16 @@ class ImportUri {
}
func importVmess(vmess: VmessUri) {
if vmess.fp.isEmpty {
vmess.fp = "chrome"
}
if vmess.security.isEmpty {
vmess.security = "none"
}
if vmess.sni.count == 0 {
vmess.sni = vmess.address
}
let v2ray = V2rayConfig()
var vmessItem = V2rayOutboundVMessItem()
@@ -232,12 +242,10 @@ class ImportUri {
v2ray.streamKcp.header.type = vmess.type
v2ray.streamKcp.uplinkCapacity = vmess.uplinkCapacity
v2ray.streamKcp.downlinkCapacity = vmess.downlinkCapacity
v2ray.streamKcp.seed = vmess.kcpSeed
// h2
if v2ray.streamH2.host.count == 0 {
v2ray.streamH2.host = [""]
}
v2ray.streamH2.host[0] = vmess.netHost
v2ray.streamH2.host = [vmess.netHost]
v2ray.streamH2.path = vmess.netPath
// ws
@@ -250,7 +258,13 @@ class ImportUri {
// tcp
v2ray.streamTcp.header.type = vmess.type
if v2ray.streamNetwork == "tcp" && v2ray.streamTcp.header.type == "http" {
var tcpReq = TcpSettingHeaderRequest()
tcpReq.path = [vmess.netPath]
tcpReq.headers.host = [vmess.netHost]
v2ray.streamTcp.header.request = tcpReq
}
// quic
v2ray.streamQuic.header.type = vmess.type
@@ -296,6 +310,19 @@ class ImportUri {
}
func importVless(vmess: VlessUri) {
if vmess.fp.isEmpty {
vmess.fp = "chrome"
}
if vmess.security.isEmpty {
vmess.security = "none"
}
if vmess.flow.isEmpty {
vmess.flow = "xtls-rprx-vision"
}
if vmess.sni.count == 0 {
vmess.sni = vmess.address
}
let v2ray = V2rayConfig()
v2ray.serverProtocol = V2rayProtocolOutbound.vless.rawValue
@@ -310,10 +337,6 @@ class ImportUri {
vmessItem.users = [user]
v2ray.serverVless = vmessItem
if vmess.sni.count == 0 {
vmess.sni = vmess.address
}
// stream
v2ray.streamNetwork = vmess.type
v2ray.streamSecurity = vmess.security
@@ -329,12 +352,10 @@ class ImportUri {
// kcp
v2ray.streamKcp.header.type = vmess.type
v2ray.streamKcp.seed = vmess.kcpSeed
// h2
if v2ray.streamH2.host.count == 0 {
v2ray.streamH2.host = [""]
}
v2ray.streamH2.host[0] = vmess.host
v2ray.streamH2.host = [vmess.host]
v2ray.streamH2.path = vmess.path
// ws
@@ -343,11 +364,17 @@ class ImportUri {
// grpc
v2ray.streamGrpc.serviceName = vmess.path
v2ray.streamGrpc.multiMode = vmess.type == "multi" // v2rayN
v2ray.streamGrpc.multiMode = vmess.grpcMode == "multi" // v2rayN
// tcp
v2ray.streamTcp.header.type = vmess.type
if v2ray.streamNetwork == "tcp" && v2ray.streamTcp.header.type == "http" {
var tcpReq = TcpSettingHeaderRequest()
tcpReq.path = [vmess.path]
tcpReq.headers.host = [vmess.host]
v2ray.streamTcp.header.request = tcpReq
}
// quic
v2ray.streamQuic.header.type = vmess.type
@@ -398,7 +425,6 @@ class ImportUri {
svr.address = trojan.host
svr.port = trojan.port
svr.password = trojan.password
svr.flow = trojan.flow
v2ray.serverTrojan = svr
v2ray.enableMux = false
@@ -492,7 +518,7 @@ func importByClash(clash: clashProxy) -> ImportUri? {
item.address = clash.server
item.port = clash.port
item.id = clash.uuid ?? ""
item.security = clash.cipher ?? "auto"
item.security = clash.cipher ?? "none" // vless encryption
item.type = clash.network ?? "tcp"
item.sni = clash.sni ?? clash.server
if clash.security == "reality" {
+1 -1
View File
@@ -149,7 +149,7 @@ class PingServer: NSObject, URLSessionDataDelegate {
vCfg.httpHost = "127.0.0.1"
vCfg.socksHost = "127.0.0.1"
vCfg.httpPort = String(bindPort)
vCfg.socksPort = String(bindPort + 1)
vCfg.socksPort = String(bindPort - 1)
jsonText = vCfg.combineManual()
do {
+22 -8
View File
@@ -109,14 +109,17 @@ final class PreferencePacViewController: NSViewController, PreferencePane {
let session = URLSession(configuration: getProxyUrlSessionConfigure())
let task = session.dataTask(with: URLRequest(url: reqUrl)){(data: Data?, response: URLResponse?, error: Error?) in
if error != nil {
self.tips.stringValue = "Failed to download latest GFW List: \(String(describing: error))"
DispatchQueue.main.async {
self.tips.stringValue = "Failed to download latest GFW List: \(String(describing: error))"
}
} else {
if data != nil {
if let outputStr = String(data: data!, encoding: String.Encoding.utf8) {
do {
try outputStr.write(toFile: GFWListFilePath, atomically: true, encoding: String.Encoding.utf8)
self.tips.stringValue = "gfwList has been updated"
DispatchQueue.main.async {
self.tips.stringValue = "gfwList has been updated"
}
NSLog("\(self.tips.stringValue)")
// save to UserDefaults
@@ -124,20 +127,28 @@ final class PreferencePacViewController: NSViewController, PreferencePane {
if GeneratePACFile(rewrite: true) {
// Popup a user notification
self.tips.stringValue = "PAC has been updated by latest GFW List."
DispatchQueue.main.async {
self.tips.stringValue = "PAC has been updated by latest GFW List."
}
NSLog("\(self.tips.stringValue)")
}
} catch {
// Popup a user notification
self.tips.stringValue = "Failed to Write latest GFW List."
DispatchQueue.main.async {
self.tips.stringValue = "Failed to Write latest GFW List."
}
NSLog("\(self.tips.stringValue)")
}
} else {
self.tips.stringValue = "Failed to download latest GFW List."
DispatchQueue.main.async {
self.tips.stringValue = "Failed to download latest GFW List."
}
}
} else {
// Popup a user notification
self.tips.stringValue = "Failed to download latest GFW List."
DispatchQueue.main.async {
self.tips.stringValue = "Failed to download latest GFW List."
}
self.tryDownloadByShell(gfwPacListUrl: gfwPacListUrl)
}
}
@@ -153,7 +164,9 @@ final class PreferencePacViewController: NSViewController, PreferencePane {
NSLog("curl result: \(msg)")
if GeneratePACFile(rewrite: true) {
// Popup a user notification
self.tips.stringValue = "PAC has been updated by latest GFW List."
DispatchQueue.main.async {
self.tips.stringValue = "PAC has been updated by latest GFW List."
}
}
}
}
@@ -256,6 +269,7 @@ func getPacUserRules() -> String {
||github.com
||chat.openai.com
||openai.com
||chatgpt.com
"""
do {
let url = URL(fileURLWithPath: PACUserRuleFilePath)
@@ -149,14 +149,14 @@ final class PreferenceRoutingViewController: NSViewController, PreferencePane, N
@IBAction func goHelp(_ sender: Any) {
guard let url = URL(string: "https://toutyrater.github.io/basic/routing/") else {
guard let url = URL(string: "https://xtls.github.io/config/routing.html#routingobject") else {
return
}
NSWorkspace.shared.open(url)
}
@IBAction func goHelp2(_ sender: Any) {
guard let url = URL(string: "https://github.com/v2ray/domain-list-community") else {
guard let url = URL(string: "https://xtls.github.io/config/routing.html#routingobject") else {
return
}
NSWorkspace.shared.open(url)
+31
View File
@@ -28,6 +28,8 @@ class VmessUri {
var alpn: String = ""
var sni: String = ""
var fp: String = ""
var grpcMode:String = ""
var kcpSeed:String = ""
/**
vmess://base64(security:uuid@host:port)?[urlencode(parameters)]
@@ -145,6 +147,13 @@ class VmessUri {
case "remark":
self.remark = param[1].urlDecoded()
break
case "serviceName":
self.netPath = param[1]
break
case "mode":
self.grpcMode = param[1]
case "seed":
self.kcpSeed = param[1]
default:
break
}
@@ -219,6 +228,8 @@ class VmessUri {
self.fp = json["fp"].stringValue
// type:none\http\srtp\utp\wechat-video
self.type = json["type"].stringValue
self.kcpSeed = json["seed"].stringValue
self.grpcMode = json["mode"].stringValue
}
}
@@ -519,6 +530,8 @@ class VlessUri {
var fp: String = "" // fingerprint
var pbk: String = "" // reality public key
var sid: String = "" // reality shortId
var grpcMode:String = ""
var kcpSeed:String = ""
// vless://f2a5064a-fabb-43ed-a2b6-8ffeb970df7f@00.com:443?flow=xtls-rprx-splite&encryption=none&security=xtls&sni=aaaaa&type=http&host=00.com&path=%2fvl#vless1
func encode() -> String {
@@ -538,6 +551,9 @@ class VlessUri {
URLQueryItem(name: "fp", value: self.fp),
URLQueryItem(name: "pbk", value: self.pbk),
URLQueryItem(name: "sid", value: self.sid),
URLQueryItem(name: "serviceName", value: self.path),
URLQueryItem(name: "mode", value: self.grpcMode),
URLQueryItem(name: "seed", value: self.kcpSeed)
]
return (uri.url?.absoluteString ?? "") + "#" + self.remark
@@ -567,6 +583,9 @@ class VlessUri {
break
case "flow":
self.flow = item.value as! String
if self.flow.isEmpty {
self.flow = "xtls-rprx-vision"
}
break
case "encryption":
self.encryption = item.value as! String
@@ -598,6 +617,18 @@ class VlessUri {
case "sid":
self.sid = item.value as! String
break
case "headerType":
self.type = item.value as! String
break
case "seed":
self.kcpSeed = item.value as! String
break
case "serviceName":
self.path = item.value as! String
break
case "mode":
self.grpcMode = item.value as! String
break
default:
break
}
+4 -4
View File
@@ -298,14 +298,14 @@ class RoutingItem: NSObject, NSCoding {
// parse default settings
func parseDefaultSettings() -> V2rayRouting {
var rules: [V2rayRoutingSettingRule] = []
var rules: [V2rayRoutingRule] = []
let (blockDomains, blockIps) = parseDomainOrIp(domainIpStr: self.block)
let (proxyDomains, proxyIps) = parseDomainOrIp(domainIpStr: self.proxy)
let (directDomains, directIps) = parseDomainOrIp(domainIpStr: self.direct)
// // rules
var ruleProxyDomain, ruleProxyIp, ruleDirectDomain, ruleDirectIp, ruleBlockDomain, ruleBlockIp, ruleDirectIpDefault, ruleDirectDomainDefault: V2rayRoutingSettingRule?
var ruleProxyDomain, ruleProxyIp, ruleDirectDomain, ruleDirectIp, ruleBlockDomain, ruleBlockIp, ruleDirectIpDefault, ruleDirectDomainDefault: V2rayRoutingRule?
// proxy
if proxyDomains.count > 0 {
ruleProxyDomain = getRoutingRule(outTag: "proxy", domain: proxyDomains, ip: nil, port: nil)
@@ -400,8 +400,8 @@ class RoutingItem: NSObject, NSCoding {
return settings
}
func getRoutingRule(outTag: String, domain:[String]?, ip: [String]?, port:String?) -> V2rayRoutingSettingRule {
var rule = V2rayRoutingSettingRule()
func getRoutingRule(outTag: String, domain:[String]?, ip: [String]?, port:String?) -> V2rayRoutingRule {
var rule = V2rayRoutingRule()
rule.outboundTag = outTag
rule.type = "field"
rule.domain = domain

Some files were not shown because too many files have changed in this diff Show More