2023-11-17 01:44:01 -05:00
|
|
|
<?php
|
|
|
|
namespace App\Protocols;
|
|
|
|
|
|
|
|
use App\Utils\Helper;
|
2025-01-06 12:20:11 -05:00
|
|
|
use App\Contracts\ProtocolInterface;
|
2023-11-17 01:44:01 -05:00
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
class SingBox implements ProtocolInterface
|
2023-11-17 01:44:01 -05:00
|
|
|
{
|
2025-01-06 12:20:11 -05:00
|
|
|
public $flags = ['sing-box', 'hiddify'];
|
2023-11-17 01:44:01 -05:00
|
|
|
private $servers;
|
|
|
|
private $user;
|
2024-05-14 09:57:36 -04:00
|
|
|
private $config;
|
2023-11-17 01:44:01 -05:00
|
|
|
|
|
|
|
public function __construct($user, $servers, array $options = null)
|
|
|
|
{
|
|
|
|
$this->user = $user;
|
|
|
|
$this->servers = $servers;
|
|
|
|
}
|
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
public function getFlags(): array
|
|
|
|
{
|
|
|
|
return $this->flags;
|
|
|
|
}
|
|
|
|
|
2023-11-17 01:44:01 -05:00
|
|
|
public function handle()
|
|
|
|
{
|
2024-05-14 09:57:36 -04:00
|
|
|
$appName = admin_setting('app_name', 'XBoard');
|
|
|
|
$this->config = $this->loadConfig();
|
|
|
|
$this->buildOutbounds();
|
2024-07-18 18:29:35 -04:00
|
|
|
$this->buildRule();
|
2023-12-03 09:17:33 -05:00
|
|
|
$user = $this->user;
|
2023-11-17 01:44:01 -05:00
|
|
|
|
2024-07-18 18:29:35 -04:00
|
|
|
return response()
|
|
|
|
->json($this->config)
|
2025-01-06 12:20:11 -05:00
|
|
|
->header('profile-title', 'base64:' . base64_encode($appName))
|
2023-12-03 09:17:33 -05:00
|
|
|
->header('subscription-userinfo', "upload={$user['u']}; download={$user['d']}; total={$user['transfer_enable']}; expire={$user['expired_at']}")
|
2024-07-18 18:29:35 -04:00
|
|
|
->header('profile-update-interval', '24');
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function loadConfig()
|
|
|
|
{
|
|
|
|
$defaultConfig = base_path('resources/rules/default.sing-box.json');
|
|
|
|
$customConfig = base_path('resources/rules/custom.sing-box.json');
|
|
|
|
$jsonData = file_exists($customConfig) ? file_get_contents($customConfig) : file_get_contents($defaultConfig);
|
|
|
|
|
|
|
|
return json_decode($jsonData, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildOutbounds()
|
|
|
|
{
|
2024-05-14 09:57:36 -04:00
|
|
|
$outbounds = $this->config['outbounds'];
|
|
|
|
$proxies = [];
|
2023-11-17 01:44:01 -05:00
|
|
|
foreach ($this->servers as $item) {
|
|
|
|
if ($item['type'] === 'shadowsocks') {
|
2024-05-24 10:45:27 -04:00
|
|
|
$ssConfig = $this->buildShadowsocks($item['password'], $item);
|
2024-05-14 09:57:36 -04:00
|
|
|
$proxies[] = $ssConfig;
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
if ($item['type'] === 'trojan') {
|
|
|
|
$trojanConfig = $this->buildTrojan($this->user['uuid'], $item);
|
2024-05-14 09:57:36 -04:00
|
|
|
$proxies[] = $trojanConfig;
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
if ($item['type'] === 'vmess') {
|
|
|
|
$vmessConfig = $this->buildVmess($this->user['uuid'], $item);
|
2024-05-14 09:57:36 -04:00
|
|
|
$proxies[] = $vmessConfig;
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
if ($item['type'] === 'vless') {
|
|
|
|
$vlessConfig = $this->buildVless($this->user['uuid'], $item);
|
2024-05-14 09:57:36 -04:00
|
|
|
$proxies[] = $vlessConfig;
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
if ($item['type'] === 'hysteria') {
|
|
|
|
$hysteriaConfig = $this->buildHysteria($this->user['uuid'], $item, $this->user);
|
2024-05-14 09:57:36 -04:00
|
|
|
$proxies[] = $hysteriaConfig;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($outbounds as &$outbound) {
|
|
|
|
if (in_array($outbound['type'], ['urltest', 'selector'])) {
|
|
|
|
array_push($outbound['outbounds'], ...array_column($proxies, 'tag'));
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-14 09:57:36 -04:00
|
|
|
$outbounds = array_merge($outbounds, $proxies);
|
|
|
|
$this->config['outbounds'] = $outbounds;
|
2023-11-17 01:44:01 -05:00
|
|
|
return $outbounds;
|
|
|
|
}
|
|
|
|
|
2024-07-18 18:29:35 -04:00
|
|
|
/**
|
|
|
|
* Build rule
|
|
|
|
*/
|
2025-01-06 12:20:11 -05:00
|
|
|
protected function buildRule()
|
|
|
|
{
|
2024-07-18 18:29:35 -04:00
|
|
|
$rules = $this->config['route']['rules'];
|
|
|
|
// Force the nodes ip to be a direct rule
|
|
|
|
array_unshift($rules, [
|
2025-01-06 12:20:11 -05:00
|
|
|
'ip_cidr' => collect($this->servers)->pluck('host')->map(function ($host) {
|
2024-07-18 18:29:35 -04:00
|
|
|
return filter_var($host, FILTER_VALIDATE_IP) ? [$host] : Helper::getIpByDomainName($host);
|
|
|
|
})->flatten()->unique()->values(),
|
|
|
|
'outbound' => 'direct',
|
|
|
|
]);
|
|
|
|
$this->config['route']['rules'] = $rules;
|
|
|
|
}
|
|
|
|
|
2023-11-17 01:44:01 -05:00
|
|
|
protected function buildShadowsocks($password, $server)
|
|
|
|
{
|
|
|
|
$array = [];
|
|
|
|
$array['tag'] = $server['name'];
|
|
|
|
$array['type'] = 'shadowsocks';
|
|
|
|
$array['server'] = $server['host'];
|
|
|
|
$array['server_port'] = $server['port'];
|
2025-01-06 12:20:11 -05:00
|
|
|
$array['method'] = data_get($server, 'protocol_settings.cipher');
|
2023-11-17 01:44:01 -05:00
|
|
|
$array['password'] = $password;
|
|
|
|
|
|
|
|
return $array;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected function buildVmess($uuid, $server)
|
|
|
|
{
|
2025-01-06 12:20:11 -05:00
|
|
|
$protocol_settings = $server['protocol_settings'];
|
|
|
|
$array = [
|
|
|
|
'tag' => $server['name'],
|
|
|
|
'type' => 'vmess',
|
|
|
|
'server' => $server['host'],
|
|
|
|
'server_port' => $server['port'],
|
|
|
|
'uuid' => $uuid,
|
|
|
|
'security' => 'auto',
|
|
|
|
'alter_id' => 0,
|
|
|
|
'transport' => [],
|
|
|
|
'tls' => $protocol_settings['tls'] ? [
|
|
|
|
'enabled' => true,
|
|
|
|
'insecure' => data_get($protocol_settings, 'tls_settings.allow_insecure'),
|
|
|
|
'server_name' => data_get($protocol_settings, 'tls_settings.server_name')
|
|
|
|
] : null
|
|
|
|
];
|
2023-11-17 01:44:01 -05:00
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
$transport = match ($protocol_settings['network']) {
|
|
|
|
'tcp' => [
|
|
|
|
'type' => 'http',
|
|
|
|
'path' => \Arr::random(data_get($protocol_settings, 'network_settings.header.request.path', []))
|
|
|
|
],
|
|
|
|
'ws' => [
|
|
|
|
'type' => 'ws',
|
|
|
|
'path' => data_get($protocol_settings, 'network_settings.path'),
|
|
|
|
'headers' => data_get($protocol_settings, 'network_settings.headers.Host') ? ['Host' => data_get($protocol_settings, 'network_settings.headers.Host')] : null,
|
|
|
|
'max_early_data' => 2048,
|
|
|
|
'early_data_header_name' => 'Sec-WebSocket-Protocol'
|
|
|
|
],
|
|
|
|
'grpc' => [
|
|
|
|
'type' => 'grpc',
|
|
|
|
'service_name' => data_get($protocol_settings, 'network_settings.serviceName')
|
|
|
|
],
|
|
|
|
default => null
|
|
|
|
};
|
2023-11-17 01:44:01 -05:00
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
if ($transport) {
|
|
|
|
$array['transport'] = array_filter($transport, fn($value) => !is_null($value));
|
|
|
|
}
|
2023-11-17 01:44:01 -05:00
|
|
|
return $array;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected function buildVless($password, $server)
|
|
|
|
{
|
2025-01-06 12:20:11 -05:00
|
|
|
$protocol_settings = data_get($server, 'protocol_settings', []);
|
2023-11-17 01:44:01 -05:00
|
|
|
$array = [
|
|
|
|
"type" => "vless",
|
|
|
|
"tag" => $server['name'],
|
|
|
|
"server" => $server['host'],
|
|
|
|
"server_port" => $server['port'],
|
|
|
|
"uuid" => $password,
|
|
|
|
"packet_encoding" => "xudp"
|
|
|
|
];
|
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
if ($protocol_settings['tls']) {
|
|
|
|
$tlsConfig = [
|
|
|
|
'enabled' => true,
|
|
|
|
'flow' => data_get($protocol_settings, 'flow', ''),
|
|
|
|
'insecure' => data_get($protocol_settings, 'tls_settings.allow_insecure'),
|
|
|
|
'server_name' => data_get($protocol_settings, 'tls_settings.server_name'),
|
|
|
|
'utls' => [
|
|
|
|
'enabled' => true,
|
|
|
|
'fingerprint' => Helper::getRandFingerprint()
|
|
|
|
]
|
|
|
|
];
|
|
|
|
if ($protocol_settings['tls'] == 2) {
|
|
|
|
$tlsConfig['reality'] = [
|
|
|
|
'enabled' => true,
|
|
|
|
'public_key' => data_get($protocol_settings, 'reality_settings.public_key'),
|
|
|
|
'short_id' => data_get($protocol_settings, 'reality_settings.short_id')
|
2023-11-17 01:44:01 -05:00
|
|
|
];
|
|
|
|
}
|
2025-01-06 12:20:11 -05:00
|
|
|
|
2023-11-17 01:44:01 -05:00
|
|
|
$array['tls'] = $tlsConfig;
|
|
|
|
}
|
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
$transport = match ($protocol_settings['network']) {
|
|
|
|
'tcp' => data_get($protocol_settings, 'network_settings.header.type') == 'http' ? [
|
|
|
|
'type' => 'http',
|
|
|
|
'path' => data_get($protocol_settings, 'network_settings.header.request.path')
|
|
|
|
] : null,
|
|
|
|
'ws' => [
|
|
|
|
'type' => 'ws',
|
|
|
|
'path' => data_get($protocol_settings, 'network_settings.path'),
|
|
|
|
'headers' => data_get($protocol_settings, 'network_settings.headers.Host') ? ['Host' => [data_get($protocol_settings, 'network_settings.headers.Host')]] : null,
|
|
|
|
'max_early_data' => 2048,
|
|
|
|
'early_data_header_name' => 'Sec-WebSocket-Protocol'
|
|
|
|
],
|
|
|
|
'grpc' => [
|
|
|
|
'type' => 'grpc',
|
|
|
|
'service_name' => data_get($protocol_settings, 'network_settings.serviceName')
|
|
|
|
],
|
|
|
|
'h2' => [
|
|
|
|
'type' => 'http',
|
|
|
|
'host' => data_get($protocol_settings, 'network_settings.host') ? [data_get($protocol_settings, 'network_settings.host')] : null,
|
|
|
|
'path' => data_get($protocol_settings, 'network_settings.path')
|
|
|
|
],
|
|
|
|
default => null
|
|
|
|
};
|
|
|
|
|
|
|
|
if ($transport) {
|
|
|
|
$array['transport'] = array_filter($transport, fn($value) => !is_null($value));
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return $array;
|
|
|
|
}
|
|
|
|
|
2024-05-14 09:57:36 -04:00
|
|
|
protected function buildTrojan($password, $server)
|
2023-11-17 01:44:01 -05:00
|
|
|
{
|
2025-01-06 12:20:11 -05:00
|
|
|
$protocol_settings = $server['protocol_settings'];
|
|
|
|
$array = [
|
|
|
|
'tag' => $server['name'],
|
|
|
|
'type' => 'trojan',
|
|
|
|
'server' => $server['host'],
|
|
|
|
'server_port' => $server['port'],
|
|
|
|
'password' => $password,
|
|
|
|
'tls' => [
|
|
|
|
'enabled' => true,
|
|
|
|
'insecure' => data_get($protocol_settings, 'allow_insecure', false),
|
|
|
|
'server_name' => data_get($protocol_settings, 'server_name')
|
|
|
|
]
|
2023-11-17 01:44:01 -05:00
|
|
|
];
|
2025-01-06 12:20:11 -05:00
|
|
|
$transport = match (data_get($protocol_settings, 'network')) {
|
|
|
|
'grpc' => [
|
|
|
|
'type' => 'grpc',
|
|
|
|
'service_name' => data_get($protocol_settings, 'network_settings.serviceName')
|
|
|
|
],
|
|
|
|
'ws' => [
|
|
|
|
'type' => 'ws',
|
|
|
|
'path' => data_get($protocol_settings, 'network_settings.path'),
|
|
|
|
'headers' => data_get($protocol_settings, 'network_settings.headers.Host') ? ['Host' => [data_get($protocol_settings, 'network_settings.headers.Host')]] : null,
|
|
|
|
'max_early_data' => 2048,
|
|
|
|
'early_data_header_name' => 'Sec-WebSocket-Protocol'
|
|
|
|
],
|
|
|
|
default => null
|
|
|
|
};
|
|
|
|
$array['transport'] = $transport;
|
2023-11-17 01:44:01 -05:00
|
|
|
return $array;
|
|
|
|
}
|
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
protected function buildHysteria($password, $server, $user): array
|
2023-11-17 01:44:01 -05:00
|
|
|
{
|
2025-01-06 12:20:11 -05:00
|
|
|
$protocol_settings = $server['protocol_settings'];
|
|
|
|
$baseConfig = [
|
2023-11-17 01:44:01 -05:00
|
|
|
'server' => $server['host'],
|
|
|
|
'server_port' => $server['port'],
|
2025-01-06 12:20:11 -05:00
|
|
|
'tag' => $server['name'],
|
2023-11-17 01:44:01 -05:00
|
|
|
'tls' => [
|
|
|
|
'enabled' => true,
|
2025-01-06 12:20:11 -05:00
|
|
|
'insecure' => $protocol_settings['tls']['allow_insecure'],
|
|
|
|
'server_name' => $protocol_settings['tls']['server_name']
|
2023-11-17 01:44:01 -05:00
|
|
|
]
|
|
|
|
];
|
2025-01-06 12:20:11 -05:00
|
|
|
$speedConfig = [
|
|
|
|
'up_mbps' => $protocol_settings['bandwidth']['up'],
|
|
|
|
'down_mbps' => $protocol_settings['bandwidth']['down'],
|
|
|
|
];
|
|
|
|
$versionConfig = match ($server['version'] ?? 1) {
|
|
|
|
2 => [
|
|
|
|
'type' => 'hysteria2',
|
|
|
|
'password' => $password,
|
|
|
|
'obfs' => $protocol_settings['obfs']['open'] ? [
|
|
|
|
'type' => $protocol_settings['obfs']['type'],
|
|
|
|
'password' => $protocol_settings['obfs']['password']
|
|
|
|
] : null,
|
|
|
|
],
|
|
|
|
default => [
|
|
|
|
'type' => 'hysteria',
|
|
|
|
'auth_str' => $password,
|
|
|
|
'obfs' => $protocol_settings['obfs']['password'],
|
|
|
|
'disable_mtu_discovery' => true,
|
|
|
|
]
|
|
|
|
};
|
2023-11-17 01:44:01 -05:00
|
|
|
|
2025-01-06 12:20:11 -05:00
|
|
|
return array_merge(
|
|
|
|
$baseConfig,
|
|
|
|
$speedConfig,
|
|
|
|
$versionConfig
|
|
|
|
);
|
2023-11-17 01:44:01 -05:00
|
|
|
}
|
2024-11-08 04:26:31 -05:00
|
|
|
}
|