diff --git a/app/Http/Controllers/V1/Admin/Server/HysteriaController.php b/app/Http/Controllers/V1/Admin/Server/HysteriaController.php index a77aaba0..bc45a48c 100644 --- a/app/Http/Controllers/V1/Admin/Server/HysteriaController.php +++ b/app/Http/Controllers/V1/Admin/Server/HysteriaController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\V1\Admin\Server; use App\Http\Controllers\Controller; use App\Models\ServerHysteria; +use App\Utils\Helper; use Illuminate\Http\Request; class HysteriaController extends Controller @@ -24,10 +25,18 @@ class HysteriaController extends Controller 'rate' => 'required|numeric', 'up_mbps' => 'required|numeric|min:1', 'down_mbps' => 'required|numeric|min:1', + 'obfs' => 'nullable', + 'obfs_password' => 'nullable', 'server_name' => 'nullable', 'insecure' => 'required|in:0,1' ]); + if(isset($params['obfs'])) { + if(!isset($params['obfs_password'])) $params['obfs_password'] = Helper::getServerKey($request->input('created_at'), 16); + } else { + $params['obfs_password'] = null; + } + if ($request->input('id')) { $server = ServerHysteria::find($request->input('id')); if (!$server) { diff --git a/app/Http/Controllers/V1/Server/UniProxyController.php b/app/Http/Controllers/V1/Server/UniProxyController.php index 4ed98138..031a9e50 100644 --- a/app/Http/Controllers/V1/Server/UniProxyController.php +++ b/app/Http/Controllers/V1/Server/UniProxyController.php @@ -28,6 +28,7 @@ class UniProxyController extends Controller } $this->nodeType = $request->input('node_type'); if ($this->nodeType === 'v2ray') $this->nodeType = 'vmess'; + if ($this->nodeType === 'hysteria2') $this->nodeType = 'hysteria'; $this->nodeId = $request->input('node_id'); $this->serverService = new ServerService(); $this->nodeInfo = $this->serverService->getServer($this->nodeId, $this->nodeType); @@ -118,9 +119,14 @@ class UniProxyController extends Controller 'server_port' => $this->nodeInfo->server_port, 'server_name' => $this->nodeInfo->server_name, 'up_mbps' => $this->nodeInfo->up_mbps, - 'down_mbps' => $this->nodeInfo->down_mbps, - 'obfs' => Helper::getServerKey($this->nodeInfo->created_at, 16) + 'down_mbps' => $this->nodeInfo->down_mbps ]; + if ($this->nodeInfo->version == 1) { + $response['obfs'] = $this->nodeInfo->obfs_password ?? null; + } elseif ($this->nodeInfo->version == 2) { + $response['obfs'] = $this->nodeInfo->obfs ?? null; + $response['obfs-password'] = $this->nodeInfo->obfs_password ?? null; + } break; } $response['base_config'] = [ diff --git a/app/Protocols/ClashMeta.php b/app/Protocols/ClashMeta.php index f0655ccb..4ce9dab9 100644 --- a/app/Protocols/ClashMeta.php +++ b/app/Protocols/ClashMeta.php @@ -52,6 +52,10 @@ class ClashMeta array_push($proxy, self::buildTrojan($user['uuid'], $item)); array_push($proxies, $item['name']); } + if ($item['type'] === 'hysteria') { + array_push($proxy, self::buildHysteria($user['uuid'], $item)); + array_push($proxies, $item['name']); + } } $config['proxies'] = array_merge($config['proxies'] ? $config['proxies'] : [], $proxy); @@ -244,6 +248,38 @@ class ClashMeta return $array; } + public static function buildHysteria($password, $server) + { + $array = []; + $array['name'] = $server['name']; + $array['server'] = $server['host']; + $array['port'] = $server['port']; + $array['udp'] = true; + //Todo:完善客户端上下行 + //$array['up'] = $server['up_mbps']; + //$array['down'] = $server['down_mbps']; + + if (isset($server['server_name'])) $array['sni'] = $server['server_name']; + + if ($server['version'] === 2) { + $array['type'] = 'hysteria2'; + $array['password'] = $password; + if (isset($server['obfs'])){ + $array['obfs'] = $server['obfs']; + $array['obfs-password'] = $server['obfs_password']; + } + } else { + $array['type'] = 'hysteria'; + $array['auth_str'] = $password; + if (isset($server['obfs']) && isset($server['obfs_password'])){ + $array['obfs'] = $server['obfs_password']; + } + $array['protocol'] = 'udp'; + } + + return $array; + } + private function isMatch($exp, $str) { return @preg_match($exp, $str); diff --git a/app/Protocols/General.php b/app/Protocols/General.php index 3278b414..369e88db 100644 --- a/app/Protocols/General.php +++ b/app/Protocols/General.php @@ -36,6 +36,9 @@ class General if ($item['type'] === 'trojan') { $uri .= self::buildTrojan($user['uuid'], $item); } + if ($item['type'] === 'hysteria') { + $uri .= self::buildHysteria($user['uuid'], $item); + } } return base64_encode($uri); } @@ -147,7 +150,7 @@ class General if (isset($kcpSettings['header']['type'])) $config['headerType'] = $kcpSettings['header']['type']; if (isset($kcpSettings['seed'])) $config['path'] = Helper::encodeURIComponent($kcpSettings['seed']); $output .= "&headerType={$config['headerType']}" . "&seed={$config['path']}"; - } + } if ((string)$server['network'] === 'ws') { $wsSettings = $server['network_settings']; if (isset($wsSettings['path'])) $config['path'] = Helper::encodeURIComponent($wsSettings['path']); @@ -164,11 +167,11 @@ class General $quicSettings = $server['network_settings']; if (isset($quicSettings['security'])) $config['quicSecurity'] = $quicSettings['security']; if (isset($quicSettings['header']['type'])) $config['headerType'] = $quicSettings['header']['type']; - + $output .= "&quicSecurity={$config['quicSecurity']}" . "&headerType={$config['headerType']}"; - + if ((string)$quicSettings['security'] !== 'none' && isset($quicSettings['key'])) $config['path'] = Helper::encodeURIComponent($quicSettings['key']); - + $output .= "&key={$config['path']}"; } if ((string)$server['network'] === 'grpc') { @@ -195,4 +198,33 @@ class General return $uri; } + public static function buildHysteria($password, $server) + { + $remote = filter_var($server['host'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) ? '[' . $server['host'] . ']' : $server['host']; + $name = Helper::encodeURIComponent($server['name']); + + if ($server['version'] == 2) { + $uri = "hysteria2://{$password}@{$remote}:{$server['port']}/?insecure={$server['insecure']}&sni={$server['server_name']}"; + if (isset($server['obfs']) && isset($server['obfs_password'])) { + $uri .= "&obfs={$server['obfs']}&obfs-password={$server['obfs_password']}"; + } + } else { + $uri = "hysteria://{$remote}:{$server['port']}/?"; + $query = http_build_query([ + 'protocol' => 'udp', + 'auth' => $password, + 'insecure' => $server['insecure'], + 'peer' => $server['server_name'] + //'upmbps' => $server['up_mbps'], + //'downmbps' => $server['up_mbps'] + ]); + $uri .= $query; + if (isset($server['obfs']) && isset($server['obfs_password'])) { + $uri .= "&obfs={$server['obfs']}&obfsParam{$server['obfs_password']}"; + } + } + $uri .= "#{$name}\r\n"; + return $uri; + } + } diff --git a/database/install.sql b/database/install.sql index 139d6da6..59f25813 100644 --- a/database/install.sql +++ b/database/install.sql @@ -229,6 +229,8 @@ CREATE TABLE `v2_server_hysteria` ( `sort` int(11) DEFAULT NULL, `up_mbps` int(11) NOT NULL, `down_mbps` int(11) NOT NULL, + `obfs` varchar(64) DEFAULT NULL, + `obfs_password` varchar(255) DEFAULT NULL, `server_name` varchar(64) DEFAULT NULL, `insecure` tinyint(1) NOT NULL DEFAULT '0', `created_at` int(11) NOT NULL, diff --git a/database/update.sql b/database/update.sql index a3c0ae85..fa834057 100644 --- a/database/update.sql +++ b/database/update.sql @@ -716,6 +716,10 @@ ALTER TABLE `v2_server_vless` ALTER TABLE `v2_server_hysteria` ADD `version` int(11) NOT NULL AFTER `id`; +ALTER TABLE `v2_server_hysteria` + ADD `obfs` varchar(64) NULL AFTER `down_mbps`, + ADD `obfs_password` varchar(255) NULL AFTER `obfs`; + UPDATE v2_server_vless SET tls_settings = REPLACE(tls_settings, 'shortId', 'short_id'); diff --git a/public/assets/admin/umi.js b/public/assets/admin/umi.js index 2114123f..77601eb9 100644 --- a/public/assets/admin/umi.js +++ b/public/assets/admin/umi.js @@ -102059,7 +102059,7 @@ ))), y.a.createElement("div", { className: "row" }, y.a.createElement("div", { - className: "form-group col-md-12 col-xs-12" + className: "form-group col-md-3 col-xs-12" }, y.a.createElement("label", null, "HYSTERIA\u7248\u672c"), y.a.createElement(N["a"], { value: parseInt(e.version), style: { @@ -102125,20 +102125,64 @@ value: e.server_name, onChange: e=>this.formChange("server_name", e.target.value) })), y.a.createElement("div", { - className: "form-group" + className: "row" + }, (parseInt(e.version) == 1) && y.a.createElement("div", { + className: "form-group col-md-6 col-xs-12" + }, y.a.createElement("label", null, "\u6df7\u6dc6\u65b9\u5f0fobfs"), y.a.createElement(N["a"], { + value: e.obfs, + style: { + width: "100%" + }, + onChange: e=>this.formChange("obfs", e) + }, y.a.createElement(N["a"].Option, { + key: 0, + value: null + }, "\u65e0"), y.a.createElement(N["a"].Option, { + key: 1, + value: "xplus" + }, "xplus"))), (parseInt(e.version) == 1 && e.obfs === "xplus") && y.a.createElement("div", { + className: "form-group col-md-6 col-xs-12" + }, y.a.createElement("label", null, "\u6df7\u6dc6\u5bc6\u7801obfsParam"), y.a.createElement(s["a"], { + value: e.obfs_password, + placeholder: "\u7559\u7a7a\u81ea\u52a8\u751f\u6210", + onChange: e=>this.formChange("obfs_password", e.target.value) + })), (parseInt(e.version) == 2) && y.a.createElement("div", { + className: "form-group col-md-6 col-xs-12" + }, y.a.createElement("label", null, "\u6df7\u6dc6\u65b9\u5f0fobfs"), y.a.createElement(N["a"], { + value: e.obfs, + style: { + width: "100%" + }, + onChange: e=>this.formChange("obfs", e) + }, y.a.createElement(N["a"].Option, { + key: 0, + value: null + }, "\u65e0"), y.a.createElement(N["a"].Option, { + key: 1, + value: "salamander" + }, "salamander"))), (parseInt(e.version) == 2 && e.obfs === "salamander") && y.a.createElement("div", { + className: "form-group col-md-6 col-xs-12" + }, y.a.createElement("label", null, "\u6df7\u6dc6\u5bc6\u7801obfs_password"), y.a.createElement(s["a"], { + value: e.obfs_password, + placeholder: "\u7559\u7a7a\u81ea\u52a8\u751f\u6210", + onChange: e=>this.formChange("obfs_password", e.target.value) + }))), y.a.createElement("div", { + className: "row" + }, y.a.createElement("div", { + className: "form-group col-md-6 col-xs-12" }, y.a.createElement("label", null, "\u4e0a\u884c\u5e26\u5bbd"), y.a.createElement(s["a"], { addonAfter: "Mbps", placeholder: "\u8bf7\u6839\u636e\u5b9e\u9645\u7f51\u7edc\u60c5\u51b5\u5c3d\u91cf\u51c6\u786e\u5730\u586b\u5199", value: e.up_mbps, onChange: e=>this.formChange("up_mbps", e.target.value) })), y.a.createElement("div", { - className: "form-group" + className: "form-group col-md-6 col-xs-12" }, y.a.createElement("label", null, "\u4e0b\u884c\u5e26\u5bbd"), y.a.createElement(s["a"], { addonAfter: "Mbps", placeholder: "\u8bf7\u6839\u636e\u5b9e\u9645\u7f51\u7edc\u60c5\u51b5\u5c3d\u91cf\u51c6\u786e\u5730\u586b\u5199", value: e.down_mbps, onChange: e=>this.formChange("down_mbps", e.target.value) - })), y.a.createElement("div", { + }))), y.a.createElement("div", { className: "form-group" }, y.a.createElement("label", null, y.a.createElement(u["a"], { placement: "top" @@ -102155,7 +102199,7 @@ }, y.a.createElement(N["a"].Option, { value: "" }, "\u65e0"), n.map(t=>{ - if ("trojan" === t.type && t.id !== e.id) + if ("hysteria" === t.type && t.id !== e.id) return y.a.createElement(N["a"].Option, { key: Math.random(), value: t.id