Files
Xboard/app/Protocols/Surfboard.php
yootus ec49ba3fd1 Loon和Surfboard适配anytls (#854)
* Loon适配anytls

* Surfboard适配anytls

Surfboard适配anytls
2026-04-02 15:47:41 +08:00

230 lines
8.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Protocols;
use App\Utils\Helper;
use Illuminate\Support\Facades\File;
use App\Support\AbstractProtocol;
use App\Models\Server;
class Surfboard extends AbstractProtocol
{
public $flags = ['surfboard'];
public $allowedProtocols = [
Server::TYPE_SHADOWSOCKS,
Server::TYPE_VMESS,
Server::TYPE_TROJAN,
Server::TYPE_ANYTLS,
];
const CUSTOM_TEMPLATE_FILE = 'resources/rules/custom.surfboard.conf';
const DEFAULT_TEMPLATE_FILE = 'resources/rules/default.surfboard.conf';
public function handle()
{
$servers = $this->servers;
$user = $this->user;
$appName = admin_setting('app_name', 'XBoard');
$proxies = '';
$proxyGroup = '';
foreach ($servers as $item) {
if (
$item['type'] === Server::TYPE_SHADOWSOCKS
&& in_array(data_get($item, 'protocol_settings.cipher'), [
'aes-128-gcm',
'aes-192-gcm',
'aes-256-gcm',
'chacha20-ietf-poly1305',
'2022-blake3-aes-128-gcm',
'2022-blake3-aes-256-gcm',
'2022-blake3-chacha20-poly1305'
])
) {
// [Proxy]
$proxies .= self::buildShadowsocks($item['password'], $item);
// [Proxy Group]
$proxyGroup .= $item['name'] . ', ';
}
if ($item['type'] === Server::TYPE_VMESS) {
// [Proxy]
$proxies .= self::buildVmess($item['password'], $item);
// [Proxy Group]
$proxyGroup .= $item['name'] . ', ';
}
if ($item['type'] === Server::TYPE_TROJAN) {
// [Proxy]
$proxies .= self::buildTrojan($item['password'], $item);
// [Proxy Group]
$proxyGroup .= $item['name'] . ', ';
}
if ($item['type'] === Server::TYPE_ANYTLS) {
$proxies .= self::buildAnyTLS($item['password'], $item);
$proxyGroup .= $item['name'] . ', ';
}
}
$config = subscribe_template('surfboard');
// Subscription link
$subsURL = Helper::getSubscribeUrl($user['token']);
$subsDomain = request()->header('Host');
$config = str_replace('$subs_link', $subsURL, $config);
$config = str_replace('$subs_domain', $subsDomain, $config);
$config = str_replace('$proxies', $proxies, $config);
$config = str_replace('$proxy_group', rtrim($proxyGroup, ', '), $config);
$upload = round($user['u'] / (1024 * 1024 * 1024), 2);
$download = round($user['d'] / (1024 * 1024 * 1024), 2);
$useTraffic = $upload + $download;
$totalTraffic = round($user['transfer_enable'] / (1024 * 1024 * 1024), 2);
$unusedTraffic = $totalTraffic - $useTraffic;
$expireDate = $user['expired_at'] === NULL ? '长期有效' : date('Y-m-d H:i:s', $user['expired_at']);
$subscribeInfo = "title={$appName}订阅信息, content=上传流量:{$upload}GB\\n下载流量{$download}GB\\n剩余流量{$unusedTraffic}GB\\n套餐流量{$totalTraffic}GB\\n到期时间{$expireDate}";
$config = str_replace('$subscribe_info', $subscribeInfo, $config);
return response($config, 200)
->header('content-disposition', "attachment;filename*=UTF-8''" . rawurlencode($appName) . ".conf");
}
public static function buildShadowsocks($password, $server)
{
$protocol_settings = $server['protocol_settings'];
$config = [
"{$server['name']}=ss",
"{$server['host']}",
"{$server['port']}",
"encrypt-method=" . data_get($protocol_settings, 'cipher'),
"password={$password}",
'tfo=true',
'udp-relay=true'
];
if (data_get($protocol_settings, 'plugin') && data_get($protocol_settings, 'plugin_opts')) {
$plugin = data_get($protocol_settings, 'plugin');
$pluginOpts = data_get($protocol_settings, 'plugin_opts', '');
// 解析插件选项
$parsedOpts = collect(explode(';', $pluginOpts))
->filter()
->mapWithKeys(function ($pair) {
if (!str_contains($pair, '=')) {
return [];
}
[$key, $value] = explode('=', $pair, 2);
return [trim($key) => trim($value)];
})
->all();
switch ($plugin) {
case 'obfs':
$config[] = "obfs={$parsedOpts['obfs']}";
if (isset($parsedOpts['obfs-host'])) {
$config[] = "obfs-host={$parsedOpts['obfs-host']}";
}
if (isset($parsedOpts['path'])) {
$config[] = "obfs-uri={$parsedOpts['path']}";
}
break;
}
}
$config = array_filter($config);
$uri = implode(',', $config);
$uri .= "\r\n";
return $uri;
}
public static function buildVmess($uuid, $server)
{
$protocol_settings = $server['protocol_settings'];
$config = [
"{$server['name']}=vmess",
"{$server['host']}",
"{$server['port']}",
"username={$uuid}",
"vmess-aead=true",
'tfo=true',
'udp-relay=true'
];
if (data_get($protocol_settings, 'tls')) {
array_push($config, 'tls=true');
if (data_get($protocol_settings, 'tls_settings')) {
$tlsSettings = data_get($protocol_settings, 'tls_settings');
if (data_get($tlsSettings, 'allow_insecure')) {
array_push($config, 'skip-cert-verify=' . ($tlsSettings['allow_insecure'] ? 'true' : 'false'));
}
if ($sni = data_get($tlsSettings, 'server_name')) {
array_push($config, "sni={$sni}");
}
}
}
if (data_get($protocol_settings, 'network') === 'ws') {
array_push($config, 'ws=true');
if (data_get($protocol_settings, 'network_settings')) {
$wsSettings = data_get($protocol_settings, 'network_settings');
if (isset($wsSettings['path']) && !empty($wsSettings['path']))
array_push($config, "ws-path={$wsSettings['path']}");
if (isset($wsSettings['headers']['Host']) && !empty($wsSettings['headers']['Host']))
array_push($config, "ws-headers=Host:{$wsSettings['headers']['Host']}");
}
}
$uri = implode(',', $config);
$uri .= "\r\n";
return $uri;
}
public static function buildTrojan($password, $server)
{
$protocol_settings = $server['protocol_settings'];
$config = [
"{$server['name']}=trojan",
"{$server['host']}",
"{$server['port']}",
"password={$password}",
data_get($protocol_settings, 'server_name') ? "sni=" . data_get($protocol_settings, 'server_name') : "",
'tfo=true',
'udp-relay=true'
];
if (data_get($protocol_settings, 'allow_insecure')) {
array_push($config, !!data_get($protocol_settings, 'allow_insecure') ? 'skip-cert-verify=true' : 'skip-cert-verify=false');
}
$config = array_filter($config);
$uri = implode(',', $config);
$uri .= "\r\n";
return $uri;
}
public static function buildAnyTLS($password, $server)
{
$protocol_settings = data_get($server, 'protocol_settings', []);
$config = [
"{$server['name']}=anytls",
"{$server['host']}",
"{$server['port']}",
"password={$password}",
"tfo=true",
"udp-relay=true"
];
// SNI
if ($serverName = data_get($protocol_settings, 'tls.server_name')) {
$config[] = "sni={$serverName}";
}
// 跳过证书校验
if (data_get($protocol_settings, 'tls.allow_insecure')) {
$config[] = "skip-cert-verify=true";
}
$config = array_filter($config);
return implode(',', $config) . "\r\n";
}
}