dd/app/Services/ServiceFnPoolV2rayTo.php
2026-08-11 16:10:04 +08:00

535 lines
17 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($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;
}
}