dd/app/Services/ServiceFnPoolV2rayTo.php
2026-08-12 10:56:55 +08:00

618 lines
20 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
declare(strict_types=1);
namespace App\Services;
class ServiceFnPoolV2rayTo
{
public function singbox($cPoolV2ray)
{
$i = 0;
foreach ($cPoolV2ray as $aPoolV2ray) {
$i++;
// echo $i.":<br>";
if ($i <= 0) {
// continue;
}
if ($i >= 100) {
// continue;
}
$aPoolSingbox = $this->singbox_row($aPoolV2ray);
if (!$aPoolSingbox) continue; //测试用
t($aPoolV2ray);
t($aPoolSingbox);
echo "=======================================================";
}
exit;
}
public function singbox_row($aPoolV2ray)
{
$aNode = $aPoolV2ray;
if (isset($aPoolV2ray['protocolType'])) {
$aPoolV2ray['protocolType'] = $this->removeUtf8Bom($aPoolV2ray['protocolType']);
}
$sProtocol = $aPoolV2ray['protocolType'] ?? '';
if (isset($aPoolV2ray["config"]) && !is_array($aPoolV2ray["config"])) {
$aPoolV2ray["config"] = rawurldecode($aPoolV2ray["config"]);
}
$sModTo = 'mod_to_'.$sProtocol;
if (!method_exists($this, $sModTo)) {
return false; // 没做对应处理的直接排除
}
$aNode = $this->$sModTo($aPoolV2ray);
return $aNode;
}
private function mod_to_socks__($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$sPwd = $this->tomBase64Decode($sPwd);
$aTmp = explode(":", $sPwd, 2);
$sUser = $aTmp[0] ?? '';
$sPwd = $aTmp[1] ?? '';
$aTmp = explode(":", $sConfig, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = $aTmp[1] ?? '';
$aNode = [
"type" => "socks",
"tag" => $aV2ray['country'] ?? '',
"server"=> $sAddr,
"server_port" => (int) $iPort,
"username" => $sUser,
"password" => $sPwd
];
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_trojan__($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode("?", $sConfig, 2);
$sAddrFull = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode(":", $sAddrFull, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = $aTmp[1] ?? '';
$aNode = [
"type" => "trojan",
"tag" => $aV2ray['country'] ?? '',
"server"=> $sAddr,
"server_port" => (int) $iPort,
"password" => $sPwd
];
$aCustomYuan = explode("&", $sConfig);
$aCustom = [];
foreach ($aCustomYuan as $sRow) {
$aRow = explode("=", $sRow, 2);
$k = $aRow[0] ?? '';
$v = $aRow[1] ?? '';
$aCustom[$k] = $v;
}
$aTls = []; // 层1的tls
$aUtls = []; // 层2的utls
if (isset($aCustom["sni"])) { // 写层1的tls
$aTls["server_name"] = $this->parseServerName($aCustom["sni"], $aCustom["sni"]);
}
if (isset($aCustom["host"])) { // 写层1的tls -- host覆盖sni=host优先
$aTls["server_name"] = $this->parseServerName($aCustom["host"], $aCustom["host"]);
}
if (isset($aCustom["alpn"])) { // 写层1的tls
$aTls["alpn"] = explode(",", $aCustom["alpn"]);
}
if (isset($aCustom["allowInsecure"])) { // 写层1的tls
$aTls["insecure"] = (bool) $aCustom["allowInsecure"];
}
if (isset($aCustom["fp"])) { // 写层2的utls
$aUtls["fingerprint"] = $aCustom["fp"];
}
if (!empty($aUtls)) { // 层2的utls并入tls
$aUtls["enabled"] = true;
$aTls["utls"] = $aUtls;
}
if (!empty($aTls)) { // 层1的tlstls并入node
$aTls["enabled"] = true;
$aNode["tls"] = $aTls;
}
$aTransport = [];
if ($aCustom["type"] == "ws" && isset($aTls["server_name"])) {
$aTransport["headers"]["Host"] = $aTls["server_name"];
}
// if (isset($aCustom["type"]) && $aCustom["type"] != "tcp" && $aCustom["type"] != "none" && $aCustom["type"] != "raw") {
// $aTransport["type"] = $aCustom["type"];
// if ($aCustom["type"] == "ws" && isset($aTls["server_name"])) {
// $aTransport["headers"]["Host"] = $aTls["server_name"];
// }
// }
if (isset($aCustom["path"])) {
$aTransport["path"] = rawurldecode($aCustom["path"]);
}
if (!empty($aTransport)) {
if (isset($aCustom["type"]) && $aCustom["type"] != "none" && $aCustom["type"] != "raw") {
$aTransport["type"] = $aCustom["type"];
}
// if (empty($aTransport["type"])) {
// $aTransport["type"] = "tcp";
// }
$aNode["transport"] = $aTransport;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_hysteria2__($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode("?", $sConfig, 2);
$sAddrFull = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$aTmp = explode(":", $sAddrFull, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = $aTmp[1] ?? '';
$cCustom = explode("&", $sConfig);
$aCustom = [];
foreach ($cCustom as $sCustom) {
$aTmp = explode("=", $sCustom);
$k = $aTmp[0] ?? '';
$v = $aTmp[1] ?? '';
$aCustom[$k] = $v;
}
$aNode = [
"type" => "hysteria2",
"tag" => $aV2ray['country'] ?? '',
"server"=> $sAddr,
"server_port" => (int) $iPort,
"password" => $sPwd
];
$aTls = [];
if (isset($aCustom['sni'])) {
$aTls['server_name'] = $this->parseServerName($aCustom['sni'], $aCustom['sni']);
}
if (isset($aCustom['insecure'])) {
$aTls['insecure'] = (bool) $aCustom['insecure'] ?? 0;
}
if (!empty($aTls)) {
$aTls['enabled'] = true;
$aNode["tls"] = $aTls;
}
$aObfs = [];
if (isset($aCustom["obfs"])) {
$aObfs["type"] = $aCustom["obfs"];
}
if (isset($aCustom["obfs-password"])) {
$aObfs["password"] = $aCustom["obfs-password"];
}
if (!empty($aObfs)) {
$aNode["obfs"] = $aObfs;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_ss__($aV2ray)
{
$sConfig = $aV2ray["config"] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? '';
$sConfig = $aTmp[1] ?? '';
$sPwd = $this->tomBase64Decode($sPwd);
$aTmp = explode(":", $sPwd, 2);
$sPwdType = $aTmp[0] ?? '';
$sPwd = $aTmp[1] ?? '';
$aTmp = explode(":", $sConfig, 2);
$sAddr = $aTmp[0] ?? '';
$iPort = $aTmp[1] ?? '';
// 推荐的现代化 SS 算法白名单
$aAllowedCiphers = [
'2022-blake3-aes-128-gcm',
'2022-blake3-aes-256-gcm',
'2022-blake3-chacha20-poly1305',
'aes-128-gcm',
'aes-192-gcm',
'aes-256-gcm',
'chacha20-ietf-poly1305',
'xchacha20-ietf-poly1305',
];
if (!in_array($sPwdType, $aAllowedCiphers, true)) {
return false;
}
$aNode = [
"type" => "shadowsocks",
"tag" => $aV2ray['country'] ?? '',
"server" => $sAddr,
"server_port" => (int) $iPort,
"method" => $sPwdType,
"password" => $sPwd,
];
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function mod_to_vmess(array $aV2ray): array
{
// 1. 类型安全防御:确保 $aConfig 一定是数组
$aConfig = $aV2ray['config'] ?? [];
if (is_string($aConfig)) {
$aConfig = json_decode($aConfig, true) ?? [];
}
if (!is_array($aConfig)) {
$aConfig = [];
}
// 2. 基础节点属性映射
$aNode = [
'type' => 'vmess',
'tag' => (string) ($aV2ray['country'] ?? ''),
'server' => (string) ($aConfig['add'] ?? ''),
'server_port' => (int) ($aConfig['port'] ?? 0),
'uuid' => (string) ($aConfig['id'] ?? ''),
'security' => !empty($aConfig['scy']) ? (string) $aConfig['scy'] : 'auto',
'alter_id' => (int) ($aConfig['aid'] ?? 0),
];
// 3. 传输层 (Transport) 映射逻辑
$sV2rayNet = strtolower((string) ($aConfig['net'] ?? 'tcp'));
$sV2rayType = strtolower((string) ($aConfig['type'] ?? 'none'));
$sRawPath = (string) ($aConfig['path'] ?? '');
$sPath = !empty($sRawPath) ? rawurldecode($sRawPath) : '/';
$sHost = (string) ($aConfig['host'] ?? '');
$aTransport = [];
// 分支 ATCP + HTTP 伪装
if ($sV2rayNet === 'tcp' && $sV2rayType === 'http') {
$aTransport['type'] = 'http';
$aTransport['path'] = $sPath;
if (!empty($sHost)) {
$aTransport['host'] = array_map('trim', explode(',', $sHost));
}
}
// 分支 BWebSocket 传输
elseif ($sV2rayNet === 'ws') {
$aTransport['type'] = 'ws';
$aTransport['path'] = $sPath;
if (!empty($sHost)) {
$aTransport['headers'] = ['Host' => $sHost];
}
}
// 分支 CgRPC 传输
elseif ($sV2rayNet === 'grpc') {
$aTransport['type'] = 'grpc';
$aTransport['service_name'] = $sRawPath;
}
// 分支 D其他非 TCP 传输(如 mkcp, quic
elseif (!in_array($sV2rayNet, ['tcp', 'none', 'raw', ''], true)) {
$aTransport['type'] = $sV2rayNet;
}
if (!empty($aTransport)) {
$aNode['transport'] = $aTransport;
}
// 4. TLS 配置映射
$sTlsType = strtolower((string) ($aConfig['tls'] ?? ''));
if ($sTlsType === 'tls') {
$sServerName = !empty($sHost) ? $sHost : $aNode['server'];
// 兼容类内 parseServerName 方法
if (method_exists($this, 'parseServerName')) {
$sServerName = $this->parseServerName($sServerName, $sServerName);
}
$aNode['tls'] = [
'enabled' => true,
'server_name' => $sServerName,
];
}
// 5. 替换配置并输出同等结构
$aV2ray['config'] = $aNode;
return $aV2ray;
}
// private function mod_to_vmess($aV2ray)
// {
// $aNode = [];
// $aConfig = $aV2ray["config"];
//
// $aNode["type"] = "vmess";
// $aNode["tag"] = $aV2ray['country'] ?? '';
// $aNode["server"] = $aConfig["add"] ?? '';
// $aNode["server_port"] = $aConfig["port"] ?? ''; // Cannot access offset of type string on string
// $aNode["server_port"] = (int) $aNode["server_port"];
// $aNode["uuid"] = $aConfig["id"] ?? '';
// $aNode["security"] = $aConfig["scy"] ?? 'auto';
// $aNode["alter_id"] = $aConfig["aid"] ?? 0; // 猜的
// $aNode["alter_id"] = (int) $aNode["alter_id"];
//
// $aTransport = [];
// if (isset($aConfig["net"]) && $aConfig["net"] != "none" && $aConfig["net"] != "raw") {
// $aTransport["type"] = $aConfig["net"];
// }
// if (isset($aConfig["path"])) {
// $aTransport["path"] = rawurldecode($aConfig["path"]);
// }
// if (isset($aConfig["host"])) {
// $aTransport["headers"]["Host"] = $aConfig["host"];
// }
// if (!empty($aTransport)) {
// if (empty($aTransport["type"])) {
// $aTransport["type"] = "tcp";
// }
// $aNode["transport"] = $aTransport;
// }
//
// $aTls = [];
// if (isset($aConfig["tls"])) {
// if ($aConfig["tls"] == "tls") {
// $aTls["server_name"] = !empty($aConfig['host']) ? $aConfig['host'] : $aConfig['add'];
// $aTls["server_name"] = $this->parseServerName($aTls["server_name"], $aTls["server_name"]);
// }
// }
// if (!empty($aTls)) {
// $aTls["enabled"] = true;
// $aNode["tls"] = $aTls;
// }
//
// $aV2ray["config"] = $aNode;
//
// return $aV2ray;
// } // 这是原版代码,不行。你给我重写。传入和返回值一致即可
private function mod_to_vless__($aV2ray)
{
$sConfig = $aV2ray['config'] ?? '';
$aTmp = explode("@", $sConfig, 2);
$sPwd = $aTmp[0] ?? ''; // *
$sConfig = $aTmp[1] ?? '';
$aTmp = explode("?", $sConfig, 2);
$sAddrFull = $aTmp[0];
$sConfig = $aTmp[1];
$aTmp = explode(":", $sAddrFull, 2);
$sAddr = $aTmp[0] ?? ''; // *
$sPort = $aTmp[1] ?? ''; // *
$aCustomTmp = explode("&", $sConfig);
$aCustom = [];
foreach ($aCustomTmp as $aCustomRow) {
$aTmp = explode("=", $aCustomRow, 2);
$k = $aTmp[0] ?? '';
$v = $aTmp[1] ?? '';
$aCustom[$k] = $v; // *
}
$aNode = [
'type' => 'vless',
'tag' => $aV2ray['country'] ?? '',
'server' => $sAddr,
'server_port' => (int) $sPort,
'uuid' => $sPwd,
];
if (isset($aCustom["packetEncoding"]) && $aCustom["packetEncoding"] != "none") {
$aNode["packet_encoding"] = $aCustom["packetEncoding"];
}
if (isset($aCustom["flow"])) {
$aNode["flow"] = $aCustom["flow"];
}
$aTransport = [];
if (isset($aCustom["type"]) && $aCustom["type"] != "raw") {
$aTransport["type"] = $aCustom["type"];
}
if (isset($aCustom["headerType"]) && $aCustom["headerType"] == "http") {
$aTransport["type"] = "http"; // 整个http覆盖默认tcp
}
if (isset($aCustom["path"])) {
$aTransport["path"] = rawurldecode($aCustom["path"]);
}
if (isset($aCustom["host"])) {
$aTransport["headers"]["Host"] = $aCustom["host"];
}
if (isset($aCustom["serviceName"])) {
$aTransport["service_name"] = $aCustom["serviceName"];
}
if (!empty($aTransport)) {
if (empty($aTransport["type"])) {
$aTransport["type"] = "tcp";
}
$aNode["transport"] = $aTransport;
}
$aTls = []; // 本体
$aUtls = []; // 二层
$aReality = []; // 二层
if (isset($aCustom["fp"])) { // 二层的utls构建
if ($aCustom["fp"] == "random") {
$aCustom["fp"] = "chrome";
}
$aUtls["fingerprint"] = $aCustom["fp"];
}
if (!empty($aUtls)) { // 二层的utls封装进tls
$aUtls["enabled"] = true;
$aTls["utls"] = $aUtls;
}
if (isset($aCustom["security"])) {
$sSecurity = $aCustom["security"];
if ($sSecurity == "reality") { // 二层的reality构建
if (isset($aCustom["pbk"])) {
$aReality["public_key"] = $aCustom["pbk"];
}
if (isset($aCustom["sid"])) {
$aReality["short_id"] = (string) $aCustom["sid"];
}
}
}
if (!empty($aReality)) { // 二层的reality封建进tls
$aReality["enabled"] = true;
$aTls["reality"] = $aReality;
}
if (isset($aCustom["alpn"])) {
$sTmp = $aCustom["alpn"] ?? "";
$aTmp = explode(",", $sTmp);
$aTls["alpn"] = $aTmp;
}
if (isset($aCustom["sni"])) { // 本体的tls构建
$aTls["server_name"] = $this->parseServerName($aCustom["sni"], $aCustom["sni"]);
}
if (isset($aCustom["encryption"]) && $aCustom["encryption"] != "none") { // 本体的tls构建
$aTls["encryption"] = $aCustom["encryption"];
}
if (!empty($aTls)) { // 本体的tls封装进node
$aTls["enabled"] = true;
$aNode["tls"] = $aTls;
}
$aV2ray["config"] = $aNode;
return $aV2ray;
}
private function tomBase64Decode(mixed $xInput): string
{
// 1. 防御性类型检查
if (!is_string($xInput) || $xInput === '') {
return (string) $xInput;
}
// 2. 开启严格模式解码
$sDecoded = base64_decode($xInput, true);
if ($sDecoded === false) {
return $xInput;
}
// 3. 反向验证:确保它不是像 humanity 这种恰好能被解码的纯普通文本
if (base64_encode($sDecoded) !== $xInput) {
return $xInput;
}
// 4. 核心:校验解码后的数据是否为合法 UTF-8 文本,且不包含不可打印控制字符(\x00-\x08, \x0B-\x0C, \x0E-\x1F
$bIsValidUtf8 = mb_check_encoding($sDecoded, 'UTF-8');
$bHasControlChars = (bool) preg_match('/[\x00-\x08\x0B\x0C\x0E-\x1F]/', $sDecoded);
if ($bIsValidUtf8 && !$bHasControlChars) {
return $sDecoded; // 解码出来是合法的正常文本(包含中文、英文、数字等)
}
// 解码出来包含真正的二进制控制符(乱码),说明输入原本就是纯字符串,返回原值
return rawurldecode($xInput);
}
public function test_allXieyi($cPoolV2ray)
{
$aXieyi = [];
foreach ($cPoolV2ray as $aV2ray) {
$sXieyi = $aV2ray["protocolType"];
$sXieyi = $this->removeUtf8Bom($sXieyi);
$iXieyi = $aXieyi[$sXieyi] ?? 0;
$iXieyi++;
$aXieyi[$sXieyi] = $iXieyi;
}
return $aXieyi;
}
private function removeUtf8Bom(string $sInput): string
{
if (str_starts_with($sInput, "\xEF\xBB\xBF")) {
return substr($sInput, 3);
}
return $sInput;
}
private function parseServerName(string $sSni, string $sFallbackServer): string
{
$sSni = trim($sSni);
$sSni = rawurldecode($sSni);
if (empty($sSni)) {
return $sFallbackServer;
}
// 针对包含 http:// 或 https:// 的情况进行解析
if (preg_match('#^https?://#i', $sSni)) {
$aUrl = parse_url($sSni);
$sSni = $aUrl['host'] ?? '';
}
// 剔除可能残余的路径与端口号 (例如 domain.com/path 或 domain.com:8080)
if (str_contains($sSni, '/')) {
$sSni = explode('/', $sSni)[0];
}
if (str_contains($sSni, ':')) {
$sSni = explode(':', $sSni)[0];
}
// 若提取后仍为空,回退到节点自身的 server 字段
return !empty($sSni) ? $sSni : $sFallbackServer;
}
}