Skip to content

Commit 4670091

Browse files
committed
Add create subscription; SIP008
1 parent 4c5cbac commit 4670091

14 files changed

Lines changed: 457 additions & 40 deletions

YtFlowApp/CoreFfi.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,12 +171,47 @@ namespace winrt::YtFlowApp::implementation
171171
unwrap_ffi_result<FfiNoop>(ytflow_core::ytflow_proxy_group_create(name, type, conn_ptr));
172172
return (uint32_t)((uintptr_t)ptrRaw & 0xFFFFFFFF);
173173
}
174+
uint32_t FfiConn::CreateProxySubscriptionGroup(char const *name, char const *format, char const *url) &
175+
{
176+
std::lock_guard _guard(conn_mu);
177+
const auto [ptrRaw, metaRaw] = unwrap_ffi_result<FfiNoop>(
178+
ytflow_core::ytflow_proxy_group_create_subscription(name, format, url, conn_ptr));
179+
return (uint32_t)((uintptr_t)ptrRaw & 0xFFFFFFFF);
180+
}
174181
std::vector<FfiProxy> FfiConn::GetProxiesByProxyGroup(uint32_t proxyGroupId) &
175182
{
176183
std::lock_guard _guard(conn_mu);
177184
return unwrap_ffi_buffer<std::vector<FfiProxy>>(
178185
ytflow_core::ytflow_proxy_get_by_proxy_group(proxyGroupId, conn_ptr));
179186
}
187+
FfiProxyGroupSubscription FfiConn::GetProxySubscriptionByProxyGroup(uint32_t proxyGroupId) &
188+
{
189+
std::lock_guard _guard(conn_mu);
190+
return unwrap_ffi_buffer<FfiProxyGroupSubscription>(
191+
ytflow_core::ytflow_proxy_subscription_query_by_proxy_group_id(proxyGroupId, conn_ptr));
192+
}
193+
void FfiConn::UpdateProxySubscriptionRetrievedByProxyGroup(uint32_t proxyGroupId,
194+
std::optional<uint64_t> uploadBytes,
195+
std::optional<uint64_t> downloadBytes,
196+
std::optional<uint64_t> totalBytes,
197+
char const *expiresAt) &
198+
{
199+
uint64_t const *uploadBytesPtr{nullptr}, *downloadBytesPtr{nullptr}, *totalBytesPtr{nullptr};
200+
if (uploadBytes.has_value())
201+
{
202+
uploadBytesPtr = &*uploadBytes;
203+
}
204+
if (downloadBytes.has_value())
205+
{
206+
downloadBytesPtr = &*downloadBytes;
207+
}
208+
if (totalBytes.has_value())
209+
{
210+
totalBytesPtr = &*totalBytes;
211+
}
212+
unwrap_ffi_result<FfiNoop>(ytflow_core::ytflow_proxy_subscription_update_retrieved_by_proxy_group_id(
213+
proxyGroupId, uploadBytesPtr, downloadBytesPtr, totalBytesPtr, expiresAt, conn_ptr));
214+
}
180215
uint32_t FfiConn::CreateProxy(uint32_t proxyGroupId, char const *name, uint8_t const *proxy, size_t proxyLen,
181216
uint16_t proxyVersion) &
182217
{
@@ -203,6 +238,12 @@ namespace winrt::YtFlowApp::implementation
203238
unwrap_ffi_result<FfiNoop>(
204239
ytflow_core::ytflow_proxy_reorder(proxyGroupId, rangeStartOrder, rangeEndOrder, moves, conn_ptr));
205240
}
241+
void FfiConn::BatchUpdateProxyInGroup(uint32_t proxyGroupId, uint8_t const *newProxyBuf, size_t newProxyBufLen) &
242+
{
243+
std::lock_guard _guard(conn_mu);
244+
unwrap_ffi_result<FfiNoop>(
245+
ytflow_core::ytflow_proxy_batch_update_by_group(proxyGroupId, newProxyBuf, newProxyBufLen, conn_ptr));
246+
}
206247
std::vector<FfiResource> FfiConn::GetResources() &
207248
{
208249
std::lock_guard _guard(conn_mu);

YtFlowApp/CoreFfi.h

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,44 @@ namespace winrt::YtFlowApp::implementation
8484
std::string type;
8585
};
8686
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FfiProxyGroup, id, name, type)
87+
struct FfiProxyGroupSubscription
88+
{
89+
std::string format;
90+
std::string url;
91+
std::optional<uint64_t> upload_bytes_used;
92+
std::optional<uint64_t> download_bytes_used;
93+
std::optional<uint64_t> bytes_total;
94+
std::optional<std::string> expires_at;
95+
std::optional<std::string> retrieved_at;
96+
};
97+
inline void from_json(nlohmann::json const &json, FfiProxyGroupSubscription &r)
98+
{
99+
json.at("format").get_to(r.format);
100+
json.at("url").get_to(r.url);
101+
if (nlohmann::json const uploadBytesUsedDoc = json.value("upload_bytes_used", nlohmann::json());
102+
uploadBytesUsedDoc != nullptr)
103+
{
104+
r.upload_bytes_used = {uploadBytesUsedDoc.get<uint64_t>()};
105+
}
106+
if (nlohmann::json const downloadBytesUsedDoc = json.value("download_bytes_used", nlohmann::json());
107+
downloadBytesUsedDoc != nullptr)
108+
{
109+
r.download_bytes_used = {downloadBytesUsedDoc.get<uint64_t>()};
110+
}
111+
if (nlohmann::json const bytesTotalDoc = json.value("bytes_total", nlohmann::json()); bytesTotalDoc != nullptr)
112+
{
113+
r.bytes_total = {bytesTotalDoc.get<uint64_t>()};
114+
}
115+
if (nlohmann::json const expiresAtDoc = json.value("expires_at", nlohmann::json()); expiresAtDoc != nullptr)
116+
{
117+
r.expires_at = {expiresAtDoc.get<std::string>()};
118+
}
119+
if (nlohmann::json const retrievedAtDoc = json.value("retrieved_at", nlohmann::json());
120+
retrievedAtDoc != nullptr)
121+
{
122+
r.retrieved_at = {retrievedAtDoc.get<std::string>()};
123+
}
124+
}
87125
struct FfiProxy
88126
{
89127
uint32_t id{};
@@ -118,16 +156,16 @@ namespace winrt::YtFlowApp::implementation
118156
{
119157
return;
120158
}
121-
if (nlohmann::json const etagDoc = json.value("etag", nlohmann::json{nullptr}); etagDoc != nullptr)
159+
if (nlohmann::json const etagDoc = json.value("etag", nlohmann::json()); etagDoc != nullptr)
122160
{
123161
r.etag = {etagDoc.get<std::string>()};
124162
}
125-
if (nlohmann::json const lastModifiedDoc = json.value("last_modified", nlohmann::json{nullptr});
163+
if (nlohmann::json const lastModifiedDoc = json.value("last_modified", nlohmann::json());
126164
lastModifiedDoc != nullptr)
127165
{
128166
r.last_modified = {lastModifiedDoc.get<std::string>()};
129167
}
130-
if (nlohmann::json const retrievedAtDoc = json.value("retrieved_at", nlohmann::json{nullptr});
168+
if (nlohmann::json const retrievedAtDoc = json.value("retrieved_at", nlohmann::json());
131169
retrievedAtDoc != nullptr)
132170
{
133171
r.retrieved_at = {retrievedAtDoc.get<std::string>()};
@@ -153,16 +191,16 @@ namespace winrt::YtFlowApp::implementation
153191
{
154192
return;
155193
}
156-
if (nlohmann::json const gitTagDoc = json.value("git_tag", nlohmann::json{nullptr}); gitTagDoc != nullptr)
194+
if (nlohmann::json const gitTagDoc = json.value("git_tag", nlohmann::json()); gitTagDoc != nullptr)
157195
{
158196
r.git_tag = {gitTagDoc.get<std::string>()};
159197
}
160-
if (nlohmann::json const releaseTitleDoc = json.value("release_title", nlohmann::json{nullptr});
198+
if (nlohmann::json const releaseTitleDoc = json.value("release_title", nlohmann::json());
161199
releaseTitleDoc != nullptr)
162200
{
163201
r.release_title = {releaseTitleDoc.get<std::string>()};
164202
}
165-
if (nlohmann::json const retrievedAtDoc = json.value("retrieved_at", nlohmann::json{nullptr});
203+
if (nlohmann::json const retrievedAtDoc = json.value("retrieved_at", nlohmann::json());
166204
retrievedAtDoc != nullptr)
167205
{
168206
r.retrieved_at = {retrievedAtDoc.get<std::string>()};
@@ -196,13 +234,19 @@ namespace winrt::YtFlowApp::implementation
196234
void RenameProxyGroup(uint32_t id, char const *name) &;
197235
void DeleteProxyGroup(uint32_t id) &;
198236
uint32_t CreateProxyGroup(char const *name, char const *type) &;
237+
uint32_t CreateProxySubscriptionGroup(char const *name, char const *format, char const *url) &;
199238
std::vector<FfiProxy> GetProxiesByProxyGroup(uint32_t proxyGroupId) &;
239+
FfiProxyGroupSubscription GetProxySubscriptionByProxyGroup(uint32_t proxyGroupId) &;
240+
void UpdateProxySubscriptionRetrievedByProxyGroup(uint32_t proxyGroupId, std::optional<uint64_t> uploadBytes,
241+
std::optional<uint64_t> downloadBytes,
242+
std::optional<uint64_t> totalBytes, char const *expiresAt) &;
200243
uint32_t CreateProxy(uint32_t proxyGroupId, char const *name, uint8_t const *proxy, size_t proxyLen,
201244
uint16_t proxyVersion) &;
202245
void UpdateProxy(uint32_t proxyId, char const *name, uint8_t const *proxy, size_t proxyLen,
203246
uint16_t proxyVersion) &;
204247
void DeleteProxy(uint32_t proxyId) &;
205248
void ReorderProxy(uint32_t proxyGroupId, int32_t rangeStartOrder, int32_t rangeEndOrder, int32_t moves) &;
249+
void BatchUpdateProxyInGroup(uint32_t proxyGroupId, uint8_t const *newProxyBuf, size_t newProxyBufLen) &;
206250
std::vector<FfiResource> GetResources() &;
207251
void DeleteResource(uint32_t resourceId) &;
208252
uint32_t CreateResourceWithUrl(char const *key, char const *type, char const *local_file, char const *url) &;

YtFlowApp/CoreProxy.cpp

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,21 @@ namespace winrt::YtFlowApp::implementation
112112
host = uri.hostText;
113113
return PluginDecodeResult::Success;
114114
}
115-
PluginDecodeResult Sip002Decoder::DecodeObfs(ParsedUri const &uri, ProxyPlugin &plugin, std::string_view tcpNext,
116-
std::string_view)
115+
116+
PluginDecodeResult Sip002Decoder::DecodeSip003Plugin(std::string_view pluginName, std::string_view pluginOpts,
117+
std::string_view defaultHost, ProxyPlugin &plugin,
118+
std::string_view tcpNext)
117119
{
118-
std::string empty{};
119-
auto const &param = uri.GetQueryValue("plugin").value_or(std::ref(empty)).get();
120-
if (!param.starts_with("obfs-local;"))
120+
if (pluginName == "")
121121
{
122122
return PluginDecodeResult::Ignore;
123123
}
124+
if (pluginName != "obfs-local")
125+
{
126+
return PluginDecodeResult::Fail;
127+
}
124128
std::map<std::string_view, std::string_view> obfsMap;
125-
for (auto const kv : std::views::split(std::string_view(param.data() + 11, param.size() - 11), ';'))
129+
for (auto const kv : std::views::split(pluginOpts, ';'))
126130
{
127131
std::string_view kvStr(kv.begin(), kv.end());
128132
auto const eqPos = kvStr.find('=');
@@ -136,7 +140,7 @@ namespace winrt::YtFlowApp::implementation
136140
std::string_view host = obfsMap["obfs-host"];
137141
if (host.empty())
138142
{
139-
host = uri.hostText;
143+
host = defaultHost;
140144
}
141145
if (obfsMap["obfs"] == "http")
142146
{
@@ -159,6 +163,20 @@ namespace winrt::YtFlowApp::implementation
159163
}
160164
return PluginDecodeResult::Success;
161165
}
166+
PluginDecodeResult Sip002Decoder::DecodeObfs(ParsedUri const &uri, ProxyPlugin &plugin, std::string_view tcpNext,
167+
std::string_view)
168+
{
169+
std::string empty{};
170+
auto const &param = uri.GetQueryValue("plugin").value_or(std::ref(empty)).get();
171+
auto const semiPos = param.find(';');
172+
if (semiPos == std::string::npos)
173+
{
174+
return DecodeSip003Plugin("", std::string_view(param.data(), param.size()), uri.hostText, plugin, tcpNext);
175+
}
176+
return DecodeSip003Plugin(std::string_view(param.data(), semiPos),
177+
std::string_view(param.data() + semiPos + 1, param.size() - semiPos - 1),
178+
uri.hostText, plugin, tcpNext);
179+
}
162180
PluginDecodeResult Sip002Decoder::DecodeTls(ParsedUri const &, ProxyPlugin &, std::string_view, std::string_view)
163181
{
164182
// TODO: tls
@@ -457,7 +475,7 @@ namespace winrt::YtFlowApp::implementation
457475
}
458476
PluginDecodeResult HttpDecoder::DecodeTls(ParsedUri const &, ProxyPlugin &, std::string_view, std::string_view)
459477
{
460-
// TODO: tls
478+
// TODO: https tls
461479
return PluginDecodeResult::Ignore;
462480
}
463481
PluginDecodeResult HttpDecoder::DecodeUdp(ParsedUri const &)
@@ -514,7 +532,7 @@ namespace winrt::YtFlowApp::implementation
514532
uint16_t alterId;
515533
try
516534
{
517-
auto const enableVlessDoc = m_linkDoc.value("enable_vless", nlohmann::json{nullptr});
535+
auto const enableVlessDoc = m_linkDoc.value("enable_vless", nlohmann::json());
518536
if (enableVlessDoc == true || enableVlessDoc == "true")
519537
{
520538
return PluginDecodeResult::Fail;
@@ -643,33 +661,22 @@ namespace winrt::YtFlowApp::implementation
643661
{
644662
return PluginDecodeResult::Ignore;
645663
}
646-
auto const enableXtlsDoc = m_linkDoc.value("enable_xtls", nlohmann::json{nullptr});
664+
auto const enableXtlsDoc = m_linkDoc.value("enable_xtls", nlohmann::json());
647665
if (enableXtlsDoc == true || enableXtlsDoc == "true")
648666
{
649667
return PluginDecodeResult::Fail;
650668
}
651669
// TODO: fingerprint
652670
nlohmann::json sniDoc = nullptr, alpnDoc = nullptr;
653671
auto const isWs = m_linkDoc.value("net", "tcp") == "ws";
654-
if (m_linkDoc.at("alpn").get<std::string>().find("h2") != std::string::npos)
672+
auto const rawAlpn = m_linkDoc.value("alpn", "");
673+
if (isWs && rawAlpn == "h2,http/1.1")
655674
{
656-
// WebSocket does not support h2 yet
657-
if (isWs)
658-
{
659-
return PluginDecodeResult::Fail;
660-
}
675+
// websocket-client is ALPN-aware. Omit alpn to enable h2 probe.
661676
}
662-
alpnDoc = ParseAlpn(m_linkDoc.value("alpn", ""));
663-
if (alpnDoc.empty())
677+
else
664678
{
665-
if (isWs)
666-
{
667-
alpnDoc = {"http/1.1"};
668-
}
669-
else
670-
{
671-
alpnDoc = {"h2", "http/1.1"};
672-
}
679+
alpnDoc = ParseAlpn(rawAlpn);
673680
}
674681
if (m_linkDoc.value("sni", "") != "")
675682
{

YtFlowApp/CoreProxyImpl.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,16 @@ namespace winrt::YtFlowApp::implementation
8484

8585
struct Sip002Decoder
8686
{
87+
static PluginDecodeResult DecodeSip003Plugin(std::string_view pluginName, std::string_view pluginOpts,
88+
std::string_view defaultHost, ProxyPlugin &plugin,
89+
std::string_view tcpNext);
90+
8791
std::string DecodeName(ParsedUri const &uri);
8892
PluginDecodeResult DecodeProtocol(ParsedUri const &uri, ProxyPlugin &plugin, std::string_view tcpNext,
8993
std::string_view udpNext);
9094
PluginDecodeResult DecodeRedir(ParsedUri const &uri, std::string &host, uint16_t &port);
9195
PluginDecodeResult DecodeObfs(ParsedUri const &uri, ProxyPlugin &plugin, std::string_view tcpNext,
92-
std::string_view udpNext); // TODO: obfs
96+
std::string_view udpNext);
9397
PluginDecodeResult DecodeTls(ParsedUri const &uri, ProxyPlugin &plugin, std::string_view tcpNext,
9498
std::string_view udpNext);
9599
PluginDecodeResult DecodeUdp(ParsedUri const &uri);

0 commit comments

Comments
 (0)