mirror of
https://github.com/bolucat/Archive.git
synced 2026-04-22 16:07:49 +08:00
Update On Sun Jul 21 20:29:04 CEST 2024
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
Generated
+4
-4
@@ -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
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}')
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-1
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
@@ -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.
+507
-239
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -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)";
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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" {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user